Handler、AsyncTask、OkHttp、RxJava:一道面試題引發(fā)的血案

轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處,謝謝!

命令模式:Handler.post
享元模式:Message.obtain
模板方法模式:AsyncTask
責(zé)任鏈模式:OkHttp
建造者模式:Response、Request
原型模式:OkHttpClient
觀察者模式:RxJava

分析源碼,我還沒那個(gè)實(shí)力,就不寫了,但原理還是要知道的。如果被面試官問了,說兩個(gè)就可以了,別太認(rèn)真,小心hold不??!

Android中的線程通信機(jī)制:消息機(jī)制

Android 中消息模型偽代碼實(shí)現(xiàn)?

class Message{
    Object obj;用于存儲(chǔ)對(duì)象類型數(shù)據(jù)
    int what;表示你要做什么
    ...
}
class MessageQueue{
    Object[] messages;
    void put(Message msg){}
    Message take(){}
}
class Looper{
    MessageQueue msgQ;
    void loop(){
        while(isLoop){}
    }
    void cancel(){}
    ...
}
class Handler{
    void sendMessage(Message msg){}
    void handleMessage(Message msg){}
    ...
}

在Android系統(tǒng)中,主線程不可以執(zhí)行耗時(shí)操作,子線程可以執(zhí)行耗時(shí)卻不可以直接更新UI,所以,當(dāng)子線程在執(zhí)行耗時(shí)操作的過程中,如果需要更新UI,則可以發(fā)出一個(gè)“消息”給主線程,當(dāng)主線程收到該“消息”后,對(duì)“消息”進(jìn)行處理,即更新UI,從而完成主線程和子線程的協(xié)作。
在消息機(jī)制中,會(huì)使用到:

  • Handler消息的發(fā)送者和處理者
  • Message消息的載體
  • MessageQueue消息隊(duì)列,是若干條消息的容器
  • Looper輪循者

Handler

通過調(diào)用Handler對(duì)象的sendMessage(Message)方法可以在子線程中向主線程發(fā)送消息。
通過重寫void handleMessage(Message)方法可以決定如何處理消息,Handler默認(rèn)是運(yùn)行在主線程的,所以,可以直接更新UI。

  • 使用Handler發(fā)送消息
    • boolean sendMessage(Message msg)
    • boolean sendMessageDelayed(Message msg, long delayMillis)延遲delayMillis毫秒后發(fā)出消息
    • boolean sendMessageAtTime(Message msg, long uptimeMillis)在指定的時(shí)間點(diǎn)發(fā)出消息,其中,參數(shù)uptimeMillis表示從Java元年(1970-01-01 00:00:00)至今經(jīng)歷的毫秒數(shù)
    • boolean sendEmptyMessage(int what)發(fā)送空的消息,本質(zhì)上并不是空消息,而是該方法可以幫助開發(fā)者獲取消息對(duì)象,使得開發(fā)者在調(diào)用該方法之前,不用手動(dòng)創(chuàng)建消息對(duì)象,參數(shù)int what表示消息對(duì)象的what屬性
    • boolean sendEmptyMessageDelayed(int what, long delayMillis)延遲delayMillis毫秒后發(fā)出空消息
    • boolean sendEmptyMessageAtTime(int what, long uptimeMillis)在指定的時(shí)間點(diǎn)發(fā)出消息
    • post???()系列方法
  • 處理消息的方式
    • 自定義類繼承自Handler,重寫void handleMessage(Message msg)方法,在該方法中決定如何處理消息
    • 自定義類實(shí)現(xiàn)Handler.Callback,重寫boolean handleMessage(Message msg)方法,在該方法中決定如何處理消息,然后創(chuàng)建出自定義的對(duì)象,用于new Handler(Handler.Callback callback)構(gòu)造方法的參數(shù)
    • 在獲取Message對(duì)象時(shí),使用Message類的靜態(tài)方法obtain(Handler handler, Runnable callback),該方法的參數(shù)Runnable callback就是處理消息的對(duì)象
  • 消息的分發(fā):處理消息的優(yōu)先級(jí)
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

Handler類有send???()系列方法、sendEmpty???()系列方法、post???()系列方法,都可以實(shí)現(xiàn)發(fā)送消息,具體的使用應(yīng)該根據(jù)實(shí)際情況來進(jìn)行選取,在不熟練的情況下,始終使用sendMessage(Message)即可。
有3種處理消息的方式,其中,在獲取Message對(duì)象時(shí),指定Message中的Runnable callback這種方式使用相對(duì)比較麻煩,一般不推薦,至于是繼承自Handler,還是實(shí)現(xiàn)Handler.Callback可以自由選擇。

Handler在哪個(gè)線程處理消息?

在默認(rèn)情況下,Handler是在主線程處理消息的,所以,可以直接更新UI。
不可以直接在子線程中創(chuàng)建Handler對(duì)象,必須先調(diào)用過Looper.prepare()方法,且,Looper必須調(diào)用了loop()方法才會(huì)輪循消息隊(duì)列,取出消息交給Handler處理。
在默認(rèn)情況下,主線程已經(jīng)存在調(diào)用了prepare()和loop()方法的Looper,所以,在主線程中創(chuàng)建Handler對(duì)象時(shí)不需要考慮Looper的問題。
Handler在哪個(gè)線程處理消息,完全取決于該Handler對(duì)象關(guān)聯(lián)到的是哪個(gè)線程的Looper對(duì)象!即:如果在子線程創(chuàng)建Handler,關(guān)聯(lián)到的是主線程的Looper對(duì)象,該Handler會(huì)在主線程處理線程。
在沒有顯式的指定Looper對(duì)象時(shí),會(huì)默認(rèn)獲取創(chuàng)建Handler對(duì)象的線程的Looper,可表現(xiàn)為:在哪個(gè)線程創(chuàng)建Handler,就在哪個(gè)線程處理消息。
如果已經(jīng)顯式的指定Looper對(duì)象,則由Looper對(duì)象的線程來決定在哪個(gè)線程處理消息。

Message

  • Message的常用屬性有:
    • int arg1用于在消息中封裝int類型的數(shù)據(jù)
    • int arg2同上
    • Object obj用于在消息中封裝任意類型的數(shù)據(jù)
    • int what用于標(biāo)識(shí)消息的類型,通常使用靜態(tài)常量
  • Message類靜態(tài)obtain()方法
    Message類靜態(tài)obtain()方法可以從消息池中獲取已有的、空閑的消息對(duì)象,如果消息中沒有消息對(duì)象,或沒有空閑的對(duì)象,則創(chuàng)建新的對(duì)象。
    該obtain()方法被重載了多次:
    • Message obtain()
    • Message obtain(Message msg)
    • Message obtain(Handler handler)
    • Message obtain(Handler handler, int what)
    • Message obtain(Handler handler, Runnable callback)
    • Message obtain(Handler handler, int what, Object obj)
    • Message obtain(Handler handler, int what, int arg1, int arg2)
    • Message obtain(Handler handler, int what, int arg1, int arg2, Object obj)
      如果在使用obtain()系列方法獲取消息對(duì)象時(shí)指定了Handler對(duì)象,則在發(fā)送消息時(shí),應(yīng)該使用消息對(duì)象的sendToTarget()方法實(shí)現(xiàn)消息的發(fā)送。
      通過Handler對(duì)象的obtainMessage()系列方法也可以獲取Message對(duì)象,這一系列方法的本質(zhì)依然是通過Message類的靜態(tài)obtain()方法實(shí)現(xiàn)的,所以,如果沒有必要的話,就始終使用Message類的obtain()系列方法獲取Message對(duì)象即可。
      永遠(yuǎn)不要new Message(),而是使用Message類的靜態(tài)方法obtain()獲取消息對(duì)象。

HandlerThread與IntentService

HandlerThread是一個(gè)線程類,它的作用是便捷的獲取一個(gè)運(yùn)行在子線程的Looper對(duì)象。
在使用HandlerThread時(shí),必須遵循以下流程,不可調(diào)整代碼的先后順序:

  • 創(chuàng)建HandlerThread的線程對(duì)象
  • 開啟線程
  • 獲取Looper對(duì)象

使用HandlerThread的目的只是為了獲取運(yùn)行在子線程的Looper,進(jìn)而,可以通過該Looper創(chuàng)建出在子線程處理消息的Handler,這樣的Handler在接收到消息后可以直接執(zhí)行耗時(shí)操作,當(dāng)然,這樣的Handler是不可以直接更新UI的。
普通的Service由于進(jìn)程優(yōu)先級(jí)較高,所以適合執(zhí)行耗時(shí)操作,但是,Service也是運(yùn)行在主線程的,所以在執(zhí)行耗時(shí)操作時(shí),也是必須開啟子線程的,同時(shí),每個(gè)Service在使用完成后應(yīng)該停止??偨Y(jié)的結(jié)果就是:使用Service多半需要另啟子線程,且使用完成后停止該Service。

  • IntentService的特點(diǎn):
    • 可以不用開發(fā)者顯式的開啟線程而直接編寫耗時(shí)任務(wù)的
    • 任務(wù)執(zhí)行完畢后自我銷毀的
    • 可以一次性“計(jì)劃”多項(xiàng)任務(wù),這些任務(wù)會(huì)依次執(zhí)行
  • 使用IntentService時(shí)的注意事項(xiàng):
    • 由于onHandleIntent()是運(yùn)行在子線程的,所以,可以直接編寫耗時(shí)任務(wù),但是,不可以更新UI
    • 自定義的IntentService終究是Service類,必須注冊
    • 自定義的IntentService的子類,必須提供無參數(shù)的構(gòu)造方法,否則,系統(tǒng)將無法對(duì)自定義的IntentService的子類進(jìn)行管理和維護(hù)
    • 基于IntentService的工作特性,這樣的Service是必須通過startService()方式激活,而不可以使用bindService()方法來激活
    • 如果需要重寫IntentService中的生命周期方法,切記在重寫時(shí)必須調(diào)用父類的對(duì)應(yīng)的生命周期方法

使用線程的方式

  • Thread + 消息機(jī)制
    • 優(yōu)點(diǎn):可以多項(xiàng)任務(wù)同時(shí)執(zhí)行,使用非常靈活,可以只在Activity里使用,也可以Activity+Service使用……
    • 缺點(diǎn):不宜同時(shí)開啟過多的急劇消耗性能的任務(wù),如果需要保障生命力,則還需要配合Service
  • IntentService + ResultReceiver
    • 優(yōu)點(diǎn):生命力頑強(qiáng),多項(xiàng)任務(wù)依次執(zhí)行
    • 缺點(diǎn):代碼略多 / 使用相對(duì)繁瑣 / 必須有Service
  • AsyncTask
    • 優(yōu)點(diǎn):可以在任何位置使用,例如在Activity中,或在Service中……代碼的管理非常集中,多項(xiàng)任務(wù)依次執(zhí)行
    • 缺點(diǎn):多項(xiàng)任務(wù)依次執(zhí)行
      AsyncTask:異步任務(wù)

AsyncTask

