Android的單線程模型


當一個程序第一次啟動時,Android會同時啟動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如:用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。所以主線程通常又被叫做UI線程。

在開發Android 應用時必須遵守單線程模型的原則: Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。

如果在非UI線程中直接操作UI線程,會拋出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views,這與普通的java程序不同。

由於UI線程負責事件的監聽和繪圖,因此,必須保證UI線程能夠隨時響應用戶的需求,UI線程里的操作應該向中斷事件那樣短小,費時的操作(如網絡連接)需要另開線程,否則,如果UI線程超過5s沒有響應用戶請求,會彈出對話框提醒用戶終止應用程序。

如果在新開的線程中需要對UI進行設定,就可能違反單線程模型,因此android采用一種復雜的Message Queue機制保證線程間通信。

 

 

Message Queue:

Message Queue是一個消息隊列,用來存放通過Handler發布的消息。Android在第一次啟動程序時會默認會為UI thread創建一個關聯的消息隊列,可以通過Looper.myQueue()得到當前線程的消息隊列,用來管理程序的一些上層組件,activities,broadcast receivers 等等。你可以在自己的子線程中創建Handler與UI thread通訊。 

通過Handler你可以發布或者處理一個消息或者是一個Runnable的實例。每個Handler都會與唯一的一個線程以及該線程的消息隊列管理。

Looper扮演着一個Handler和消息隊列之間通訊橋梁的角色。程序組件首先通過Handler把消息傳遞給Looper,Looper把消息放入隊列。Looper也把消息隊列里的消息廣播給所有的Handler,Handler接受到消息后調用handleMessage進行處理。

實例如下:

public void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      setContentView(R.layout.main);      editText = (EditText) findViewById(R.id.weather_city_edit);      Button button = (Button) findViewById(R.id.goQuery);      button.setOnClickListener(this);       Looper looper = Looper.myLooper();  //得到當前線程的Looper實例,由於當前線程是UI線程也可以通過Looper.getMainLooper()得到       messageHandler = new MessageHandler(looper);  //此處甚至可以不需要設置Looper,因為 Handler默認就使用當前線程的Looper   }   public void onClick(View v) {      new Thread() {         public void run() {             Message message = Message.obtain();             message.obj = "abc";             messageHandler.sendMessage(message);  //發送消息         }      }.start();   }   Handler messageHandler = new Handler {      public MessageHandler(Looper looper) {         super(looper);     }     public void handleMessage(Message msg) {         setTitle((String) msg.obj);      }  }

對於這個實例,當這個activity執行玩oncreate,onstart,onresume后,就監聽UI的各種事件和消息。

當我們點擊一個按鈕后,啟動一個線程,線程執行結束后,通過handler發送一個消息,由於這個handler屬於UI線程,因此這個消息也發送給UI線程,然后UI線程又把這個消息給handler處理,而這個handler是UI線程創造的,他可以訪問UI組件,因此,就更新了頁面。

由於通過handler需要自己管理線程類,如果業務稍微復雜,代碼看起來就比較混亂,因此android提供了AsyncTask類來解決此問題。

 

AsyncTask:

首先繼承一下此類,實現以下若干方法,

onPreExecute(), 該方法將在執行實際的后台操作前被UI thread調用。可以在該方法中做一些准備工作,如在界面上顯示一個進度條。 

doInBackground(Params...), 將在onPreExecute 方法執行后馬上執行,該方法運行在后台線程中。這里將主要負責執行那些很耗時的后台計算工作。

可以調用publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。 

onProgressUpdate(Progress...),在publishProgress方法被調用后,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。 

onPostExecute(Result), 在doInBackground 執行完成后,onPostExecute 方法將被UI thread調用,后台的計算結果將通過該方法傳遞到UI thread.

使用時需要遵循以下規則:

1)Task的實例必須在UI thread中創建 

2)execute方法必須在UI thread中調用 

3)不要手動的調用這些方法,只調用execute即可

4)該task只能被執行一次,否則多次調用時將會出現異常

示例如下:

public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.main);          editText = (EditText) findViewById(R.id.weather_city_edit);          Button button = (Button) findViewById(R.id.goQuery);          button.setOnClickListener(this);   }    public void onClick(View v) {          new GetWeatherTask().execute(“aaa”);   }   class GetWeatherTask extends AsyncTask<String, Integer, String> {       protected String doInBackground(String... params) {            return getWetherByCity(params[0]);       }      protected void onPostExecute(String result) {            setTitle(result);     }   }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM