[转]Android平台Gallery2应用分析(二)---线程池及应用入口分析

ThreadPool

先看成员变量Executor mExecutor。
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
用线程池来管理的好处是,可以保证系统稳定运行,适用与有大量线程,高工作量的情景下使用,假如要展示1000张图片如果创建1000个线程去加载,系统肯定会死掉。用线程池就可以避免这个问题,可以用5个线程轮流执行,5个一组,执行完的线程不直接回收而是等待下次执行,这样对系统的开销就可以减小不少。
=======================Executor的译文部分==========================
Executor是Java工具类,执行提交给它的Runnable任务。该接口提供了一种基于任务运行机制的任务提交方法,包括线程使用详细信息,时序等等。Executor通常用于替代创建多线程。例如:你可能会使用以下方式来代替创建线程集合中的线程new Thread(new(RunnableTask())).start()。

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. Executor executor = anExecutor;  
  2.  executor.execute(new RunnableTask1());  
  3.  executor.execute(new RunnableTask2());  
  4.  ...  

尽管如此,Executor接口没有明确要求执行过程是异步的。举个最简单的例子,一个Executor可以在调用者的线程中运行提交的任务。

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. class DirectExecutor implements Executor {  
  2.     public void execute(Runnable r) {  
  3.         r.run();  
  4.     }  
  5. }  

更典型的是,任务也可以运行在其他的线程而不是调用者线程。以下代码就是在Executor中生成新的线程。

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. class ThreadPerTaskExecutor implements Executor {  
  2.     public void execute(Runnable r) {  
  3.         new Thread(r).start();  
  4.     }  
  5. }  

很多Executor的实现按照任务的实现方式和时间来分类,下面的代码将提交的任务序列化给第二个Executor,阐述了一个组合的Executor。

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1.  class SerialExecutor implements Executor {  
  2.    final Queue tasks = new ArrayDeque();  
  3.    final Executor executor;  
  4.    Runnable active;  
  5.   
  6.    SerialExecutor(Executor executor) {  
  7.      this.executor = executor;  
  8.      
  9.    public synchronized void execute(final Runnable r) {  
  10.      tasks.offer(new Runnable() {  
  11.        public void run() {  
  12.          try {  
  13.            r.run();  
  14.          } finally {  
  15.            scheduleNext();  
  16.          }  
  17.        }  
  18.      });  
  19.      if (active == null) {  
  20.        scheduleNext();  
  21.      }  
  22.    }  
  23.   
  24.    protected synchronized void scheduleNext() {  
  25.      if ((active = tasks.poll()) != null) {  
  26.        executor.execute(active);  
  27.      }  
  28.    }  
  29.  }  
  30. }  

以上代码简答讲就是执行一个 SerialExecutor时,先执行Runnable的run(),然后再从Tasks任务堆栈中找到当前激活的任务并执行。
在这个package包中实现的Executor实现了ExecutorService,它是个扩展接口。
而threadPoolExecutor类提供了一个扩展的线程池实现。Executors类给这些Executors提供了方便的工程方法。
内存一致性效果:在提交一个Runnable对象给Executor执行之前,线程中的行为可能先在另一个线程中发生。
=======================Executor的译文部分==========================

 

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
根据线程池的执行策略,Executor的execute()可能在新线程中执行,或者在线程池中的某个线程中执行,也可能是在调用者线程中执行。ExecutorService在Executor的基础上增加了两个核心方法:
1、Future<?> submit(Runnable task)
2、<T> Future<T> submit(Callable<T> task)
差异点:这两个方法都可以向线程池提交任务,区别在于Runnable执行完run()有返回值,而Callable执行完call()后有返回值。
共同点:submit都返回Future对象,Future对象可以阻塞线程直到运行完毕,也可以取消任务执行和检测任务是否执行完毕。
在executors类里面提供了一些静态工厂,生成一些常用的线程池:
1、newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2、newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3、newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
4、newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
5、newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
下面再介绍下ThreadPoolExecutor函数,以便对线程池有进一步认识:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler);
corePoolSize: 线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略
当一个任务通过execute(Runnable)方法欲添加到线程池时:
1、如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2、如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
也就是说,处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
简单的例子:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. ThreadPoolTestMain.java  
  2. package threadpool.test;  
  3.   
  4. import java.util.concurrent.ArrayBlockingQueue;  
  5. import java.util.concurrent.ThreadPoolExecutor;  
  6. import java.util.concurrent.TimeUnit;  
  7.   
  8.   
  9. public class ThreadPoolTestMain {  
  10.     private static final int CORE_POOL_SIZE = 2;  
  11.     private static final int MAX_POOL_SIZE = 4;  
  12.     private static final int KEEP_ACTIVE_TIME = 3;  
  13.     private static final int TASK_NUM = 10;  
  14.     private static final int PRODUCE_SLEEP_TIME = 10;  
  15.       
  16.     static public void main(String[] args) {  
  17.     // 构造一个线程池    
  18.         ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,   
  19.                             MAX_POOL_SIZE,   
  20.                             KEEP_ACTIVE_TIME,   
  21.                             TimeUnit.SECONDS,   
  22.                             new ArrayBlockingQueue<Runnable>(3),    
  23.                             new ThreadPoolExecutor.DiscardOldestPolicy());  
  24.           
  25.         for (int i = 1; i < TASK_NUM; i++) {  
  26.             String name = "Task" + i;  
  27.             try {  
  28.                 System.out.println("ThreadPoolTestMain: put a task: " + name);  
  29.                 threadPool.execute(new ThreadPoolTask(name));  
  30.                 Thread.sleep(20);  
  31.             } catch (Exception err) {  
  32.                 err.printStackTrace();  
  33.             }  
  34.         }  
  35.     }  
  36. }  
  37.   
  38. ThreadPoolTask.java  
  39. package threadpool.test;  
  40.   
  41. public class ThreadPoolTask implements Runnable {  
  42.     private String mTaskName;  
  43.     private static int CONSUME_SLEEP_TIME = 2000;  
  44.       
  45.     public ThreadPoolTask(String name) {  
  46.         mTaskName = name;  
  47.     }  
  48.       
  49.     @Override  
  50.     public void run() {  
  51.         // TODO Auto-generated method stub  
  52.         System.out.println(Thread.currentThread().getName());  
  53.         System.out.println("ThreadPoolTask :" + mTaskName);  
  54.           
  55.         try {  
  56.             Thread.sleep(CONSUME_SLEEP_TIME);  
  57.         } catch (Exception err) {    
  58.             err.printStackTrace();  
  59.         }  
  60.     }  
  61. }  

 

