編輯:關於Android編程
我們知道在Android系統中,我們執行完耗時操作都要另外開啟子線程來執行,執行完線程以後線程會自動銷毀。想象一下如果我們在項目中經常要執行耗時操作,如果經常要開啟線程,接著又銷毀線程,這無疑是很消耗性能的?那有什麼解決方法呢?
使用線程池 使用HandlerThread本篇博客主要講解HandlerThread,關於線程池的原理,請自行了解。
HandlerThread是Google幫我們封裝好的,可以用來執行多個耗時操作,而不需要多次開啟線程,裡面是采用handler和Looper實現的
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
HandlerThread handlerThread = new HandlerThread("myHandlerThread");
該參數表示線程的名字,可以隨便選擇。
2. 啟動我們創建的HandlerThread線程
handlerThread.start();將我們的handlerThread與Handler綁定在一起。
mThreadHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { checkForUpdate(); if(isUpdate){ mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO); } } };
注意必須按照以上三個步驟來,下面在講解源碼的時候會分析其原因
public class MainActivity extends AppCompatActivity { private TextView mTv; Handler mMainHandler = new Handler(); private Handler mThreadHandler; private static final int MSG_UPDATE_INFO = 0x100; private HandlerThread mHandlerThread; private boolean isUpdate=true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTv = (TextView) findViewById(R.id.tv); initHandlerThread(); } private void initHandlerThread() { mHandlerThread = new HandlerThread("xujun"); mHandlerThread.start(); mThreadHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { checkForUpdate(); if(isUpdate){ mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO); } } }; } /** * 模擬從服務器解析數據 */ private void checkForUpdate() { try { //模擬耗時 Thread.sleep(1200); mMainHandler.post(new Runnable() { @Override public void run() { String result = "實時更新中,當前股票行情:%d"; result = String.format(result, (int) (Math.random() * 5000 + 1000)); mTv.setText(Html.fromHtml(result)); } }); } catch (InterruptedException e) { e.printStackTrace(); } } @Override protected void onResume() { isUpdate=true; super.onResume(); mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO); } @Override protected void onPause() { super.onPause(); isUpdate=false; mThreadHandler.removeMessages(MSG_UPDATE_INFO); } @Override protected void onDestroy() { super.onDestroy(); mHandlerThread.quit(); mMainHandler.removeCallbacksAndMessages(null); } }
運行以上測試代碼,將可以看到如下效果圖
* 官方源代碼如下,是基於sdk23的,可以看到,只有一百多行代碼而已*
public class HandlerThread extends Thread { int mPriority; int mTid = -1; Looper mLooper; public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } public HandlerThread(String name, int priority) { super(name); mPriority = priority; } /** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { } @Override public void run() { mTid = Process.myTid(); Looper.prepare(); //持有鎖機制來獲得當前線程的Looper對象 synchronized (this) { mLooper = Looper.myLooper(); //發出通知,當前線程已經創建mLooper對象成功,這裡主要是通知getLooper方法中的wait notifyAll(); } //設置線程的優先級別 Process.setThreadPriority(mPriority); //這裡默認是空方法的實現,我們可以重寫這個方法來做一些線程開始之前的准備,方便擴展 onLooperPrepared(); Looper.loop(); mTid = -1; } public Looper getLooper() { if (!isAlive()) { return null; } // 直到線程創建完Looper之後才能獲得Looper對象,Looper未創建成功,阻塞 synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; } public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; } /** * Returns the identifier of this thread. See Process.myTid(). */ public int getThreadId() { return mTid; } }
public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } public HandlerThread(String name, int priority) { super(name); mPriority = priority; }
有兩個構造方法,一個參數的和兩個參數的,name代表當前線程的名稱,priority為線程的優先級別
public void run() { mTid = Process.myTid(); Looper.prepare(); //持有鎖機制來獲得當前線程的Looper對象 synchronized (this) { mLooper = Looper.myLooper(); //發出通知,當前線程已經創建mLooper對象成功,這裡主要是通知getLooper方法中的wait notifyAll(); } //設置線程的優先級別 Process.setThreadPriority(mPriority); //這裡默認是空方法的實現,我們可以重寫這個方法來做一些線程開始之前的准備,方便擴展 onLooperPrepared(); Looper.loop(); mTid = -1; }還記得我們前面我們說到使用HandlerThread的時候必須笑調用start()方法,接著才可以將我們的HandlerThread和我們的handler綁定在一起嗎?其實原因就是我們是在run()方法才開始初始化我們的looper,而我們調用HandlerThread的start()方法的時候,線程會交給虛擬機調度,由虛擬機自動調用run方法
mHandlerThread.start(); mThreadHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { checkForUpdate(); if(isUpdate){ mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO); } } };這裡我們為什麼要使用鎖機制和notifyAll();,原因我們可以從getLooper()方法中知道
public Looper getLooper() { if (!isAlive()) { return null; } // 直到線程創建完Looper之後才能獲得Looper對象,Looper未創建成功,阻塞 synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
總結:在獲得mLooper對象的時候存在一個同步的問題,只有當線程創建成功並且Looper對象也創建成功之後才能獲得mLooper的值。這裡等待方法wait和run方法中的notifyAll方法共同完成同步問題。
//調用這個方法退出Looper消息循環,及退出線程 public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; } //調用這個方法安全地退出線程 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; }
跟蹤這兩個方法容易知道只兩個方法最終都會調用MessageQueue的queue(boolean safe)方法
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; //安全退出調用這個方法 if (safe) { removeAllFutureMessagesLocked(); } else {//不安全退出調用這個方法 removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
不安全的會調用removeAllMessagesLocked();這個方法,我們來看這個方法是怎樣處理的,其實就是遍歷Message鏈表,移除所有信息的回調,並重置為null
private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; }
安全地會調用removeAllFutureMessagesLocked();這個方法,它會根據Message.when這個屬性,判斷我們當前消息隊列是否正在處理消息,沒有正在處理消息的話,直接移除所有回調,正在處理的話,等待該消息處理處理完畢再退出該循環。因此說quitSafe()是安全的,而quit()方法是不安全的,因為quit方法不管是否正在處理消息,直接移除所有回調。
private void removeAllFutureMessagesLocked() { final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p != null) { //判斷當前隊列中的消息是否正在處理這個消息,》沒有的話,直接移除所有回調 if (p.when > now) { removeAllMessagesLocked(); } else {//正在處理的話,等待該消息處理處理完畢再退出該循環 Message n; for (;;) { n = p.next; if (n == null) { return; } if (n.when > now) { break; } p = n; } p.next = null; do { p = n; n = p.next; p.recycleUnchecked(); } while (n != null); } } }
矩陣變換類package test.com.opengles7_1;import android.opengl.Matrix;/** * Created by hbin
前面的“錘子快捷鍵”相關文章已經分析了輸入事件的讀取,處理,分發。我們知道事件的傳遞是以window為單位傳遞的,即server只負責將事件傳遞給
目錄概述StickHeaderItemDecoration是用於顯示固定頭部的item裝飾類,擴展來自系統的ItemDecoration.本文參考了一部分sticky-h
“點九”圖片概述 “點九”是andriod平台的應用軟件開發裡的一種特殊的圖片形式,文件擴展名為:.9.png。 我們都知道android平台有多種不同的分辨