1. 程式人生 > >Android面試系列之非同步訊息處理相關

Android面試系列之非同步訊息處理相關

我們在平時的專案開發中,肯定會遇到處理非同步任務的場景。因為Android中的UI執行緒是不安全的,我們需要更新ui的話就必須在ui執行緒上進行操作。否則就會拋異常。
這個時候我們就需要用到非同步訊息處理了

比如,在子執行緒中請求資料,拿到資料後告訴ui執行緒去更新介面,
在子執行緒下載檔案告訴ui執行緒下載進度,ui執行緒去更新進度等等。

我們常用的方式就是通過Handler, AnyncTask或者IntentService來處理非同步訊息。

Handler

什麼是Handler
handler是Android中用來處理訊息的一種機制,我們可以簡單的理解為handler就是用來更新ui的。

handler的使用方法
1.handler.post(runnable)**

1.在ui執行緒中建立Handler,注意Handler在哪個執行緒中建立,就會與哪個執行緒進行繫結。所以我們一般都在主執行緒上建立Handler。

private static Handler handler = new Handler();

2.在子執行緒中,通過handler.post()來更新ui

  class WorkThread extends Thread {
        @Override
        public void run() {
      /*      runOnUiThread(new Runnable() {
                @Override
public void run() { tv.setText("aaa"); } }); tv.post(new Runnable() { @Override public void run() { tv.setText("ccc"); } });*/
handler.post(new
Runnable() { @Override public void run() { tv.setText("bbb"); } }); } }

2.handler.send(Message)

1.在ui執行緒中建立Handler並重寫handleMessage方法,根據msg.what處理訊息

   private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:

                    tv.setText("aaa");
                    break;
            }
        }
    };

2.再需要傳遞訊息的地方建立Message,並通過handler.sendMessage()傳送訊息

 Message msg = new Message();
 msg.what = 1;
 handler.sendMessage(msg);

handler機制的原理
實際上在我們在建立Handler的時候,就已經把handler和所線上程繫結到一起了。然後我們通過handler傳送的訊息就會在該執行緒中獲取到訊息並進行處理。

來看看原始碼:
這是Handler構造方法的原始碼。
這裡寫圖片描述
可以看到,Handler在構造方法中通過Looper.myLooper()獲取了一個Looper物件,並通過Looper物件獲取到一個訊息佇列。

再來看看myLooper()中的原始碼:
這裡寫圖片描述

可以看到,myLooper()返回的是當前執行緒的Looper物件,注意:每一個執行緒只有一個Looper物件。

而myQueue返回了Looper中的MessageQueue.可以看到訊息佇列實在Looper的構造方法中被創建出來的。

那麼問題來了
這個Looper物件是什麼時候放進去的呢?
我們來看看Loopper類中的這個方法
這裡寫圖片描述
可以看到,實際上是在perpare這個方法中設定了Looper

這麼一來,就清楚是怎麼回事兒了。
Handler在那個執行緒中建立,就會跟哪個執行緒繫結,並且與Looper和Message相關聯。

要記住的方法:

Lopper.prepaer()
Looper.loop() 實際上原始碼就是建立了個死迴圈,去訊息佇列中獲取訊息。獲取到訊息就通過handler.dispatchMessage(msg)將訊息傳送出去。
handleMessage: 處理接收到的訊息

handler引起的記憶體洩漏及解決辦法

原因:靜態內部類持有外部類的匿名引用,導致外部的activity無法釋放。

解決辦法:將handler宣告為static即可,這樣的話,handler物件由於是靜態型別無法持有外部activity的引用,但是這樣Handler不再持有外部類的引用,導致程式不允許在Handler中操作activity中的物件了,這時候我們需要在Handler物件中增加一個對activity的弱引用;
如果使用的是handler.post()的話,就在activity的onDestory()方法中呼叫 handler.removeCallbacks();

 static class MyHandler extends Handler {
        WeakReference<MainActivity> mActivityReference;

        MyHandler(MainActivity activity) {
            mActivityReference = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            final MainActivity activity = mActivityReference.get();
            if (activity != null) {
               activity.tv.setText("aaa");

            }
        }
    }

    private MyHandler handler = new MyHandler(this);

AsyncTask

什麼是AsyncTask
AsyncTask是Android提供的一個輕量級的非同步任務處理類,本質是對執行緒池和Handler的封裝,方便我們使用。

AsyncTask的使用方法
AsyncTask本身是一個抽象類,我們使用時直接繼承自AsyncTask並實現相應的方法即可。

AsyncTask定義了三種泛型型別 Params,Progress和Result。


Params           啟動任務執行的輸入引數,
Progress         後臺任務執行的百分比。
Result           後臺執行任務最終返回的結果

AsyncTask有五個常用方法:

onPreExecute()                  在耗時操作執行之前呼叫,主要是用來做一些初始化操作,實在ui執行緒上呼叫。
doInBackground(Params…)onPreExecute之後呼叫,用來執行耗時操作,在工作執行緒中執行
publicProgress(Progress…)doInBackground中呼叫,可以用來更新任務的進度。
onProgressUpdate(Progress…)      執行在主執行緒,可以用來更新進度
onPostExecute(Result)            執行在主執行緒,返回任務執行的結果
onCancelled()                    取消任務
package com.yzq.handler;

