在Android中,只可以在UiThread(UI主线程)才可以直接更新界面,不然会抛出异常。
WHY:
防止多个线程来修改界面,导致混乱
通过同步锁来防止界面混乱会导致性能降低 。
在Android中,长时间的工作(比如联网)都需要在workerThread(分线程/工作线程)中执行。
在分线程中获取服务器数据后,可通过消息的传递进行线程间的通信,到主线程中更新界面显示。
Message可以理解为线程间通讯的消息单元,可通过message携带需要传递的数据。
封装数据:
public int what // 标识消息的id
public int arg1 // 可用来携带int类型的数据
public int arg2 // 可用来携带int类型的数据
public Object obj //可用来携带一个对象数据
与一个Handler对象绑定
Handler target //绑定的一个handler对象,由该handler对象发送和处理该消息。
创建对象:
Message.obtain() 从**消息池**中获取消息 /new Message() 重新创建一个消息。
Handler是消息Message的处理器,同时负责消息的发送、处理和未处理消息的移除工作。
具体方法
发送即时消息:sendMessage(Message msg)
发送延时消息:sendMessage(Message msg, long time) 【延时处理】
发送带标识的空消息:sendEmptyMessage(int what)
立即发送Message到队列的最前面:sendMessageAtFrontOfQueue()
处理消息:handleMessage(Message msg) 【这是一个回调方法,需要重写回调方法来处理消息】
移除还未处理的消息:removeMessage(int what)
MessageQueue用来存放通过Handler发送过来的消息
消息队列MessageQueue是一个按Message的when排序的优先级队列,先进先出。
即时消息:when = 发送时的当前时间
延时消息:when = 发送时的当前时间 + 延迟时间
MessageQueue内部使用链表来实现,容易对队列进行删/增结点
消息泵,不断地从MessageQueue中抽取Message执行。一个MessageQueue对应一个Looper。
负责循环取出消息队列MessageQueue里面的当前需要处理的消息Message
负责将取出的消息Message交给对应的目标Handler来处理。
处理完之后,将Mesaage缓存到消息池中,以备复用。
1.`Activity的runOnUiThread方法在主线程中调用。(内部还是使用下面的Handler的post方法)
//在分线程获取数据,获取到数据在主线程中更新UI
new Thread(){
@Override
public void run() {
super.run();
final String jsonResult = request(httpUrl, httpArg);
//分线程获取网络数据,获取数据完后再调用下面的方法
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if(jsonResult!=null)
TextView.setText(jsonResult);
//主线程UiThread中更新界面
}
});
}
}.start();
2. View.post(Runnable) : Runnable与View在同一个线程。
mScrollView.post(new Runnable() {
@Override
public void run() {
mScrollView.scrollTo(0,0);
}
});
handler的post方法:Runnable与handler在同一个线程。
//在分线程获取数据,获取到数据在主线程中更新UI
new Thread(){
@Override
public void run() {
super.run();
final String jsonResult = request(httpUrl, httpArg);
//分线程获取网络数据
// (mHandler在主线程)
mHandler.post(new Runnable() {
@Override
public void run() {
if(jsonResult!=null)
TextView.setText(jsonResult);
//主线程UiThread中更新界面
}
});
}
}.start();
创建Handler成员变量对象,并重写其handleMessage(Message msg)方法。
在分线程/主程序创建Message对象,使用Message消息携带异步获得的消息。
使用Handler对象发送Message
在handleMessage(Message msg) 回调方法中获取数据。(也可以在该方法中发送延时消息,从而形成一种定时循环,停止循环时使用removeMessage(int what)方法即可)
// 写在MainActiviy中
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==0)
TextView.setText((String) msg.obj);
}
};
// 写在MainActivity的onCreate中
new Thread(){
@Override
public void run() {
super.run();
//异步线程获取数据
final String jsonResult = request(httpUrl, httpArg);
//在分线程/主程序创建Message对象,使用Message消息携带异步消息。
Message msg = Message.obtain();
msg.what = 0;
msg.obj = jsonResult;
handler.sendMessage(msg);//发送即时消息
}
}.start();
“`
主要通过Handler中的dispatchMessage方法对Message对象处理
下图为dispatchMessage(Message msg)的源码
主要体现在 Handler.post(Runnable r) / View.post(Runnable r) 方法中。该方法将传入的参数Runnable对象封装成一个Message对象,该Message对象的callback就是传入Runnable对象r.
在dispatchMessage源码中,当Message对象的回调callback不为空时,是通过handleCallback来处理该消息的。下面为handleCallback的源码:
private final void handleCallback(Message message) {
message.callback.run();
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
public Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
//TODO
return false;
//返回false时不对该消息拦截,因此下面的消息会被再handleMessage处理
//返回true时对该消息拦截,因此下面的消息不会被再处理
//可以不复写下面的方法
}
}){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//TODO
}
};
如果通过按钮的点击事件来使用handler传递消息启动异步工作,那么一个异步工作启动后要限制住按钮的可操作性,防止启动多个异步工作。(比如设置逻辑:如果再次点击按钮,则重新启动异步工作,而不是再次启动另一个异步工作;或者设置按钮不可以点击。)
在没有AsyncTask之前,我们可以使用Handler+Thread来实现异步任务的功能需求。
AsyncTask是对Handler和Thread的封装,使用它编码更简洁,效率更高。
AsyncTask封装了ThreadPool线程池,实现了线程的复用,比直接使用Thread效率更高。
API
1.AsyncTask //不能使用基本数据类型
Params : 启动任务执行的输入可变参数泛型
Progress:后台任务执行的百分比 //一般为Integer类型
Result:后台执行任务后返回的结果泛型
2.execute(Params...params)
//启动任务
3.void onPreExecute()
//在分线程工作开始之前在UIThread中执行
4.Result doInBackground(Params..params)
//在分线程中执行,完成任务的主要哦工作
5.void onPostExecute(Result reslut )
// 在doInBackground执行完后在UiThread中执行,一般用来更新界面
6.publishProgress(Progress..values)
//在分线程中,发布当前进度
7.void onProgressUpdate(Progress..values)
//在主线程中,更新当前进度
在分线程里不要进行UI操作,比如在doInBackground执行Toast提示操作。
实例:
new DemoAsyncTask(.......).execute(...);
//自行设计泛型/参数,进度和返回结果的类型,补充各个以上方法即可
AysncTask是一个抽象类:下列代码片段摘自AsyncTask源码(Android-23)
AsyncTask的三种状态:等待、运行、完成
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
AsyncTask的构造方法
A. Params和Result为我们实现AsyncTask时传入的参数和返回结果类型
B. 在构造函数中,我们写的后台操作 doInBackground 先被封装为一个WorkerRunnable对象mWorker,再将mWorker封装为一个FutureTask对象mFuture。mFuture的任务执行结束后调用done方法通过handler来传递返回的结果:
C. 在构造方法中,mFuture任务的执行线程并没有马上启动。
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
------------------------
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
AsyncTask中的execute方法
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
然后再看一下executeOnExecutor方法:
A. 在该方法中,先判断AsyncTask对象的状态,状态不为等待时则抛出异常
B. 然后在方法中调用我们的预处理方法 onPreExecute(在主线程中调用),最后设置一下异步任务对象的状态和参数,再将上述的FutureTask对象mFuture执行。
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
C. exec是一个SerialExecutor对象(线程池),这里将mFuture在线程池中执行。
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
AsyncTask执行任务时并行/串行?
在Android 1.6之前的版本,AsyncTask是串行的,在1.6至2.3的版本,改成了并行的。在2.3之后的版本又做了修改,可以支持并行和串行,当想要串行执行时,直接执行execute()方法,如果需要并行执行,则要执行executeOnExecutor(Executor)
AysncTask会使用到线程池,当异步任务特别庞大时,线程池的优势就会显示出来。当异步任务比较多时,可使用AsyncTask来完成异步操作。单个任务使用AsyncTask会销毁较多的资源。
当然AsyncTask的线程池个数也是有限的
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
以8核手机为例,其核心线程数是9个,最大线程是17,所能最大加入的任务数是128+17=145
Android异步消息处理机制完全解析,带你从源码的角度彻底理解