handler消息處理機制

移動開發(fā)中,我們要處理好主線程與子線程之間的關系,耗時的操作應安排到子線程中,避免阻塞主線程,導致ANR
異步處理技術是提高應用性能,解決主線程和子線程之間通信的關鍵。
異步處理技術有很多種,常見的有Thread,AsyncTask,Handler&Looper,Executor等

QQ截圖20170729093531.png

Thread

線程是java的一個概念,實際是執(zhí)行任務的基本單元
創(chuàng)建線程的兩種方法
繼承Thread類并重寫run方法。

實現(xiàn)Runnable接口并實現(xiàn)run方法

Android中各種類型的線程本質上都是基于Linux系統(tǒng)的pthreads,在應用層可分為三種線程
主線程
主線程稱為UI線程,隨著應用啟動而啟動,主線程用來描述Android組件,同時刷新屏幕上的UI元素。
Android系統(tǒng)如果檢測到非主線程更新UI組件,那么會報出CalledFromWrongThreadException異常,只有在主線程才能操作UI,是因為Android的UI工具包不是線程安全的。主線程中創(chuàng)建Handler會順序執(zhí)行接收到的消息,包括從其他線程發(fā)送的消息。
Binder線程
Binder線程用于不同進程間線程通信,每個進程都維護了一個線程池,用來處理其他進程中線程發(fā)送消息,這些包括系統(tǒng)服務,intents,ContentProviders,和service等
應用不需要關心需要Binder線程,因為系統(tǒng)會優(yōu)先將請求轉換為只用主線程。
一個典型的需要使用Bnder線程的場景是應用提供一個給其他進程通過AIDL接口綁定的service
后臺線程
在應用中顯示創(chuàng)建的線程都是后臺線程,也就是當剛創(chuàng)建出來時,這些線程的執(zhí)行體是空的,需要手動添加任務在Linux系統(tǒng)層面,主線程和后臺線程是一樣的。在Android框架中,通過WindowManager賦予了主線程只能處理UI更新以及后臺線程不能直接操作UI的限制。

HandlerThread

HandlerThread集成了Looper和MessageQueue的線程,并啟動HandlerThread時,會共同生成Looper和MessageQueue,然后等待消息進行處理,它的run方法如下

使用HandlerThread的好處是開發(fā)者不需要自己創(chuàng)建和維護Looper

HandlerThread handlerThread = new HandlerThread("123");
handlerThread.start();

handler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

Handler中只有一個消息隊列,隊列中消息的是順序執(zhí)行的,因此是線程安全的,吞吐量會收到一定影響,隊列中的任務可能會被前面沒有執(zhí)行完的任務阻塞。
HandlerThread的內部機制確保創(chuàng)建Looper和發(fā)送消息之間不存在競態(tài)條件,這個是通過將HandlerThread.getLooper()實現(xiàn)為一個阻塞操作,只有當HandlerThread準備好接受消息之后才返回。

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        //如果線程已經啟動,那么looper準備好之前應先等待
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

如果具體的業(yè)務要求在HandlerThread開始接受消息之前要進行默寫初始化操作,可以重寫HandlerThread的onLooperPrepared函數(shù)。

intentService

intentService具有service一樣的生命周期,同時也提供了后臺線程中處理異步機制。
intentService在一個后臺線程中順序執(zhí)行所有任務。
通過Context.startService傳遞一個Intent類型的參數(shù)啟動intentService的異步執(zhí)行。
如果此時intentService正在運行中,那么這個新的intent將會進入隊列進行排隊,直到后臺處理完隊列前面的任務;
如果此時intentService沒有在運行,那么將會啟動一個新的intentService,
當后臺線程隊列的所有任務執(zhí)行完成之后,intentService將結束它的生命周期,因此intentService不需要手動結束。
intentService是通過HandlerThread來實現(xiàn)后臺處理任務。

使用
intentService是一個抽象類,使用前需要繼承它并實現(xiàn)onHandlerIntent方法,這個方法中實現(xiàn)具體的后臺處理業(yè)務邏輯,同時需要在子類的構造方法中需要調用super(String name)傳入子類的名字。
語法如下

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super(MyIntentService.class.getName());
        setIntentRedelivery(true);
        //setIntentRedelivery設置為true,那么intentService的onStartCommand方法返回START_REDELIVER_INTENT.
        //如果onHandleIntent返回之前進程死掉了,那么進程會重新啟動,intent將會重新投遞。
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        //這個方法是在后臺線程中調用
    }
}

