XhstormR


On a dark desert highway Cool wind in my hair


Android Handler

Updated on 2016-10-29

ThreadLocal

WeakReference

Source Insight

  • Handler:负责发送和处理 Message。
  • ThreadLocal:负责保存 Looper,作为线程的局部变量。
    • Looper:负责接收并转发 Message。
      • MessageQueue:负责存储 Message 的容器。

post ➜ Runnable

MainActivity.java

public class MainActivity extends Activity {
    private ImageView mImageView;
    private final Handler mHandler = new Handler();
    private final Runnable mRunnable = new Runnable() {     A:第一种(postDelayed)
        private int[] colors = {Color.parseColor("red"), Color.parseColor("blue"), Color.parseColor("green")};     颜色
        private int index;     索引

        @Override
        public void run() {
            index++;
            index %= 3;
            mImageView.setBackgroundColor(colors[index]);     设置背景颜色
            mHandler.postDelayed(this, 1000);     封装线程体为 Message 中的 callback,再传递至 Looper 中的 MessageQueue,并延迟 1 秒后执行
        }                                           ↳ 使用了递归思想,无限循环
    };
    private Thread mThread = new Thread() {     B:第二种(post)
        private int[] colors = {Color.parseColor("red"), Color.parseColor("blue"), Color.parseColor("green")};     颜色

        @Override
        public void run() {
            while (!Thread.interrupted()) {     判断线程是否为中断状态
                for (final int color : colors) {     遍历颜色
                    mHandler.post(new Runnable() {     封装线程体为 Message 中的 callback,再传递至 Looper 中的 MessageQueue
                        @Override
                        public void run() {
                            mImageView.setBackgroundColor(color);     设置背景颜色
                        }
                    });
                    try {
                        Thread.sleep(1000);     当前线程进入阻塞状态 1 秒
                    } catch (InterruptedException e) {     阻塞状态下中断线程将引发该异常,且中断状态将被清除
                        return;     直接返回
                    }
                }
            }
        }
    };

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

        mImageView = (ImageView) findViewById(R.id.imageView);     需实现每隔 1 秒更换此 ImageView 背景颜色
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);     A 移除 Looper 中的 MessageQueue 中所有等待处理的 Message
        mThread.interrupt();     B 中断线程
    }

    public void onClick(View view) {     A
        switch (view.getId()) {
            case R.id.button1:
                mHandler.post(mRunnable);     封装线程体为 Message 中的 callback,再传递至 Looper 中的 MessageQueue
                break;
            case R.id.button2:
                mHandler.removeCallbacksAndMessages(null);     移除 Looper 中的 MessageQueue 中所有等待处理的 Message
                break;
        }
    }

    public void onClick(View view) {     B
        switch (view.getId()) {
            case R.id.button1:
                mThread.start();     启动线程
                break;
            case R.id.button2:
                mThread.interrupt();     中断线程
                break;
        }
    }
}

activity_main.xml

<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:padding="16dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <ImageView
            android:layout_gravity="center"
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:id="@+id/imageView"/>
    <Button
            android:onClick="onClick"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="开始"
            android:id="@+id/button1"/>
    <Button
            android:onClick="onClick"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="停止"
            android:id="@+id/button2"/>
</LinearLayout>

sendMessage ➜ Message

MainActivity.java

public class MainActivity extends Activity {
    private TextView mTextView;
    private final Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {     处理 Message(主线程)
            mTextView.setText(1 + ":" + msg.what + ":" + msg.arg1 + ":" + msg.arg2 + ":" + msg.obj);
            return msg.what != 0;     true 代表拦截(不传递),false 代表不拦截(传递)
        }
    }) {
        @Override
        public void handleMessage(Message msg) {     处理 Message(主线程)
            mTextView.setText(2 + ":" + msg.what + ":" + msg.arg1 + ":" + msg.arg2 + ":" + msg.obj);
        }
    };

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

        mTextView = (TextView) findViewById(R.id.textView);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);     移除 Looper 中的 MessageQueue 中所有等待处理的 Message
    }

    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.button:
                new Thread() {
                    @Override
                    public void run() {
                        第一种
                        Message message = new Message();
                        message.what = 0;
                        message.arg1 = 123;
                        message.arg2 = 456;
                        message.obj = "你好";
                        mHandler.sendMessage(message);     传递至 Looper 中的 MessageQueue,再发送给对应的 Handler 对象处理

                        第二种(推荐)
                        Message message = mHandler.obtainMessage(0, 123, 456, "你好");     重用 Message
                        message.sendToTarget();     传递至 Looper 中的 MessageQueue,再发送给对应的 Handler 对象处理
                    }
                }.start();     启动线程
                break;
        }
    }
}

activity_main.xml

