close
執行緒Thread、HandlerThread的Android範例
如果有用過java差不多都有學過Thread-多執行緒
這裡先做Thread小介紹:
多執行緒的機制可以同時執行多個程式區塊,使程式執行的效率變高
如果要某類別啟動執行緒則必須繼承自Thread類別,而執行緒的處理必須撰寫在run()方法內,但執行時是呼叫start()
class 類別名稱 extends Thread { 類別裡的成員資料; 類別裡的方法; 修飾子 run() //改寫Thread類別裡的run()方法 { 以執行緒處理的程序; } }
在連續呼叫兩次start()時會同時執行 (建議程序內使用迴圈+延遲來看出是否有同時執行)
如本身以繼承某父類別只能使用Runnable介面
class 類別名稱 implements Runnable { 類別裡的成員資料; 類別裡的方法; 修飾子 run() { 以執行緒處理的程序; } }
執行時一樣使用start()
以上為Thread的小介紹,那跟Android有什麼關係呢?
原因是因為在Android中,如果做超過5秒就會被系統強制關閉(會收到Application Not Responsed簡稱ANR)
onCreate()如果做超過10秒就會跳ANR,所以繁重的事情不能放在onCreate()裡,那麼解決辦法當然就是使用多執行緒
Thread為執行緒(臨時工)
Runnable為申請的執行內容
一般的Thread就如同臨時工都是指定執行內容在 publice run() 裡面用執行start()做完就可以閃人了
有一種Thread性質不一樣會先執行 start() 等待工作透過Runnable申請到來
這種Thread執行緒如特約工人(上班族)一樣就算沒事也要執行著等待工作,而且需要有經紀人做規劃工作:
HandlerThread 為特約工人(上班族)
Handler 為經紀人
一般Android執行時會有個UI Thread或叫main Thread,這是程式本身原有的執行緒,用於管理畫面呈現....等等事物也是程式的進入點
而main Thread本身也是HandlerThread(特約工人),可以給予他一個Handler(經紀人)做接收訊息用途
有時候需要做一些比較花時間的運算,為了避免UI Thread停頓阻塞住我們必須使用另一個HandlerThread(特約工人)來幫我們做花時間的工作
如果在onCreate()內使用UI Thread需等到執行完畢才會去執行畫面呈現等等事物,所以為了避免UI工人太過於繁忙,背景動作都派遣給另一個工人,而畫面呈現時在使用UI Thread
以下為使用Thread與HandlerThread範例:
package com.example.thread_demo; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { HandlerThread ht=new HandlerThread("name"); //宣告在背景工作的特約工人,"name"為工人名子 Handler h,uih; //宣告2個經紀人,uih為UI Thread的經紀人 Thread t; //普通的執行緒工人 TextView t01; Button b01,cancel; int ht_i=1,t_i=10; //用來計數HandlerThread與Thread兩位工人各別顯示的行數 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); //連結控制項 t01.setText("Test Start:\n"); uih=new Handler(); //uih為UI Thread的經紀人可以直接使用=new Handler()獲取UI Thread,之後將能透過經紀人指派UI工人做事 ht.start(); //先讓特約工人開始執行(如果沒有執行不能交給經紀人) h=new Handler(ht.getLooper()); //把特約工人交給經紀人,找到特約工人的經紀人才能開始派遣工作 //作為HandlerThread添加工作的按鈕 b01.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub h.post(r0); //經紀人指派r0工作給工人 } }); //作為註銷HandlerThread執行緒指定任務的按鈕 cancel.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub if(h!=null) //用來取消排在任務列內的所有r0任務(如已經開始執行該任務,則無法取消) h.removeCallbacks(r0); t01.append("註銷HandlerThread內指定的任務\n"); } }); //讓臨時工執行緒派遣工作 t=new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub try { //顯示10次後就能閃人 while(t_i>0){ //判斷是否使用Thread顯示字串 runOnUiThread(new Runnable() { //可以使用此方法臨時交給UI做顯示 @Override public void run() { // TODO Auto-generated method stub t01.append("Thread臨時工倒數第"+t_i+"次顯示\n"); t_i--; } }); Thread.sleep(5000); //每隔5秒顯示一次 } runOnUiThread(new Runnable() { //可以使用此方法臨時交給UI做顯示 public void run(){ t01.append("Thread結束工作閃人\n"); } }); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); t.start(); //Thread開始執行 } //連結控制項 public void init(){ t01=(TextView) this.findViewById(R.id.t01); b01=(Button) this.findViewById(R.id.b01); cancel=(Button) this.findViewById(R.id.cancel); } //設置r0工作,每秒在t01添加一行(5次) private Runnable r0=new Runnable(){ //工作內容寫進run(),繁雜的工作可以給另一個特約工人做 public void run(){ try{ runOnUiThread(new Runnable() { //可以臨時交給UI做顯示 public void run(){ t01.append("特約工人接到工作開始執行:\n"); } }); Thread.sleep(1000); //每隔1秒顯示一次 for(int i=0;i<5;i++){ uih.post(ui0); //給UI Thread經紀人指派ui0工作,才不會崩潰(因android工具須給UI Thread工人做) Thread.sleep(1000); //每隔1秒顯示一次 } } catch(Exception e){ e.printStackTrace(); } } }; //設置ui0工作,替特約工人顯示TextView private Runnable ui0=new Runnable(){ @Override public void run() { // TODO Auto-generated method stub t01.append("特約工人執行第"+ht_i+"次\n"); ht_i++; } }; @Override protected void onDestroy(){ super.onDestroy(); if(ht!=null) //關閉該執行緒(需等待目前的任務執行完畢才會關閉) ht.quit(); } }
當你的執行緒需要不斷大量的更新 UI 的時後,還是需要使用UI Thread,所以提供非常方便的API
Activity.runOnUiThread( Runnable action )
顧名思義,引數所帶入的action,將會保證在UI Thread被執行
已經在剛剛的範例中示範過了
..... runOnUiThread(new Runnable() { public void run(){ //這裡為想讓他執行的工作 } }); .....
Android 4.0 之後,有明文規定所有的網路行為都不能在主執行緒(Main Thread)執行,主執行緒又稱UI執行緒(UI Thread),如果要在主執行緒做網路的事,就需要使用
Thread-執行緒或是AsyncTask-異步任務
任何有關UI的東西都在主執行緒中執行,若是你的程式佔據主執行緒很久,使用者體驗會非常的差
(當你什麼事都丟給UI Thread做時,會導致該Thread阻塞,自然無法去解決使用者的操作而導致LAG)
//使用該LOG可以知道當下是使用哪個執行緒,1為主執行緒 Log.d("text","thread id="+String.valueOf(Thread.currentThread().getId()).toString);
Thread可以理解為做完一件事馬上閃人的打工族
HandlerThread則是在未使用quit關閉前能不斷丟給他不同工作去執行的上班族
以上是個人在執行緒方面的一些理解,有更多資訊會在上來補充
文章標籤
全站熱搜
留言列表