Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android平台Gallery2應用分析(二)---線程池及應用入口分析

Android平台Gallery2應用分析(二)---線程池及應用入口分析

編輯:關於Android編程

ThreadPool 先看成員變量Executor mExecutor。
線程池的基本思想還是一種對象池的思想,開辟一塊內存空間,裡面存放了眾多(未死亡)的線程,池中線程執行調度由池管理器來處理。當有線程任務時,從池中取一個,執行完成後線程對象歸池,這樣可以避免反復創建線程對象所帶來的性能開銷,節省了系統的資源。
用線程池來管理的好處是,可以保證系統穩定運行,適用與有大量線程,高工作量的情景下使用,假如要展示1000張圖片如果創建1000個線程去加載,系統肯定會死掉。用線程池就可以避免這個問題,可以用5個線程輪流執行,5個一組,執行完的線程不直接回收而是等待下次執行,這樣對系統的開銷就可以減小不少。
=======================Executor的譯文部分==========================
Executor是Java工具類,執行提交給它的Runnable任務。該接口提供了一種基於任務運行機制的任務提交方法,包括線程使用詳細信息,時序等等。Executor通常用於替代創建多線程。例如:你可能會使用以下方式來代替創建線程集合中的線程new Thread(new(RunnableTask())).start()。

Executor executor = anExecutor;
 executor.execute(new RunnableTask1());
 executor.execute(new RunnableTask2());
 ...
盡管如此,Executor接口沒有明確要求執行過程是異步的。舉個最簡單的例子,一個Executor可以在調用者的線程中運行提交的任務。

 class DirectExecutor implements Executor {
     public void execute(Runnable r) {
         r.run();
     }
 }
更典型的是,任務也可以運行在其他的線程而不是調用者線程。以下代碼就是在Executor中生成新的線程。

 class ThreadPerTaskExecutor implements Executor {
     public void execute(Runnable r) {
         new Thread(r).start();
     }
 }
很多Executor的實現按照任務的實現方式和時間來分類,下面的代碼將提交的任務序列化給第二個Executor,闡述了一個組合的Executor。

 class SerialExecutor implements Executor {
   final Queue tasks = new ArrayDeque();
   final Executor executor;
   Runnable active;

   SerialExecutor(Executor executor) {
     this.executor = executor;
   
   public synchronized void execute(final Runnable r) {
     tasks.offer(new Runnable() {
       public void run() {
         try {
           r.run();
         } finally {
           scheduleNext();
         }
       }
     });
     if (active == null) {
       scheduleNext();
     }
   }

   protected synchronized void scheduleNext() {
     if ((active = tasks.poll()) != null) {
       executor.execute(active);
     }
   }
 }
}
以上代碼簡答講就是執行一個 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、 Future submit(Callable 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 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處理被拒絕的任務。
簡單的例子:

ThreadPoolTestMain.java
package threadpool.test;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class ThreadPoolTestMain {
	private static final int CORE_POOL_SIZE = 2;
	private static final int MAX_POOL_SIZE = 4;
	private static final int KEEP_ACTIVE_TIME = 3;
	private static final int TASK_NUM = 10;
	private static final int PRODUCE_SLEEP_TIME = 10;
	
	static public void main(String[] args) {
	// 構造一個線程池  
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, 
							MAX_POOL_SIZE, 
							KEEP_ACTIVE_TIME, 
							TimeUnit.SECONDS, 
							new ArrayBlockingQueue(3),  
							new ThreadPoolExecutor.DiscardOldestPolicy());
        
        for (int i = 1; i < TASK_NUM; i++) {
        	String name = "Task" + i;
        	try {
        		System.out.println("ThreadPoolTestMain: put a task: " + name);
        		threadPool.execute(new ThreadPoolTask(name));
        		Thread.sleep(20);
        	} catch (Exception err) {
        		err.printStackTrace();
        	}
        }
	}
}

ThreadPoolTask.java
package threadpool.test;

