AysnTask详解

一、AsynTask简介

AysnTask是android的轻异步操作,和handler一样是后台处理耗时操作,并且更新UI的一种方式。它的函数原型:

public final AsyncTask<Params, Progress, Result> execute(Params... params)

它的三个泛型参数分别代表:传入的参数、更新UI的参数、最后在主线程运行的参数的类型。这些参数当用不到时候可以用java.lang.Void代替,下面详细介绍一下参数:

AysnTask类主要覆写下面几个函数:

这些函数的运行顺序、参数、作用是:

onPreExecute():无参数,它是第一个运行的函数,是为后台运行做准备的,一般用来对UI做一些标记或者回去Ui的参数,如获取progressBar的max;它运行在主线程中。

doInBackground(Params ... params ) :它的参数为定义AsyncTask时传入的第一个参数类型,也是execute传入的参数的类型。返回的类型是定义AsyncTask时传入的第三个参数类型,即是后台运行结果传入到主线程中的数据,是onPostExecute方法的参数。该方法运行在后台进程中。

onProgressUpdate(progerss...progress):它的参数是定义AsyncTask时传入的第二个参数类型;它是用来更新UI的函数,运行在主线程中。想运行次函数必须在doInbackground()函数里调用publishProgress()函数,该方法里调用了此函数。

onPostExecute(Result... result):它的参数是doInBackground返回的值。运行在主线程中,可以修改UI。

onCancelled(result:Result) 它的参数是doInBackground,当在主程序中调用asynTask.cancel(true)时,会调用此函数,此函数将停止后台进程doInBackground()的执行,并取代onPostExecute,而执行此函数和oncancelled(),此方法和execute()一样只能执行一次,虽然在主线程中但是在此修改UI是无效的。比如可以在下载时候调用这个函数来执行取消取消下载后的操作。

oncancelled:执行完onCancelled(result:Result) 后就紧接着执行,释放掉asynTask自己。在函数里,它会执行Tread的interrupt方法结束后台线程。注意,这个inteerrupt方法只有在线程运行的时候才可以终止,当处于sleep或者wait的时候就会曝InterruptedException异常,即后台的线程未被停止。读者想了解更多可以参考:http://blog.csdn.net/srzhz/article/details/6804756

调用AsynTask的函数是execute() ,只能执行一次,且当activity被销毁时候,再创建(比如横竖屏切换)时候就会新建activity 执行execute(),你会发现新的execute()会等到之前的执行完了执行,而之前的acitivity被销毁了,若有对UI的操作就会出错,原因在下一节AsynTask使用注意事项中讲到。

取消释放AysnTask的方法是:cancel(true),也只能执行一次。

 

下面给出一个下载的demo,其中执行的过程未实现只是更新progressBar:

public class MainActivity extends AppCompatActivity {

