一、基础解读
Android 的Handler 用于处理消息队列
handler 牛津词典的翻译:
1. 驯兽员;(尤指)驯犬员。2. 搬运工;操作者。3. 组织者;顾问
handler的构造方法:
Handler()
Default constructor associates this handler with the Looper for the current thread.(默认构造函数将此处理程序与当前线程的Looper关联。)
Handler(Handler.Callback callback)
Constructor associates this handler with the Looper for the current thread and takes a callback interface in which you canhandle messages.(构造函数将此处理程序与当前线程的Looper相关联,并采用可处理消息的回调接口。)
Use the provided Looper instead of the default one.(使用提供的Looper而不是默认的Looper。)
Handler(Looper looper, Handler.Callback callback)
Use the provided Looper instead of the default one and take a callback interface in which tohandle messages.(使用提供的Looper而不是默认的Looper,并在其中处理消息的回调接口。)
可以看出来,所有的构造函数,都与Looper有关。
Handler的方法:
void | dispatchMessage(Message msg) Handle system messages here.(在这里处理系统消息。) |
final void | |
final Looper | |
getMessageName(Message message) Returns a string representing the name of the specified message.(返回表示指定消息名称的字符串。默认实现将返回消息回调的类名(如果有)或消息“what”字段的十六进制表示形式。) | |
void | handleMessage(Message msg) Subclasses must implement this to receive messages. (子类必须实现这个才能接收消息。) |
final boolean | hasMessages(int what, Object object) Check if there are any pending posts of messages with code 'what' and whose obj is 'object' in the message queue. (方法中what用来判断对应的逻辑,Object用来接收所传递过来的数据。并做处理操作) |
final boolean | hasMessages(int what) Check if there are any pending posts of messages with code 'what' in the message queue. |
final Message | obtainMessage(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成员。) |
final Message | obtainMessage() (从全局消息池中返回一条新消息。比创建和分配新实例更高效。检索到的消息将其处理程序设置为此实例(Message.target == this)。如果你不想要那个设施,只需调用Message.obtain()。) Returns a new Message from the global message pool. |
final Message | obtainMessage(int what, int arg1, int arg2, Object obj) Same as obtainMessage(), except that it also sets the what, obj, arg1,and arg2 values on the returned Message. |
final Message | obtainMessage(int what) Same as obtainMessage(), except that it also sets the what member of the returned Message. |
final Message | obtainMessage(int what, Object obj) Same as obtainMessage(), except that it also sets the what and obj members of the returned Message. |
final boolean | Causes the Runnable r to be added to the message queue.(导致Runnable r被添加到消息队列中。 runnable将在该处理程序所连接的线程上运行) |
final boolean | postAtFrontOfQueue(Runnable r) Posts a message to an object that implements Runnable. (将消息发布到实现Runnable的对象。) |
final boolean | postAtTime(Runnable r, Object token, long uptimeMillis) Causes the Runnable r to be added to the message queue, to be run at a specific time given by uptimeMillis. (导致Runnable r被添加到消息队列中,以在由uptimeMillis给定的特定时间运行。) |
final boolean | postAtTime(Runnable r, long uptimeMillis) Causes the Runnable r to be added to the message queue, to be run at a specific time given by uptimeMillis. |
final boolean | postDelayed(Runnable r, 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 void | Remove any pending posts of Runnable r that are in the message queue. |
final void | removeCallbacks(Runnable r, Object token) Remove any pending posts of Runnable r with Object token that are in the message queue. |
final void | removeCallbacksAndMessages(Object token) Remove any pending posts of callbacks and sent messages whose obj is token. |
final void | removeMessages(int what) Remove any pending posts of messages with code 'what' that are in the message queue. (使用消息队列中的代码“what”在消息队列中删除。) |
final void | removeMessages(int what, Object object) Remove any pending posts of messages with code 'what' and whose obj is 'object' that are in the message queue. |
final boolean | sendEmptyMessage(int what) Sends a Message containing only the what value.(仅发送仅包含what的消息。) |
final boolean | sendEmptyMessageAtTime(int what, long uptimeMillis) Sends a Message containing only the what value, to be delivered at a specific time.(在指定的时间过后,发送包含what的消息给handler) |
final boolean | sendEmptyMessageDelayed(int what, long delayMillis) Sends a Message containing only the what value, to be delivered after the specified amount of time elapses. |
final boolean | sendMessage(Message msg) Pushes a message onto the end of the message queue after all pending messages before the current time. |
final boolean | sendMessageAtFrontOfQueue(Message msg) Enqueue a message at the front of the message queue, to be processed on the next iteration of the message loop. |
boolean | sendMessageAtTime(Message msg, long uptimeMillis) Enqueue a message into the message queue after all pending messages before the absolute time (in milliseconds) uptimeMillis. |
final boolean | sendMessageDelayed(Message msg, long delayMillis) Enqueue a message into the message queue after all pending messages before (current time + delayMillis). |
toString() 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。示意图如下
new Handler的时候Handler就已经拿到了线程的Looper 。MessagQueue
handler发送消息:
把Handler保存到Message里。
把Message保存到messageQueue里。
ActivityThread.java主线程入口类
在main()方法中存入了Looper.prepareMainLooper();(这里已经创建了Looper,messagequeue)
然后不断地执行获取消息的方法:Looper.loop();去出message,然后调用handler的dispatchMessage(msg);
示例图:
四、什么情况下使用handler?
最简单的消息发送
主线程使用Handler, 主线程里或子线程里发送消息,或延迟发送消息的方式更新UI
如,
启动应用时Splash页面的延迟2,3秒后,跳转到主页面
加载完页面的各个控件后,再加载线程下载图片,最后更新图片等等
private static final int WHAT_UPDATE_ICON = 1;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case WHAT_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 {
publicDownloadOneByOne() {
super(DownloadOneByOne.class.getSimpleName());
}
@Override
protectedvoid onLooperPrepared() {
super.onLooperPrepared();
// 初始化下载组件
}
}
private HandlerThread mHandlerThread;
private Handler downloadHandler = new Handler(){
@Override
public voidhandleMessage(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 extendsAppCompatTextView {
/**
* 总时间
*/
private long seconds;
/**
* 当前分钟
*/
private long minutes;
/**
* 当前秒数
*/
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 int marginEnd;
private StringBuilder content = new StringBuilder();
publicCountDownView(Context context, @Nullable AttributeSet attrs) {
super(context,attrs);
}
@Override
protectedvoid 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);
}
privatevoid setMarginEnd(int marginEnd) {
LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.setMarginEnd(marginEnd);
layoutParams.resolveLayoutDirection(layoutParams.getLayoutDirection());
}
@Override
protectedvoid onDetachedFromWindow() {
super.onDetachedFromWindow();
if(handler.hasMessages(WHAT_TICK)) {
handler.removeMessages(WHAT_TICK);
}
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case WHAT_DONE:
setVisibility(View.GONE);
break;
default:
setText(content.toString());
handler.post(runnable);
break;
}
}
};
/***
* 设置倒计时
* @parammillis
*/
public voidsetCountDownMills(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 = new Runnable() {
@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);
}
};
privateStringBuilder appendZeroWhenLower10(long value) {
if (value < 10) {
content.append("0").append(value);
} else {
content.append(value);
}
return content;
}
}
结合IntentService的使用
使用IntentService处理耗时的任务相对比较简单,我们来个有难度的,结合AlarmManager的调度,息屏唤醒IntentService定时处理任务的案例来讲
当我们的应用进入后台activity栈的时候,注册并启动AlarmManager任务
private static final String ACTION_WAKE_UP = "com.doze.cpu.wakeup";
public static voidregisterAlarm(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 = new Intent();
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 extendsWakefulBroadcastReceiver {
@Override
public voidonReceive(Context context, Intent intent) {
Intent wakefulIntent = new Intent(context, WorkService.class);
startWakefulService(context,wakefulIntent);
schedule(context);
}
startWakefulService的源码
public static ComponentName startWakefulService(Contextcontext, Intent intent) {
synchronized(mActiveWakeLocks) {
int id = 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);
return comp;
}
}
在IntentService里,我们在onHandleIntent处理我们的任务后,再调用
WakefulBroadcastReceiver的静态方法completeWakefulIntent,释放WakeLock,减少电量的消耗
public class WorkService extendsIntentService {
...
@Override
protectedvoid onHandleIntent(Intent intent) {
Log.e(WakeCPUReceiver.TAG, "WorkService is working");
// TODO 处理我们的任务
WakeCPUReceiver.completeWakefulIntent(intent);
}
}
completeWakefulIntent源码
public static boolean completeWakefulIntent(Intentintent) {
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 theintent.
Log.w("WakefulBroadcastReceiver", "No activewake 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等异常。)