通過這篇文章你能學習到什麼?
- 通過子線程更新UI組件:handler
- AsyncTask
首先我們介紹handler。
一、異步消息處理的主要組成部分
-
Messger
在線程之間傳遞消息,而且其內部攜帶少量信息。用於在不同的線程之間交換數據。
-
Handler
用於發送和處理信息。一般發送信息使用其handler.sendmessger()方法,經過一系列的處理以後,最後會發送到handler的handleMessger()方法中。
-
MessgerQueue
用於存放所有handler發送的信息。這些信息會一直存放在隊列中,等待被處理。
-
Looper
looper相當於messgerQueue的管家。每當messgerQueue中存在一條信息時,就會調用looper中的loop()方法,將信息取出。並傳遞到handle的handleMessger()方法中。
二、代碼演示
我們主要想實現通過點擊按鈕,改變TextView中的顯示內容。
首先我們在xml文件中創建一個button以及一個textview。(代碼較爲簡單,就不展示了)
然後修改MainActiviy:
public class MainActivity extends AppCompatActivity {
public static final int UPDATE_text = 1; //設置一個標識符
private TextView textView;
@SuppressLint("HandlerLeak")
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case UPDATE_text:
textView.setText("nihaoshijie"); //改變Ui組件內容
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { //設置一個點擊事件
@Override
public void onClick(View v) {
new Thread(){ //設置一個子線程
@Override
public void run() {
Message message = new Message(); //設置一個messger對象
message.what = UPDATE_text;
handler.sendMessage(message); //發送數據
}
}.start();
}
});
}
}
具體的代碼如上圖圖所示,下面我們來根據代碼分析下其異步處理的主要思路。
三、思路分析
首先我們需要明白一個點:
Android不推薦在子線程中直接更新UI組件,可能會出現不可知的錯誤。
而我們用handler異步處理機制。先在主線程中創建一個handler對象,並重寫其handleMessger方法。然後當子線程中需要對UI進行處理的時候,就會創建一個messger對象,並通過handler將其發送出去。之後這條信息就會被添加到MessgerQueue的隊列中等待被處理。而Looper會一直不斷的嘗試從MessgerQueue取出待處理信息,最後分發到Handler的handleMessger()方法中。由於handler是在主線程中創建的,所以handleMessger方法也在主線程中運行。這樣就能安心的對ui進行操作啦。
下面我們來介紹AsyncTask
一、基本使用
首先我們需要爲AsyncTask類指定三個泛型參數
-
Params
在執行AsyncTask需要傳遞的參數,可用於在後臺任務使用。
-
Progress
後臺任務執行時,如果需要在界面上顯示當前的進度,則使用這裏指定的泛型作爲進度單位。
-
Result
當任務執行完成以後,如果需要對結果進行返回,則使用這裏指定的泛型作爲返回值類型。
重寫幾個方法
-
onPreExecute()
這個方法在後臺任務開始執行之前調用,用於進行一些界面上的初始化操作。
-
doInBackground()
這個方法中的所有代碼都會在子線程中運行,我們應該在這個去處理所有的耗時任務。
-
onProgressUpdate()
這個方法可以對UI進行操作,利用參數中的數值就可以對界面元素進行相應的更新。
-
onPostExecute()
當後臺任務執行完畢並且用過return語句進行返回的時候,這個方法就會被調用。返回的數據會作爲參數傳遞到此方法中,可以利用一些返回的數據進行一些UI操作。
二、具體代碼演示
我們來製作一個倒計時的程序:
首先xml文件代碼:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="50dp" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="倒計時"
android:textSize="30sp"
android:layout_gravity="center"
/>
<Button
android:id="@+id/bt"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="開始倒計時"
android:textSize="20sp"/>
</LinearLayout>
然後是MainActivity中的代碼:
public class MainActivity extends AppCompatActivity {
private EditText et;
private TextView tv;
private Button bt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindID();
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int time = Integer.parseInt(et.getText().toString());
new MyTask().execute(time);
}
});
}
private void bindID() {
et = findViewById(R.id.et);
tv = findViewById(R.id.tv);
bt = findViewById(R.id.bt);
}
@SuppressLint("StaticFieldLeak")
class MyTask extends AsyncTask<Integer,Integer,String> {
@Override
protected String doInBackground(Integer... integers) {
for (int i=integers[0];i>0;i--){
try {
Thread.sleep(1000);
publishProgress(i);//調用onProgressUpdate方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "計時結束";
}
@SuppressLint("SetTextI18n")
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
tv.setText(values[0]+"");
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
tv.setText(s);
}
}
}
整體的代碼邏輯還是比較簡單的,主要是使用一下AsyncTask這個方法。
那麼對於Android的異步消息處理機制就說到這裏了。本來想加上一個RxJava的開源框架,但是篇幅有限。下次有機會再說~