Android(安卓)线程 Handler详解
一.Handler的常用用法
1.Handler在UI线程接收消息,在子线程或者UI线程发送消息
接收消息代码
private Handler uiHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: int num1 = msg.arg1; int num2 = msg.arg2; String result = (String) msg.obj; Log.d("TAG", "主线程即UI线程 接收 主线程发送的Message num1: " + num1 + " num2: " + num2 + " result: " + result); break; case 2: int num11 = msg.arg1; int num22 = msg.arg2; String result1 = (String) msg.obj; Log.d("TAG", "主线程即UI线程 接收 子线程发送的Message num1: " + num11 + " num2: " + num22 + " result: " + result1); break; } } };
发送消息代码(子线程)
private void sendMsgFromNewUIThread() { new Thread(new Runnable() { @Override public void run() { Message msg = Message.obtain(); msg.what = 2; msg.arg1 = 33; msg.arg2 = 44; msg.obj = "子线程发送的消息"; uiHandler.sendMessage(msg); } }).start(); }
发送消息代码(UI线程) 一般不用
private void sendMsgFromUIThread() { Message msg = Message.obtain(); msg.what = 1; msg.arg1 = 11; msg.arg2 = 22; msg.obj = "主线程发送的消息"; uiHandler.sendMessage(msg); }
结果
主线程即UI线程 接收 主线程发送的Message num1: 11 num2: 22 result: 主线程发送的消息主线程即UI线程 接收 子线程发送的Message num1: 33 num2: 44 result: 子线程发送的消息
2.Handler在子线程接收消息
代码
private void createNewThreadHandler() { new Thread(new Runnable() { @Override public void run() { new Handler().postDelayed(new Runnable() { @Override public void run() { Log.d("TAG", "延时200毫秒执行的代码..."); } }, 200); } }).start(); }
上述代码在大部分手机会报错,魅族测试机报错信息
即 Can't create handler inside thread that has not called Looper.prepare()
修改后的代码
private void createNewThreadHandler() { new Thread(new Runnable() { @Override public void run() { new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { Log.d("TAG", "延时200毫秒执行的代码..."); } }, 200); } }).start(); }
结果
TAG: 延时200毫秒执行的代码...
报错原因
非主线程中默认没有开启Looper,而Handler对象必须绑定Looper对象,需要调用Looper.prepare()来给线程创建一个消息循环,调用Looper.loop()来使消息循环起作用。当然在new Handler对象时,在构造方法中传入Looper.getMainLooper()也可以解决报错的问题。
二.Handler详解
1.创建Message注意
在使用Handler进行线程通讯时,必不可少的要创建Message,而为了内存优化在创建Message时,最好不要new一个Message。
正确的写法
Message msg = Message.obtain();msg.what = 2;msg.arg1 = 33;msg.arg2 = 44;msg.obj = "Message发送的消息";
正确的写法
Message msg = handler.obtainMessage();msg.what = 2;msg.arg1 = 33;msg.arg2 = 44;msg.obj = "Message发送的消息";
备注
handler.obtainMessage();内部调用了Message.obtain();
如下
public final Message obtainMessage(){ return Message.obtain(this);}
错误的写法
Message msg = new Message();msg.what = 2;msg.arg1 = 33;msg.arg2 = 44;msg.obj = "Message发送的消息";
即,创建Message使用Message的缓存池。
Message缓存池源码
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
大致可以这样理解。通过Message缓存池方式获取Message对象时,首先判断缓存池中有没有Message,如果有就直接取出使用,如果没有这时才new一个Message。
2.Handler常用的几个方法
发送消息
Handler.sendMessage(msg);
延时XXX毫秒发送消息
Handler.sendMessageDelayed(msg,100);
UI线程刷新UI
Handler.post(new Runnable() { @Override public void run() { }});
UI线程延时XXX毫秒刷新UI
uiHandler.postDelayed(new Runnable() { @Override public void run() { }},100);
3.使用Handler时,注意内存泄露问题
为什么会出现内存泄露的问题
首先Handler使用是用来进行线程间通信的,所以新开的线程会持有Handler引用。如果在Activity等中创建Handler,并且是非静态内部类的形式,就有可能造成内存泄露。
首先,非静态内部类是会隐式持有外部类的引用,所以当其他线程持有了该Handler,线程没有被销毁,则意味着Activity会一直被Handler持有引用而无法导致回收Activity。
同时,MessageQueue中如果存在未处理完的Message,Message的target也是对Activity等的持有引用,也会造成内存泄露。
举例
匿名内部类 方式声明Handler
private Handler uiHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { } } };
这样的话,就有可能造成内存泄露。
解决方法
静态内部类+弱引用方式+Activity销毁时销毁Handler
静态内部类
static class MyHandler extends Handler { private WeakReference activityWeakReference; MyHandler(Activity activity) { activityWeakReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (null != activityWeakReference) { Activity act = activityWeakReference.get(); if (ActivityUtils.isActivityIsAlive(act)) { switch (msg.what) { case 1: String result = (String) msg.obj; Log.d("TAG", "内部静态类方式声明Handler获取Msg----:" + result); break; } } } } }
Activity全局声明
private Handler handler = new MyHandler(this);
发送Message
private void sendMsgFromNewThread() { Message message = Message.obtain(); message.what = 1; message.obj = "子线程发送Message"; handler.sendMessage(message); }
销毁Handler
@Override protected void onDestroy() { super.onDestroy(); if (null != handler) { handler.removeCallbacksAndMessages(null); handler = null; Log.d("TAG", "销毁Handler"); } }
4.Handler+Looper+MessageQueue+Message
上面提到在子线程中声明Handler时,要手动添加Looper.prepare()和Looper.loop()方法【也可 new Handler(Looper.getMainLooper())】那么在主线程(UI线程)中为什么不需求呢。
因为APP初始化的时候都会执行ActivityThread的main方法,代码如下
public static void main(String[] args) { ... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } ... Looper.loop();}
图解
更多相关文章
- 在Android中使用ExecutorService、Executors、Feature
- Android故障积累----(1/N)
- 监听方法Android之Home键监听封装
- (转)Android中Handler引起的内存泄露
- Android(安卓)- webview通过js调用Android方法
- Android(安卓)Handler removeMessages引发postDelayed失效的问题
- android UEventObserver的用法
- PackageManagerService(Android5.1)深入分析(四)安装应用
- Android跨进程通信IPC之16——Binder之native层C++篇--获取服务