handler处理机制
安卓中子线程不能动态改变主线程中UI组件的属性。程序启动时,Activity会启动主线程,也就是UI线程,用于处理用户输入、将计算的结果展示给用户;在处理一些可能发生阻塞的操作时,需要启动子线程,防止主线程阻塞,比如:用户请求网络下载资源时,UI不能出现“卡顿”,多线程的例子比比皆是。
eg:
final TextView textView= findViewById(R.id.tv);
Button button= (Button) findViewById(R.id.button);件
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//创建新线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 尝试更新主线程中Text View组件的属性
textView.setText("mask");
}
});
thread.start(); //开启线程
}
});
运行结果:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8913)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1557)
at android.view.View.requestLayout(View.java:24694)
at android.view.View.requestLayout(View.java:24694)
at android.view.View.requestLayout(View.java:24694)
at android.view.View.requestLayout(View.java:24694)
at android.view.View.requestLayout(View.java:24694)
at android.view.View.requestLayout(View.java:24694)
at android.view.View.requestLayout(View.java:24694)
at android.widget.TextView.checkForRelayout(TextView.java:9750)
at android.widget.TextView.setText(TextView.java:6314)
at android.widget.TextView.setText(TextView.java:6142)
at android.widget.TextView.setText(TextView.java:6094)
at com.xxx.a02_handler.MainActivity$1$1.run(MainActivity.java:26)
at java.lang.Thread.run(Thread.java:929)
报错的意思:只有创建这个view的线程才能操作这个view。TextVIew组件在主线程中创建,那子线程中就无法获取这个组件的控制权。所以必须有一种方式让子线程去通知主线程,然后主线程去修改组件的属性值。
Handler是安卓提供的一种消息处理机制,通过handler发送和处理 Message 对象到其所在线程的 MessageQueue 中,当Looper轮询到该Message时,使用handler.handlerMaeesage()来处理。
回到案例中,主线程要能用handler处理消息,可以注册一个回调。只需要重写 Handler 类中处理消息的方法。当新启动的线程发送消息时,Handler类中处理消息的方法就会被自动回调。
三种方法如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = findViewById(R.id.tv); //获取文本框组件
Button button= findViewById(R.id.button); //获取按钮组件
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//创建新线程
new Thread(new Runnable() {
@Override
public void run() {
//要执行的操作
Message message = new Message();
message.what = 0;
Bundle bundle = new Bundle();
bundle.putString("name","mask");
message.setData(bundle);
mhandler.sendMessage(message);
}
}).start(); //开启线程
}
Handler mhandler = new Handler() {
// 注册处理消息的回调
@Override
public void handleMessage(@NonNull Message msg) {
if(msg.what == 0) {
textView.setText(msg.getData().getString("name"));
}
}
};
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = findViewById(R.id.tv);
Button button= findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
Message message = null;
@Override
public void onClick(View v) {
//创建新线程
new Thread(new Runnable() {
@Override
public void run() {
//要执行的操作
message = new Message();
message.what = 0;
Bundle bundle = new Bundle();
bundle.putString("name","mask");
message.setData(bundle);
mhandler.post(t);
}
}).start(); //开启线程
}
Handler mhandler = new Handler();
Thread t = new Thread() {
@Override
public void run() {
textView.setText(message.getData().getString("name"));
}
};
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = findViewById(R.id.tv); //获取文本框组件
Button button= findViewById(R.id.button); //获取按钮组件
button.setOnClickListener(new View.OnClickListener() {
Message message = null;
@Override
public void onClick(View v) {
//创建新线程
new Thread(new Runnable() {
@Override
public void run() {
//要执行的操作
message = new Message();
message.what = 0;
Bundle bundle = new Bundle();
bundle.putString("name","mask");
message.setData(bundle);
MainActivity.this.runOnUiThread(t);
}
}).start(); //开启线程
}
Thread t = new Thread() {
@Override
public void run() {
textView.setText(message.getData().getString("name"));
}
};
});
}
Handler工作原理:
Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.
每个Handler实例对应一个线程和这个线程的消息队列。当创建一个Handler,就会和Looper绑定。handler发送消息和可运行对象传递到该Looper的消息队列,并在该Looper的线程上执行。
主线程中,会自动初始化一个对象,因此在程序中可以直接创建Handler,然后就可以通过 Handler 进行发送消息和处理消息。在子线程中,必须手动创建一个 Looper 对象,并通过 loop() 方法启动 Looper。 在子线程中使用 Handler 的步骤如下:
- 调用 Looper 的 prepare() 方法为当前的线程创建 Looper 对象,在创建 Looper 对象的构造器中会创建与之配套的 MessageQueue。
- 创建 Handler 子类的实例,重写 handlerMessage() 方法用来处理来自于其他线程的消息。
- 调用 Looper 的 loop() 方法启动 Looper。
eg:在子线程1中再次创建子线程2,从而子线程1中创建一个looper,这时候子线程1中也有一套handler处理机制的流程。代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = findViewById(R.id.tv); //获取文本框组件
Button button= findViewById(R.id.button); //获取按钮组件
Looper.getMainLooper();
button.setOnClickListener(new View.OnClickListener() {
Message message = null;
@Override
public void onClick(View v) {
Log.d("xixi","main:"+android.os.Process.myTid());
//创建新线程
new Thread(subThread).start(); //开启线程
}
Thread subThread = new Thread() {
@Override
public void run() {
Looper.prepare();
Log.d("xixi","outer:"+android.os.Process.myTid());
new Thread() {
@Override
public void run() {
Log.d("xixi","inner:"+android.os.Process.myTid());
message = new Message();
message.what = 0;
Bundle bundle = new Bundle();
bundle.putString("name","mask");
message.setData(bundle);
handler.sendMessage(message);
}
}.start();
Looper.loop();
}
Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
Log.d("xixi",message.getData().getString("name"));
}
};
};
});
}
运行结果:
D/xixi: main:25406
D/xixi: outer:26836
D/xixi: inner:26837
D/xixiu: mask
android.os.Looper主要函数:
Looper.prepare(); // 为当前线程创建一个Looper对象
Looper.getMainLooper(); // 返回当前应用主线程的Looper对象
Looper.myLooper(); // 返回当前线程的Looper对象
Looper.loop(); // 启动Looper。从消息队列中获取Message,并执行msg.target.dispatchMessage(msg),msg.target其实是绑定到消息队列的Handler