handler详解

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

Handler(Looperlooper)

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)

finalLoopergetLooper()

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.

final booleanpost(Runnabler)

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

StringtoString()

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容