Android 异步任务:AsyncTask深入探讨

来源:互联网 发布:英雄无敌3 数据修改 编辑:程序博客网 时间:2024/06/05 04:48

围绕 Thread和Handler基础实现的UI帮助类。可以在非UI线程中处理事务并将结果给UI主线程。
1 最多几秒钟的操作,如果需要保持后台任务执行很长时间。强烈建议使用java.util.concurrent包提供的APIs. 比如Executor、ThreadPoolExecutor、FutureTask等
2 继承AsyncTask<Params,progress,Result> 在调用execute()时传入Params.
3 必须实现Result doInBackground(Params…p) 具体指的是execute()时传入可变参数Params。
返回值是继承AsyncTask<Params,progress,Result>时的Result泛型实例化类型。
比如MyLoginAsyncTask extends AsyncTask<User user,Integer,Boolean> 传入的User参数,返回Boolean类型。
还要有接收任务处理的结果方法 onPostExecute(Result r);

4 可选实现 onPreExecute() 和 onProgressUpdate()
5 如果实现onProgressUpdate() 来更新UI进度显示View则要在doInBackground(Params…p)发布进度(publishProgress())
6 onPreExecute() 、onProgressUpdate()、onPostExecute(Result r);运行在Ui进程内。
7 执行顺序onPreExecute()[通常准备UI]-> doInBackground(Params…p)[可以调用publishProgress()->onProgressUpdate()]->onPostExecute(Result r) or onCanclled()
8 AsyncTask
只能在Ui线程中被创建 加载 和调用。开发者不能手动调用常用方法。
9*任务实例化后只能被执行一次 再次执行会抛出异常。
异步任务保证以下操作是线程安全的:
10 创建时设置外部参数或者在onPreExecute()引用这些参数
11 doInBackground(Params…p)设置参数和引用他们

荔枝:

public class UserRegisteTask extends AsyncTask<Void, Void, Integer> {    private final User user;    UserRegisteTask(String number, String password ,String name) {        user = new User();        user.setNameid(number);        user.setName(name);        user.setPassword(password);    }    @Override    protected Integer doInBackground(Void... params) {        UserService mUserService = new UserService(eventHandler);        return mUserService.register(user);    }    @Override    protected void onPreExecute() {        showProgress(true);    }    @Override    protected void onPostExecute(final Integer code) {        mRegisteTask = null;        showProgress(false);        if (code == ServicesResponseCode.REGIST_SUCCESS) {            Toast.makeText(LoginActivity.this,"注册成功!",Toast.LENGTH_LONG).show();        } else if(code == ServicesResponseCode.USER_HAS_EXIST){            mPhoneNumberView.setError("用户已经存在");            mPhoneNumberView.requestFocus();        }    }    @Override    protected void onCancelled() {        mRegisteTask = null;        showProgress(false);    }}

深入剖析:
1 先来看Android 的一般UI中执行费时操作的办法
创建Runnable 执行体
创建Handler 并定义接受的消息体

执行Thread(Runnable) 在run方法发送消息

依赖于 Looper-Handler (message)
2 AsyncTask也要创建线程并向UI线程中依赖的Looper发送Message消息

和1的区别就是用了concurrent并发框架来做这件事
用了线程池来处理UI发出的多个Runnable

那么UI线程只要创建AsyncTask并将要执行的任务offer给并发框架就可以。
并发框架会分发线程任务。执行完每个任务后会向UI线程发送Message 结果

执行任务时也会发送Progress消息(得自己调用PublishProgress())
AsyncTask将Ui线程执行体包装成FutureTask(Callable)交给Executor
FutureTask在并发框架内执行完毕后会调用finishCompletion()
在该方法中会调用done()
我们在AsyncTask中创建FutureTask时覆写done()方法并调用get()方法就可以拿到执行结果。向UI线程发送Message 结果

3 再看一下代码实现
泛型参数多说两句:因为早晚类型擦出 只是编程时提前告诉该参数类型以保证类型安全和容器参数一致性
理解时候可以先从心里将AsyncTask<Void, Void, Integer>做类型擦除。
用的时候想成提前做<T>这种的类型限定。

3.1
定义为虚基类AsyncTask
告诉创建必须实现方法doInBackground(Params… params)

3.2
类类型:(不管创建多少AsyncTask实例 都共用的)
BlockingQueue<Runnable> 线程池中多个线程共享的阻塞队列
ThreadPoolExecutor 线程池执行者
SerialExecutor 顺序执行者 还在UI进程中 向ThreadPoolExecutor offer(提供执行体);自身有一个容器ArrayDeque<Runnable>

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);        }    }}

值得一提的是 第二个scheduleNext()在UI进程中 有开启线池工作的作用
第一个在线程体中,起到持续执行ArrayDeque<Runnable>容器中的FutrueTask的作用

看这段代码,感受到了浓浓的 生产者和消费者 模型呀!
不过这个代码将FutureTask又包装了一次。真正的FutureTask 是在阻塞队列里被等待执行的。

InternalHandler 也是类类型:用作向UI通知结果数据和过程进度数据
单重判断方法,锁加到了 类AsyncTask.class上

private static Handler getHandler() {    synchronized (AsyncTask.class) {        if (sHandler == null) {            sHandler = new InternalHandler();        }        return sHandler;    }}

3.3
实例类型(每个实例不同)
创建AsyncTask时判断本实例运行状态相应的抛出 :

         RUNNING      throw IllegalStateException   already running          FINISHED      throw new IllegalStateException  executed only once 

也就是说每个实例只能被执行一次。我们每次new了最好在最后将实例设为null。
mWorker:对具体业务的包装
mFuture:对mWorker做的可自动回调用结果处理办法的包装

4
弄个思路图瞅瞅

这里写图片描述
5**也有缺点**

每次任务都要弄个AsyncTask子类并覆写方法
如果一个Ui组件内有好几个任务 那得高好几个~

所以最好自己在封装一个框架 直接告诉任务名字和传入任务实体 11月18号更新

原创粉丝点击