編輯:關於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開源項目- 匯總
Android 開源項目第一篇——個性化控件(View)篇包括ListView、ActionBar、Menu、ViewPager、Gallery、
Android RadioButton 圖片位置與大小實例詳解
Android RadioButton 圖片位置與大小Java:rgGroup = (RadioGroup) findViewById(R.id.re_group);
Music app框架設計及總結
總體上Music App分為UI界面、服務兩個模塊,其中關於音樂文件的播放都由服務負責,服務配合AIDL使用的,界面綁定服務後可以拿到服務裡所有參數及狀態進行UI刷新。A
Android 自定View實現仿QQ運動步數圓弧及動畫效果
在之前的Android超精准計步器開發-Dylan計步中的首頁用到了一個自定義控件,和QQ運動的界面有點類似,還有動畫效果,下面就來講一下這個View是如何繪制的。1.先