handler机制
主线程中不能存在耗时操作,所有主线程会将耗时操作分配给子线程
可以通过handler来实现主线程和子线程的通信
在xml文件中进行布局
一个textview和button
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="结果"
android:textSize="30sp"/>
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start"
android:onClick="start"
tools:ignore="OnClick">
</Button>
</LinearLayout>
通过点击事件进行通信
public void start(View view){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String strData = getData();
Message message = new Message();
message.what=0;
message.obj=strData;
mhandler.sendMessage(message);
}
});
thread.start();
Toast.makeText(MainActivity.this, "点击开始", Toast.LENGTH_SHORT).show();
}
创建一个耗时方法
public String getData(){
StringBuilder sb = new StringBuilder();
for(int i =0 ; i<100 ; i++){
sb.append(i);
}
try { Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
result = sb.toString();
return result;
}
new一个handler
private final Handler mhandler = new Handler(Looper.myLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 0){
String strData = (String) msg.obj;
textView.setText(strData);
Toast.makeText(MainActivity.this, "主线程接受结果", Toast.LENGTH_SHORT).show();
}
}
};
完整代码
package com.example.handletest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.tv);
}
private final Handler mhandler = new Handler(Looper.myLooper()){ //()中有一个警告关于内存泄漏,添加一个Looper.myLooper
@Override
public void handleMessage(@NonNull Message msg) { //new完handler后重写handlermessage方法
super.handleMessage(msg);
if (msg.what == 0){ // 通过msg.what的值进行匹配对应的子线程
String strData = (String) msg.obj;
textView.setText(strData); // 将子线程运行出的结果通过主线程来设置
Toast.makeText(MainActivity.this, "主线程接受结果", Toast.LENGTH_SHORT).show();
}
}
};
public void start(View view){
Thread thread = new Thread(new Runnable() { // 创建一个线程Thread
@Override
public void run() {
String strData = getData();
Message message = new Message(); // new一个Message,处理文本
message.what=0;
message.obj=strData;
mhandler.sendMessage(message); //通过handler机制的sendMessage方法发送文本
}
});
thread.start(); // 别忘记启动线程
Toast.makeText(MainActivity.this, "点击开始", Toast.LENGTH_SHORT).show();
}
private String getData(){ // 写一个获取数据的耗时方法
String result;
StringBuilder sb = new StringBuilder();
for (int i = 0; i<100; i++){
sb.append(i);
}
try {
Thread.sleep(3000);
}catch (InterruptedException e){
e.printStackTrace();
}
result = sb.toString();
return result;
}
}
handler
使用Handler
的原因:将工作线程需操作UI
的消息 传递 到主线程,使得主线程可根据工作线程的需求 更新UI
,从而避免线程操作不安全的问题
looper
-
Looper可以让一个线程具有循环工作的特性,也就说可以把线程编程Looper线程。
-
每个线程只能也最多有一个Looper对象,这个Looper对象是一个Thredlocal,可以保证当前线程操作的Looper对象一定是当前线程自己的。
-
Looper内部又一个MQ,调用loop()方法后线程开始不断的从MQ中去消息交给后面的Handler处理。
message
Message是线程之间传递信息的载体,包含了对消息的描述和任意的数据对象。Message中包含了两个额外的int字段和一个object字段,这样在大部分情况下,使用者就不需要再做内存分配工作了。虽然Message的构造函数是public的,但是最好是使用Message.obtain( )或Handler.obtainMessage( )函数来获取Message对象,因为Message的实现中包含了回收再利用的机制,可以提供效率。
messageQuery
MessageQueue用来容纳Message队列的,其中的Message是由Looper来分发的,Message不能直接添加到MessageQueue中,而是要通过与Looper关联的Handler去添加。