详解Android Handler 机制 (一)用法全解

mm.jpeg

ps:这是关于Android Handler 机制的第一篇文章,主要来说一下Handler的用法,本文尽量归纳完全,如有缺漏,欢迎补充。

Handler的主要作用是切换线程,以及隐式的充当接口回调的作用,当子线程网络请求结束后,通过handler发送消息给主线程,这一点都不多说了。

常用用法

先来贴一种最常用的用法:

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //...处理逻辑
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendEmptyMessage(MainActivity.HANDLER_MAIN_CODE);
            }
        });

这种方法是大家最常用的,使用Handler的匿名内部类,去覆写handleMessage方法,来处理当消息到达时所做出的反应。这里埋一个伏笔:

报黄

这里IDEA在提示可能会发生内存泄漏,具体为什么会发生内存泄漏,针对问题的具体分析以及最后封装SafeHandler,我会在第三篇Handler文章中讲述。

Message

Message是Handler发送的消息的数据载体。

先看一下Message,Message用来承载数据,承载数据的实现是Message的几个public的成员变量:

    public int what;

    public int arg1;

    public int arg2;

    public Object obj;
  
  • what: 这是一个int类型的变量,通常用来在handleMessage处做判断,当一个handler发送多条message时,用来辨别这是哪一条消息
  • arg1,arg2,当发送的Message需要承载的data是整数时,可以简单的使用这两个参数来承载。
  • Obj,这是一个Object对象,因为有这个变量,我们所能承载的data就多样化了,只要是对象都可以通过message传递,List、数组等等。
    另外Message还提供了通过Bundle来传递数据:
    Bundle也是一个数据载体,这里就不多说了。
/**
     * Like getData(), but does not lazily create the Bundle.  A null
     * is returned if the Bundle does not already exist.  See
     * {@link #getData} for further information on this.
     * @see #getData()
     * @see #setData(Bundle)
     */
    public Bundle peekData() {
        return data;
    }

    /**
     * Sets a Bundle of arbitrary data values. Use arg1 and arg2 members
     * as a lower cost way to send a few simple integer values, if you can.
     * @see #getData()
     * @see #peekData()
     */
    public void setData(Bundle data) {
        this.data = data;
    }
Message池

Message中维持着一个Message池,所以通常我们无需new Message(),可以调用:
Message message = Message.obtain();,节省内存空间。

Handler处理消息

Handler处理消息除了文章开头所说到的一种,其实还有两种:
message的callback和handler的mCallback,一共有三种,为什么我可以这么说呢?下面上源码

public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

这个dispatchMessage()方法是在Lopper.loop方法的无限循环中调用的:

    for(;;) {
        ...
        msg.target.dispatchMessage(msg);
        ...
    }

这个msg就是我们发送的消息,target就是我们发送消息的handler。这里看不懂的也没关系,毕竟涉及到源码,之后的第二篇会将。现在只需要知道这个dispathchMessage方法是在消息接收处调用的。然后继续返回去看这个方法的内部实现。
这个方法通过三层if-else嵌套,也就规定了这三种Handler处理消息的方法的优先级:


flowchart.png

可以看到我们常用的第三种方法,覆写handlerMessage是优先级最低的。

msg.callback

既然是msg的一个属性,那么肯定是通过msg承载callback来发送出去,先看一下callback的定义

    @UnsupportedAppUsage
    /*package*/ Runnable callback;

这是一个Runnable,然后看一下handler的handleCallback(msg);方法:

private static void handleCallback(Message message) {
        message.callback.run();
}

这样我们就知道了,消息接受处在收到message之后,如果message的callback不为空,就调用这个callback的run方法。下面看基于将事件包装成Message的Runnable的两种方法:

在Message的构造函数,或者obtain方法中直接传入Runnable实例
    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, "消息接受后执行这段代码,这段代码运行在主线程"
                                                  ,Toast.LENGTH_SHORT).show();
                    }
                };
                Message message = Message.obtain(mHandler, runnable);
                mHandler.sendMessage(message);
            }
        });
}
mHandler.post(Runnable runnable)

相比于上一种方法,这种方法可能更加形象,将一个携带事件的Runnable,post到MesageQueue中,然后交给主线程执行:

    private Handler mHandler = new Handler();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               
                
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, "这段代码交给主线程去执行", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
mHandler.mCallback

这个方法其实和匿名内部类的写法大同小异,只不过是把覆写的handleMessage方法中的逻辑转移到mHandler的mCallback中,下面直接上代码:

Handler.Callback callback = new Handler.Callback() {
      @Override
      public boolean handleMessage(@NonNull Message msg) {
          Toast.makeText(MainActivity.this, "收到消息", Toast.LENGTH_SHORT).show();
          return true;
      }
  };

  private Handler mHandler = new Handler(callback);



  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              mHandler.sendMessage(Message.obtain());
          }
      });
}

注意handleMessage的返回时,如果返回true,那么表示处理条消息,不再向下执行。如果返回false,那么表示没有成功处理这条消息,接着向下执行,也是就执行Handler的handleMessage方法

Handler.Callback callback = new Handler.Callback() {
       @Override
       public boolean handleMessage(@NonNull Message msg) {
           Toast.makeText(MainActivity.this, "收到消息", Toast.LENGTH_SHORT).show();
           //...处理消息
           if (...) {
               return false;
           }
           return true;
       }
   };


   private Handler mHandler = new Handler(callback) {
       @Override
       public void handleMessage(@NonNull Message msg) {
           super.handleMessage(msg);
           Toast.makeText(MainActivity.this, 
                   "如果callback的handleMessage返回false,那么接着执行这里"
                   , Toast.LENGTH_SHORT).show();
       }
   };


   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               mHandler.sendMessage(Message.obtain());
           }
       });
}

总结

这样终于把Handler的三种处理消息的用法搞清楚了,需要注意的是,第一种方法和其他两种是互斥的,只要Message的callback不为空,那么就执行callback中的run方法的逻辑,而不会执行handleMessage。

我们需要知道的是,Handler机制在Android中的作用绝对不仅仅是给开发者切换线程,Handler机制在维持一个APP的正常运作。一个点击事件,启动一个Activity等等基础功能,都离不开Handler。具体是如何运作的,就要看Handler机制的源码了。

既然都看完了,大家点个赞再走呀

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