Android Handler 基础使用

01.Handler常见使用方式

  • handler机制大家都比较熟悉呢。在子线程中发送消息,主线程接受到消息并且处理逻辑。如下所示

    • 一般handler的使用方式都是在主线程中定义Handler,然后在子线程中调用mHandler.sendXx()方法,这里有一个疑问可以在子线程中定义Handler吗?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class MainActivity extends AppCompatActivity {

    private TextView tv ;

    /**
    * 在主线程中定义Handler,并实现对应的handleMessage方法
    */
    public static Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
    if (msg.what == 101) {
    Log.i("MainActivity", "接收到handler消息...");
    }
    }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tv = (TextView) findViewById(R.id.tv);
    tv.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    new Thread() {
    @Override
    public void run() {
    // 在子线程中发送异步消息
    mHandler.sendEmptyMessage(1);
    }
    }.start();
    }
    });
    }
    }

02.在子线程中定义Handler

  • 直接在子线程中创建handler,看看会出现什么情况?

    • 运行后可以得出在子线程中定义Handler对象出错,难道Handler对象的定义或者是初始化只能在主线程中?其实不是这样的,错误信息中提示的已经很明显了,在初始化Handler对象之前需要调用Looper.prepare()方法。
    • Handler的工作是依赖于Looper的,而Looper(与消息队列)又是属于某一个线程(ThreadLocal是线程内部的数据存储类,通过它可以在指定线程中存储数据,其他线程则无法获取到),其他线程不能访问。因此Handler就是间接跟线程是绑定在一起了。因此要使用Handler必须要保证Handler所创建的线程中有Looper对象并且启动循环。因为子线程中默认是没有Looper的,所以会报错。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    tv.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    new Thread() {
    @Override
    public void run() {
    Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
    if (msg.what == 1) {
    Log.i(TAG, "在子线程中定义Handler,接收并处理消息");
    }
    }
    };
    }
    }.start();
    }
    });
  • 如何正确运行。在这里问一个问题,在子线程中可以吐司吗?答案是可以的,只不过有条件。

    • 这样程序已经不会报错,那么这说明初始化Handler对象的时候我们是需要调用Looper.prepare()的,那么主线程中为什么可以直接初始化Handler呢?难道是主线程创建handler对象的时候,会自动调用Looper.prepare()方法的吗?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    tv.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    new Thread() {
    @Override
    public void run() {
    Looper.prepare();
    Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
    if (msg.what == 1) {
    Log.i(TAG, "在子线程中定义Handler,接收并处理消息");
    }
    }
    };
    //获取Looper对象
    mLooper = Looper.myLooper();
    Looper.loop();
    //在适当的时候退出Looper的消息循环,防止内存泄漏
    mLooper.quit();
    }
    }.start();
    }
    });

03.Handler的post方法和view的post方法

  • Handler的post方法实现很简单,如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    mHandler.post(new Runnable() {
    @Override
    public void run() {

    }
    });

    public final boolean post(Runnable r){
    return sendMessageDelayed(getPostMessage(r), 0);
    }
  • view的post方法也很简单,如下所示

    • 可以发现其调用的就是activity中默认保存的handler对象的post方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
    return attachInfo.mHandler.post(action);
    }
    ViewRootImpl.getRunQueue().post(action);
    return true;
    }

    public void post(Runnable action) {
    postDelayed(action, 0);
    }

    public void postDelayed(Runnable action, long delayMillis) {
    final HandlerAction handlerAction = new HandlerAction(action, delayMillis);

    synchronized (this) {
    if (mActions == null) {
    mActions = new HandlerAction[4];
    }
    mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
    mCount++;
    }
    }

04.避免子线程手动创建looper

  • 下面这种使用方式,是非常危险的一种做法

    • 在子线程中,如果手动为其创建Looper,那么在所有的事情完成以后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待的状态,而如果退出Looper以后,这个线程就会立刻终止,因此建议不需要的时候终止Looper。(【 Looper.myLooper().quit(); 】)
    1
    2
    3
    4
    5
    6
    7
    8
    new Thread(new Runnable() {
    @Override
    public void run() {
    Looper.prepare();
    Toast.makeText(MainActivity.this, "run on Thread", Toast.LENGTH_SHORT).show();
    Looper.loop();
    }
    }).start();

05.解决Handler内存泄漏

  • 解决Handler内存泄露主要2点

    • 有延时消息,要在Activity销毁的时候移除Messages
    • 匿名内部类导致的泄露改为匿名静态内部类,并且对上下文或者Activity使用弱引用。
  • 问题代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class MainActivity extends AppCompatActivity {
    private Handler mHandler = new Handler();
    private TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mTextView = (TextView) findViewById(R.id.text); //模拟内存泄露
    mHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
    mTextView.setText("yangchong");
    }
    }, 2000);
    }
    }
  • 造成内存泄漏原因分析

    • 上述代码通过内部类的方式创建mHandler对象,此时mHandler会隐式地持有一个外部类对象引用这里就是MainActivity,当执行postDelayed方法时,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,MessageQueue是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。
  • 解决方案

    • 第一种解决办法

      • 要想避免Handler引起内存泄漏问题,需要我们在Activity关闭退出的时候的移除消息队列中所有消息和所有的Runnable。
      • 上述代码只需在onDestroy()函数中调用mHandler.removeCallbacksAndMessages(null);就行了。
      1
      2
      3
      4
      5
      6
      7
      8
      @Override
      protected void onDestroy() {
      super.onDestroy();
      if(handler!=null){
      handler.removeCallbacksAndMessages(null);
      handler = null;
      }
      }
    • 第二种解决方案

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      //自定义handler
      public static class HandlerHolder extends Handler {
      WeakReference<OnReceiveMessageListener> mListenerWeakReference;
      /**
      * @param listener 收到消息回调接口
      */
      HandlerHolder(OnReceiveMessageListener listener) {
      mListenerWeakReference = new WeakReference<>(listener);
      }

      @Override
      public void handleMessage(Message msg) {
      if (mListenerWeakReference!=null && mListenerWeakReference.get()!=null){
      mListenerWeakReference.get().handlerMessage(msg);
      }
      }
      }

      //创建handler对象
      private HandlerHolder handler = new HandlerHolder(new OnReceiveMessageListener() {
      @Override
      public void handlerMessage(Message msg) {
      switch (msg.what){
      case 1:
      TextView textView1 = (TextView) msg.obj;
      showBottomInAnimation(textView1);
      break;
      case 2:
      TextView textView2 = (TextView) msg.obj;
      showBottomOutAnimation(textView2);
      break;
      }
      }
      });

      //发送消息
      Message message = new Message();
      message.what = 1;
      message.obj = textView;
      handler.sendMessageDelayed(message,time);


      即推荐使用静态内部类 + WeakReference 这种方式。每次使用前注意判空。