天天看點

Android深入了解:Handler + Looper + MessageHandler + Looper + Message:生産者 + 消費者 + 倉庫(任務隊列)1.Handler(生産者add)2.HandlerThread3.IntentService4.Looper(消費者get and deal)5.Message(任務,産品)(Intent, Runnable, Message)6.MessageQueue(任務隊列,産品池)7.應用案例

聲明:本文是一篇對Handler相關内容的整理(經過相當一段時間,幾次内容增減),有相當部分内容來源網絡,其中融入部分作者本身的了解,并加以整理。如有涉及到哪位老師的原作,在此深表感謝!

目錄

目錄

Handler + Looper + Message:生産者 + 消費者 + 倉庫(任務隊列)

1.Handler(生産者add)

1.1.Handler建立對象

1.2.Handler發送消息

1.3.Handler處理消息

1.4.Handler移除消息

2.HandlerThread

2.1.定義:

2.2.适用場景:

2.3.源碼:

2.4.DEMO:

3.IntentService

3.1.定義:

3.2.源碼:

3.3.DEMO:

3.3.1.多檔案下載下傳:

3.3.2.廣播更新UI:

3.4.總結:

4.Looper(消費者get and deal)

4.1.定義:

4.2.機制:

4.3.應用:

1.ActivityThread

2.ActivityThread響應消息

3.ApplicationThread

4.IApplicationThread

5.App啟動流程

4.4.quit:

5.Message(任務,産品)(Intent, Runnable, Message)

5.1.定義:

5.2.關鍵屬性:

5.3.構造方式:

6.MessageQueue(任務隊列,産品池)

6.1.定義:

7.應用案例

7.1.子線程發消息給子線程

7.2.主線程發消息給子線程

7.3.子線程發消息給主線程

7.4.AsyncTask

Handler + Looper + Message:生産者 + 消費者 + 倉庫(任務隊列)

作者淺見。

Handler和Looper是多對一的關系,Looper和MessageQueue是一對一的關系。一個線程中,隻有一個Looper 和 MessageQueue,但可以有多個Handler。Handler發送消息時,本身作為關聯的target存入消息,Looper擷取消息時,最終調用消息關聯的Handler進行處理。

1.Handler(生産者add)

Handler:線程間通訊機制,是由Looper和MessageQueue來建構消息機制的。Handler發送消息給MessageQueue,并處理Looper分發過來的消息。

線程間通訊:

Handler在某線程把Message或者Runnable發送到目标線程的MessageQueue,然後由目标線程Looper對MessageQueue循環排程,再召喚發送消息的Handler進行處理。這個時候,處理過程:

@Override

public void handleMessage(Message msg) {
    super.handleMessage(msg);
}
           

将在Looper所屬線程(目标線程)執行,即完成線程切換。

1.1.Handler建立對象

每個Handler對象都會綁定一個Looper對象,每個Looper對象對應一個消息隊列(MessageQueue)。如果在建立Handler時不指定與其綁定的Looper對象,系統預設會将目前線程的Looper綁定到該Handler上。

在主線程中(系統會為主線程自動建立Looper對象,開啟消息循環)可以直接使用new Handler() 建立Handler對象,自動與主線程的Looper對象綁定。

new Handler() 等價于new Handler(Looper.myLooper())。Looper.myLooper():擷取目前程序的looper對象,類似的 Looper.getMainLooper() 用于擷取主線程的Looper對象。

在非主線程中直接這樣建立Handler則會報錯,因為Android系統預設情況下非主線程中沒有開啟Looper,而Handler對象必須綁定Looper對象。這種情況下,需先在該線程中手動開啟Looper(Looper.prepare()-->Looper.loop()),然後将其綁定到Handler對象上;或者通過Looper.getMainLooper(),獲得主線程的Looper,将其綁定到此Handler對象上。

建立Handler對象,還可以通過傳入Callback接口類型方式建立。

public Handler(Callback callback) {
    this(callback, false);
}
public interface Callback {
    public boolean handleMessage(Message msg);
}
           

1.2.Handler發送消息

Handler發送的消息都會加入到Looper的MessageQueue中。Handler隻有一個消息隊列,即MessageQueue。

關于:

handler.post(new Runnable() {
      @Override
      public void run() {
                        
      }
});
           

通過post()傳進去的Runnable對象将會被封裝成消息對象後傳入MessageQueue;使用Handler.sendMessage()将消息對象直接加入到消息隊列中。

使用post()将Runnable對象放到消息隊列中後,當Looper輪循到該Runnable執行時,實際上并不會單獨開啟一個新線程,而仍然在目前Looper綁定的線程中執行,Looper隻是調用了該線程對象的run()而已。如,在子線程中定義了更新UI的指令,若直接開啟将該線程執行,則會報錯;而通過post()将其加入到主線程的Looper中并執行,就可以實作UI的更新。

使用sendMessage()将消息對象加入到消息隊列後,當Looper輪詢到該消息時,就會調用Handler的handleMessage()來對其進行處理。再以更新UI為例,使用這種方法的話,就先将主線程的Looper綁定在Handler對象上,重載handleMessage()來處理UI更新,然後向其發送消息就可以了。

如果Handler對象與其調用者在同一線程中,如果在Handler的消息處理方法中設定了延時操作,則調用線程也會堵塞,因為Looper輪循是線性的,是以Handler處理Message的過程也是線性的。

