通过这篇文章你能学习到什么?
- 通过子线程更新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的开源框架,但是篇幅有限。下次有机会再说~