zoukankan      html  css  js  c++  java
  • async分析

    转自CSDN博客:https://blog.csdn.net/zty19901005/article/details/50727106

    AsyncTask概述

    AsyncTask是安卓为简化子线程访问ui操作提供的一个线程类工具类,但是AsncTask在Android系统的不断升级中,经过了多次修改,导致不同的API版本上使用AsyncTask具有不同的表现,接下来主要分析AsyncTask的使用与注意事项。

    AsyncTask是一个轻量级的一部任务类,它可以在子线程执行任务,然后把执行结果分发到主线程进行处理,原理上来讲,AsyncTask是对线程池和Handler的封装,而且AsyncTask**并不适合执行特别耗时的后台任务**,否则很有可能造成内存泄漏。

    AsyncTask使用

    AsyncTask的定义如下:

     
    public abstract class AsyncTask<Params, Progress, Result>{.....}
    
    • 1
    • 2

    AsyncTask定义了三个泛型参数,如果不需要传递具体的参数可以使用Void代替,这三个泛型参数与AsyncTask定义的几个核心方法有关:

     
     
     
    //主线程执行,异步任务执行前被调用   
    protected void onPreExecute() {
    }
    
    //工作线程执行,用于执行任务,方法参数由泛型参考Params确定,返回参数有Result参数规定
    protected abstract Result doInBackground(Params... params);
    //主线程执行,用于处理doInBackground的返回值
    oprotected void onPostExecute(Result result) {
    }
    
    //需要在doInBackground方法中调用,用于更新任务的执行进度,方法参数有Progress泛型参数规定
    protected final void publishProgress(Progress... values) {
    //在主线程被调用,当在doInBackground调用publishProgress分发任务进度是,此方法就会被调用
    protected void onProgressUpdate(Progress... values) {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    其次,AsyncTask还提供了onCancel方法,它同样在主线程执行,当调用AsyncTask的cancel方法时,任务被取消,onCancel被调用,而onPostExecute不会被调用

    AsyncTask的使用注意事项

    1. AsyncTask的类必须在主线程被加载,这意味着第一次访问AsycnTask必须在主线程,当然这个过程在Android4.1以上版本系统已经自动完成,在android5.0的源码中ActivityThread的main方法中就掉用了AsyncTask.init()方法(具体为什么必须在主线程被加载,稍后分析)

    2. AsyncTask对象必须在住下次被创建

    3. execute方法必须在主线程被调用

    4. 不要主动调用onPreExecute等方法

    5. 一个AsyncTask只能被执行一次,否则会有异常

    6. AsyncTask在不同系统版本的表现

      • Android1.6之前:AsyncTask是串行执行的
      • Android1.6开始:AsyncTask开始使用线程池处理并行任务,他是并行的
      • Android3.0开始:为了避免AsyncTask带来的并发错误,AsyncTask又开始使用单个线程来执行任务,但那时在3.0之后,可以使用AsyncTask的executeOnExecutor方法来并行的执行任务。

    AsyncTask的工作原理

    了解AsyncTask的工作原理,先从它的execute方法开始分析:

     
     
     public final AsyncTask<Params, Progress, Result> execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
        }
    
     public final AsyncTask<Params, Progress, Result> 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    execute调用了executeOnExecutor方法,executeOnExecutor方法会把mFuture交给sDefaultExecutor去执行,看到这里,我们需要先了解一下executeOnExecutor中的几个变量: 
    - mStatus: 用于记录一个AsyncTask的运行状态 
    - mWorker:它的类型是WorkRunnbale在AsyncTask构造方法被创建,WorkRunnbale实现了Callable接口,mWorker作为WorkRunnbale的子类,实现了来自Callable的call方法,并在call方法中调用了doInBackground方法。 
    - mFuture:它的实际类型是FutureTask,是一个并发相关类,它的父类实现了Runnable和Future接口,它本身接收一个Callable类型对象,当它被提交给一个任务执行器是,它的run(来自Runnable)方法被调用,而在run中会调用Callable的call方法。

    具体我们可以看AsyncTask的构造方法:

     
    public AsyncTask() {
            mWorker = new WorkerRunnable<Params, Result>() {
                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<Result>(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);
                    }
                }
            };
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    可以看到mFuture接收的Callable对象正是mWorker。

    接着分析executeOnExecutor方法,任务执行的参数params赋值给了mWorker.mParams,然后把mFuture叫给了sDefaultExecutor执行,下面来分析一些这个sDefaultExecutor:

     
    private static class SerialExecutor implements Executor {
            final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
            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);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    sDefaultExecutor就是一个SerialExecutor,它内部维护着一个mTasks队列,用于存储进程内AsyncTask提交的任务,看SerialExecutor的execute方法,接收的mFuture并不是直接运行,而是封装成为一个Runnable插入到队列中去,然后判断mActive是否为null,如果mActive=null,则会调用scheduleNext取出一个任务交给THREAD_POOL_EXECUTOR去真的执行任务,只有当一个任务执行完毕,才会再一次调用scheduleNext方法,由此分析AsyncTask确实是串行执行任务的。

    可见AsyncTask有两个线程池: 
    - SerialExecutor 用于对任务进行串行调度,而不是真的执行任务 
    - THREAD_POOL_EXECUTOR真正的任务执行器,

    上面分析到当mFutrue被提交到线程池中,他的run方法被调用,run方法调用Callable发call,这里对应的就是mWorker的call方法:

     
     mWorker = new WorkerRunnable<Params, Result>() {
                    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);
                    }
                };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在call方法中,调用了AsyncTask的doInBackground方法,然后调用postResult(result)方法分发执行结果:

     
     
     
        private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这里调用getHandler()获取的Handler发送了消息,消息中封装了任务的执行结果。先来看一下 
    getHandler(),

     
     
        private static Handler getHandler() {
            synchronized (AsyncTask.class) {
                if (sHandler == null) {
                    sHandler = new InternalHandler();
                }
                return sHandler;
            }
        }
     private static class InternalHandler extends Handler {
            public InternalHandler() {
                super(Looper.getMainLooper());
            }
    
            @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
            @Override
            public void handleMessage(Message msg) {
                AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
                switch (msg.what) {
                    case MESSAGE_POST_RESULT:
                        // There is only one result
                        result.mTask.finish(result.mData[0]);
                        break;
                    case MESSAGE_POST_PROGRESS:
                        result.mTask.onProgressUpdate(result.mData);
                        break;
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    它是Handler的子类,在调用getHandler()方法如果没有初始化,则它会被初始化,这里也说明了为什么AsyncTask必须在主线程调用的原因,

    各个说到postResult方法把执行结果封装成AsyncTaskResult对象,然后发送了一个MESSAGE_POST_RESULT类型的消息,很显然这个消息会在InternalHandler中被处理,处理代码如下:

    result.mTask.finish(result.mData[0]);
    
    • 1
    • 2

    AsyncTask的finish被调用:

     
     
     
     
        private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    方法很简单,如果任务被取消,则调用onCancelled方法,否则调用onPostExecute方法,最后把任务的状态置为FINISHED。

  • 相关阅读:
    最近这段时间我,想在2008 的基础上,写2011 有的工具 不知道会样,这次又机会研究ploy
    Screen 可以查找屏幕pos系类的函数
    笔记1
    Ubuntu下如何安装 tar.bz2 文件
    安装ubuntu遇到“BusyBox”问题
    android luancher 如何添加快捷方式
    转 Android 源代码结构
    修改apk图标
    Linux Ubuntu 下如何安装 .SH文件
    解放你的电源键!!不用刷机不用装软件!超简单修改搜索锁屏、HOME键唤醒~~~~~
  • 原文地址:https://www.cnblogs.com/qiyi001/p/8861768.html
Copyright © 2011-2022 走看看