<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:padding="16dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <Button
            android:onClick="onClick"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="发送"
            android:id="@+id/button"/>
    <TextView
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="New Text"
            android:textSize="20sp"
            android:id="@+id/textView"/>
</LinearLayout>

防止 Handler 导致内存泄漏

  • 导致原因:
    • 非静态的内部类默认持有其外部类的强引用。
    • Handler 对象发送的 Message 对象会持有此 Handler 对象的引用。
    • Message ➜ Handler ➜ Activity ➜ Activity 资源无法回收(内存泄漏)
  • 解决方案:
    • 声明 Handler 为 static 或者使用 Handler.Callback 接口或者为其新建类文件。
    • 通过弱引用持有外部 Activity。
  • 弱引用:只持有弱引用的对象随时都会被回收,用于避免内存泄漏。(WeakReference)
  • 软引用:只持有软引用的对象内存不足时回收,用于实现缓存机制。(SoftReference)
  • 引用级别:强引用 > 软引用 > 弱引用 > 虚引用
public class MainActivity extends Activity {
    private static final Runnable mRunnable = new Runnable() {     静态匿名内部类
        @Override
        public void run() {
            Log.w("Tag", Thread.currentThread().toString());
        }
    };
    private final MyHandler mMyHandler = new MyHandler(this);
    private TextView mTextView;

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

        mTextView = (TextView) findViewById(R.id.textView);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);     移除 Looper 中的 MessageQueue 中所有等待处理的 Message
    }

    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.button:
                new Thread() {
                    @Override
                    public void run() {
                        Message message = mMyHandler.obtainMessage(0, 123, 456, "你好");
                        mMyHandler.sendMessage(message);     传递至 Looper 中的 MessageQueue,再发送给对应的 Handler 对象处理
                        mMyHandler.post(mRunnable);     封装线程体为 Message 中的 callback,再传递至 Looper 中的 MessageQueue
                    }
                }.start();     启动线程
                break;
        }
    }

    private static class MyHandler extends Handler {     静态内部类
        private final WeakReference<MainActivity> mActivity;     通过弱引用持有外部 Activity

        private MyHandler(MainActivity activity) {     构造方法
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity mainActivity = mActivity.get();
            if (mainActivity != null) {     如果不为空
                mainActivity.mTextView.setText(msg.what + ":" + msg.arg1 + ":" + msg.arg2 + ":" + msg.obj);
            }
        }
    }
}

自定义 Looper 线程

public class MainActivity extends Activity {
    private final MyHandler mMyHandler = new MyHandler();     与主线程进行关联

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

        mMyHandler.sendEmptyMessage(1);     Message 在主线程进行处理

        MyThread myThread = new MyThread();
        myThread.start();     启动线程
        try {
            Thread.sleep(100);     阻塞 0.1 秒,等待线程体实例化 mMyHandler,避免空指针异常
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread.mMyHandler.sendEmptyMessage(2);     Message 在子线程进行处理
    }

    private static class MyThread extends Thread {
        private MyHandler mMyHandler;

        @Override
        public void run() {
            Looper.prepare();     为当前线程创建 Looper 并与之绑定
            mMyHandler = new MyHandler();          与子线程进行关联(实例化 Handler 的同时会自动绑定当前线程的 Looper)
            Looper.loop();     Looper 开始工作,无限循环,阻塞方法
        }

        public void stopLoop() {
            Looper.myLooper().quitSafely();     Looper 停止工作
        }
    }

    private static class MyHandler extends Handler {     静态内部类
        @Override
        public void handleMessage(Message msg) {
            Log.w("Tag", msg.what + "::" + Thread.currentThread().toString());
        }
    }
}
----
输出:
21106-21387/com.example.myapp.myapplication W/Tag: 2::Thread[Thread-214,5,main]
21106-21106/com.example.myapp.myapplication W/Tag: 1::Thread[main,5,main]

HandlerThread

public class MainActivity extends Activity {
    private final HandlerThread mHandlerThread = new HandlerThread("123");     已封装好的 Looper 线程,推荐这种方式
    private MyHandler mMyHandler1;
    private MyHandler mMyHandler2;

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

        mMyHandler1 = new MyHandler();     无参构造方法
        mMyHandler1.sendEmptyMessage(0);     Message 在主线程进行处理

        mHandlerThread.start();     启动线程
        mMyHandler2 = new MyHandler(mHandlerThread.getLooper());     有参构造方法
        mMyHandler2.sendEmptyMessage(0);     Message 在子线程进行处理
    }

    private static class MyHandler extends Handler {     静态内部类
        private MyHandler() {     无参构造方法(实例化 Handler 的同时会自动绑定当前线程的 Looper)
        }

        private MyHandler(Looper looper) {     有参构造方法(指定 Looper 对象)
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            Log.w("Tag", msg.what + "::" + Thread.currentThread().toString());
        }
    }
}
----
输出:
2853-4062/com.example.myapp.myapplication W/Tag: 0::Thread[123,5,main]
2853-2853/com.example.myapp.myapplication W/Tag: 0::Thread[main,5,main]