    private TextView text;
    private ProgressBar progressBar;
    private   MyAsynTask task;
    boolean isDownload = true;
    private Button bt,bt2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.text);
        bt = (Button) findViewById(R.id.bt);
        bt2 = (Button) findViewById(R.id.bt2);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        task = new MyAsynTask();
        task.execute();
    }

    public void pause(View view) {
        //起初把暂停的代码放入cancle中,发现cancle只能执行一次,就每次执行时创建new task发现可以,且task的progress没有初始化为0.
        MyAsynTask task = new MyAsynTask();
        task.cancel(true);


    }

    public void cancel(View view) {
        task.cancel(true);
        text.setText("down load has been canceled !");
        bt2.setEnabled(false);
        bt.setEnabled(false);
    }
    class MyAsynTask extends AsyncTask<String , Integer , String>
    {
        int progress = 0;
        int maxProgress;

        @Override
        protected void onPreExecute() {
            System.out.println("onPreExecute");
            maxProgress = progressBar.getMax();
            isDownload = true;
            super.onPreExecute();
        }


        @Override
        protected String doInBackground(String... params) {
            System.out.println("doInBackground");
            while(progress<100) {
                if (isDownload) {
                    try {
                        Thread.sleep(1000);
                        progress++;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    publishProgress();
                }
                else
                {
                    synchronized (MyAsynTask.class)
                    {
                        try {
                            MyAsynTask.class.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            return null;
        }

        //运行在主线程中
        @Override
        protected void onProgressUpdate(Integer... values) {
            System.out.println("onProgressUpdate");
            text.setText("Has download "+ progress +"%");
            progressBar.setProgress(progress);
            super.onProgressUpdate(values);
        }

        @Override
        protected void onCancelled(String s) {
            System.out.println("onCancelled   string   "+ isDownload);
            //这里设置UI无效
//            text.setText("down load has been canceled !");
//            bt2.setEnabled(false);
//            bt.setEnabled(false);

            //不要放在这放在点击函数里
//            if(isDownload)
//            {
//                isDownload = false;
//                bt.setText("start");
//            }
//            else
//            {
//                synchronized (MyAsynTask.class)
//                {
//                    isDownload = true;
//                    bt.setText("pause");
//                    MyAsynTask.class.notifyAll();
//                }
//            }

            super.onCancelled(s);
        }

        @Override
        protected void onCancelled() {
            System.out.println("onCancelled");
        }

        //运行在主线程中
        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
        }
    }
View Code

从实验中发现,当调用了pause(View view)方法中调用cancel(true)后,重新new MyAsynTask时,任然后可以继续下载,且原来task的progress未被重置为0。分析原因:

1、当我第一次调用cancel的时候是新建的asyntask占用了第一次创建的锁从而挂起了下载进程doBackground,再释放了自己。

2、当我第二次点击时候,创建了新的asyntask,但是isDownload为false直接挂起了来到cancle里将之前的挂起的下载进程唤醒了,新建的asyntask任然在wait(),然而又cancel释放掉了自己。

这样不断创建对象释放自己会影响程序的性能。

 二、AsynTask使用注意事项

1、导致内存泄漏

  AsynTask并非随activity的销毁而销毁,activity被销毁后,AsyncTask会一直执行, 直到doInBackground()方法执行完毕。即使销毁activity的时候调用了cancel方法,后台进程由于阻塞任然未停止(上面讲oncancel时候讲到过)。它会一直保留着创建了AsyncTask的Activity的引用,即使activity被销毁了,其内存任然无法回收最终导致内存泄漏。

2、运行出错

  同上面的一样,activity销毁了,而doInBackground()执行完了后执行postExecute时候或者updateProgress跟新UI的时候activity已经不存在了程序报错,从而runtimeException。(若只是activity重建了比如横竖屏切换不会崩溃,只是更新UI失效

3、更新界面延迟

       屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,跟新UI时候你会发现必须等到之前的doBackground方法执行完毕后才开始执行重新创建的activity里的asynTask的dobackground方法,再执行UI的更新,原因看源码:

 通过源码发现,调用excute()执行后台操作时候有2种执行方法,即对应的2种执行器:ThreadPoolExecutor 和 SerialExecutor即串行和并行2种执行器,意思大家通过字面可以理解了。而通过

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

可以看出穿行的是默认的执行器。下面外面分析下串行执行器:

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

我们可以发现

THREAD_POOL_EXECUTOR.execute(mActive);

里面是用了个同步锁(锁是执行器),执行器内部有个线程工作队列,sPoolWorkQueue当我们调用excute时候就会把doInBackground加载到队列里,只有前面的进程执行完了才会释放执行器,因此外面之前创建的asyntask还未执行完因此会继续执行完了外面的新加载的activity里的AsynTask才开始跟新。当然可以用并行执行器来执行。

task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

4、并行、串行的选择

      在Android 1.6之前的版本,AsyncTask是串行的,在1.6至2.3的版本,改成了并行的。在2.3之后的版本又做了修改,可以支持并行和串行,当想要串行执行时,直接执行execute()方法,如果需要并行执行,则要执行executeOnExecutor(Executor)。当然选择并行执行率高,但对与线程就不好管理容易浪费资源。

 

posted @ 2016-11-14 11:48  Lammy  阅读(1381)  评论(0编辑  收藏  举报