浅析android中AsyncTask
16lz
2021-01-26
作用
android是单线程模型。如果在UI主线程中执行耗时操作。可能导致ANR(应用无响应)。系统就会弹出一个ANR对话框。用户选择等待或者离开应用
注意:ANR出现场景:
- 主线程被IO操作(4.0以后主线程中不允许进行网络IO操作)阻塞。
- 主线程中进行耗时的操作。
- 主线程中进行错的操作,如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响应这些消息,并调用相关的回调函数
更多相关文章
- iOS之extern关键字
- android中开启线程
- Android上的蓝牙通信功能的开发:BluetoothChat例程分析
- Android(安卓)的消息机制(1)
- Android(安卓)Handle机制
- Android之Http协议编程02
- Android(安卓)多线程 \Looper 分享
- Android(安卓)Activity为什么要细化出onCreate、onStart、onResu
- Handler android~~