了解Android核心:Looper,Handler和HandlerThread
Android中的主线程由 looper 和 Handlers 组成。所以了解创建无阻碍的响应式 UI 很重要。
- MessageQueue 是一个队列,其中包含消息任务。
- Handler 在 MessageQueue 中以任务形式排队,Looper 在任务出现时执行它们 MessageQueue.
- Looper 使线程保持活动状态,循环 MessageQueue 并向相应 Handler 的进程发送消息。
- Thread 通过调用 Looper 的 quit() 方法终止。
Handler 及其组件
- Handler:框架的重要对象,它具有双重责任。它绑定到给定线程的特定 Looper,并提供了将消息发送到相关 MessageQueue 的方法。该处理器还负责在实际执行消息的内容。
- Message:封装有关在特定线程上执行的操作信息。
- Runnable:抽象一个线程可以执行的任何操作的接口。
- MessageQueue:表示线程消耗的消息队列。
- Looper:负责循环的对象,循环检查 MessageQueue 以查看是否有消息要运行并将其发送到特定的 Handler。每个线程只能有一个 Looper。
现在,开始编写代码了。
例子1:
使用 Thread:
创建主线程处理程序
Handler handler = new Handler(Looper.getMainLooper());
thread = new Thread(new Runnable() {
@Override
public void run() {
handler2 = new Handler(Looper.getMainLooper());
handler2.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"hello world",Toast.LENGTH_SHORT).show();
}
});
}
});
使用 HandlerThread:
handlerThread = new HandlerThread("name");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"世界你好",Toast.LENGTH_SHORT).show();
}
});
注意:使用完 HandlerThread 后,要调用 quit() 方法退出。
例子2:
进度条
MainActivity.java
public class MainActivity extends AppCompatActivity {
Handler handler;
Thread thread;
Button botton;
int MAX = 100;
TextView textView;
HandlerThread handlerThread;
ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initThread();
}
private void initView(){
botton = (Button)findViewById(R.id.start_progress);
progressBar = (ProgressBar)findViewById(R.id.progressBar);
textView = (TextView)findViewById(R.id.textView);
progressBar.setMax(MAX);
}
private void initThread(){
handler = new Handler(){
@Override
public void handleMessage(Message message){
super.handleMessage(message);
textView.setText(message.what+"");
}
};
thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<100;i++){
progressBar.setProgress(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = new Message();
msg.what = i;
msg.obj = "obj";
handler.sendMessage(msg);
}
}
});
}
public void onClick(View view){
switch (view.getId()){
case R.id.start_progress:
thread.start();
break;
default:
break;
}
}
public void onDestroy(){
super.onDestroy();
handlerThread.quit();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<Button
android:id="@+id/start_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/progressBar"
android:layout_marginLeft="24dp"
android:layout_marginStart="24dp"
android:layout_marginTop="62dp"
android:onClick="onClick"
android:text="Start" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/start_progress"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginEnd="85dp"
android:layout_marginRight="85dp"
android:textSize="16sp"
android:text="Count 1"
android:layout_alignTop="@+id/start_progress" />
</RelativeLayout>
例子3:
看到这一点,您可能不太了解,也许会感觉到我在哪里。
接下来,我们使用一个简单的示例来验证和解决我们内心的疑虑。
在这里,我们放置一个按钮,单击后更改TextView的值。
还设置延迟以模拟要处理的大量数据。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void buttonClick(View view)
{
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
TextView myTextView = (TextView)findViewById(R.id.myTextView);
myTextView.setText("Button Pressed");
}
}
<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<TextView
android:text="hello world"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:id="@+id/myTextView" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="click me"
android:id="@+id/button"
android:layout_below="@+id/myTextView"
android:layout_centerHorizontal="true"
android:layout_marginTop="45dp"
android:onClick="buttonClick" />
</RelativeLayout>
运行后发现,延迟了6秒后才更新视图,这在实际开发中,会严重影响用户体验,造成一种严重卡顿的现象。
要解决这个问题,我们可以使用多线程的方法,修改后如下。
public void buttonClick(View view)
{
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(6000);
Log.d("thread:","hello world");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
在主线程以外的线程中更新用户界面元素违反了Android开发的关键规则。因此,为了更新用户界面,有必要实现线程的处理程序(Handler)。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
TextView myTextView = (TextView)findViewById(R.id.myTextView);
myTextView.setText("Button click");
};
};
再次修改 buttonClick() 中的内容
public void buttonClick(View view)
{
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(6000);
Log.d("thread:","hello world");
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0);
}
});
thread.start();
}
拓展内容:handler 几种方法
方法 | 描述 |
---|---|
sendMessage | 将消息推送到消息队列的末尾。 |
sendMessageDelayed | 将消息推送到消息队列的末尾。 |
sendMessageAtTime | 将消息推送到消息队列的末尾。 |
sendEmptyMessage | 发送仅包含单个int代码的消息。 |
sendEmptyMessageDelayed | 在指定的时间过后发送要传递的消息。 |
sendEmptyMessageAtTime | 发送消息以在指定的绝对时间传递。 |
使用 messaga 对象保存数据
public void buttonClick(View view)
{
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(6000);
Log.d("thread:","hello world");
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("name","anny");
msg.setData(bundle);
handler.sendMessage(msg);
}
});
thread.start();
}
obtainmessage() 是从消息池中拿来一个msg 不需要另开辟空间new
取出数据并赋值给 TextView
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
Bundle bundle = msg.getData();
String name = bundle.getString("name");
TextView myTextView = (TextView)findViewById(R.id.myTextView);
myTextView.setText(name);
};
};
例子4:
为线程创建Looper和MessageQueue:
public class Test extends Thread{
public Handler handler;
public void run(){
Looper.prepare();
handler = new Handler(){
@Override
public void handleMessage(Message message){
super.handleMessage(message);
Log.d("obj:","hello world");
}
};
Looper.loop();
}
}
仔细看你会发现,我们在这里使用了 Looper.prepare()和 Looper.loop()
为什么前面的示例不起作用?为什么在这里使用它?
1:在默认线程下,主线程系统将自动创建Looper对象并启动消息循环。
2:在主线程中mainHandler = new Handler()等同于new Handler(Looper.myLooper())
将消息发送到 MessageQueue
第一种方式:Message
Message msg = new Message();
msg.obj = "Ali send message";
handler.sendMessage(msg);
第二种方式:Runnable
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// this will run in the main thread
}
});
拓展:
方法 | 描述 |
---|---|
post | 立即让Runnable排队执行。 |
postAtTime | 使Runnable排队以在以毫秒为单位的绝对时间执行。 |
postDelayed | 使Runnable排队以指定的毫秒延迟后执行。 |
postAtFrontOfQueue | 立即将Runnable排队到要执行的前端。 |
Android 提供了 HandlerThread (线程的子类) 来简化过程。在内部,它以健壮的方式执行与我们相同的操作,因此,请始终使用 HandlerThread。
public class Test extends HandlerThread {
public Handler handler;
public Test(String name) {
super(name);
}
@Override
protected void onLooperPrepared(){
handler = new Handler(getLooper()){
@Override
public void handleMessage(Message message){
super.handleMessage(message);
//接收消息
}
};
}
}
我们在调用 onLooperPrepared 时实例化了 Handler。因此,Handler 可以将其与 Looper 关联。
1:仅在 start() 调用 HandlerThread 之后,即线程运行之后,才准备 Looper。
2:只有在准备好之后,Handler 才能与 HandlerThread Looper 关联。
另一种方式创建 HandlerThread 的方式(之前提过):
HandlerThread handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();
Looper looper = handlerThread.getLooper();
Handler handler = new Handler(looper);
重要说明:请记住,在完成后台线程或活动onDestroy()方法之后调用handlerThread.quit()。
上一篇: cache2go源码最后一讲 - examples
下一篇: 试一试
推荐阅读
-
Android消息处理机制Looper和Handler详解
-
Android消息机制(3)- Handler和Looper
-
简述android线程间消息处理机制(Looper、Handler和Message)
-
Android异步消息机制-深入理解Handler、Looper和MessageQueue之间的关系
-
Android异步消息机制-深入理解Handler、Looper和MessageQueue之间的关系
-
Android系统学习总结3--Looper和Handler分析
-
android Looper HandlerThread MessageQueue Handler Message(完善中......)
-
Android Handler、Message、MessageQueue和Looper官方说明
-
「Android」消息驱动Looper和Handler类分析
-
详解Android Handler机制和Looper Handler Message关系