Handler
理解Handler的实现原理
在处理耗时操作时,需要使用单独的线程去处理(下载、上传、读大文件等),来防止这个操作阻塞UI线程(主线程)。
那我们在使用子线程的时候要注意的问题是:
不能在子线程中访问UI工具包(UI组件)API 15 android4.0后强制限制
目的是保正UI组件的线程安全。
那在Android中多个线程相互通讯的解决方法,可以使用Handler来完成。
1、通常我们会创建一个Handler的实现类,并重写handleMessage方法来处理消息
如:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case UPDATE_NUMBER:
button_validNumber.setText(msg.arg1+"s");
break;
case SUCCESS_NUMBER:
button_validNumber.setText("获取验证码");
button_validNumber.setEnabled(true);
break;
}
}
}
在子线程需要更新UI组件的地方,通过Handler来给UI线程发送一个消息:
class MyThread implements Runnable{
@Override
public void run() {
for (int i=60;i>=0;i--){
//获取一个消息对象
Message m = handler.obtainMessage();
m.what = UPDATE_NUMBER;
m.arg1 = i;
handler.sendMessage(m);//发送消息
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//发送倒计时完成的消息
handler.sendEmptyMessage(SUCCESS_NUMBER);
}
}
2、Handler API使用:
<1>Message 对象:表示要传输的消息对象
what: 什么(用于要发送的消息对象的一个标记,处理时判断用)
arg1/arg2: 两个用于存放整数数据的变量(要发送的数据是整数时可以使用这两个变量存值)
obj:用于存放任何数据对象,本身是一个Object类型
handler.obtainMessage(); 获取一个消息对象(*****)
handler.sendEmptyMessage(); 发送一个不带数据的消息对象(*****)
handler.sendMessage(); 发送一个消息对象(*****)
handler.sendMessageAtTime(); 在指定时间发送一个消息对象
handler.sendMessageDelayed(); 延迟指定时间发送一个消息对象
handler.sendEmptyMessageAtTime(); 在指定时间发送一个不带数据的消息对象
handler.sendEmptyMessageDelayed(); 延迟指定时间发送一个不带数据的消息对象
发送一个消息,并把该消息对象添加到队列的最前面(插队),不建议使用
handler.sendMessageAtFrontOfQueue();
handler.post(); 把一个Runnable对象加入到对队中,依次执行,会主线程中。
3、Handler的实现原理:
面试题:阐述Handler的实现原理。
处理过程:
从handler中获取一个消息对象,把数据封装到消息对象中,通过handler的send...方法把消息
push到MessageQueue队列中,Looper对象会轮询MessageQueue队列,把消息对象取出,
通过dispatchMessage分发给Handler,再回调用Handler实现的handleMessage方法处理消息。
Handler的实现中适及以下对象:
1、Handler本身:负责消息的发送和处理
2、Message:消息对象
3、MessageQueue:消息队列(用于存放消息对象的数据结构)
4、Looper:消息队列的处理者(用于轮询消息队列的消息对象,
取出后回调handler的dispatchMessage进行消息的分发,dispatchMessage方法会回调
handleMessage方法把消息传入,由Handler的实现类来处理)
Message对象的内部实现是链表,最大长度是50,用于缓存消息对象,达到重复利用消息对象的目的,
以减少消息对象的创建,所以通常我们要使用obtainMessage方法来获取消息对象
Handler的消息处理机制是线程安全的
创建Handler时会创建Looper,Looper对象的创建又创建了MessageQueue
4、Handler导致的内存泄露问题分析
什么是内存泄露:
当一个对象A被其它对象B所引用,那么当B对象没有释放时,A对象不能被回收,那么A对象可能造成
内存泄露。
什么是内存溢出:
当一个应用程序内存泄露过多,或者内存不足时,程序会发生内存溢出,导致程序异常中断(崩了)
解决内存泄露来防止内存溢出的方法:
(1)在编写代码一定要注意是否存在内存泄露问题
(2)项目测试可以使用内存泄露的检查工具
在处理Handler的内存泄露方法:
(1)在结束Activity时,删除所有回调和消息处理
handler.removeCallbacksAndMessages(null);
(2)使用弱引用+静态
private static class MyHandler extends Handler{
//把所依赖的引用对象,使用弱引用,那么系统在内存不足时会优先回收该对象
private WeakReference<MainActivity> weakReference;
public MyHandler(MainActivity activity){
weakReference = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = weakReference.get();
if (activity!=null){
activity.button_validNumber.setText("xxxx");
}
}
}
5、应用程序闪屏页
public class SplashActivity extends Activity {
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
handler.postDelayed(new Runnable() {
@Override
public void run() {
start();
}
},3000);
}
private void start() {
startActivity(new Intent(this,MainActivity.class));
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
handler = null;
Log.i("handler","onDestroy");
}
}
6、AsyncTask工具类的使用
实现一个异步任务可以使用Handler+线程,Android给我们提供了另一种更轻量级的实现方法
就是使用AsyncTask来完成。
AsyncTask是一个抽象类,通常我们需要去创建一个类继承AsyncTask来实现一个异步任务,
AsyncTask定义三个泛型参数类型:Params,Progress,Result
Params表示执行任务时需要参数类型
Progress表示执行任务时进度值的类型
Result表示任务完成后返回值的类型
这个三个参数分别在创建AsyncTask子类时通过泛型指定类型
//一个异步任务
class MyAsyncTask extends AsyncTask<String,Integer,String>{
//此方法在UI线程中调用,实例化MyAsyncTask执行任务时开始调用,在方法中进行一些准备工作
@Override
protected void onPreExecute() {
super.onPreExecute();
textView_update.setText("开始更新...");
}
//此方法在后台线程中执行,用来执行一个异步任务
@Override
protected String doInBackground(String... params) {
for (int i=0;i<=100;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i); //发布当前进度值,该方法会触发UI线程回调onProgressUpdate方法
}
return "success";
}
@Override
protected void onProgressUpdate(Integer... values) {
textView_update.setText("当前进度为:"+values[0]+"%");
}
@Override
protected void onPostExecute(String s) {
textView_update.setText(s);
}
}
下载图片示例:
<1>布局文件中定义一个下载按钮和一个进度条组件:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下载"
android:onClick="downloadClick"
android:id="@+id/button2_download"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_toEndOf="@+id/textView_update" />
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:visibility="gone"
android:layout_above="@+id/button2_download"
android:layout_centerHorizontal="true"
android:layout_marginBottom="38dp" />
<2>定义一个下载的AsyncTask任务类:DownloadTask
//下载任务
class DownloadTask extends AsyncTask<String,Integer,String>{
@Override
protected void onPreExecute() {
super.onPreExecute();
progressBar.setProgress(0);
progressBar.setVisibility(View.VISIBLE);
}
@Override
protected String doInBackground(String... params) {
try {
URL url = new URL(params[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int contentLen = conn.getContentLength();//资源的总大小
publishProgress(FLAG_CONTENT_LEN,contentLen);//把资源的总大小更新到进度条上
BufferedInputStream in =
new BufferedInputStream(conn.getInputStream());
BufferedOutputStream out =
new BufferedOutputStream(new FileOutputStream(new File("/sdcard/Download/mm.jpg")));
int len = -1;
byte[] bytes = new byte[1024];
while((len=in.read(bytes))!=-1){
out.write(bytes,0,len);
publishProgress(FLAG_UPDATE_PROGRESS,len);
}
out.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
return "error";
}
return "success";
}
@Override
protected void onPostExecute(String s) {
if("success".equals(s)){
Toast.makeText(Main2Activity.this, "下载完成", Toast.LENGTH_SHORT).show();
}else if("error".equals(s)){
Toast.makeText(Main2Activity.this, "下载失败", Toast.LENGTH_SHORT).show();
}
progressBar.setVisibility(View.GONE);
}
@Override
protected void onProgressUpdate(Integer... values) {
switch (values[0]){
case FLAG_UPDATE_PROGRESS:
progressBar.incrementProgressBy(values[1]);
break;
case FLAG_CONTENT_LEN:
progressBar.setMax(values[1]);
break;
}
}
}
<3>在Activity 中按钮单击事件中实现下载:
添加两个权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
两个标记常量
private static final int FLAG_CONTENT_LEN = 0x1; //用来设置进度条的总大小
private static final int FLAG_UPDATE_PROGRESS = 0x2; //用来更新进度
private ProgressBar progressBar;
progressBar = (ProgressBar) findViewById(R.id.progressBar);
//下载按钮事件
public void downloadClick(View view){
//不能创建一个对象后反复执行execute,只能用一次
new DownloadTask().execute("http://f.hiphotos.baidu.com/image/h%3D200/sign=a2c37cfc0846f21fd6345953c6246b31/00e93901213fb80e0ee553d034d12f2eb9389484.jpg");
}