回顾一下Handler跨线程通信:
Handler
的使用
- 在接收线程中创建
Looper
对象。主线程中的Looper
对象已经由系统创建。 - 在接收线程中创建
Handler
对象,复写handleMessage
方法处理消息。 - 开启
Looper
的消息循环,主线程中已由系统开启。 在发送线程创建
Message
,并使用接收线程中的Handler
引用发送消息。
测试Handler
的Activity
代码:public class MainActivity extends AppCompatActivity { public static final String TAG = "MainActivity"; private Handler mThreadHandler; private Handler mMainHandler = new Handler(new Handler.Callback() { // 主线程接收到子线程发送的消息 @Override public boolean handleMessage(Message msg) { if (msg.obj != null) { Log.e(TAG, msg.obj.toString()); mThreadReplyText.setText("收到子线程回复:" + msg.obj.toString()); } return true; } }); private EditText mMainEditText; private TextView mThreadReplyText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMainEditText = findViewById(R.id.main_edit_text); mThreadReplyText = findViewById(R.id.thread_reply_text); new Thread(new Runnable() { @Override public void run() { Looper.prepare(); mThreadHandler = new Handler(new Handler.Callback() { // 子线程接收到主线程发送的消息并回复 @Override public boolean handleMessage(Message msg) { if (msg.obj != null) { Message reply = Message.obtain(); reply.obj = "主线程说\"" + msg.obj.toString()+"\""; // 在子线程中使用主线程中的Handler对象的引用向主线程发送消息 mMainHandler.sendMessage(reply); } return true; } }); Looper.loop(); } }).start(); } public void onClick(View view) { switch (view.getId()) { case R.id.main_send_button: Message message = Message.obtain(); message.obj = mMainEditText.getText(); // 主线程向子线程发送消息:在主线程中使用子线程中的Handler对象的引用向子线程发送消息 if (mThreadHandler != null) { mThreadHandler.sendMessage(message); } else { while (mThreadHandler == null) { Log.e(TAG, "子线程还没有完成ThreadHandler的创建"); if (mThreadHandler != null) { Log.e(TAG, "ThreadHandler创建完成!"); mThreadHandler.sendMessage(message); } } } break; } } }
XML文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" android:padding="20dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/main_edit_text" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:hint="主线程对子线程说:"/> <Button android:id="@+id/main_send_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送" android:onClick="onClick"/> </LinearLayout> <TextView android:id="@+id/thread_reply_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16sp" android:paddingTop="20dp" android:paddingBottom="20dp"/> </LinearLayout>
Handler
的工作原理
Handler
的消息传递机制涉及到四个部分:
1. Message
:线程间传递的对象。
2. MessageQueue
: 消息队列,用来存放Handler
发布的Message
.
3. Handler
:负责将Message
插入到MessageQueue
中以及对MessageQueue
中的Message
进行处理。
4. Looper
:负责从MessageQueue
中取出Message
,并交给Handler
.
其中:
+ Looper
存储在ThreadLocal
中,Looper
在创建时会同时创建MessageQueue
,作为其成员对象.因此Looper
和MessageQueue
是属于创建者线程的,各线程之间的Looper
和MessageQueue
相互独立。
+ Handler
在创建时会从当前线程的ThreadLocal
中取得Looper
.
+ 发送消息时,在发送线程中调用接收线程中的Handler
的sendMessage
方法,过程中,Handler
会将自身赋予到Message
的target
中,并将Message
插入到Handler
对应的MessageQueue
中。
+ 而接收线程中的Looper
在循环过程中会取出这个Message
,通过Message.target
取出接收线程中的Handler
,并将消息交Handler
对象处理。由此实现了跨线程通信。
+ 要注意的是:线程与Looper
和MessageQueue
是一对一的关系,即一个线程只维护一个Looper
和一个MessageQueue
;而线程与Handler
的关系是一对多,即一个线程可以有很多Handler
,一个Handler
只对应一个线程,这也是为什么Handler
在发送消息时,为什么要将自身赋给Message.target
的原因。
Handler内存泄露的解决方法
方法1:通过程序逻辑进行保护。
+ 关闭Activity
的时候停掉后台线程,这样就相当于切断了Handler
和外部连接的线,Activity
自然会在合适的时候被回收。
+ 如果你的Handler
是被delay的Message
持有了引用,那么在Activity
销毁前使用相应的Handler
的removeCallbacksAndMessages()
方法,把消息对象从消息队列移除就行了。
方法2:将Handler
声明为静态类
+ 静态类不持有外部类的对象,这样即使Handler
在运行,Activity
也可以被回收。
+ 由于静态类的Handler
不再持有外部类对象,如果要操作Activity
需要增加一个Activity
的弱引用。