浅析android中AsyncTask

来源:互联网 发布:淘宝网安全背心 编辑:程序博客网 时间:2024/06/02 04:36

作用

android是单线程模型。如果在UI主线程中执行耗时操作。可能导致ANR(应用无响应)。系统就会弹出一个ANR对话框。用户选择等待或者离开应用
注意:ANR出现场景:


  1. 主线程被IO操作(4.0以后主线程中不允许进行网络IO操作)阻塞。
  2. 主线程中进行耗时的操作。
  3. 主线程中进行错的操作,如Thread.wait Thread.sleep
    Android系统会监视应用响应情况:如果应用在5秒内没有响应用户输入事件(如按键或者触摸)或者Broadcase Receiver在10秒内未完成相关的处理都会弹出ANR。

如何避免:
  • 基本的思路就是将IO操作在工作线程来处理,减少其他耗时操作和错误操作
  • 使用AsyncTask处理耗时IO操作。
  • 使用Thread或者HandlerThread时,调用Process.setThreadPriority
    (Process.THREAD_PRIORITY_BACKGROUND)设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同。
  • 使用Handler处理工作线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程。
  • Activity的onCreate和onResume回调中尽量避免耗时的代码
  • BroadcastReceiver中onReceive代码也要尽量减少耗时,建议使用IntentService处理。

AsyncTask

android中可以使用Handler和AsyncTask来实现异步机制。
Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新
AsyncTask是抽象类。开发者需要继承后使用。
继承AsyncTask 时需要给AsyncTask指定三个类型。

  • params 任务启动时候传递的参数。即调用execute()时,传递的参数。
  • progress 该参数是在doInbackground()函数中调用publishProgress()将进程执行的值传递给doProgressUpdate()函数做参数。
  • result 是doInbackground()返回的值得类型。该函数返回的值会传递给doPostExecute()函数。进行任务完成后的处理。

继承AsyncTask必须要重写doInbackground()。在该函数中具体执行异步任务。
- doPreExecute()(在函数doInbackground()执行之前调用做一些准备工作)
- doPostExecute()(在函数doInbackground()执行之后进行相关的处理。并且doInbackground()的返回值会传递给该函数做参数)
- doProgressUpdate()()(在doInbackground()函数中调用publishProgress()后该函数被调用,将进程执行的值通过publishProgress()传递给doProgressUpdate()函数做参数)

public class MainActivity extends Activity {   private ImageView imageView;    private ProgressBar progressBar;    public static String        url="https://img3.doubanio.com/lpic/s28385426.jpg";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        imageView=(ImageView)findViewById(R.id.iamgeview);        progressBar=(ProgressBar)findViewById(R.id.progress);        new MyAsyncTask().execute(url);    }    //注意三个参数,分别是execute(url)函数传入参数的类型,progress的返回值,protected Bitmap doInBackground的返回值    class MyAsyncTask extends AsyncTask<String,Void,Bitmap>{        @Override        protected Bitmap doInBackground(String... params) {            //取出参数            String url=params[0];            Bitmap bitmap=null;            InputStream is=null;            try {                URLConnection urlc=new URL(url).openConnection();                is=urlc.getInputStream();                BufferedInputStream bis=new BufferedInputStream(is);                bitmap= BitmapFactory.decodeStream(bis)                is.close();                bis.close();            } catch (IOException e) {                e.printStackTrace();            }            return bitmap;        }        @Override        protected void onProgressUpdate(Void... values) {            super.onProgressUpdate(values);        }        @Override        protected void onPreExecute() {            super.onPreExecute();            //任务执行前进度条设为visible。            progressBar.setVisibility(View.VISIBLE);        }        @Override        protected void onPostExecute(Bitmap bitmap) {            super.onPostExecute(bitmap);            //任务执行完返回下载的图片,进度条隐藏,在子线程中更新界面            progressBar.setVisibility(View.GONE);            imageView.setImageBitmap(bitmap);        }    }}

AsyncTask取消

public class MainActivity extends Activity {    private ProgressBar progressBar;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        progressBar=(ProgressBar)findViewById(R.id.progress);        new MyAsyncTask().execute();    }    class MyAsyncTask extends AsyncTask<Void,Integer,Void>{        @Override        protected Void doInBackground(Void... params) {            for(int i=0;i<100;i++)            {                publishProgress(i);                try {                    Thread.sleep(300);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            return null;        }        @Override        protected void onProgressUpdate(Integer... values) {            super.onProgressUpdate(values);            //更新progressbar            progressBar.setMax(100);            progressBar.setProgress(values[0]);        }    }}

该代码段运行会出现这种问题:第一次打开应用正常执行。progressbar未更新完然后按返回键。再次打开应用时,应用会等上一个线程执行完然后才会执行下一个任务。所以看到,等一会progressbar才开始更新(等上一次执行时打开的线程执行完成)
所以避免这种情况可以让任务执行的生命周期和activity相关联

public class MainActivity extends Activity {    private ProgressBar progressBar;    private MyAsyncTask myAsyncTask;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        progressBar=(ProgressBar)findViewById(R.id.progress);        myAsyncTask=new MyAsyncTask();        myAsyncTask.execute();    }    @Override    protected void onStop() {        super.onStop();        if(myAsyncTask!=null&&myAsyncTask.getStatus()==AsyncTask.Status.RUNNING)        //cancel()方法只是设置AsyncTask为cancel状态,并没有停止线程的执行。所在doInbackground()中判断如果AsyncTask为cancel状态就停止循环        {            myAsyncTask.cancel(true);        }    }    class MyAsyncTask extends AsyncTask<Void,Integer,Void>{        @Override        protected Void doInBackground(Void... params) {            for(int i=0;i<100;i++)            {                if(isCancelled())                {                    break;                }                publishProgress(i);                try {                    Thread.sleep(300);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            return null;        }        @Override        protected void onProgressUpdate(Integer... values) {            super.onProgressUpdate(values);            progressBar.setMax(100);            progressBar.setProgress(values[0]);        }    }}

总结

  • AsyncTask只能在UI线程中创建和执行execute()
  • 重写的AsycTask只能由系统自动调用,不可手动调用
  • 一个AsynTask只能被执行一次,多次执行可能会出现问题。如上面的例子。
  • AsyncTask的本质是一个静态的线程池,AsyncTask派生出的子类可以实现不同的异步任务,这些任务都是提交到静态的线程池中执行。
  • 当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数
0 0