Handler的初识
一.为什么需要使用Handler
学习之前我们先做一个例子说明为什么要使用Handler:
实现点击开始下载后5秒后 把正在下载改成下载完成
布局文件代码:
这<?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"
android:orientation="vertical"
tools:context="com.example.a22120.day4_handler.MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="正在下载"
android:gravity="center"
android:textSize="30dp"
android:id="@+id/lay_tv_down"
/>
<Button
android:layout_width="match_parent"
android:layout_gravity="center"
android:layout_height="50dp"
android:text="开始下载"
android:id="@+id/lay_btn_down"
/>
</LinearLayout>
MainActivity代码:
package com.example.a22120.day4_handler;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
TextView main_text;
Button main_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindId();
}
private void bindId() {
main_text = findViewById(R.id.lay_tv_down);
main_btn = findViewById(R.id.lay_btn_down);
main_btn.setOnClickListener(this);
main_text.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.lay_btn_down:
/* new Tread(new Run).start() 为快捷创建一个子线程*/
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
main_text.setText("下载完成");
}
}).start();
}
}
}
注意;当你使用此代码的方法点击按钮时是实现不了我们需要的效果的!!AS报下图错误
因为子线程是不能进行UI的更新,只有UI线程(即主线程)才可以进行UI的更新
或许有人说既然子线程不行那我们就把这个修改的操作放入主线程里吧。(注:主线程即:UI线程)
但实际上也是不可行的,停顿5秒属于耗时操作。
Android有规定不能在主线程中耗时操作所以会在子线程中执行耗时操作,之后提交请求,让主线程做UI更新。
所以引出Handler。
二.什么是Handler
Handler 是SDK中处理异步消息的核心类。主要接受子线程发送的数据, 并用此数据配合主线程更新UI
作用:可以用于更新UI (但不仅仅是这一个)
在官方的描述中提到了Handler两个主要作用:
- 延时处理消息或者Runnable
- 跨进程通信
Handler不仅可以完成主线程与子线程之间的通信,也可以做到子线程与子线程之间的通信
三.Handler的工作机制
1.Handler的工作机制简单来说是这样的
1.Message 消息,理解为线程间通讯的数据单元。例如后台子线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。
2.Message Queue 消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
3.Handler是Message的主要处理者,负责将Message添加到消息队列以及对消- 息队列中的Message进行处理。
4.Looper 循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message
Queue里面的Message,并交付给相应的Handler进行处理。
注意:
1. 一个Activity中只有一个主线程(即UI线程)
2.UI线程会自动创建MessageQueue ,和一个Looper
3.只会存在一个MessageQueue ,和一个Looper
2.MessageQueue
MessageQueue是用来存放Message的集合,并由Looper实例来分发里面的Message对象。同时,message并不是直接加入到MessageQueue中的, 而是通过与Looper对象相关联的MessageQueue.IdleHandler 对象来完成的。我们可以通过Looper.myQueue() 方法来获得当前线程的MessageQueue。
3.Looper
Looper是线程用来运行消息循环(message loop)的类。默认情况下,线程并没有与之关联的Looper,可以通过在线程中调用Looper.prepare() 方法来获取,并通过Looper.loop() 无限循环地获取并分发MessageQueue中的消息,直到所有消息全部处理。典型用法如下:
四.怎么使用Handler
使用Handler完成之前的例子
将MainActivity改成
package com.example.a22120.day4_handler;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
TextView main_text;
Button main_btn;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
main_text.setText("下载完成");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindId();
}
private void bindId() {
main_text = findViewById(R.id.lay_tv_down);
main_btn = findViewById(R.id.lay_btn_down);
main_btn.setOnClickListener(this);
main_text.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.lay_btn_down:
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(1);
}
}).start();
}
}
}
注意:**sendEmptyMessage(1)中的参数1
其实这个1只是识别用的。比如说,你这个activity中有两个要发送消息的
sendEmptyMessage(0)和sendEmptyMessage(1),那么,在接收消息的时候用到的方法handleMessage(Message msg),如果if(msg.what ==0)证明是第一个发过来的消息,同理如果if(msg.what ==1)证明是第二个发过来的消息。这里面数可以随便写。sendEmptyMessage(100),那么>if(msg.what == 100)就是对应地方发过来的消息。
将更新UI的操作放在了handleMessage方法中
注意:这里使用的是sendEmptyMessage()方法发送消息,使用handlerMessage接收。
五.使用Handler制作一个倒计时
UI代码:
<?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="com.example.a22120.day4_handler.Main2Activity"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="输入计时"/>
<EditText
android:layout_width="150dp"
android:layout_height="50dp"
android:id="@+id/lay2_time"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="秒"/>
<Button
android:layout_width="200dp"
android:layout_height="50dp"
android:text="开始计时"
android:id="@+id/btn_big"
/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="300dp"
android:text="0"
android:textSize="200dp"
android:gravity="center"
android:id="@+id/big_time"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="正在计时...."
android:textSize="40dp"
android:id="@+id/titles"
/>
</LinearLayout>
Activity代码
package com.example.a22120.day4_handler;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class Main2Activity extends AppCompatActivity implements View.OnClickListener{
private Button mian_btn_big;
private TextView main_time_tvbig,title;
private EditText main_et_time;
int time=10;
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.obj!=null){
String s= (String) msg.obj;
title.setText(s);
}else {
main_time_tvbig.setText(msg.what + "s"); //收到的就是time
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
bindID();
}
private void bindID() {
main_et_time=findViewById(R.id.lay2_time);
main_time_tvbig=findViewById(R.id.big_time);
mian_btn_big=findViewById(R.id.btn_big);
title=findViewById(R.id.titles);
mian_btn_big.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_big:
time=Integer.parseInt(main_et_time.getText().toString());
new Thread(new Runnable() {
@Override
public void run() {
while (time>=0){
try {
handler.sendEmptyMessage(time);
Thread.sleep(1000);
time--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Message msg= handler.obtainMessage();
msg.obj="计时完成....";
handler.sendMessage(msg);
}
}).start();
break;
default:
break;
}
}
}
下图重点
- msg.abj是只有在计时器到了0时才会被创建,即图一的3位置。
- m