import android.os.AsyncTask;

/**
 * Created by yzq on 2017/5/26.
 */

public class task extends AsyncTask<Integer, Integer, String> {


    /*任務執行前呼叫  執行在主執行緒 */
    @Override
    protected void onPreExecute() {

        super.onPreExecute();
    }

    /*執行耗時任務  執行在後臺執行緒*/
    @Override
    protected String doInBackground(Integer... params) {
        publishProgress();//更新進度
        return null;
    }

    /*執行在主執行緒  更新進度*/
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    /*執行在主執行緒  返回執行完成後的結果*/
    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
    }


    /*取消任務時呼叫*/
    @Override
    protected void onCancelled() {
        super.onCancelled();
    }
}

AsyncTask的機制原理
1.AsyncTask本質上是一個靜態的執行緒池,其派生出來的子類可以實現不同的非同步任務,這些任務都會提交到執行緒池中去執行
2.耗時操作是在doInBackground中執行的
3.當任務狀態(pendding,running,finished)改變後,工作執行緒會向ui執行緒傳送訊息,AsyncTask內部的InternalHandler會響應這些訊息並執行相關回調函式

AsyncTask注意事項
1、AsyncTask不適合特別耗時的任務
AsyncTask的生命週期和Activity的生命週期不同步,Activity銷燬了但是AsyncTask中的任務還是會繼續執行完畢,一個最典型的例子就是Activity的橫豎屏切換,AsyncTask中引用的Activity不是當前的Activity,onPostExecute()中執行的仍然是上一個Activity。還有一個原因是因為AsyncTask在執行長時間的耗時任務時也會持有一個Activity物件,即使這個Activity已經不可見了,Android也無法對這個Activity進行回收,導致記憶體洩露。

2、AsyncTask只能在主執行緒中建立以及使用
AsyncTask被用於執行非同步任務,然後更新UI,所以最後的onPostExecute()方法執行在建立該AsyncTask物件的執行緒中,如果不在主執行緒中建立以及使用,就達不到更新UI的目的。

3、一個AsyncTask物件只能執行一次
一個AsyncTask物件只能執行一次,即只能呼叫一次execute方法,否則會報執行時異常

4、AsyncTask在不同的Android版本下的並行和序列問題
關於AsyncTask的並行和序列問題,在不同的API下是有不同的。在Android1.6之前,AsyncTask是序列執行任務的;到了Android1.6時,開始採用執行緒池來並行執行任務;在Android3.0之後的版本中,AsyncTask又開始用一個執行緒序列執行任務。雖然Android3.0之後採用序列方式執行任務,但我們可以通過AsyncTask的executeOnExecutor(exe,params),自定義一個執行緒池來並行執行任務。

HandlerThread

什麼是HandlerThread
在Android我們經常會遇到耗時操作,一般我們都是開啟一個子執行緒執行耗時操作。執行完成後GC會自動銷燬執行緒,如果再有耗時操作的話再去建立執行緒,然後再銷燬執行緒。這樣是很消耗系統資源的。
還有Android中,子執行緒預設是沒有開啟Looper迴圈的,也就是說,如果我們想在子執行緒中使用Handler的話,我們需要手動呼叫Looper.prepare()啟用Looper。這樣一來我們就需要手動去維護Looper,很麻煩。
所以,谷歌公司給我們封裝好了一個類就是HandlerThread以便於我們使用。

HandlerThread本質上是一個執行緒類,繼承自Thread。
HandlerThread本身有自己的Looper物件。
講HandlerThread中的Looper物件獲取後傳給Handler,就可以在Handler中的HandleMessage中處理非同步訊息。

優點與缺點
優點:不會有阻塞,減少對效能的消耗
缺點:不能同時進行多工處理,效率不高。HandlerThread是一個序列佇列,內部只有一個執行緒。

HandlerThread的作用
一般是用在子執行緒跟子執行緒進行通訊

IntentService

IntentService是什麼
IntentService本質上就是繼承自Service,其內部封裝了HandlerThread,IntentService和Service的生命週期一樣,且啟動方式也一樣,當IntentService中的耗時任務執行完畢後,IntentService會自動停止,多次啟動IntentService時。耗時任務會以佇列的形式存在於IntentService中,並在onHandlerIntent方法中一個一個的執行。

使用方法
1,繼承自IntentService並重寫其構造方法和onHandleIntent()方法
2.在HandlerIntent()方法中處理耗時操作


public class MyIntentService extends IntentService {


    public MyIntentService() {
        super("yzq");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {

        L.i("onHandleIntent"+intent.getStringExtra("i"));
    }
}

3.啟動service,注意要在清單配置檔案中生命service

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for (int i=0;i<10;i++){
            Intent intent=new Intent(MainActivity.this,MyIntentService.class);
            intent.putExtra("i",i+"");
            startService(intent);

        }

    }
}