當然在AndroidManifest.xml文件中進行注冊

Executor

創(chuàng)建和銷毀對象們都是存在開銷的。
如果應用中頻繁的創(chuàng)建和銷毀線程,改善應用體驗。
Executor的框架定義是一個名為Executor接口定義,Executor主要目的是分離任務的創(chuàng)建和它的執(zhí)行。

public interface Executor {
    void execute(Runnable command);
}

可以自己實現(xiàn)Executor并重寫execute直接創(chuàng)建線程來執(zhí)行Runnable

public class MyExecutor implements Executor {
    @Override
    public void execute(Runnable command) {
        new Thread(command).start();
    }
}

預定義線程池

實際開發(fā)中execute,需要增加類似隊列,任務優(yōu)先級等功能,最終實現(xiàn)一個線程池。

ThreadPoolExecutor

預定義線程池都是基于ThreadPoolExecutor 類上創(chuàng)建的,通過ThreadPoolExecutor 開發(fā)者可以自定義線程池的一些線程池的一些行為。
**構造函數(shù)的五個參數(shù)

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

//參 1corePoolSize 核心線程池
核心線程會一直存在于線程池中,即使當前沒有任務需要處理。
當線程數(shù)小于核心線程數(shù)時,即使當前有空閑的線程,線程池也會優(yōu)先創(chuàng)建線程來處理任務。
//參2 maximumPoolSize 最大線程數(shù)
當線程數(shù)大于核心線程數(shù),且任務隊列已經滿了,這時線程池會創(chuàng)建新的線程,直到線程數(shù)量達到最大線程數(shù)為止。
//參3 keepAliveTime 線程的空閑存活時間
當線程的空閑時間超過這個值時,線程會被銷毀,直到線程數(shù)等于核心線程數(shù)。
//參4 unit keepAliveTime的單位
可選的有TimeUnit.NANOSECONDS,MICROSECONDS,MILLISECONDS,SECONDS。
//參5 workQueue 線程池所使用的任務緩沖隊列**
-放等待任務的--有效的緩存序列BlockingQueue<Runnable>

執(zhí)行

//3. 創(chuàng)建自定義的線程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
        3,//核心線程池
        6,//最大運行的線程數(shù)量
        1,//存活時間,指定是等待任務的存活時間
        TimeUnit.HOURS,//時間單位
        new LinkedBlockingQueue<Runnable>()//緩沖隊列,用來存放那些處于等待中的 任務---一般都這樣寫
);
threadPoolExecutor.execute(new DownloadTask(1));
//移除線程池
//threadPoolExecutor.remove()
//glide也是多任務的
}

/**
* 創(chuàng)建下載任務--繼承Runnable
* 下載任務,負責完成去服務器下載文件
* 它里面的作用就是http下載模塊的功能
*/
class DownloadTask implements Runnable {
private int num;
public DownloadTask(int num) {
    this.num = num;//這個參數(shù)是為了區(qū)分線程設立的
    Log.e("tag", "線程" + num + " waitting。。。");
}
//執(zhí)行耗時操作--run方法執(zhí)行說明已經開始進行操作了
@Override
public void run() {
    Log.e("tag", "線程" + num + "開始運行。。。");
    SystemClock.sleep(5000);
    Log.e("tag", "線程" + num + "over了----------");
}
}

Executor框架為開發(fā)者提供了預定義的線程實現(xiàn)

1:FixedThreadPool-線程數(shù)量固定的線程池。
特點:當線程處于空閑狀態(tài)時,它們并不會被回收,除非線程池被關閉。當所有線程都處于活動狀態(tài)時,新任務都會處于等待狀態(tài),直到有線程空閑出來
創(chuàng)建:Executors.newFixedThreadPool(3); 參數(shù)表示線程的個數(shù)
2:CachedThreadPool緩存型池子線程數(shù)量不定的線程池
特點:它只有非核心線程,當線程池中的線程都處于活動狀態(tài)時,線程池會創(chuàng)建新的線程來處理新任務,否則就會利用空閑的線程來處理新任務。空閑線程會等待60s來執(zhí)行新任務
能reuse的線程,必須是timeout IDLE內的池中線程,缺省timeout是60s,超過這個IDLE時長,線程實例將被終止及移出池。
創(chuàng)建:Executors.newCachedThreadPool();
3:ScheduleThreadPool - 核心線程數(shù)量是固定的
特點:核心線程固定,非核心線程沒有限制,并且當非核心線程閑置時,被立即回收
創(chuàng)建:Executors.newScheduledThreadPool(3);
4:SingleThreadExcutor - 線程池內部只有一個核心線程
特點:它確保所有的任務都在同一個線程中按順序執(zhí)行。
創(chuàng)建: Executors.newSingleThreadExecutor();

