編輯:關於Android編程
先來看下源碼吧:
/** * 創建一個新的線程類,同時包含有一個Looper. 然後可以使用這個Looper創建一個Handler. * Note:包含有一個Looper意味著什麼? * 啟動線程時啟動looper消息循環,looper內置有messageQueue。這個線程專用來處理消息的了 * 看文章:Android多線程消息處理機制,http://blog.csdn.net/fesdgasdgasdg/article/details/52081773 */ public class HandlerThread extends Thread { int mPriority;//線程優先級 int mTid = -1;//進程id Looper mLooper;//核心對象 public HandlerThread(String name) { super(name); //獲取線程默認的優先級,0 mPriority = Process.THREAD_PRIORITY_DEFAULT; } /** * 構造函數 * @param name 線程名稱 * @param priority 線程優先級. 必須從android.os.Process獲取,而不能從java.lang.Thread獲取. */ public HandlerThread(String name, int priority) { super(name); mPriority = priority; } /** * 如果需要在looper循環前執行一些操作時,可以顯式的重寫此方法 */ protected void onLooperPrepared() { } /** * 線程的run方法,在線程start()後,就開始執行run方法, * 在run裡面Looper.prepare()創建looper... * 此內容參考:Android多線程消息處理機制,http://blog.csdn.net/fesdgasdgasdg/article/details/52081773 */ @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared();//循環前的回調 Looper.loop(); mTid = -1; } /** * 此方法返回當前線程關聯的looper, * 如果當前線程沒有啟動或者已停止等原因,此方法返回null * 如果此方法已經啟動,此方法將阻塞知道looper被初始化 * @return The looper. */ public Looper getLooper() { //如果當前線程不處在運行狀態,則返回null if (!isAlive()) { return null; } // 如果此方法已經啟動,此方法將阻塞知道looper被初始化 synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; } /** * 退出線程的looper循環. * 使looper線程終止消息循環,不再處理消息隊列中的任何消息 * 當looper調用quit之後,任何嘗試post message到messageQueue中的操作都會失敗 * 比喻:Handler的sendMessage(Message)方法會返回false * 使用這個方法可能不安全,因為在looper停止之前可能有message沒有被執行。 * 考慮使用quitSafely()方法代替quit()方法,以確保所有待完成的工作有序完成。 * @return 如果looper調用了quit,則返回true。如果線程還沒有開始執行則返回false * * @see #安全退出 */ public boolean quit() { Looper looper = getLooper();//獲取當前線程的looper if (looper != null) { looper.quit(); return true;//成功的調用了quit,返回true } return false; } /** * 安全的退出線程looper循環。 * 當messageQueue中所有已到期的message都處理完後,終止looper線程的消息循環。 * 但是由於延時的消息需要等待,故這些消息將不會被處理。 * Pending delayed messages with due times in the future will not be delivered. * 當looper調用quit之後,任何嘗試post message到messageQueue中的操作都會失敗 * 比喻:Handler的sendMessage(Message)方法會返回false * 如果線程沒有被啟動,或者已經結束則返回false。否則在調用quitSafely()之後返回true。 */ public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { looper.quitSafely(); return true; } return false; } /** * 返回此線程的標識符. 參考Process.myTid(). */ public int getThreadId() { return mTid; } }
這麼一分析,是否都清楚了?無非就是個Thread,在裡面內置了一個Looper,looper會自帶一個MessageQueue。
在Thread的run方法中使用looper.prepqre()創建了looper。然後賦給當前線程的looper成員變量,供外面的handler使用。
接著調用looper.loop()方法啟動消息循環。
前面也講過可以自己創建LooperThread線程,HandlerThread就是google提供的經典實例,只不過HandlerThread裡面提供了線程安全訪問,退出消息循環等方法。
具體的用法簡單的1C, 下面就用自己創建的LooperThread為例,如果你不喜歡的話,直接把LooperThread替換成現有的HandlerThread類即可。
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Handler mHandler; private LooperThread thread; private TextView show; private Button start; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); show = (TextView) findViewById(R.id.show); start = (Button) findViewById(R.id.start); start.setOnClickListener(this); init(); } private void init() { thread = new LooperThread(); thread.start(); while (thread.getLooper() == null) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } mHandler = new Handler(thread.getLooper()) { @Override public void handleMessage(Message msg) { //處理消息 Log.v("looper的handler", Thread.currentThread().getName() + " - " + msg.what); //修改textview會報錯的。 //show.setText(""); } }; } @Override public void onClick(View v) { mHandler.sendEmptyMessage(200); } /** * Looper線程 */ class LooperThread extends Thread { private Looper looper; public Looper getLooper() { return looper; } @Override public void run() { super.run(); Looper.prepare(); looper = Looper.myLooper(); Looper.loop(); } } }
初始化的時候先創建LooperThread線程,且啟動。
然後裡面就會有looper了。
在創建handler的時候傳入上面的looper。那麼此時的handler已和LooperThread的消息隊列綁定了。這個handler所發送和處理的消息只經過LooperThread的消息循環,
跟UI線程已有的消息循環沒關系了。
最後有一點需要注意:
public void handleMessage(Message msg) { //處理消息 Log.v("looper的handler", Thread.currentThread().getName() + " - " + msg.what); //修改textview會報錯的。 //show.setText(""); }
這個處理消息的方法,不能再處理UI線程創建的UI控件了。如果你真心想修改某一個ui,那的保證這個ui必須在LooperThread裡面創建和添加到window中去。
Android 開源項目第一篇——個性化控件(View)篇包括ListView、ActionBar、Menu、ViewPager、Gallery、
Android RadioButton 圖片位置與大小Java:rgGroup = (RadioGroup) findViewById(R.id.re_group);
總體上Music App分為UI界面、服務兩個模塊,其中關於音樂文件的播放都由服務負責,服務配合AIDL使用的,界面綁定服務後可以拿到服務裡所有參數及狀態進行UI刷新。A
在之前的Android超精准計步器開發-Dylan計步中的首頁用到了一個自定義控件,和QQ運動的界面有點類似,還有動畫效果,下面就來講一下這個View是如何繪制的。1.先