GalleryAppImpl

在androidManifest.xml中注册Application标签,应用创建时就会被初始化,维护应用内部全局数据。
主要看几个函数:initializeAsyncTask(), GalleryUtils.initialize(this),

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private void initializeAsyncTask() {  
  2.     // AsyncTask class needs to be loaded in UI thread.  
  3.     // So we load it here to comply the rule.  
  4.     try {  
  5.         Class.forName(AsyncTask.class.getName());  
  6.     } catch (ClassNotFoundException e) {  
  7.     }  
  8. }  

Class.forName(AsyncTask.class.getName())会返回AsyncTask这个类,并要求JVM查找并加载AsyncTask 类,也就是说JVM会执行AsyncTask类的静态代码段。

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public static void initialize(Context context) {  
  2.     DisplayMetrics metrics = new DisplayMetrics();  
  3.     WindowManager wm = (WindowManager)  
  4.             context.getSystemService(Context.WINDOW_SERVICE);  
  5.     wm.getDefaultDisplay().getMetrics(metrics);  
  6.     sPixelDensity = metrics.density;  
  7.     Resources r = context.getResources();  
  8.     TiledScreenNail.setPlaceholderColor(r.getColor(  
  9.             R.color.bitmap_screennail_placeholder));  
  10.     initializeThumbnailSizes(metrics, r);  
  11. }  

GalleryUtil是Gallery的工具类,获得了屏幕参数,WindowManager,Resource等。
onCreate中由于mStitchingProgressManager赋值为null,所以暂时未调用getDataManager()。这个函数下面会介绍到,主要用来管理Gallery的数据路径层次。

Gallery

从launcher进入Gallery,图库应用会进入应用主入口Gallery.java。onCreate()中加载布局main.xml。

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     …...  
  4.     setContentView(R.layout.main);  
  5.   
  6.     if (savedInstanceState != null) {  
  7.         getStateManager().restoreFromState(savedInstanceState);  
  8.     } else {  
  9.         initializeByIntent();  
  10.     }  
  11. }  

savedInstanceState是在Activity在onPause时保存状态用的,这里暂时为null。

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1.     private void initializeByIntent() {  
  2.         Intent intent = getIntent();  
  3.         String action = intent.getAction();  
  4.   
  5.         if (Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)) {  
  6.             startGetContent(intent);  
  7.         } else if (Intent.ACTION_PICK.equalsIgnoreCase(action)) {  
  8.             // We do NOT really support the PICK intent. Handle it as  
  9.             // the GET_CONTENT. However, we need to translate the type  
  10.             // in the intent here.  
  11.             Log.w(TAG, "action PICK is not supported");  
  12.             String type = Utils.ensureNotNull(intent.getType());  
  13.             if (type.startsWith("vnd.android.cursor.dir/")) {  
  14.                 if (type.endsWith("/image")) intent.setType("image/*");  
  15.                 if (type.endsWith("/video")) intent.setType("video/*");  
  16.             }  
  17.             startGetContent(intent);  
  18.         } else if (Intent.ACTION_VIEW.equalsIgnoreCase(action)  
  19.                 || ACTION_REVIEW.equalsIgnoreCase(action)){  
  20.             startViewAction(intent);  
  21.         } else {  
  22.             startDefaultPage();  
  23.         }  
  24. }  

从这个函数看,如果从相册应用图标进入,会走startDefaultPage流程,如果外部打开图片,会走startGetContent流程。先看默认的startDefaultPage流程吧。

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1.     public void startDefaultPage() {  
  2.         PicasaSource.showSignInReminder(this);  
  3.         Bundle data = new Bundle();  
  4.         data.putString(AlbumSetPage.KEY_MEDIA_PATH,  
  5.                 getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));  
  6.         getStateManager().startState(AlbumSetPage.class, data);  
  7.         mVersionCheckDialog = PicasaSource.getVersionCheckDialog(this);  
  8.         if (mVersionCheckDialog != null) {  
  9.             mVersionCheckDialog.setOnCancelListener(this);  
  10.         }  
  11. }  

data里面存了相册顶层路径:"/combo/{/mtp,/local/all,/picasa/all}"

 

欢迎转载和技术交流,转载请帮忙注明出处,http://blog.csdn.net/discovery_by_joseph,谢谢!

posted @ 2017-02-16 16:34  得即高歌失即休  阅读(148)  评论(0)    收藏  举报