AsyncTask

AsyncTask是在框架基礎上進行的封裝,它實現(xiàn)將耗時任務移到工作線程中執(zhí)行,同時提供方便的接口實現(xiàn)工作線程和主線程通信
一個應用中使用所有AsyncTask實例會共享全局的屬性,也就是說AsyncTask中的任務使串行執(zhí)行,那么應用中的所有的AsyncTask都會進行排隊,只有等前面的任務執(zhí)行完成之后才會執(zhí)行下一個AsyncTask中的任務,在mTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,"aa");或者在APILevel大于13的系統(tǒng)上execute都有這個效果
如果是異步執(zhí)行,AsyncTask中的ThreadPoolExecutor 指定的核心線程數(shù)是CPU+1。
即:在四核CPU上,最多只有5個任務可以同時進行,其他任務需要在隊列中排隊,等待空閑的線程。(昨天看貌似變了)

public abstract class AsyncTask<Params, Progress, Result> {
    private static final String LOG_TAG = "AsyncTask";

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;
    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
}

使用

VirusTask mTask = new VirusTask();
// 執(zhí)行任務—參數(shù)可變參數(shù)
mTask.execute("haha", "hehe");

//AsyncTask執(zhí)行任務每次只能執(zhí)行一次.因此再次調用要重新new對象
//定義子類繼承此抽象類
// 泛型1 運行中 指定doInBackground的參數(shù)類型 也是execute方法的參數(shù)類型
// 泛型2 進度更新 onProgressUpdate的參數(shù)類型 publishProgress的參數(shù)類型
// 泛型3 結束 onPostExecute的參數(shù)類型 doInBackground的返回值類型

private class VirusTask extends AsyncTask<String, Integer, String> {
    //執(zhí)行任務前會指定的方法 運行在UI線程做一些準備工作
    @Override
    protected void onPreExecute() {
        super.onPreExecute();

    }

    //doInBackground處理耗時任務 運行在子線程(默認只重寫此方法)(其他方法都在ui線程)
    @Override
    protected String doInBackground(String... params) {
        return "aaaaa";
    }

    //onPostExecute任務結束后執(zhí)行的方法 運行在UI線程一般用來更新UI
    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
    }

    //onProgressUpdate更新進度 運行在UI線程
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }
}

Handler消息異步處理機制

執(zhí)行流程
Handler在創(chuàng)建時會與當前所在的線程的Looper對象相關聯(lián)(如果當前線程的Looper為空或不存在,則會拋出異常。
此時需要在線程中主動調用Looper.prepare()來創(chuàng)建一個Looper對象)。handler通過send或post發(fā)送消息,sendmessage中調用messageQueue的enqueueMesssage將消息傳入messageQueue中,Looper的loop方法調用MessageQueue的next方法返回信息,通過msg.Target.DispatchMessage來處理信息,callback中實現(xiàn)處理。一般會調用handler的handlermessage方法

Handler使用

方式一: post(Runnable)
創(chuàng)建一個工作線程,實現(xiàn) Runnable 接口,實現(xiàn) run 方法,處理耗時操作

new Thread(new Runnable() {
   @Override
   public void run() {
       /**
          耗時操作
        */
      handler.post(new Runnable() {
          @Override
          public void run() {
              /**
                更新UI
               */
          }
      });
   }
 }).start();

創(chuàng)建一個 handler,通過 handler.post/postDelay,投遞創(chuàng)建的 Runnable,在 run 方法中進行更新 UI 操作。

方式二: sendMessage(Message)
創(chuàng)建一個工作線程,繼承 Thread,重新 run 方法,處理耗時操作
創(chuàng)建一個 Message 對象,設置 what 標志及數(shù)據(jù)
通過 sendMessage 進行投遞消息
創(chuàng)建一個handler,重寫 handleMessage 方法,根據(jù) msg.what 信息判斷,接收對應的信息,再在這里更新 UI。

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {      //判斷標志位
            case 1:
                /**
                 獲取數(shù)據(jù),更新UI
                */
                break;
        }
    }
};


public class WorkThread extends Thread {

    @Override
    public void run() {
        super.run();
       /**
         耗時操作
        */

        //從全局池中返回一個message實例,避免多次創(chuàng)建message(如new Message)
        Message msg =Message.obtain();  
        msg.obj = data;
        msg.what=1;   //標志消息的標志
        handler.sendMessage(msg);
    }

}

new WorkThread().start();

Handler 存在的問題

內存方面
Handler 被作為 Activity 引用,如果為非靜態(tài)內部類,則會引用外部類對象。當 Activity finish 時,Handler可能并未執(zhí)行完,從而引起 Activity 的內存泄漏。故而在所有調用 Handler 的地方,都用靜態(tài)內部類。
異常方面
當 Activity finish 時,在 onDestroy 方法中釋放了一些資源。此時 Handler 執(zhí)行到 handlerMessage 方法,但相關資源已經被釋放,從而引起空指針的異常。
避免
如果是使用 handlerMessage,則在方法中加try catch。
如果是用 post 方法,則在Runnable方法中加try catch。

Handler 的改進

內存方面:使用靜態(tài)內部類創(chuàng)建 handler 對象,且對 Activity 持有弱引用
異常方面:不加 try catch,而是在 onDestory 中把消息隊列 MessageQueue 中的消息給 remove 掉。則使用如下方式創(chuàng)建 handler 對象:

/**
 * 為避免handler造成的內存泄漏
 * 1、使用靜態(tài)的handler,對外部類不保持對象的引用
 * 2、但Handler需要與Activity通信,所以需要增加一個對Activity的弱引用
 */
private static class MyHandler extends Handler {
    private final WeakReference<Activity> mActivityReference;    

    MyHandler(Activity activity) {
        this.mActivityReference = new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity = (MainActivity) mActivityReference.get();  //獲取弱引用隊列中的activity
        switch (msg.what) {    //獲取消息,更新UI
            case 1:
                byte[] data = (byte[]) msg.obj;
                activity.threadIv.setImageBitmap(activity.getBitmap(data));
                break;
        }
    }
}

并在 onDesotry 中銷毀:

@Override
protected void onDestroy() {
    super.onDestroy();
    //避免activity銷毀時,messageQueue中的消息未處理完;故此時應把對應的message給清除出隊列
    handler.removeCallbacks(postRunnable);   //清除runnable對應的message
    //handler.removeMessage(what)  清除what對應的message
}

Handler 的使用實現(xiàn)

耗時操作采用從網絡加載一張圖片

繼承 Thread 或實現(xiàn) Runnable 接口的線程,與 UI 線程進行分離,其中 Runnable 與主線程通過回調接口進行通信,降低耦合,提高代碼復用性。
在 Activity 中創(chuàng)建 handler 對象,調用工作線程執(zhí)行

public class MainActivity extends AppCompatActivity {

ImageView threadIv;
ImageView runnableIv;
SendThread sendThread;
PostRunnable postRunnable;
private final MyHandler handler = new MyHandler(this);

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    threadIv = (ImageView) findViewById(R.id.thread_iv);
    runnableIv = (ImageView) findViewById(R.id.runnable_iv);

    sendThread = new SendThread(handler);
    sendThread.start();

    postRunnable = new PostRunnable(handler);
    postRunnable.setRefreshUI(new PostRunnable.RefreshUI() {
        @Override
        public void setImage(byte[] data) {
            runnableIv.setImageBitmap(getBitmap(data));
        }
    });
    new Thread(postRunnable).start();
}

