Android Handler 的必修课一
程序之美
前言
handler 一個人們熟知的词汇,做Android开发的小伙伴们肯定真的是再熟悉不过了,Handler 是一个消息分发对象。handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制,我们可以发消息,也可以通过它处理消息。
handler 的作用
1、解决多线程更新UI
举个例子来说就,如下图所示:
package com.example.handlerdemo;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView tvInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvInfo = findViewById(R.id.tvShow);
tvInfo.setText("我是主线程");
ShowThread showThread = new ShowThread();
showThread.start();
}
class ShowThread extends Thread{
@Override
public void run() {
super.run();
tvInfo.setText("我是子线程");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
运行结果:
运行成功了,这个能说明子线程可以更新UI么?
我们进一步验证一下:
修改代码如下,我们加入了一个Button,按钮触发更新TextView,如下:
package com.example.handlerdemo;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView tvInfo;
private Button btnClick;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvInfo = findViewById(R.id.tvShow);
tvInfo.setText("我是主线程");
btnClick = findViewById(R.id.btnClick);
btnClick.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ShowThread showThread = new ShowThread();
showThread.start();
}
});
}
class ShowThread extends Thread{
@Override
public void run() {
super.run();
tvInfo.setText("我是子线程");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
发现点击 button 后 textview 的内容实际上发生了更改的,然后程序崩溃了。抛出如下异常:
AndroidRuntime: FATAL EXCEPTION: Thread-176
Process: com.example.handlerdemo, PID: 11201
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6357)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:874)
由上可知,子线程是不可以更新UI的,为什么子线程中不能更新UI,这是因为在子线程中更新UI不是线程安全的,在Android源码ViewRootImpl的checkThread方法中也对UI做了验证。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
那么handler的原理是什么呢?
下面我们就说下Handler 的原理。
Handler的原理
1、handler封装消息的发送体(主要包括消息接受者,消息内容等)
2、Looper——消息封装的载体。(1)内部包含一个MessageQueue,所有的Handler发送的消息都会进入到这个消息队列;(2)Looper.Looper方法,就是一个死循环,不停地从MessageQueue获取消息,如果有消息便处理消息,没有消息就阻塞等待。
3、MessageQueue,一个消息队列,存储消息的容器,可以实现添加消息,便于处理消息
4、handler内部与Looper相关联,在handler内部可以找到Looper,handler->Looper->MessageQueue,handler发送消息就是向MessageQueue队列发送消息。
总结:handler负责发送消息,Looper负责接收handler发送的消息,并把消息回传给handler的接收者。
handler 用法
1、post(Runnable);
2、postDelayed(Runnable ,long);
3、sentMessage
4、sentMessageDelayed
这里就不详细介绍了,就说下,小伙伴们一定都知道,post和send的用法,post是异步的消息发送,send是同步的消息发送。细心的小伙伴一定可以发现,post和postDelayed内部都是一个线程Runnable,所以会开启一个线程执行任务。所以它是异步的,不阻塞队列的。因为异步,所以任务不会马上执行,排入消息队列,由系统调度执行。而sendMessage是同步的,调用则会马上执行。
结束语
好了,今天就先介绍个大概吧,小伙伴们可以先了解下,有条件的小伙伴可以实践下相关的handler的用法,体验下他们的用处和差异,后续我会进一步展开,进一步深入讲解handler的运行机制。也希望小伙伴们能够多提宝贵建议,共同进步,共同成长。