public class ThreadPoolTask implements Runnable {
	private String mTaskName;
	private static int CONSUME_SLEEP_TIME = 2000;
	
	public ThreadPoolTask(String name) {
		mTaskName = name;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(Thread.currentThread().getName());
		System.out.println("ThreadPoolTask :" + mTaskName);
		
        try {
            Thread.sleep(CONSUME_SLEEP_TIME);
        } catch (Exception err) {  
            err.printStackTrace();
        }
	}
}

GalleryAppImpl 在androidManifest.xml中注冊Application標簽,應用創建時就會被初始化,維護應用內部全局數據。
主要看幾個函數:initializeAsyncTask(), GalleryUtils.initialize(this),
    private void initializeAsyncTask() {
        // AsyncTask class needs to be loaded in UI thread.
        // So we load it here to comply the rule.
        try {
            Class.forName(AsyncTask.class.getName());
        } catch (ClassNotFoundException e) {
        }
    }
Class.forName(AsyncTask.class.getName())會返回AsyncTask這個類,並要求JVM查找並加載AsyncTask 類,也就是說JVM會執行AsyncTask類的靜態代碼段。
    public static void initialize(Context context) {
        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager wm = (WindowManager)
                context.getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getMetrics(metrics);
        sPixelDensity = metrics.density;
        Resources r = context.getResources();
        TiledScreenNail.setPlaceholderColor(r.getColor(
                R.color.bitmap_screennail_placeholder));
        initializeThumbnailSizes(metrics, r);
    }
GalleryUtil是Gallery的工具類,獲得了屏幕參數,WindowManager,Resource等。
onCreate中由於mStitchingProgressManager賦值為null,所以暫時未調用getDataManager()。這個函數下面會介紹到,主要用來管理Gallery的數據路徑層次。

Gallery 從launcher進入Gallery,圖庫應用會進入應用主入口Gallery.java。onCreate()中加載布局main.xml。
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        …...
        setContentView(R.layout.main);

        if (savedInstanceState != null) {
            getStateManager().restoreFromState(savedInstanceState);
        } else {
            initializeByIntent();
        }
    }
savedInstanceState是在Activity在onPause時保存狀態用的,這裡暫時為null。
    private void initializeByIntent() {
        Intent intent = getIntent();
        String action = intent.getAction();

        if (Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)) {
            startGetContent(intent);
        } else if (Intent.ACTION_PICK.equalsIgnoreCase(action)) {
            // We do NOT really support the PICK intent. Handle it as
            // the GET_CONTENT. However, we need to translate the type
            // in the intent here.
            Log.w(TAG, "action PICK is not supported");
            String type = Utils.ensureNotNull(intent.getType());
            if (type.startsWith("vnd.android.cursor.dir/")) {
                if (type.endsWith("/image")) intent.setType("image/*");
                if (type.endsWith("/video")) intent.setType("video/*");
            }
            startGetContent(intent);
        } else if (Intent.ACTION_VIEW.equalsIgnoreCase(action)
                || ACTION_REVIEW.equalsIgnoreCase(action)){
            startViewAction(intent);
        } else {
            startDefaultPage();
        }
}
從這個函數看,如果從相冊應用圖標進入,會走startDefaultPage流程,如果外部打開圖片,會走startGetContent流程。先看默認的startDefaultPage流程吧。
    public void startDefaultPage() {
        PicasaSource.showSignInReminder(this);
        Bundle data = new Bundle();
        data.putString(AlbumSetPage.KEY_MEDIA_PATH,
                getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
        getStateManager().startState(AlbumSetPage.class, data);
        mVersionCheckDialog = PicasaSource.getVersionCheckDialog(this);
        if (mVersionCheckDialog != null) {
            mVersionCheckDialog.setOnCancelListener(this);
        }
}
data裡面存了相冊頂層路徑:"/combo/{/mtp,/local/all,/picasa/all}"

歡迎轉載和技術交流,轉載請幫忙注明出處,http://blog.csdn.net/discovery_by_joseph,謝謝!


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved