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