Android 组件Service(二)之Handler、AsyncTask异步消息
Handler消息机制
Android 不允许子线程中进行UI操作的,但有时必须在子线程里去执行一些耗时的任务,根据任务执行结果来更新相应的UI控件。
Android提供了一套异步消息处理机制,完美解决了在子线程中进行的UI操作问题。Handler机制设计目的是要解决多线程的问题,比如多个线程去更新UI,或造成混乱,性能低下。Handler通过消息队列,保证消息处理的有序进行。这所有的更新UI操作都是在主线程中依次处理的。
关于Handler 类中的一些方法的使用
Android中异步消息处理主要有四部分组成:Message、 Handler、 MessageQueue和Looper。
- Message
是在线程之间传递的消息,可在内部携带少量的信息,用于在不同线程之间交换数据。 - Handler
用于发送和处理消息的。发送消息一般使用Handler的sendMessage()方法,发出的消息经过一系列处理后会传递到 Handler的handlerMessage()方法中 - MessageQueue
这是消息队列的意思,用于存放所有通过Handler发送的信息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。 - Looper
是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无线循环当中,然后每当发现MessageQueue中存在一条信息,就会将它取出并传递到Handler的handleMessage()方法中。每个线程只会有一个Looper对象。
现在来看handler消息处理的流程:
1. 在主线程中创建Handler对象,并重写handlerMessage()方法;
2. 当子线程需要操作UI时,就在子线程中创建Message对象,并通过Handler发出这消息;
3. 这消息添加到MessageQueue队列中,Looper可以取出这队列中的消息,分回给handlerMessage()中,这时handlerMessage方法中的代码就在主线程(UI线程)中了。
package com.example.androidthreadtest;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
class MainActivity extends AppCompatActivity {
private TextView tv;
private Button btn;
public static final int UPDATE_TEXT = 0;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn:
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
}
}).start();
break;
default:
break;
}
}
});
handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
tv.setText("It`s changed.");
break;
default:
break;
}
}
};
}
}
AsyncTask
这个工具更加方便在子线程里对UI进行操作,可以十分简单的从子线程切换到主线程。实际上是实现了 异步消息处理机制 ,这个只是做好了封装。
UI所在的线程一般被称为主线程,这里进行的操作越短越快越好,让用户感受到界面的流畅。所以要把费时的任务放到子线程中去,AsyncTask这些异步消息处理机制,为了让子线程切换到主线程上,可以进行UI操作。
AsyncTask<Params, Progress, Result>
是一个抽象类,必须创建一个子类去继承它,指定三个泛型参数:
1. Params、执行时需要传入的参数
2. Progress、后台执行任务时,如果需要在界面上显示当前的进度,这里用指定的泛型为进度单位
3. Result、当任务执行完毕后,如果需要对结果进行返回,则用指定的泛型作为返回值类型
经常需要重写以下四个方法
1. onPreExecute() 后台任务执行前,界面初始化的工作;
2. doInBackground(Params…) 方法中所有代码都会在子线程中运行,不过应该在这里去处理耗时的任务。这个方法不可以进行UI操作的,如果需要更新UI元素,如当前任务执行进度,可以在后台任务中调用publishProgress(Progress..)方法之后onProgressupdate()便会调用;
3. onProgressupdate(Progress…) 这个方法中可以对UI进行操作,利用参数中的数值对UI界面元素进行相应地更新;
4. onPostExecute(Result…) 后台任务执行完毕并通过return语句进行返回时,这个方法就会被调用。返回的数据作为参数传递到此方法中,进行一些UI操作。
简单来说,使用AsyncTask 的诀窍就是,在doInBackground()方法中去执行具体的耗时任务,在onProgressUpdate()方法中进行UI 操作,在onPostExecute()方法中执行一些任务的收尾工作。
public class MyAsyncTask extends AsyncTask<Void,String,String> {
public ProgressDialog dialog;
@Override
protected void onPreExecute(){
Log.d("MyAsyncTask", "onPreExecute...");
}
@Override
protected String doInBackground(Void...params) {
Log.d("MyAsyncTask", "doInBackground...");
//在这子线程任务中调用publishProgress()更新数据到UI上。
publishProgress("aaa");
return null;
}
@Override
protected void onProgressUpdate(String... params){
Log.d("MyAsyncTask", "onProgressUpdate..."+params);
}
@Override
protected void onPostExecute(String s){
super.onPostExecute(s);
Log.d("MyAsyncTask", "onPostExecute...");
}
}
package com.example.downloadservicetest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button btn;
private Button btn_update;
private MyAsyncTask myAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myAsyncTask = new MyAsyncTask();
btn = (Button) findViewById(R.id.btn);
btn_update = (Button) findViewById(R.id.btn_update);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//执行后台任务
myAsyncTask.execute();
}
});
}
}
上面的例子比较简单,仅供实验参考