异步通信1——Handler详解

278 阅读2分钟

一、什么是Handler

Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue。

  1. 可以让对应的Message和Runnable在未来的某个时间点进行相应处理。(就是在子线程耗时操作完成后,可以通知主线程更新UI)。
  2. 让自己想要处理的耗时操作放在子线程,让更新UI的操作放在主线程。

二、Handler的使用方法

2.1 post(runnable)

  1. 在成员变量中创建Handler(该Handler自动绑定到了主线程)
  2. 创建Runnable
  3. post(runnable), 在Runnable中更新UI
public class HandlerWithRunnableActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView statusTextView;

    private Handler mHandler = new Handler();
    private static final String TAG = "HandlerWithRunnableA---";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusTextView = findViewById(R.id.statusTextView);
        findViewById(R.id.btnDownload).setOnClickListener(this);
        Log.d(TAG, "Main thread id  = " + Thread.currentThread().getId());
    }

    @Override
    public void onClick(View v) {
        DownloadThread downloadThread = new DownloadThread();
        downloadThread.start();
    }

    class DownloadThread extends Thread {
        @Override
        public void run() {
            try {
                Log.d(TAG, "DownloadThread id = " + Thread.currentThread().getId());
                Log.d(TAG, "开始下载文件");
                Thread.sleep(5000);
                Log.d(TAG, "文件下载完成");
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        Log.d(TAG, "Runnable thread id = " + Thread.currentThread().getId());
                        HandlerWithRunnableActivity.this.statusTextView.setText("文件下载完成");
                    }
                };
                mHandler.post(runnable);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.2 sendMessage(message)

  1. 在主线程中创建Handler,复写了它的handlerMessage方法
  2. 在子线程中通过 Message 属性调用HandlerMessage方法,来将子线程中的消息传到主线程,
  3. 通过handlerMessage的复写方法进行相应的UI线程的处理
public class HandlerWithMessageActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView statusTextView;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case 1:
                    Log.d(TAG, "Runnable thread id = " + Thread.currentThread().getId());
                    HandlerWithMessageActivity.this.statusTextView.setText("文件下载OK");
                    break;
            }
        }
    };;
    private static final String TAG = "HandlerWithMessA---";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusTextView = findViewById(R.id.statusTextView);
        findViewById(R.id.btnDownload).setOnClickListener(this);
        Log.d(TAG, "Main thread id  = " + Thread.currentThread().getId());

    }

    @Override
    public void onClick(View v) {
        DownloadThread downloadThread = new DownloadThread();
        downloadThread.start();
    }

    class DownloadThread extends Thread {
        @Override
        public void run() {
            try {
                Log.d(TAG, "DownloadThread id = " + Thread.currentThread().getId());
                Log.d(TAG, "开始下载文件");
                Thread.sleep(5000);
                Log.d(TAG, "文件下载完成");
                Message msg = new Message();
                msg.what = 1;
                mHandler.sendMessage(msg);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

三、handler机制的原理

Handler由4部分组成

  • Handler
  • Message
  • MessageQueue
  • Looper

  • Looper中有一个mQueue消息队列
  • MessageQueue有一个mMessages消息
  • Message有一个Handler对象
  • Handler有mQueue和mLooper对象

四、handler引起的内存泄漏以及解决方法

原因:非静态内部类持有外部类的匿名引用,导致外部的activity无法释放

解决办法:

  1. handler内部持有外部的activity的弱引用,
  2. 并把handler改为静态内部类,用static修饰
  3. 在activity中的destroy方法中调用mHandler.removeCallback()
private static class SafeHandler extends Handler {

    private WeakReference<HandlerActivity> ref;

    public SafeHandler(HandlerActivity activity) {
        this.ref = new WeakReference(activity);
    }

    @Override
    public void handleMessage(final Message msg) {
        HandlerActivity activity = ref.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}

并且再在 Activity.onDestroy() 前移除消息,加一层保障

@Override
protected void onDestroy() {
  safeHandler.removeCallbacksAndMessages(null);
  super.onDestroy();
}

这样双重保障,就能完全避免内存泄露了。

注意:单纯的在 onDestroy 移除消息并不保险,因为 onDestroy 并不一定执行。

参考:# Handler 都没搞懂,拿什么去跳槽啊?