- 什么是Handler?
Handler是Android SDK中处理异步消息的核心类,其作用是让子线程通过与UI线程通信来更新UI界面
其运行机制如下:
- 创建一个Handler对象,系统把Handler对象,UI线程和UI线程的消息队列捆绑起来;
- 当我们在线程中处理完数据后,可以通过Handler对象将消息发出;
- 消息将会按先后顺序添加到队列中;
- UI线程中的Looper不断从消息队列中取消信息,刷新UI。
- 什么是Looper和MessageQueue?
- 在Android中创建出的普通线程默认是没有消息循环的,run方法执行完毕,线程也就结束了;如果想让线程不停的循环工作时,可以使用Looper,将普通线程变成循环线程。
- 当创建出Looper时,会自动创建出MessageQueue,一个线程只会存在一个Looper和MessageQueue,当MessageQueue中有消息时,Looper将从MessageQueue中取出消息
- 什么是Message?
Message是消息对象,子线程将需要传递到UI线程的消息放入Message对象中,Message对象可以通过Handler对象的obtainMessage方法获得
这里列举一些常用属性:
what属性:int类型的消息码,接收方用来识别是什么消息;
arg1,arg2属性:int类型,如果传递的消息仅仅是整型数字,可以将数字赋给arg1或arg2;
obj属性:Object类型,如果传递发的消息是String或任意类型时,可将数据赋给obj属性;
sendToTarget方法:将消息发送给指定的Handler对象。
- 如何使用Handler?
- 第一步需要写好需要的xml布局,这个无需多说;
- 然后需要修改这个xml布局所对应的Java文件:
- 在这个Java文件中,做好准备工作(xml文件中控件的定义与绑定ID,设置监听事件);
- 然后在onCreat方法外定义handler对象,并调用handlerMessage方法;
- 在监听事件中获取用户输入的数据(String类型),这里注意一个小问题,需要使用Integer.parseInt()方法将String类型数值转换为int类型数值。然后启动子线程 ,使用handler.sendMessage()或handler.sendEmptyMessage()方法将子线程的值返回给主线程
- 主线程接收子线程发来的数值,并且更新UI。
- 倒计时案例举例(附代码和效果图)
首先给出main.xml的布局文件:
<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.lenovo.handlerdemo.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="设置时间"
android:textSize="30sp"/>
<EditText
android:id="@+id/daojishi_edit"
android:layout_width="40dp"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="秒"
android:textSize="30sp"/>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/time_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerInParent="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="倒计时:"
android:textSize="30sp"/>
<TextView
android:id="@+id/daojishi_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="30sp"/>
</LinearLayout>
<Button
android:id="@+id/daojishi_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始计时"
android:layout_below="@id/time_layout"/>
</RelativeLayout>
</LinearLayout>
附上xml预览图
然后开始MainActivity.java的准备工作:
public class MainActivity extends AppCompatActivity {
private Button daojishiBtn;
private EditText daojishiEdit;
private TextView daojishiTV;
int time;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bind();
daojishiBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
private void bind() {
daojishiEdit = findViewById(R.id.daojishi_edit);
daojishiTV = findViewById(R.id.daojishi_tv);
daojishiBtn = findViewById(R.id.daojishi_btn);
}
}
至此便可以开始写主线程了:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
这里使用Handler方法定义了一个handler对象,用于全局的调用使用:
Handler handler = new Handler()
调用的是handleMessage方法:
public void handleMessage(Message msg)
由于主线程不能够进行费时运行,所以需要使用子线程来进行,代码如下:
daojishiBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
time = Integer.parseInt(daojishiEdit.getText().toString());//类型转换
new Thread(new Runnable() {
@Override
public void run() {
for (int i = time; i > 0; i--) {
handler.sendEmptyMessage(i);//传递i给主线程
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
});
整理一下思路,点击daijishiBtn按钮后,子线程需要获取用户输入的数值,所以定义一个全局变量time,用来获取数值
time = Integer.parseInt(daojishiEdit.getText().toString());//类型转换使用方法Integer.parseInt()
接下来创建子线程,然后在run()方法内写for循环;
for循环判定循环条件后,传值给主线程:
handler.sendEmptyMessage(i);
设置休眠时间方便我们观看运行效果:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
子线程的最后不要忘了Thread.start();
由于子线程无法更新UI界面,所以需要将数值传递给主线程,由主线程来更新UI,这里只需要调用setText()方法就可以更新UI
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
daojishiTV.setText(msg.what+"");
}
};
到这里便完成了!