1.3.Handler處理消息

Handler 在處理消息時,會有三種情況:

if :msg.callback 不為空

這在使用 Handler.postXXX(Runnable) 發送消息的時候會發生,直接調用 Runnable 的 run() 方法。

else if :mCallback 不為空

如果構造Handler時候以 Handler.Callback 為參數構造 Handler 時會發生,CallBack接口方法作為消息處理方法替代Handler.handleMessage() 執行處理操作。

Handler handler = new Handler(new Handler.Callback() {

    @Override
    public boolean handleMessage(Message message) {
        return false;
    }
});
           

else :最後就調用 Handler.handleMessage() 方法

需要我們在 Handler 子類裡重寫

1.4.Handler移除消息

public final void removeCallbacks(Runnable r){

    mQueue.removeMessages(this, r, null);

}

public final void removeMessages(int what) {

    mQueue.removeMessages(this, what, null);

}
           

2.HandlerThread

2.1.定義:

HandlerThread 是一個包含 Looper 的 Thread,我們可以直接使用這個 Looper 建立 Handler。避免在子線程中對Looper手動繁瑣的操作(Looper.prepare() ——> Looper.loop()),讓我們可以直接線上程中使用 Handler 來處理異步任務。

HandlerThread 本身是一個Thread,需要start()啟動。

HandlerThread 在run() 方法中為本線程建立了Looper(Looper.prepare()),調用 onLooperPrepared 後開啟了循環(Looper.loop())

HandlerThread 需要在子類中重寫 onLooperPrepared,做Looper啟動前的初始化工作

HandlerThread 可以指定優先級,注意這裡的參數是 Process.XXX 而不是 Thread.XXX

HandlerThread = Thread + Looper,适合在子線程中執行耗時的、可能有多個任務的操作的場景,比如說多個網絡請求操作,或者多檔案 I/O 等等。

2.2.适用場景:

為某個任務 / 回調單獨開啟線程,并提供由Handler + Looper提供任務(Message)排程機制。

個人了解:HandlerThread是一個可以通過Looper + Message 實作在子線程(Looper 和 MessageQueue在子線程,是以Looper循環消息,觸發消息處理方法也是在子線程)中依次循環執行任務的線程類。

2.3.源碼:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    /**
     * 子類需要重寫的方法,在這裡做一些執行前的初始化工作
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }
    //調用 start() 後就會執行的 run()
    @Override
    public void run() {
        mTid = Process.myTid();
	//建立了 Looepr
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();//Looper 已經建立,喚醒阻塞在擷取 Looper 的線程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();//開始循環
        mTid = -1;
    }
    /**
     * 擷取目前線程的 Looper
     * 如果線程不是正常運作的就傳回 null
     * 如果線程啟動後,Looper 還沒建立,就 wait() 等待 建立 Looper 後 notify
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason isAlive() returns false, this method will return null. If this thread
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    /**
     * @return a shared {@link Handler} associated with this thread
     * @hide
     */
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
    /**
     * Quits the handler thread's looper.
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     *
     * @see #quitSafely
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    /**
     * Quits the handler thread's looper safely.
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}
           

2.4.DEMO:

使用 HandlerThread 實作子線程完成多個下載下傳任務。

DownloadThread,它有兩個 Handler 類型的成員變量,一個是用于在子線程傳遞、執行任務,另一個用于外部傳入,在主線程顯示下載下傳狀态:

public class DownloadThread extends HandlerThread implements Handler.Callback {
    private final String KEY_URL = "url";
    public static final int TYPE_START = 1;
    public static final int TYPE_FINISHED = 2;
    /**
     * 外部傳入,通知主線程顯示下載下傳狀态
     */
    private Handler mUIHandler;
    /**
     * 内部建立,子線程傳遞、執行任務
     */
    private Handler mWorkerHandler;
    /**
     * download list
     */
    private List<String> mDownloadUrlList;
    public DownloadThread(final String name) {
        super(name);
    }
    /**
     * 執行初始化任務
     */
    @Override
    protected void onLooperPrepared() {
        super.onLooperPrepared();
        mWorkerHandler = new Handler(getLooper(), this);
        if (mUIHandler == null) {
            throw new IllegalArgumentException("No UIHandler!");
        }
        // 将接收到的任務消息挨個添加到消息隊列中
        for (String url : mDownloadUrlList) {
            Message message = mWorkerHandler.obtainMessage();
            Bundle bundle = new Bundle();
            bundle.putString(KEY_URL, url);
            message.setData(bundle);
            mWorkerHandler.sendMessage(message);
        }
    }
    public void setDownloadUrls(String... urls) {
        mDownloadUrlList = Arrays.asList(urls);
    }
    /**
     * 擷取主線程 Handler
     */
    public Handler getUIHandler() {
        return mUIHandler;
    }
    /**
     * 注入主線程 Handler
     */
    public DownloadThread setUIHandler(final Handler UIHandler) {
        mUIHandler = UIHandler;
        return this;
    }
    /**
     * 子線程中執行任務,完成後發送消息到主線程
     */
    @Override
    public boolean handleMessage(final Message msg) {
        if (msg == null || msg.getData() == null) {
            return false;
        }
        String url = (String) msg.getData().get(KEY_URL);
        //下載下傳開始,通知主線程
        Message startMsg = mUIHandler.obtainMessage(TYPE_START, "\n 開始下載下傳 @" + url);
        mUIHandler.sendMessage(startMsg);
        SystemClock.sleep(2000);    //模拟下載下傳
        //下載下傳完成,通知主線程
        Message finishMsg = mUIHandler.obtainMessage(TYPE_FINISHED, "\n 下載下傳完成 @" + url);
        mUIHandler.sendMessage(finishMsg);
        return true;
    }
    @Override
    public boolean quitSafely() {
        mUIHandler = null;
        return super.quitSafely();
    }
}
           

