Handler理解:
一、基础解读
Android 的Handler 用于处理消息队列
handler 牛津词典的翻译:
1. 驯兽员;(尤指)驯犬员。2. 搬运工;操作者。3. 组织者;顾问
handler的构造方法:
Handler()
Default constructor associates this handler with the Looper for the current thread.(默认构造函数将此处理程序与当前线程的Looper关联。)
Handler(Handler.Callbackcallback)
Constructor associates this handler with the Looper for the current thread and takes a callback interface in
which you can handle messages.(构造函数将此处理程序与当前线程的Looper相关联,并采用可处理消息的回调接口。)
Use the provided Looper instead of the default one.(使用提供的Looper而不是默认的Looper。)
Handler(Looperlooper,Handler.Callbackcallback)
Use the provided Looper instead of the default one and take a callback interface
in which to handle messages.(使用提供的Looper而不是默认的Looper,并在其中处理消息的回调接口。)
可以看出来,所有的构造函数,都与Looper有关。
Handler的方法:
voiddispatchMessage(Messagemsg)
Handle system messages here.(在这里处理系统消息。)
final voiddump(Printerpw,Stringprefix)
StringgetMessageName(Messagemessage)
Returns a string representing the name of the specified
message.(返回表示指定消息名称的字符串。默认实现将返回消息回调的类名(如果有)或消息“what”字段的十六进制表示形式。)
voidhandleMessage(Messagemsg)
Subclasses must implement this to receive messages.
(子类必须实现这个才能接收消息。)
final booleanhasMessages(int what,Objectobject)
Check if there are any pending posts of messages with code 'what' and whose obj is 'object' in the message queue.
(方法中what用来判断对应的逻辑,Object用来接收所传递过来的数据。并做处理操作)
final booleanhasMessages(int what)
Check if there are any pending posts of messages with code 'what' in the message queue.
finalMessageobtainMessage(int what, int arg1, int arg2)
Same as obtainMessage(), except that it also sets the what, arg1 and arg2 members of the returned Message.
(与obtainMessage()相同,除了它还设置返回消息的what,arg1和arg2成员。)
finalMessageobtainMessage()(从全局消息池中返回一条新消息。比创建和分配新实例更高效。检索到的消息将其处理程序设置为此实例(Message.target == this)。如果你不想要那个设施,只需调用Message.obtain()。)
Returns a new Messagefrom the global message pool.
finalMessageobtainMessage(int what, int arg1, int arg2,Objectobj)
Same as obtainMessage(), except that it also sets the what, obj, arg1,and arg2 values on the returned Message.
finalMessageobtainMessage(int what)
Same as obtainMessage(), except that it also sets the what member of the returned Message.
finalMessageobtainMessage(int what,Objectobj)
Same as obtainMessage(), except that it also sets the what and obj members of the returned Message.
Causes the Runnable r to be added to the message queue.(导致Runnable r被添加到消息队列中。 runnable将在该处理程序所连接的线程上运行)
final booleanpostAtFrontOfQueue(Runnabler)
Posts a message to an object that implements Runnable.
(将消息发布到实现Runnable的对象。)
final booleanpostAtTime(Runnabler,Objecttoken, long uptimeMillis)
Causes the Runnable r to be added to the message queue, to be run at a specific time given byuptimeMillis.
(导致Runnable r被添加到消息队列中,以在由uptimeMillis给定的特定时间运行。)
final booleanpostAtTime(Runnabler, long uptimeMillis)
Causes the Runnable r to be added to the message queue, to be run at a specific time given byuptimeMillis.
final booleanpostDelayed(Runnabler, long delayMillis)
Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses.
(导致Runnable r被添加到消息队列中,在指定的时间过后运行。 runnable将在该处理程序所连接的线程上运行。)
(如果Runnable已成功放入消息队列,则返回true。失败时返回false,通常是因为处理消息队列的活套正在退出。请注意,结果为true并不意味着将处理Runnable - 如果在发生消息传递时间之前退出了循环,则该消息将被丢弃。)
final voidremoveCallbacks(Runnabler)
Remove any pending posts of Runnable r that are in the message queue.
final voidremoveCallbacks(Runnabler,Objecttoken)
Remove any pending posts of Runnablerwith Objecttokenthat are in the message queue.
final voidremoveCallbacksAndMessages(Objecttoken)
Remove any pending posts of callbacks and sent messages whoseobjistoken.
final voidremoveMessages(int what)
Remove any pending posts of messages with code 'what' that are in the message queue.
(使用消息队列中的代码“what”在消息队列中删除。)
final voidremoveMessages(int what,Objectobject)
Remove any pending posts of messages with code 'what' and whose obj is 'object' that are in the message queue.
final booleansendEmptyMessage(int what)
Sends a Message containing only the what value.(仅发送仅包含what的消息。)
final booleansendEmptyMessageAtTime(int what, long uptimeMillis)
Sends a Message containing only the what
value, to be delivered at a specific time.(在指定的时间过后,发送包含what的消息给handler)
final booleansendEmptyMessageDelayed(int what, long delayMillis)
Sends a Message containing only the what value, to be delivered after the specified amount of time elapses.
final booleansendMessage(Messagemsg)
Pushes a message onto the end of the message queue after all pending messages before the current time.
final booleansendMessageAtFrontOfQueue(Messagemsg)
Enqueue a message at the front of the message queue, to be processed on the next iteration of the message loop.
booleansendMessageAtTime(Messagemsg, long uptimeMillis)
Enqueue a message into the message queue after all pending messages before the absolute time (in milliseconds)uptimeMillis.
final booleansendMessageDelayed(Messagemsg, long delayMillis)
Enqueue a message into the message queue after all pending messages before (current time + delayMillis).
Returns a string containing a concise, human-readable description of this object.
二、为什么要使用Handler?
1、子线程不能更改主线程的UI
2、一般来说,所有显示在界面上的控件,都是由主线程创建和操作的。
(比如进度条的使用,也是需要在线程中发送handler来更新进度)
3、每个主线程都有一个Handler,Handler运行在主线程里,它与子线程可以通过Message来传递数据。
4、如果此时需要一个耗时的操作,例如:联网读取数据, 或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭".
三、Handler的实现原理:
参考地址:https://blog.csdn.net/wanghao200906/article/details/51355018
涉及到的概念:
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
Handler、Looper、Message 和 MessageQueue 的关系:
Handler 向 MessageQueue 发送 Message,Looper 负责循环MessageQueue 中的 Message 并向Handler 分发 Message,最后 Handler 负责处理 Message。示意图如下
[if !vml]
[endif]
new Handler的时候Handler就已经拿到了线程的Looper 。MessagQueue
handler发送消息:把Handler保存到Message里。把Message保存到messageQueue里。
ActivityThread.java主线程入口类在main()方法中存入了Looper.prepareMainLooper();(这里已经创建了Looper,messagequeue)
然后不断地执行获取消息的方法:Looper.loop();去出message,然后调用handler的dispatchMessage(msg);
示例图:
[if !vml]
[endif]
四、什么情况下使用handler?
最简单的消息发送
主线程使用Handler, 主线程里或子线程里发送消息,或延迟发送消息的方式更新UI
如,启动应用时Splash页面的延迟2,3秒后,跳转到主页面加载完页面的各个控件后,再加载线程下载图片,最后更新图片等等
private static final int WHAT_UPDATE_ICON = 1;
private Handler handler = newHandler() {
@Override
public void handleMessage(Message msg) {
switch(msg.what){
caseWHAT_UPDATE_ICON:
Log.e(Tag,"receive message:"+ msg.obj);
break;
}
}
};
Message msg = handler.obtainMessage(WHAT_UPDATE_ICON);
msg.obj ="update the imageview";
handler.sendMessage(msg);
使用消息的时候,尽量使用 obtainMessage 的方式来获取Message,避免多次创建Message对象,消耗内存,效率低下。
结合HandlerThread处理耗时任务
结合HandlerThread,串行的处理单个耗时任务,如单任务下载
class DownloadOneByOne extends HandlerThread {
public DownloadOneByOne(){
super(DownloadOneByOne.class.getSimpleName());
}
@Override
protected void
onLooperPrepared() {
super.onLooperPrepared();
// 初始化下载组件
}
}
privateHandlerThread mHandlerThread;
private Handler downloadHandler = newHandler(){
@Override
public void
handleMessage(Message msg) {
super.handleMessage(msg);
String url = (String) msg.obj;
// 使用下载组件开始下载
}
};
public void initHandler() {
// 初始化Handler
mHandlerThread =newDownloadOneByOne();
mHandlerThread.start();
downloadHandler =newHandler(mHandlerThread.getLooper());
}
private void sendDownloadTask(String downloadUrl) {
// 发送下载任务
Message msg = downloadHandler.obtainMessage(WHAT_DOWNLOAD_TASK);
msg.obj = downloadUrl;
downloadHandler.sendMessage(msg);
}
倒计时View的简易实现
通过Handler我们还可以快速简易,并且不占用太多性能的实现一个简易的倒计时View。
public class CountDownView extends AppCompatTextView{
/**
*总时间
*/
private longseconds;
/**
*当前分钟
*/
private longminutes;
/**
*当前秒数
*/
private int second = 60;
private static final int SECONDS_PER_MINUTE = 60;
private static final int MILLS_PER_SECOND = 1000;
private static final int MILLS_PER_MINUTE = SECONDS_PER_MINUTE * 1000;
private static final int WHAT_DONE = 2;
private static final int WHAT_TICK = 1;
private intmarginEnd;
private StringBuilder content = newStringBuilder();
public CountDownView(Context
context, @Nullable AttributeSet attrs) {
super(context,attrs);
}
@Override
protected void
onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DeviceProfile deviceProfile =Launcher.getLauncher(getContext()).getDeviceProfile();
int size = (int) (MeasureSpec.getSize(widthMeasureSpec) / deviceProfile.inv.numColumns);
marginEnd = marginEnd ==0 ? (size - deviceProfile.iconSizePx) / 2: marginEnd;
setMarginEnd(marginEnd);
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}
private void
setMarginEnd(int marginEnd) {
LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.setMarginEnd(marginEnd);
layoutParams.resolveLayoutDirection(layoutParams.getLayoutDirection());
}
@Override
protected void
onDetachedFromWindow() {
super.onDetachedFromWindow();
if(handler.hasMessages(WHAT_TICK)){
handler.removeMessages(WHAT_TICK);
}
}
private Handler handler = newHandler() {
@Override
public void handleMessage(Message msg) {
switch(msg.what){
caseWHAT_DONE:
setVisibility(View.GONE);
break;
default:
setText(content.toString());
handler.post(runnable);
break;
}
}
};
/***
*设置倒计时
* @parammillis
*/
public void
setCountDownMills(long millis) {
seconds = (long)Math.floor(millis / MILLS_PER_SECOND);
minutes = (long)
Math.floor(millis / MILLS_PER_MINUTE) - 1;
// start after one second
handler.postDelayed(runnable,MILLS_PER_SECOND);
}
private Runnable runnable = newRunnable() {
@Override
public void run() {
if (seconds <= 0) {
handler.sendEmptyMessage(WHAT_DONE);
return;
}
seconds--;
if (second <= 0) {
second = SECONDS_PER_MINUTE;
minutes = (long) Math.floor(seconds / SECONDS_PER_MINUTE);
}
second--;
content.delete(0, content.length());
appendZeroWhenLower10(minutes);
content.append(":");
appendZeroWhenLower10(second);
if(handler.hasMessages(WHAT_TICK)) {
handler.removeMessages(WHAT_TICK);
}
handler.sendEmptyMessageDelayed(WHAT_TICK, MILLS_PER_SECOND);
}
};
private
StringBuilder appendZeroWhenLower10(long value) {
if (value < 10) {
content.append("0").append(value);
}else{
content.append(value);
}
returncontent;
}
}
结合IntentService的使用
使用IntentService处理耗时的任务相对比较简单,我们来个有难度的,结合AlarmManager的调度,息屏唤醒IntentService定时处理任务的案例来讲当我们的应用进入后台activity栈的时候,注册并启动AlarmManager任务
private static final String ACTION_WAKE_UP = "com.doze.cpu.wakeup";
public static void registerAlarm(Context
context, int wakeType) {
type = wakeType;
if (alarmManager == null)
alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (operation != null) alarmManager.cancel(operation);
schedule(context);
}
private static void schedule(Context context) {
Intent intent =newIntent();
intent.setAction(ACTION_WAKE_UP);
operation =PendingIntent.getBroadcast(context,0, intent,PendingIntent.FLAG_UPDATE_CURRENT);
switch(type) {
case 0:
AlarmUtils.setRTCWakeup(alarmManager,AlarmUtils.DEFAULT_TRIGGER_AT_MILLIS, operation);
break;
case 1:
AlarmUtils.setElapsedWakeup(alarmManager,AlarmUtils.DEFAULT_TRIGGER_AT_MILLIS, operation);
break;
}
}
定时5分钟发送一个Action为com.doze.cpu.wakeup的广播,我们的广播需要继承 WakefulBroadcastReceiver, 在onReceive里,调用startWakefulService方法,会创建一个1分钟的WakeLock,唤醒cpu处理我们的任务,我们的任务在IntentService处理最好不过了,处理完就销毁,不会有多余的占用
public class WakeCPUReceiver extends WakefulBroadcastReceiver{
@Override
public void
onReceive(Context context, Intent intent) {
Intent wakefulIntent =newIntent(context, WorkService.class);
startWakefulService(context,wakefulIntent);
schedule(context);
}
startWakefulService的源码
public static ComponentName startWakefulService(Context
context, Intent intent) {
synchronized(mActiveWakeLocks) {
intid =mNextId;
mNextId++;
if (mNextId <= 0) {
mNextId =1;
}
intent.putExtra(EXTRA_WAKE_LOCK_ID,id);
ComponentName comp = context.startService(intent);
if (comp == null) {
return null;
}
PowerManager pm =(PowerManager)context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl =pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"wake:"+ comp.flattenToShortString());
wl.setReferenceCounted(false);
wl.acquire(60*1000);
mActiveWakeLocks.put(id, wl);
returncomp;
}
}
在IntentService里,我们在onHandleIntent处理我们的任务后,再调用
WakefulBroadcastReceiver
的静态方法completeWakefulIntent,释放WakeLock,减少电量的消耗
public class WorkService extends IntentService{
...
@Override
protected void
onHandleIntent(Intent intent) {
Log.e(WakeCPUReceiver.TAG,"WorkService is working");
// TODO 处理我们的任务
WakeCPUReceiver.completeWakefulIntent(intent);
}
}
completeWakefulIntent源码
public static boolean completeWakefulIntent(Intent
intent) {
final int id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0);
if (id == 0) {
return false;
}
synchronized(mActiveWakeLocks) {
PowerManager.WakeLock wl =mActiveWakeLocks.get(id);
if (wl != null) {
wl.release();
mActiveWakeLocks.remove(id);
return true;
}
// We return true whether or not we actually found the wake lock
// the return code is defined to indicate whether the Intent contained
// an identifier for a wake lock that it was supposed to match.
// We just log a warning here if there is no wake lock found, which could
// happen for example if this function is called twice on the same
// intent or the process is killed and restarted before processing the
intent.
Log.w("WakefulBroadcastReceiver", "No active
wake lock id #"+ id);
return true;
}
}
五、Handler的溢出问题
虽然Handler很好用,但由于它可以延迟发送消息,在我们延迟启动其他组件,或者使用Activity的引用调用一些方法时,如果在延迟的过程中,Activity finish掉了,这时候就会抛出溢出的异常了。所以,我们在onDestroy的时候,记得 调用removeCallbacks,removeMessages等移除消息的方法来解决这个问题
参考链接:https://www.jianshu.com/p/916bc0645295
六、使用Handler的注意事项:
在onDestroy的时候,记得 调用removeCallbacks,removeMessages等移除消息的方法来解决内存泄露的问题(很多时候如果在页面中处理完了相关操作,在onStop()或onDestory()方法中都需要解除占用以避免OOM等异常。)