zoukankan      html  css  js  c++  java
  • 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)。当然选择并行执行率高,但对与线程就不好管理容易浪费资源。

  • 相关阅读:
    java获取当前项目或类路径
    java转义字符处理——“\”替换为“/”
    OpenModelica 在特定目录下生成仿真结果文件
    Eclipse常用设置
    java反编译器
    OMShell常用命令及遇到的问题
    Java中的单实例
    Eclipse常用设置
    Eclipse快捷键
    vlookup+match高亮显示行
  • 原文地址:https://www.cnblogs.com/bokeofzp/p/6061309.html
Copyright © 2011-2022 走看看