建立一個子線程 mWorkerHandler,在 onLooperPrepared()中初始化 Handler,使用的是 HandlerThread 建立的 Looper 。同時将外部傳入的下載下傳 url 以 Message 的方式發送到子線程中的 MessageQueue 中。

當調用 DownloadThread.start() 時,子線程中的 Looper 開始工作,會按順序取出消息隊列中的隊列處理,然後調用子線程的 Handler 處理(handleMessage()),在這個方法中進行耗時任務,然後通過 mUIHandler 将下載下傳狀态資訊傳遞到主線程

在外部Activity(Fragment)實作Hander.Callback接口,調用:

mUIHandler = new Handler(this);
mDownloadThread = new DownloadThread("下載下傳線程");
mDownloadThread.setUIHandler(mUIHandler);
mDownloadThread.setDownloadUrls("http://pan.baidu.com/s/1qYc3EDQ",
        "http://bbs.005.tv/thread-589833-1-1.html", 
        "http://list.youku.com/show/id_zc51e1d547a5b11e2a19e.html?");
mDownloadThread.start();
           

同時在Activiy的onDestroy()方法中退出下載下傳線程:

mDownloadThread.quitSafely();
           

3.IntentService

3.1.定義:

IntentService 是一個抽象類,本身是一個Service,同時又在内部建立了一個HandlerThread 。IntentService 使用工作線程逐一處理所有啟動請求。如果你不需要在 Service 中執行并發任務,IntentService 是最好的選擇。

3.2.源碼:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    //内部建立的 Handler
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            //調用這個方法處理資料
            onHandleIntent((Intent) msg.obj);
            //處理完Service就自盡了
            stopSelf(msg.arg1);
        }
    }
    //子類需要重寫的構造函數,參數是服務的名稱
    public IntentService(String name) {
        super();
        mName = name;
    }
    //設定目前服務被意外關閉後是否重新啟動
    //如果設定為 true,onStartCommand() 方法将傳回 Service.START_REDELIVER_INTENT,這樣當
    //目前程序在 onHandleIntent() 方法傳回前銷毀時,會重新開機程序,重新使用之前的 Intent 啟動這個服務
    //(如果有多個 Intent,隻會使用最後的一個)
    //如果設定為 false,onStartCommand() 方法傳回 Service.START_NOT_STICKY,當程序銷毀後也不重新開機服務
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        //建立時啟動一個 HandlerThread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        //拿到 HandlerThread 中的 Looper,然後建立一個子線程中的 Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //将 intent 和 startId 以消息的形式發送到 Handler
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     *
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    @Override
    public void onDestroy() {
        mServiceLooper.quit();    //值得學習的,在銷毀時退出 Looper
    }
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}
           

源代碼做了以下幾件事情:

(1)建立了一個 HandlerThread 預設的工作線程

(2)使用 HandlerThread 的 Looper 建立了一個 Handler,這個 Handler 執行在子線程

(3)在 onStartCommand() 中調用 onStart(),然後在 onStart() 中将 intent 和 startId 以消息的形式發送到 Handler

(4)在 Handler 中将消息隊列中的 Intent 按順序傳遞給 onHandleIntent() 

(5)在處理完所有啟動請求後自動停止服務,不需要我們調用 stopSelf()

可以看到在 handleMessage 方法中隻調用了一次onHandleIntent() 之後就調用 stopSelf() 了,那麼說,該Handler隻能執行一個任務麼?

非也!stopSelf() 方法傳遞了一個 id,這個 id 是啟動服務時 IActivityManager 配置設定的 id,當我們調用 stopSelf(id) 方法結束服務時,IActivityManager 會對比目前 id 是否為最新啟動該服務的 id,如果是就關閉服務。子線程中onHandleIntent方法經曆了耗時操作,當本次stopSelf(id)調用之前,很可能onStartCommand會多次調用,每次調用都會生成新的啟動id,是以本次stopSelf傳入的id,很可能已經不是最新啟動服務的id了。

是以隻有當最後一次啟動 IntentService 的任務執行完畢才會關閉這個服務。

注意:隻有onHandleIntent方法是執行在子線程的,因為處理消息的looper是子線程HandlerThread提供的looper。其餘方法均在主線程運作。

由于最終每個任務的處理都會調用 onHandleIntent(),是以使用 IntentService 也很簡單,隻需實作 onHandleIntent() 方法,在這裡執行對應的背景工作即可。

3.3.DEMO:

3.3.1.多檔案下載下傳:

/**
 * Description:使用 IntentService 實作下載下傳
 */