在使用AsyncTask時(shí),需要自定義類繼承自AsyncTask,首要任務(wù)是確定3個(gè)泛型:

  • Params當(dāng)執(zhí)行任務(wù)之前,是否需要準(zhǔn)備某些參數(shù),如果需要的話,這些參數(shù)應(yīng)該是哪種數(shù)據(jù)類型的
  • Progress當(dāng)執(zhí)行任務(wù)的過程中,是否需要以任何形式顯示進(jìn)度,如果需要的話,用哪種數(shù)據(jù)類型可以表示進(jìn)度值
  • Result當(dāng)任務(wù)執(zhí)行完畢以后,是否需要獲取結(jié)果,如果需要的話,獲得到表示結(jié)果的數(shù)據(jù)應(yīng)該是哪種數(shù)據(jù)類型的
    在自定義的AsyncTask類中,可能涉及的方法有:
  • Result doInBackground(Params... parmas)該方法是抽象方法,需要被重寫,且該方法默認(rèn)運(yùn)行在子線程,參數(shù)是Params類型的可變參數(shù),返回值是Result類型
  • void publishProgress(Progress... values)該方法是用于提交進(jìn)度的,通常是在doInBackground()中調(diào)用的,參數(shù)提Progress類型的可變參數(shù)
  • void onProgressUpdate(Integer... values)該方法是更新進(jìn)度的方法,每當(dāng)publishProgress()方法被調(diào)用一次,當(dāng)前onProgressUpdate()方法就會(huì)被對(duì)應(yīng)的回調(diào)一次,且該方法的參數(shù)就是publishProgress()方法的參數(shù),該方法是運(yùn)行在主線程的,可以直接更新UI
  • void onPostExecute(Result result)該方法是處理結(jié)果的方法,會(huì)在doInBackground()執(zhí)行完畢后被回調(diào)1次,且方法的參數(shù)是doInBackground()方法的返回值,該方法是運(yùn)行在主線程的,可以直接更新UI

執(zhí)行AsyncTask時(shí),調(diào)用AsyncTask對(duì)象的execute(Params... params)方法即可
使用AsyncTask時(shí),必須自定義子類實(shí)現(xiàn)繼承,并且優(yōu)先確定3個(gè)泛型,在這3個(gè)泛型中,不需要使用的類型可以使用Void表示,其中,doInBackground()方法是默認(rèn)運(yùn)行在子線程的,可以執(zhí)行耗時(shí)操作,publishProgress()方法是用于在doInBackground()的執(zhí)行過程中提交進(jìn)度的,如果需要處理進(jìn)度的顯示,必須重寫onProgressUpdate()方法,每次執(zhí)行publishProgress()都會(huì)導(dǎo)致onProgressUpdate()被回調(diào),onProgressUpdate()是運(yùn)行在主線程的,可以直接更新UI,當(dāng)doInBackground()執(zhí)行完畢,onPostExecute()將被回調(diào)1次,該方法也是運(yùn)行在主線程的,可以直接更新UI。
當(dāng)需要執(zhí)行任務(wù)時(shí),先創(chuàng)建任務(wù)對(duì)象,然后調(diào)用execute()方法執(zhí)行任務(wù)。
當(dāng)需要終止任務(wù)時(shí),可以調(diào)用任務(wù)對(duì)象的cancel(true)方法以實(shí)現(xiàn)終止。
假設(shè)有任務(wù)A、任務(wù)B均是繼承AsyncTask實(shí)現(xiàn)的,那么,不管創(chuàng)建多少個(gè)任務(wù)A、任務(wù)B的對(duì)象,這所有的任務(wù)都將是依次執(zhí)行的。

終止AsyncTask

調(diào)用任務(wù)對(duì)象的cancel(true)方法可以終止任務(wù),實(shí)際表現(xiàn)為onProgressUpdate()方法和onPostExecute()方法將不再被回調(diào),而doInBackground()并不會(huì)停止后臺(tái)的運(yùn)行!
不推薦使用這樣的方式終止任務(wù),在條件允許的情況下,應(yīng)該使得任務(wù)自然運(yùn)行結(jié)束以實(shí)現(xiàn)終止的效果

重點(diǎn)來了OkHttp、RxJava

在寫OkHttp、RxJava前,我想問大家,你有多長時(shí)間沒寫過Handler,AsyncTask了?我在Android技術(shù)群進(jìn)行詢問的時(shí)候,沒想到引發(fā)了一場口水仗,但有一位的觀點(diǎn),我很認(rèn)同。

  • 如果現(xiàn)在項(xiàng)目還用Handler那么只能說項(xiàng)目比較老,如果是新項(xiàng)目,那么就是開發(fā)者不愛學(xué)習(xí)!
  • 項(xiàng)目不一定會(huì)用到Rx,EventBus,但是對(duì)于開發(fā)者是標(biāo)配了!

