轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處,謝謝!
- GitHub: @Ricco
命令模式: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);
}
}
});
}
});