Handler详解

一、基础解读

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相关联,并采用可处理消息的回调接口。)

Handler(Looper 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

dump(Printer pw, String prefix)

final Looper

getLooper()

String

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

post(Runnable r)

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

removeCallbacks(Runnable r)

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).

String

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等异常。)

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值