當(dāng)然,也有很多反對(duì)的意見。
所以,現(xiàn)在每當(dāng)看到有關(guān)Handler,AsyncTask的技術(shù)帖,我都很反感,可這又是面試高頻率題,沒辦法,Handler還是要了解。

OkHttp使用

compile 'com.squareup.okhttp3:okhttp:3.8.1'

public class HttpUtils {
    public static void sendOkHttpRequest(String address, Callback callback) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(address).build();
        client.newCall(request).enqueue(callback);
    }
}
HttpUtils.sendOkHttpRequest(address, new Callback() {
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        final String responseText = response.body().string();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
            }
        });
    }
    @Override
    public void onFailure(Call call, IOException e) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
            }
        });
    }
});

POST請(qǐng)求

try {
            OkHttpClient okHttpClient = new OkHttpClient();
            RequestBody requestBody = new FormBody.Builder().add("username", "admin").add("password", "123456").build();
            Request request = new Request.Builder().url("http://www.baidu.com").post(requestBody).build();
            Response response = okHttpClient.newCall(request).execute();
            String responseText = response.body().string();
        } catch (IOException e) {
            e.printStackTrace();
        }

RxJava使用

老羅RxJava視頻百度云
鏈接:http://pan.baidu.com/s/1pLPp4RD 密碼:zx9h

public class RxUtils {

    private static final String TAG = RxUtils.class.getSimpleName();

    /**
     * 使用create方式
     */
    public static void createObserable() {
        //定義被觀察者,
        Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                if (!subscriber.isUnsubscribed()) {
                    subscriber.onNext("hello");
                    subscriber.onNext("world");
                    subscriber.onCompleted();
                }
            }
        });
        Subscriber<String> showsub = new Subscriber<String>() {
            @Override
            public void onCompleted() {
                Log.i(TAG, "onCompleted");
            }

            @Override
            public void onError(Throwable e) {
                Log.i(TAG, e.getMessage());
            }

            @Override
            public void onNext(String s) {
                Log.i(TAG, "result-->>" + s);
            }
        };
        observable.subscribe(showsub);//關(guān)聯(lián)被觀察者
    }
}

RxJava+OkHttp實(shí)現(xiàn)登錄

public class LoginUtils {

    private OkHttpClient client;

    public LoginUtils() {
        client = new OkHttpClient();
    }

    /**
     * 定義了login操作,使用RxAndroid的編程思想
     * @param url
     * @param params
     * @return Json字符串
     */
    public Observable<String> login(String url,Map<String,String> params){
        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
               if (!subscriber.isUnsubscribed()){
                   FormBody.Builder builder = new FormBody.Builder();
                   if (params!=null&&!params.isEmpty()){
                       for(Map.Entry<String,String> entry:params.entrySet()){
                           builder.add(entry.getKey(),entry.getValue());
                       }
                   }
                   RequestBody requestBody = builder.build();
                   //構(gòu)建post請(qǐng)求
                   Request request = new Request.Builder().url(url).post(requestBody).build();
                   client.newCall(request).enqueue(new Callback() {
                       @Override
                       public void onFailure(Call call, IOException e) {
                           subscriber.onError(e);
                       }

                       @Override
                       public void onResponse(Call call, Response response) throws IOException {
                          if (response.isSuccessful()){
                              subscriber.onNext(response.body().string());
                          }
                           subscriber.onCompleted();//訪問結(jié)束
                       }
                   });
               }
            }
        });
    }
}
button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Map<String, String> params = new HashMap<String, String>();
                params.put("username", "admin";
                params.put("password", "123456");

                new LoginUtils().login(URL_LOGIN, params).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<String>() {
                    @Override
                    public void onCompleted() {
                        dialog.dismiss();
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.i(TAG, e.getMessage());
                    }

                    @Override
                    public void onNext(String s) {
                        dialog.show();
                        Log.i(TAG,s);
                       }
                    }
                });
            }
        });
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容