一、ANR(Application Not Responding)
1.什么是ANR异常?
——指应用程序无响应。android程序中会弹出提示dialog,其中一个选项是“等待”,另一个选项是“强制退出”。
2.什么情况下会引发ANR异常?
——android应用程序的响应由ActivityManager和WindowManager来管理和监听。
1)在5秒钟之内没有相应输入事件(如返回键,屏幕触摸)
2)广播在10秒钟之内没有完成逻辑
实际应用中什么情况下会出现ANR:在主线程中执行耗时操作(如文件下载,图片下载,IO异常,数据库操作,高耗时的图片尺寸处理,复杂的视图加载等等)
3.如何解决?
——1)把耗时操作相关的代码写到子线程。(要求主线程的生命周期的方法尽量少做事情)。在android程序中提供了handler+message消息机制,使子线程能够与主线程进行通信。
——2)应用程序应避免在广播(BroadcastReceiver)中执行耗时操作或计算。但不是在子线程中做这些任务(因为BroadcastReceiver生命周期短),如果在intent广播中需要执行耗时操作,可以启动service去做。
——3)避免在intent receiver中启动activity,因为activity会创建新的视图,并且在用户正在运行的应用程序上抢占焦点。如果你的应用程序在相应intent广播时需要向用户展示什么,这个时候应该使用android提供的Notification Manager,把它发到通知栏上,让用户选择是否查看,而不是程序自己打开。因为这样会很卡,造成用户无法点击,用户体验会很差。
二、线程
1.操作系统:实时操作系统,分时操作系统(可以并发做多个任务)。从微观的角度看,操作系统还是实时的,cpu一次只能处理一件事情。但是因为cpu的执行速度是很快的。
2.什么是进程?
——(操作系统)为每一个任务分配一系列的资源(如cpu,内存等)来执行该任务,这就是进程。
3.什么是线程?
——CPU执行的最小的代码段
4.进程与线程的关系?
——进程是炒菜,线程是炒菜的每一个步骤。进程中包含很多线程。
三、Handler
一个Handler允许你发message和runnable对象。每个Handler实例都会关联一个单线程和线程消息队列,当你创建一个新的Hanlder,它就被绑定到当前所在的线程——从那时起,它将提供消息和runnable对象,当消息或runnable从消息队列里面被取出的时候,就会被执行。
一个Handler有两个主要用途:
1)执行调度消息和将要被执行的runnable
2)运行在某个线程上,共享线程的消息队列
四、异步加载(AsyncTask)
这里我们来做一个下载图片的demo。
1.首先写一个MyAsyncTask类继承AsyncTask。
public class MyAsyncTask extends AsyncTask<String,Integer,Bitmap>{
@Override
protected Bitmap doInBackground(String... params) {
return null;
}
}
代码解析:这里我们注意到AsyncTask后面带的三个参数类型——Params:输入参数(请求参数,图片url等);Progress:进度(异步任务执行的过程);Result:结果(异步任务执行完成后返回的结果)。实现的doInBackground方法通常用来执行后台任务,其会在子线程中进行。0
2.几个重要的方法实现
——onPreExecute方法:执行异步任务之前的预处理,在主线程中执行
——onProgressUpdate:更新加载进度
——onPostExecute:异步任务完成后需要处理返回的结果
3.布局文件:TextView用于显示下载的进度百分比,ProgressBar就是下载的动态进度更新,ImageView显示下载的图片。
<TextView
android:id="@+id/tv_progress"
android:text="@string/hello_world"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"/>
<ProgressBar
android:id="@+id/pb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.ProgressBar.Horizontal"/>
<Button
android:id="@+id/btn_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始下载"
android:textSize="20sp"/>
<ImageView
android:id="@+id/iv_image"
android:layout_width="match_parentm"
android:layout_height="match_parent"
android:scaleType="center"/>
4.点击下载按钮我们要开启异步任务。这里传的url就是图片的地址。
MyAsyncTask myAsyncTask = new MyAsyncTask();
myAsyncTask.execute(url);
5.在doInBackground方法中进行下载图片的相关操作。
@Override
protected Bitmap doInBackground(String... params) {
Bitmap bitmap = null;
try {
//获取URL对象
URL url = new URL(params[0]);
//打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//设置请求方式
connection.setRequestMethod("GET");
//设置连接超时
connection.setConnectTimeout(5000);
if (connection.getResponseCode()==200){
//获取文件长度
maxLength = connection.getContentLength();
//设置进度条最大进度
progressBar.setMax(maxLength);
//得到图片流
InputStream inputStream = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
}
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
6.在onPostExecute(Bitmap bitmap)方法中把异步任务处理的结果(图片)设置到ImageView上。
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
imageView.setImageBitmap(bitmap);
}
7.记得加权限。
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
到这里其实已经可以把图片显示出来,但是加载进度条还没做。OK来做进度条。这里的做法是将图片下载到本地,再读取。
String fileName =imageUrl.substring(imageUrl.lastIndexOf("/")+1);
FileOutputStream fileOutputStream = new FileOutputStream(SD_CARD_PATH+"/"+fileName);
int len = 0;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer))!=-1){
fileOutputStream.write(buffer,0,len);
//更新进度
publishProgress(len);
}
fileOutputStream.close();
inputStream.close();
bitmap=BitmapFactory.decodeFile(SD_CARD_PATH+"/"+fileName);
然后,我们在onProgressUpdate方法里面把加载进度text设上。
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressBar.setProgress(progressBar.getProgress()+values[0]);
String text = "下载进度:"+100*progressBar.getProgress()/maxLength+"%";
progressTV.setText(text);
}
大功告成。