作用
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响应这些消息,并调用相关的回调函数