线程之间的交互

public class MainActivity extends Activity {
    private final HandlerThread mHandlerThread = new HandlerThread("123");
    private MyHandler mMyHandler1;
    private MyHandler mMyHandler2;

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

        mHandlerThread.start();     启动线程

        mMyHandler1 = new MyHandler();     绑定主线程 Looper
        mMyHandler2 = new MyHandler(mHandlerThread.getLooper());     绑定子线程 Looper
    }

    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.button1:
                Message message1 = mMyHandler1.obtainMessage(1, "主线程发往子线程");
                message1.setTarget(mMyHandler2);     指定目标 Handler
                message1.sendToTarget();     传递至 Looper 中的 MessageQueue,再发送给对应的 Handler 对象处理
                break;
            case R.id.button2:
                Message message2 = mMyHandler2.obtainMessage(2, "子线程发往主线程");
                message2.setTarget(mMyHandler1);     指定目标 Handler
                message2.sendToTarget();     传递至 Looper 中的 MessageQueue,再发送给对应的 Handler 对象处理
                break;
        }
    }

    private static class MyHandler extends Handler {     静态内部类
        private MyHandler() {     无参构造方法(实例化 Handler 的同时会自动绑定当前线程的 Looper)
        }

        private MyHandler(Looper looper) {     有参构造方法(指定 Looper 对象)
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            Log.w("Tag", msg.what + "::" + msg.obj + "::" + Thread.currentThread().toString());
        }
    }
}
----
输出:
15749-18518/com.example.myapp.myapplication W/Tag: 1::主线程发往子线程::Thread[123,5,main]
15749-15749/com.example.myapp.myapplication W/Tag: 2::子线程发往主线程::Thread[main,5,main]

更新 UI 的 4 种方式

public class MainActivity extends Activity {
    private final MyHandler mMyHandler = new MyHandler(this);
    private final MyRunnable mMyRunnable = new MyRunnable(this);
    private TextView mTextView;

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

        mTextView = (TextView) findViewById(R.id.textView);
    }

    public void onClick(View view) {     这 4 种方法本质上都是通过 Handler 机制而运行在主线程
        switch (view.getId()) {
            case R.id.button1:
                new Thread() {
                    @Override
                    public void run() {
                        mMyHandler.obtainMessage(1, "你好").sendToTarget();     Handler 方法 sendMessage
                    }
                }.start();     启动新线程
                break;
            case R.id.button2:
                new Thread() {
                    @Override
                    public void run() {
                        mMyHandler.post(mMyRunnable);     Handler 方法 post
                    }
                }.start();     启动新线程
                break;
            case R.id.button3:
                new Thread() {
                    @Override
                    public void run() {
                        mTextView.post(mMyRunnable);     View 方法 post
                    }
                }.start();     启动新线程
                break;
            case R.id.button4:
                new Thread() {
                    @Override
                    public void run() {
                        MainActivity.this.runOnUiThread(mMyRunnable);     Activity 方法 runOnUiThread
                    }
                }.start();     启动新线程
                break;
        }
    }

    private static class MyHandler extends Handler {     静态内部类
        private final WeakReference<MainActivity> mActivity;     通过弱引用持有外部 Activity

        private MyHandler(MainActivity activity) {     构造方法
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            Log.w("Tag", Thread.currentThread().toString());     打印当前线程信息
            MainActivity mainActivity = mActivity.get();
            if (mainActivity != null) {     如果不为空
                mainActivity.mTextView.setText(msg.obj.toString());
            }
        }
    }

    private static class MyRunnable implements Runnable {     静态内部类
        private final WeakReference<MainActivity> mActivity;     通过弱引用持有外部 Activity

        private MyRunnable(MainActivity activity) {     构造方法
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void run() {
            Log.w("Tag", Thread.currentThread().toString());     打印当前线程信息
            MainActivity mainActivity = mActivity.get();
            if (mainActivity != null) {     如果不为空
                mainActivity.mTextView.setText("你好");
            }
        }
    }
}
----
输出:
3974-3974/com.example.myapp.myapplication W/Tag: Thread[main,5,main]
3974-3974/com.example.myapp.myapplication W/Tag: Thread[main,5,main]
3974-3974/com.example.myapp.myapplication W/Tag: Thread[main,5,main]
3974-3974/com.example.myapp.myapplication W/Tag: Thread[main,5,main]
TOP