Android异步消息处理机制——AsyncTask

原理:

使用 ThreadPool + Handler 实现:在子线程进行耗时操作,UI线程实现UI更新,防止阻塞UI线程。即:子线程向UI线程切换。

执行步骤
  1. 创建AsyncTask实例(必须在UI线程) new AsyncTask(Params… params)
  2. asynctask.excute(),使用线程池执行异步任务(必须在UI线程)
 WorkerRunnable extents Callable<T>;
 public AsyncTask(@Nullable Looper callbackLooper) {
    mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };
    mFuture = new FutureTask<T>(mWorker);
 }
  public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        // 状态检测
        *****
        
        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        
        exec.execute(mFuture); =》 sDefaultExecutor.excute(mFuture)

        return this;
    }

  1. onPreExecute():运行在UI线程,所以可以做一些任务开始前的UI更新
  2. doInBackground(params):执行在Callable(子线程)的call()中,做一些耗时操作,并且可以得到耗时结果-result,然后调用postResult(result)。将result封装成message,通过handler将message传送到UI线程中的handleMessage(message)。
public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    // 在doInBackgound中手动调用publishProgress,会发送该事件
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
  1. onProgressUpdate(result):执行在UI线程中,可以显示进度
  2. onPostExecute(result):执行在UI线程中,得到dongInBackground的耗时结果,刷新UI。
注意事项
  1. 一个AsyncTask实例只能执行一次excute,因为在excute中会有状态检测。
if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("***");
                case FINISHED:
                    throw new IllegalStateException("***");
            }
        }
mStatus = Status.RUNNING;
  1. AsyncTask的创建于执行必须在UI线程。
  2. AsyncTask使用了线程池,那么线程池的核心线程数最大线程数是如何计算的?
    // 获取当前设备德 CPU (现在普遍是6核)
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // 核心线程数范围 [2,4]
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    // 最大线程数公式:2*CPU+1
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
  1. Callable与Runnale的区别
    Callable只是一个可以返回数据的接口,只有在将它与FutureTask组合后(new FutureTask<Params,Result>(Callable callable)),才能实现子线程返回数据的功能。
    FutureTask implements RunnableFuture; RunnableFuture extents Runnable, Future。
    普通的Runnable是不带执行结果返回的。但是使用FutureTask + Callable便可以实现带有返回结果的任务。
扩展
  1. 何时出发线程池的拒绝策略?
    提交的任务数大于 CORE_POOL_SIZE 时,会优先将任务放到 队列缓冲区(QueueCapacity) 中,当把 队列缓冲区 填满后,再去判断当前运行的任务数是否大于 MAXIMUM_POOL_SIZE ,若小于则创建新的线程处理任务,若是大于时则会触发拒绝策略。总结:T(触发条件) = Runnable > (MAXIMUM_POOL_SIZE + QueueCapacity).
  1. 线程池的拒绝策略有几种?
    CallerRunsPolicy(调用者策略):
    AbortPolicy(中止策略):
    DiscardPolicy(丢弃策略):
  1. MessageQueue(链表)是实时插入Message还是延时插入Message?
bool enqueueMessage(Message msg, long when){
    ....
    // 遍历链表,按照执行时间顺序插入链表
    for(;;){
        pre = p;
        p = p.next;
        if(p == null && when < p.when){
            break;
        }
        ....
    }
    // 插入链表
    msg.next = p;
    pre.next = msg;
    ...
}

  1. Message的类型有哪些?
  1. 异步消息
// 通过Handler的构造创建异步消息,通过这种方式,则内部所有的消息都是异步的

public void Handler(Callback callback, bool async){
    ...
    mCallback = callback;
    mAsynchronous = async;
}

// 通过主动调Message.setAsynchronous(true),将消息设为异步
Message msg = Message.obtain();
msg.setAsynchronous(true);
mHandler.sendMessage(msg);

  1. 同步消息
// 凡是没有设置mAsynchronous = true的消息都是同步消息
Message msg = new Message();
msg = Message.obtain();
handler.sendMessage(msg);

handler.post(new runnable());

  1. 同步屏障:阻塞MessageQueue中的同步消息,只有正在屏障标识移除后,MessageQueue才会被唤醒。对异步消息没有作用
// 使用场景:在刷新UI时,会发送一个同步屏障,阻塞MessageQueue,保证UI线程刷新时阻塞同步消息。
ViewRootImpl中
void scheduleTraversals(){
    ···
     mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
    ···
}

void unscheduleTraversals() {
    ···
    mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    ···
}

// 发送一个消息屏障
public int postSyncBarrier(long when){return token;}
// 移除一个消息屏障
public void removeSyncBarrier(int token) {}

  1. Handler机制的阻塞与唤醒:(插入消息时唤醒,读取消息时阻塞)。结合生产者/消费者模式理解。

阻塞调用栈:Looper.loop() -> MessageQueue.next() -> nativePollOnce() -> MessgaeQueue.cpp->pollOnce() -> Looper.cpp->pollOnce() - > Lopper.cpp.pollInner() -> epoll_wait() -> read() 从队列中读取消息,没有消息时,阻塞队列。
唤醒调用栈:Handler.sendMessageAtTime() -> MessageQueue.enqueueMessage() -> nativeWake() -> MessageQueue.cpp->wake() -> Looper.cpp.wake() -> write() -> 向队列中写入消息,唤醒队列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值