/**
  為避免handler造成的內存泄漏
  1、使用靜態(tài)的handler,對外部類不保持對象的引用
  2、但Handler需要與Activity通信,所以需要增加一個對Activity的弱引用
 /
private static class MyHandler extends Handler {
    private final WeakReference<Activity> mActivityReference;

    MyHandler(Activity activity) {
        this.mActivityReference = new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity = (MainActivity) mActivityReference.get();  //獲取弱引用隊列中的activity
        switch (msg.what) {    //獲取消息,更新UI
            case 1:
                byte[] data = (byte[]) msg.obj;
                activity.threadIv.setImageBitmap(activity.getBitmap(data));
                break;
        }
    }
}

private Bitmap getBitmap(byte[] data) {
    return BitmapFactory.decodeByteArray(data, 0, data.length);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    //避免activity銷毀時,messageQueue中的消息未處理完;故此時應把對應的message給清除出隊列
    handler.removeCallbacks(postRunnable);   //清除runnable對應的message
    //handler.removeMessage(what)  清除what對應的message
}
}

方式一:實現(xiàn) runnable 接口,通過 post(Runnable)通信,并通過給定的回調接口通知 Activity 更新

public class PostRunnable implements Runnable {

    private Handler handler;
    private RefreshUI refreshUI;
    byte[] data = null;

    public PostRunnable(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void run() {
        /**
         * 耗時操作
         */
        final Bitmap bitmap = null;
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet("http://i3.17173cdn.com/2fhnvk/YWxqaGBf/cms3/FNsPLfbkmwgBgpl.jpg");
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpGet);
            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                data = EntityUtils.toByteArray(httpResponse.getEntity());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //返回結果給UI線程
        handler.post(new Runnable() {
            @Override
            public void run() {
                refreshUI.setImage(data);
            }
        });
    }

    public interface RefreshUI {
        public void setImage(byte[] data);
    }

    public void setRefreshUI(RefreshUI refreshUI) {
        this.refreshUI = refreshUI;
    }
}

方式二:繼承Thread,通過handler的sendMessage通信

public class SendThread extends Thread {

    private Handler handler;

    public SendThread(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void run() {
        super.run();
        /**
         * 耗時操作
         */
        byte[]data=null;
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet("https://d36lyudx79hk0a.cloudfront.net/p0/descr/pc27/3095587d8c4560d8.png");
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpGet);
            if(httpResponse.getStatusLine().getStatusCode()==200){
                data= EntityUtils.toByteArray(httpResponse.getEntity());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //返回結果給UI線程
        doTask(data);
    }

    /**
     * 通過handler返回消息
     * @param data
     */
    private void doTask(byte[] data) {
        Message msg =Message.obtain();  //從全局池中返回一個message實例,避免多次創(chuàng)建message(如new Message)
        msg.obj = data;
        msg.what=1;   //標志消息的標志
        handler.sendMessage(msg);
    }
}

Handler 通信機制

創(chuàng)建Handler,并采用當前線程的Looper創(chuàng)建消息循環(huán)系統(tǒng);
Handler通過sendMessage(Message)或Post(Runnable)發(fā)送消息,調用enqueueMessage把消息插入到消息鏈表中;
Looper循環(huán)檢測消息隊列中的消息,若有消息則取出該消息,并調用該消息持有的handler的dispatchMessage方法,回調到創(chuàng)建Handler線程中重寫的handleMessage里執(zhí)行。
Handler 如何關聯(lián) Looper、MessageQueue
1、Handler 發(fā)送消息

Message msg =Message.obtain(); 
msg.obj = data;
msg.what=1;   //標志消息的標志
handler.sendMessage(msg);

sendMessageQueue開始追蹤,函數(shù)調用關系:sendMessage -> sendMessageDelayed ->sendMessageAtTime,在sendMessageAtTime中,攜帶者傳來的message與Handler的mQueue一起通過enqueueMessage進入隊列了。
對于postRunnable而言,通過post投遞該runnable,調用getPostMessage,通過該runnable構造一個message,再通過 sendMessageDelayed投遞,接下來和sendMessage的流程一樣了。
2、消息入隊列
在enqueueMessage中,通過MessageQueue入隊列,并為該message的target賦值為當前的handler對象,記住msg.target很重要,之后Looper取出該消息時,還需要由msg.target.dispatchMessage回調到該handler中處理消息。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

在MessageQueue中,由Message的消息鏈表進行入隊列

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

3、Looper 處理消息
再說處理消息之前,先看Looper是如何構建與獲取的:
構造Looper時,構建消息循環(huán)隊列,并獲取當前線程

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

但該函數(shù)是私有的,外界不能直接構造一個Looper,而是通過Looper.prepare來構造的:

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));
}