public class DownloadService extends IntentService {
    private static final String TAG = "DownloadService";
    public static final String DOWNLOAD_URL = "down_load_url";
    public static final int WHAT_DOWNLOAD_FINISHED = 1;
    public static final int WHAT_DOWNLOAD_STARTED = 2;
    public DownloadService() {
        super(TAG);
    }
    private static Handler mUIHandler;
    public static void setUIHandler(final Handler UIHandler) {
        mUIHandler = UIHandler;
    }
    /**
     * 這個方法運作在子線程
     *
     * @param intent
     */
    @Override
    protected void onHandleIntent(final Intent intent) {
        String url = intent.getStringExtra(DOWNLOAD_URL);
        if (!TextUtils.isEmpty(url)) {
            sendMessageToMainThread(WHAT_DOWNLOAD_STARTED, "\n " + DateUtils.getCurrentTime() + " 開始下載下傳任務:\n" + url);
            try {
                Bitmap bitmap = downloadUrlToBitmap(url);
                SystemClock.sleep(1000);    //延遲一秒發送消息
                sendMessageToMainThread(WHAT_DOWNLOAD_FINISHED, bitmap);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 發送消息到主線程
     *
     * @param id
     * @param o
     */
    private void sendMessageToMainThread(final int id, final Object o) {
        if (mUIHandler != null) {
            mUIHandler.sendMessage(mUIHandler.obtainMessage(id, o));
        }
    }
    /**
     * 下載下傳圖檔
     *
     * @param url
     * @return
     * @throws Exception
     */
    private Bitmap downloadUrlToBitmap(String url) throws Exception {
        HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
        BufferedInputStream in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
        Bitmap bitmap = BitmapFactory.decodeStream(in);
        urlConnection.disconnect();
        in.close();
        return bitmap;
    }
}
           

主線程調用:

public void downloadImage() {
    DownloadService.setUIHandler(new Handler(this));
    Intent intent = new Intent(this, DownloadService.class);
    for (String url : urlList) {
        intent.putExtra(DownloadService.DOWNLOAD_URL, url);
        startService(intent);
    }
    mBtnDownload.setEnabled(false);
}
           

(1)設定 UI 線程的 Handler 給 IntentService

(2)使用 startService(intent) 啟動 IntentService 執行圖檔下載下傳任務

(3)在 Handler 的 handleMessage 中根據消息類型進行相應處理

在第一次啟動 IntentService 後,IntentService 仍然可以接受新的請求,接受到的新的請求被放入了工作隊列中,等待被串行執行。

3.3.2.廣播更新UI:

public class MyIntentService extends IntentService {
    /**
     * 是否正在運作
     */
    private boolean isRunning;
    /**
     *進度
     */
    private int count;
    /**
     * 廣播
     */
    private LocalBroadcastManager mLocalBroadcastManager;
    public MyIntentService() {
        super("MyIntentService");
        Logout.e("MyIntentService");
    }
    @Override
    public void onCreate() {
        super.onCreate();
        Logout.e("onCreate");
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        Logout.e("onHandleIntent");
        try {
            Thread.sleep(1000);
            isRunning = true;
            count = 0;
            while (isRunning) {
                count++;
                if (count >= 100) {
                    isRunning = false;
                }
                Thread.sleep(50);
                sendThreadStatus("線程運作中...", count);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /**
     * 發送進度消息
     */
    private void sendThreadStatus(String status, int progress) {
        Intent intent = new Intent(IntentServiceActivity.ACTION_TYPE_THREAD);
        intent.putExtra("status", status);
        intent.putExtra("progress", progress);
        mLocalBroadcastManager.sendBroadcast(intent);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        Logout.e("線程結束運作..." + count);
    }
}
           

3.4.總結:

IntentService是一個串行執行異步任務、會自盡的 Service,優先級比較高,在背景不會輕易被系統殺死;它可以接收多個 Intent 請求,然後在子線程中按順序執行。

适合于執行由UI觸發的處理多任務的背景Service任務,并可以把背景任務執行的情況通過一定的機制回報給UI。

IntentService,在onHandlerIntent()的回調裡面來處理塞到IntentService的任務。是以IntentService就不僅僅具備了異步線程的特性,還同時保留了Service不受首頁面生命周期影響的特點。我們可以在IntentService裡面通過設定鬧鐘間隔性的觸發異步任務,例如重新整理資料,更新緩存的圖檔等。

首先,因為IntentService内置的是HandlerThread作為異步線程,是以每一個交給IntentService的任務都将以隊列的方式逐個被執行到,一旦隊列中有某個任務執行時間過長,那麼就會導緻後續的任務都會被延遲處理。

其次,通常使用到IntentService的時候,我們會結合使用BroadcastReceiver把工作線程的任務執行結果傳回給主UI線程。使用廣播容易引起性能問題,我們可以使用LocalBroadcastManager來發送隻在程式内部傳遞的廣播,進而提升廣播的性能。我們也可以使用runOnUiThread()快速回調到主UI線程。

最後,包含正在運作的IntentService的程式相比起純粹的背景程式更不容易被系統殺死,該程式的優先級是介于前台程式與純背景程式之間的。

4.Looper(消費者get and deal)

4.1.定義:

消息循環管理器。每個線程隻有一個Looper,負責管理MessageQueue, 以無限循環的方式去查找MessageQueue中是否有新消息。如果有,MessageQueue中取出消息,并将消息分給對應的Handler處理;如果沒有就standby(等待)。一個線程建立Handler時首先需要建立Looper的,不然報錯:RuntimeException: No Looper; Looper.prepare() wasn't called on this thread,而且每個線程下隻能建立一個Looper,不然會報錯:RuntimeException: Only one Looper may be created per thread。

4.2.機制:

線程中預設沒有 Looper,我們需要調用 Looper.prepare() 方法為目前線程建立一個 Looper,調用Looper.loop()來使消息循環起作用,使用Looper.prepare()和Looper.loop()就可以讓消息處理在目前線程(主線程預設開啟,不需要手動調用)中完成。

Looper.loop()之後,進入無限循環:

for (;;) {    //無限循環模式
    Message msg = queue.next(); //從消息隊列中讀取消息,可能會阻塞
    if (msg == null) {    //當消息隊列中沒有消息時就會傳回,不過這隻發生在 queue 退出的時候
        return;
    }
    //...
    try {
        msg.target.dispatchMessage(msg);    //調用消息關聯的 Handler 處理消息
    } finally {
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);
        }
    }
    //...
    msg.recycleUnchecked();    //标記這個消息被回收
}
           

可以看到,loop()其實是調用 MessageQueue.next() 方法取消息,如果沒有消息的話會阻塞,直到有新的消息進入或者消息隊列退出。

拿到消息後Looper 并沒有執行消息,而是調用消息關聯的 Handler (target)處理消息,真正執行消息的還是添加消息到隊列中的那個 Handler。

是以應該說Looper屬于哪個線程的,消息處理就在哪個線程執行!!!

4.3.應用:

首先,Android 本身是由事件驅動的,在主線程中…… looper.loop() 不斷地接收事件、處理事件。四大元件的排程、輸入事件、繪制請求、每一個點選觸摸或者說Activity的生命周期都是運作在 Looper.loop() 的控制之下,如果它停止了,應用也就停止了。

隻能是UI線程中某一個消息或者說對消息的處理阻塞了 Looper.loop(),而不能是 Looper.loop() 阻塞UI線程。也可以了解為,UI線程中執行的代碼其實是執行在looper.loop()的循環周期中。

主線程同一時間隻能處理一個 Message,是以,如果UI線程中某個消息處理時間過長(點選事件業務處理操作),那麼下一次的消息比如使用者的點選事件不能處理了,整個循環就會産生卡頓,時間一長就成了ANR。

UI線程的消息處理機制(系統級)(簡單分析):

特别參考:https://blog.csdn.net/hzwailll/article/details/85339714

1.ActivityThread

ActivityThread通過 ApplicationThread 與 AMS(ActivityManagerService) 進行程序通信

(1)ActivityThread可以代表App主線程(UI線程),但是ActivityThread并不是一個Thread,嚴格意義來講,ActivityThread是初始化App程序的類,它的main() 方法是App程序初始化的入口。

也就是說,ActivityThread初始化了App程序,同時ActivityThread将作為App的初始線程,App程序内所有線程都是由這個初始線程啟動的。但是ActivityThread初始化App程序的代碼(main入口),其實是在一個實際存在的Main線程中被調用執行的。

是以ActivityThread可以代表App的主線程(UI線程,即初始線程),但是App真實的主線程是隐藏起來不可見的Main線程。

1,ActivityThread.main()

main() 方法主要做了以下幾件事情:

public static void main(String[] args) {

//....
    //建立Looper和MessageQueue對象,用于處理主線程的消息
    Looper.prepareMainLooper();
    //建立ActivityThread對象
    ActivityThread thread = new ActivityThread();
    //建立Binder通道 (建立新線程)  在attach方法中會完成Application對象的初始化,然後調用Application的onCreate()方法
    thread.attach(false);
    if (sMainThreadHandler == null) {

     sMainThreadHandler = thread.getHandler();

}

Looper.loop(); //消息循環運作
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
           

2,Looper.prepareMainLooper()

主線程Looper初始化

/主線程Looper的初始化

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
//普通線程Looper的初始化
public static void prepare() {
    prepare(true);
}
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
           

quitAllowed:标記Looper是否允許退出,當調用quit方法時會判斷該參數,如果false,抛出異常。主線程(ActivityThread)Looper不允許退出;普通線程Looper允許退出。

sMainLooper:承載主線程(ActivityThread)唯一Looper對象,并通過Looper.getMainLooper()對外提供對象通路。

3,sMainThreadHandler

if (sMainThreadHandler == null) {

     sMainThreadHandler = thread.getHandler();

}
           

擷取ActivityThread的Handler,在ActivityThread中,Handler作為final類型的成員變量在ActivityThread建立對象的時候被初始化

//ActivityThread的成員變量

final H mH = new H();

so …… 主線程(ActivityThread)的初始化它的main方法中,在此之前main首先對MainLooper進行了初始化,主線程的Handler建立時機是在ActivityThread初始化的時候。

4,ApplicationThread及Activity的建立和啟動

ApplicationThread被定義為ActivityThread的私有内部類,實作了IBinder接口,用于ActivityThread和ActivityManagerService所在程序的程序間通訊。

final ApplicationThread mAppThread = new ApplicationThread();
           

ActivityThread的main方法調用了thread.attach(false); attach中将mAppThread 作為參數傳入ActivityManagerService:

final IActivityManager mgr = ActivityManager.getService();

try {
    mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
    throw ex.rethrowFromSystemServer();
} else {
        ...
}
           

即把ApplicationThread在遠端服務AMS進行注冊綁定,後續由AMS管理ActivityThread所有生命周期。

接下來,AMS 在attachApplication中做了兩件事:

(1)建立Application

AMS. attachApplication()方法中:通過mAppThread調用ApplicationThread的bindApplication 用于建立Application。該方法會向ActivityThread發送BIND_APPLICATION消息,最終的處理方法是調用Application.onCreate()。

ApplicationThread中的bindApplication方法

public final void bindApplication(...) {

    // ...
    sendMessage(H.BIND_APPLICATION, data);
    // ...
}
           

ActivityThread中的handleBindApplication方法

private void handleBindApplication(AppBindData data) {

    // ...
    try {
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;
        // ...
        try {
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
        }
    } finally {
    }
}
           

LoadedApk中的方法,用于建立Application

public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {

    // 如果存在Application的執行個體,則直接傳回,這也說明Application是個單例
    if (mApplication != null) {
        return mApplication;
    }
    Application app = null;
    // 這裡通過反射初始化Application
    // ...
    if (instrumentation != null) {
        try {
            //調用Application的onCreate方法
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
        }
    }
    return app;
}
           

(2)建立Activity

AMS. attachApplication()方法中:通過mStackSupervisor.attachApplicationLocked(app)建立Activity,mStackSupervisor是AMS的成員變量,為Activity堆棧管理輔助類執行個體,該方法最終會調用ApplicationThread類的scheduleLaunchActivity方法,該方法也是類似于第一步,向ActivityThread的消息隊列發送建立Activity的消息,最終在ActivityThread中完成建立Activity的操作。

(3)總結:

ActivityThread.main ——> 初始化Main Looper ——> create ActivityThead ——> 注冊ApplicationThread到AMS ——> AMS通過mAppThread建立Application(ApplicationThread. bindApplication() 和 ActivityThread通過Handler + Message + Looper實作)——> AMS通過mAppThread建立Activity(ApplicationThread. scheduleLaunchActivity () 和 ActivityThread通過Handler + Message + Looper實作)——> Looper.loop()(開啟消息循環,排程執行上述操作)

Application.onCreate() 永遠早于任何一個Activity.onCreate()

2.ActivityThread響應消息

ActivityThread的内部類 H 繼承于Handler,Activity的生命周期都是依靠ActivityThread的Looper.loop,當H 收到不同Message時則采用相應措施:

System_Server:通過ApplicationThreadProxy通過Binder調用ApplicationThread的 scheduleXXX系列方法

ApplicationThread:Handler跨線程發消息給ActivityThread

ActivityThread :sendMessage  ->  handleMessage  ->  handleXXXActivvity  ->  performXXXActivity。

(1)ActivityThread.H

public final class ActivityThread {

    // ... 
    final H mH = new H();
    private class H extends Handler {
        // ...聲明的一些常量
        public void handleMessage(Message msg) {
            // ...
            switch (msg.what) {
                // 針對不同的常量,做不同的業務處理
                case LAUNCH_ACTIVITY: {
                    // ...啟動一個Activity
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    // ...
                }
                break;
                case RELAUNCH_ACTIVITY: {
                    //...
                    handleRelaunchActivity(r);
                    // ...
                }
                break;
                case PAUSE_ACTIVITY: {
                    // ...
                    handlePauseActivity((IBinder) args.arg1, false,
                            (args.argi1 & USER_LEAVING) != 0, args.argi2,
                            (args.argi1 & DONT_REPORT) != 0, args.argi3);
                    maybeSnapshot();
                    // ...
                }
                break;
                // ...
            }
            // ...
        }
        private void maybeSnapshot() {
            //...這個方法主要統計snapshot
        }
    }
}
           

H 主要作用就是根據不同的情況處理各種業務,而且處理業務的方法一般是以handle開頭,handleXXX的格式:

handleActivityConfigurationChanged()

handleBindApplication()
handleBindService()
handleCancelVisibleBehind()
handleConfigurationChanged()
handleCreateService()
handleDestroyActivity()
handleDispatchPackageBroadcast()
handleLaunchActivity()
handleLowMemory()
handleMessage()
handleNewIntent()
handlePauseActivity()
handleReceiver()
handleRelaunchActivity()
handleResumeActivity()
handleSendResult()
handleServiceArgs()
handleStopActivity()
handleStopService()
           

上述方法有的又會調用到如下的performXXX系列方法完成最終的事件處理:

performDestroyActivity()

performDestroyActivity()
performLaunchActivity()
performNewIntents()
performPauseActivity()
performPauseActivity()
performRestartActivity()
performResumeActivity()
performStopActivity()
performStopActivityInner()
performUserLeavingActivity()
           

當msg.what == LAUNCH_ACTIVITY就是調用handleLaunchActivity方法啟動一個Activity,在handleLaunchActivity中又調用了performLaunchActivity方法來建立一個Activity執行個體,完成Activity的啟動。 handleLaunchActivity源碼如下:

(2)案例一:啟動一個Activity

當msg.what == LAUNCH_ACTIVITY就是調用handleLaunchActivity方法啟動一個Activity,在handleLaunchActivity中又調用了performLaunchActivity方法來建立一個Activity執行個體,完成Activity的啟動。 handleLaunchActivity源碼如下:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

    //...調用performLaunchActivity方法完成Activity的啟動
    Activity a = performLaunchActivity(r, customIntent);
    //...
}
           

(3)案例二:棧頂Activity的onPause過程

ActivityStack.startPausingLocked()

IApplicationThread.schedulePauseActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage();
ActivityThread.H.handleMessage()
ActivityThread.handlePauseActivity()
ActivityThread.performPauseActivity()
Activity.performPause()
Activity.onPause()
           

3.ApplicationThread

Android深入了解:Handler + Looper + MessageHandler + Looper + Message:生産者 + 消費者 + 倉庫(任務隊列)1.Handler(生産者add)2.HandlerThread3.IntentService4.Looper(消費者get and deal)5.Message(任務,産品)(Intent, Runnable, Message)6.MessageQueue(任務隊列,産品池)7.應用案例

ApplicationThread和啟動Application有關!

ApplicationThread是ActivityThread的内部類,繼承ApplicationThreadNative,也是一個Binder對象。ApplicationThread内部定義了一系列scheduleXXX方法,這些方法由其外部類ActivityThread的Binder遠端代理對象(ApplicationThreadProxy)調用,之後通過Handler發出相應消息給 H 來處理。

在此處它是作為server端等待client端的請求然後進行處理,最大的client就是AMS。

private class ApplicationThread extends ApplicationThreadNative {

    //...
    schedulePauseActivity()
    scheduleStopActivity()
    scheduleResumeActivity()
    scheduleSendResult()
    scheduleLaunchActivity()
    scheduleNewIntent()
    scheduleDestroyActivity()
    scheduleReceiver()
    scheduleCreateService()
    scheduleBindService()
    scheduleUnbindService()
    scheduleServiceArgs()
    scheduleStopService()
    bindApplication()
    scheduleConfigurationChanged()
    scheduleRegisteredReceiver()
    scheduleInstallProvider()
}
           

4.IApplicationThread

定義了一系列業務通訊接口方法:

public interface IApplicationThread extends IInterface {

    void schedulePauseActivity() throws RemoteException;
    void scheduleStopActivity() throws RemoteException;
    void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
    void scheduleSleeping() throws RemoteException;
    void scheduleResumeActivity() throws RemoteException;
    void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
    void scheduleLaunchActivity() throws RemoteException;
    void scheduleRelaunchActivity() throws RemoteException;
    void scheduleNewIntent() throws RemoteException;
    void scheduleReceiver() throws RemoteException;
    void scheduleCreateBackupAgent() throws RemoteException;
    void scheduleDestroyBackupAgent()throws RemoteException;
    void scheduleCreateService() throws RemoteException;
    void scheduleBindService() throws RemoteException;
    void scheduleUnbindService() throws RemoteException;
    void scheduleServiceArgs() throws RemoteException;
    void scheduleStopService() throws RemoteException;
    void bindApplication() throws RemoteException;
    void scheduleExit() throws RemoteException;
    void scheduleSuicide() throws RemoteException;
    void scheduleConfigurationChanged() throws RemoteException;
    //...
}
           

5.App啟動流程

安卓系統啟動流程:

加載BootLoader --> 初始化Kernel --> 啟動init程序(加載1000多個.class檔案) --> init程序fork出Zygote程序 --> Zygote程序fork出SystemServer程序 --> 啟動所有系統Service

說明:

(1)當ActivityManagerService、PackageManagerService、WindowManagerService啟動之後,系統開始啟動Launcher;

(2)SystemServer:系統程序,負責管理系統服務

(3)ActivityManagerServices(AMS):是一個服務端對象,負責所有的Activity的生命周期,AMS通過Binder與Activity通信,而AMS與Zygote之間是通過Socket通信

(4)ActivityThread:App主線程,它的main()方法是APP的真正入口

(5)ApplicationThread:一個實作了IBinder接口的ActivityThread私有内部類,負責ActivityThread和AMS程序間通信

App啟動流程:

Android深入了解:Handler + Looper + MessageHandler + Looper + Message:生産者 + 消費者 + 倉庫(任務隊列)1.Handler(生産者add)2.HandlerThread3.IntentService4.Looper(消費者get and deal)5.Message(任務,産品)(Intent, Runnable, Message)6.MessageQueue(任務隊列,産品池)7.應用案例

(1)點選桌面應用圖示 ——> Launcher通過Binder發送startActivity給system_server程序中的AMS,發送啟動請求

(2)AMS通過Socket請求Zygote孵化App程序

(3)Zygote fork出App程序,并執行ActivityThread的main方法,初始化主線程Looper,建立ActivityThread線程,初始化主線程Handler,初始化ApplicationThread用于和AMS通信互動

(4)ActivityThread通過Binder向AMS發起attachApplication請求,這裡實際上就是APP程序通過Binder調用sytem_server程序中AMS的attachApplication方法,将ApplicationThread對象與AMS綁定

(5)接下來,AMS通過Binder IPC向App程序發送bindApplication請求(實際調用了ApplicationThread的bindApplication,此時ApplicationThread通過Handler發送BIND_APPLICATION消息給ActivityThread),由ActivityThread完成初始化Application的操作:Application.onCreate()

(6)接下來,AMS通過Binder IPC向App程序發送scheduleLaunchActivity請求(實際調用了ApplicationThread的scheduleLaunchActivity,此時ApplicationThread通過Handler發送LAUNCH_ACTIVITY消息給ActivityThread),由ActivityThread通過反射機制,建立目标Activity并回調Activity.onCreate()

注意AMS和主線程并不直接通信,而是AMS和主線程的内部類ApplicationThread通過Binder通信,ApplicationThread再和主線程通過Handler消息互動。

到此,App便正式啟動,開始進入Activity生命周期,執行完onCreate/onStart/onResume方法,UI渲染後顯示APP主界面。

4.4.quit:

Looper開啟loop()之後,必須保證結束quit(),quitSafely()!!!

Looper 兩種結束方式的差別:

(1)quit():

立即回收連結清單中所有消息,并且禁止後續消息入隊列。在停止後如果 Handler 還發送消息,會傳回 false,表示入隊失敗,是以這個方法是不安全的。

(2)quitSafely():

将還未執行的消息回收掉,後續進入隊列的消息将不會被處理,同時标記消息隊列為退出狀态。Handler.sendMessage 也會傳回 false。當消息隊列被标記位退出狀态時,它的 next() 方法會傳回 null,于是 Looper.loop() 循環就結束了。

5.Message(任務,産品)(Intent, Runnable, Message)

5.1.定義:

Message可以承載任意類型的對象和描述資訊,可以被發送給 Handler。

5.2.關鍵屬性:

(1)public int what;                 // 用來辨別一個消息,接收消息方可以根據/它知道這個消息是做什麼的

(2)public int arg1;

         public int arg2;                  // 資料載體:int 型

(3)public Object obj;              // 資料載體: 對象型

(4)Bundle data;    // 資料載體:複雜對象

(5)Handler target; // 發送和處理消息關聯的 Handler

(6)private static final Object sPoolSync = new Object();

(7)private static Message sPool; // 回收消息連結清單

(8)private static int sPoolSize = 0;

5.3.構造方式:

new Message();

Message.obtain();  推薦

Handler.obtainMessage();   推薦

推薦使用後兩個,會從一個消息回收池裡擷取消息,而不是建立一個,這樣可以節省記憶體。原理:如果 sPool (回收消息連結清單)存在就從複用消息連結清單頭部取一個消息,然後重置它的标志位;如果不存在複用消息連結清單就建立一個消息。

一個消息在被 Looper 處理時或者移出隊列時會被辨別為 FLAG_IN_USE,然後會被加入回收的消息連結清單,這樣我們調用 Message.obtain() 方法時就可以從回收的消息池中擷取一個舊的消息,進而節約成本。

6.MessageQueue(任務隊列,産品池)

6.1.定義:

MessageQueue(先進先出):消息隊列,管理着一個 Message 的清單,Handlers 為它添加消息,Looper 從中取消息。。雖然名為隊列,但事實上它的内部存儲結構并不是真正的隊列,而是采用單連結清單的資料結構來存儲消息清單的,其中主要有插入enqueue()和從中拿走并删除next()兩個方法。

7.應用案例

寫在Looper.loop()之後的代碼不會被立即執行,這個函數内部是一個循環,當調用後mHandler.getLooper().quit()後,loop才會中止,其後的代碼才能得以運作。

警惕線程未終止造成的記憶體洩露;譬如在Activity中關聯了一個生命周期超過Activity的Thread,在退出Activity時切記結束線程。一個典型的例子就是HandlerThread的run方法是一個死循環,它不會自己結束,線程的生命周期超過了Activity生命周期,我們必須手動在Activity的銷毀方法中中調運thread.getLooper().quit();才不會洩露。

另外,注意不要在任何子線程持有 UI 元件或者 Activity 的引用。

7.1.子線程發消息給子線程

class TestThread extends Thread{
    @Override
    public void run() {
        super.run();
        // prepare MessageQueue and looper
        Looper.prepare();
        Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
// deal message……
            }
        };
handler.sendMessage(new Message())
        // start looper
        Looper.loop();
    }
}
           

7.2.主線程發消息給子線程

(1)子線程中建立持有子線程Looper的Handler

(2)主線程使用子線程中Handler發送消息,最終在子線程的Handler中執行。

7.3.子線程發消息給主線程

方案一:子線程中建立持有主線程Looper的Handler,由該Handler在子線程發送消息到主線程的MessageQueue,并由該Handler在主線程執行。

          Handler handler = new Handler(getMainLooper());

方案二:子線程持有主線程建立的Handler對象,并由該Handler在子線程發送消息到主線程的MessageQueue,并由該Handler在主線程執行。

7.4.AsyncTask

預設情況下,所有的AsyncTask任務都是被線性排程執行的,他們處在同一個任務隊列當中,按順序逐個執行。假設你按照順序啟動20個AsyncTask,一旦其中的某個AsyncTask執行時間過長,隊列中的其他剩餘AsyncTask都處于阻塞狀态,必須等到該任務執行完畢之後才能夠有機會執行下一個任務。

為了解決線性隊列等待的問題,我們可以使用AsyncTask.executeOnExecutor()強制指定AsyncTask使用線程池并發排程任務。

使用AsyncTask很容易導緻記憶體洩漏,一旦把AsyncTask寫成Activity的内部類的形式就很容易因為AsyncTask生命周期的不确定而導緻Activity發生洩漏。

繼續閱讀