這里創(chuàng)建Looper,并把Looper對象保存在sThreadLocal中,那sThreadLocal是什么呢?

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

它是一個保存Looper的TheadLocal實例,而ThreadLocal是線程私有的數(shù)據(jù)存儲類,可以來保存線程的Looper對象,這樣Handler就可以通過ThreadLocal來保存于獲取Looper對象了
TheadLocal 如何保存與獲取Looper?

public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

public T get() {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
       values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}

在 set 中都是通過 values.put 保存當前線程的 Looper 實例,通過 values.getAfterMiss(this)獲取,其中put和getAfterMiss都有key和value,都是由Value對象的table數(shù)組保存的,那么在table數(shù)組里怎么存的呢?

table[index] = key.reference;
table[index + 1] = value;

很顯然在數(shù)組中,前一個保存著ThreadLocal對象引用的索引,后一個存儲傳入的Looper實例。
接下來看Looper在loop中如何處理消息
在loop中,一個循環(huán),通過next取出MessageQueue中的消息
若取出的消息為null,則結束循環(huán),返回。
設置消息為空,可以通過MessageQueue的quit和quitSafely方法通知消息隊列退出。
若取出的消息不為空,則通過msg.target.dispatchMessage回調到handler中去。

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
            Long.toHexString(ident) + " to 0x"
            Long.toHexString(newIdent) + " while dispatching to "
            msg.target.getClass().getName() + " "
            msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

4、handler處理消息
Looper把消息回調到handler的dispatchMessage中進行消息處理:
若該消息有callback,即通過Post(Runnable)的方式投遞消息,因為在投遞runnable時,把runnable對象賦值給了message的callback。

若handler的mCallback不為空,則交由通過callback創(chuàng)建handler方式去處理。

否則,由最常見創(chuàng)建handler對象的方式,在重寫handlerMessage中處理。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

子線程中創(chuàng)建handler,處理消息

public class MainActivity extends Activity {
    TextView valeTv;
    // 定義鍵值對
    private String KEY = "key";
    private String VALUE = "value";

    // 定義一個自己的線程
    class MyThread extends Thread {
        public Handler mHandler;

        @Override
        public void run() {
            System.out.println("線程開始運行");
            Looper.prepare();// 在線程中必須建立一個自己的looper,不能用ui線程中的
            mHandler = new Handler() {// 在新線程中創(chuàng)建Handler時必須創(chuàng)建Looper
                public void handleMessage(Message msg) {
                    if (msg.what == 111) {
                        String str = msg.getData().getString(KEY);
                        // valeTv.setText(str);//不能更新ui
                        Toast.makeText(MainActivity.this, str, 0).show();
                    }
                };
            };
            Looper.loop();
        }

    }

    private MyThread thread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        valeTv = (TextView) findViewById(R.id.vale_textView);
        thread = new MyThread();
        // 啟動線程
        thread.start();
        //sentMsg();//發(fā)在這很危險,因為發(fā)送消息的動作可能在線程執(zhí)行前,這樣就出錯了
    }

    public void buttonListener(View v) {
        sentMsg();//因為點擊事件肯定在線程開始執(zhí)行后才進行,所以這里是正確的
    }

    private void sentMsg() {
        // 創(chuàng)建消息
        Message msg = new Message();
        msg.what = 111;
        Bundle bundle = new Bundle();
        bundle.putString(KEY, VALUE);
        // 設置數(shù)據(jù)
        msg.setData(bundle);

        System.out.println("向線程發(fā)送消息");// 這句話必須在“線程開始運行”后打印才表示正確
        // 發(fā)送消息,由于線程執(zhí)行的時間不固定,這句話必須放在線程start后的一段時間才行。這里放在點擊事件中,確保線程已經開始執(zhí)行了。
        thread.mHandler.sendMessage(msg);
    }
}

內存泄漏
一是在Activity的onDestroy方法中調用handler.removeCallbacksAndMessages(null);取消所有的消息的處理,包括待處理的消息;
二是聲明handler的內部類為static。

參考

《Android高級進階》
http://www.lxweimin.com/p/0a274564a4b1
http://www.cnblogs.com/tianzhijiexian/p/3880581.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容