Handler
一.为什么要用Handler
1.主线程不能有耗时操作
2.子线程不能更新UI
二.什么是Handler
是处理异步消息的核心类(异步:不同的线程做不同的事)
三.什么是Looper
1.在安卓中创建出的普通线程默认是没有循环消息的,run方法执行完毕,线程也就结束了
2.如果让线程不停的循环工作时,可以使用Looper,将普通线程变成循环工作线程
四.什么是Message Queue
1.当创建Looper时,将会自动创建出Message Queue
2.一个线程中只会存在一个Looper和一个Message Queue
3.当Message Queue中有消息时,Looper将从Message Queue中取出消息
五.Handler怎么用
Handler的工作原理
子线程通过Handler发送消息到Message Queue,然后Looper从Message Queue拿出消息让主线程处理
Handler其中一个作用
让子线程通过UI线程通信来更新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"
android:orientation="vertical"
tools:context="com.example.myapplication.DownLoadActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="准备下载"
android:id="@+id/zhunbei_tv"
android:layout_gravity="center"/>
<Button
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="@+id/start_btn"
android:text="开始下载"
android:textSize="25sp"/>
</LinearLayout>
主类:
package com.example.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class DownLoadActivity extends AppCompatActivity implements View.OnClickListener {
private TextView zhunbei_tv;
private Button start_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_down_load);
bindID();
}
private void bindID() {
zhunbei_tv = findViewById(R.id.zhunbei_tv);
zhunbei_tv.setOnClickListener(this);
start_btn = findViewById(R.id.start_btn);
start_btn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.start_btn:
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
zhunbei_tv.setText("下载完成");
}
}).start();
break;
}
}
}
但是这样写会报错,因为子线程不能更新UI.错误如下:
下面是改正后的主类:
package com.example.myapplication;
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 DownLoadActivity extends AppCompatActivity implements View.OnClickListener {
private TextView zhunbei_tv;
private Button start_btn;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
zhunbei_tv.setText("下载完成");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_down_load);
bindID();
}
private void bindID() {
zhunbei_tv = findViewById(R.id.zhunbei_tv);
zhunbei_tv.setOnClickListener(this);
start_btn = findViewById(R.id.start_btn);
start_btn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.start_btn:
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(1);
}
}).start();
break;
}
}
}
效果图:
五秒后
创建Message传值有两种方式:建议用第一种,第二个比较耗时
第一种:
Message msg=handler.obtainMessage();
第二种:
Message msg=new Message();
这是一个线程
new Thread(new Runnable() {
@Override
public void run() {
try {
while (time > 0) {
handler.sendEmptyMessage(time);
time--;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg=handler.obtainMessage();
msg.obj="倒计时完成";
handler.sendMessage(msg);
}
}).start();
六.案例解析——倒计时Demo
1.布局文件
<?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.myapplication.DownLoadActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/zhunbei_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="倒数计时"
android:textSize="30sp" />
<EditText
android:id="@+id/daotime"
android:layout_width="100dp"
android:layout_height="50dp"
android:textSize="25sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center_horizontal|center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/start_time"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textSize="30sp" />
</LinearLayout>
<Button
android:id="@+id/start_btn"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="开始倒计时"
android:textSize="25sp" />
</LinearLayout>
布局文件的效果图:
2.activity类
- 绑定id
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_down_load);
bindID();
}
private void bindID() {
daotime = findViewById(R.id.daotime);
start_time = findViewById(R.id.start_time);
zhunbei_tv = findViewById(R.id.zhunbei_tv);
zhunbei_tv.setOnClickListener(this);
start_btn = findViewById(R.id.start_btn);
start_btn.setOnClickListener(this);
}
- 给按钮添加点击事件
要实现View.OnClickListener 接口
public class DownLoadActivity extends AppCompatActivity implements View.OnClickListener {
switch (view.getId()) {
case R.id.start_btn:
break;
- 创建handler方法
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
- 创建一个线程,里面使用handler发送消息, 在EditText控件得出来的界面上输入一个数字,按下按钮
switch (view.getId()) {
case R.id.start_btn:
start_time.setText(daotime.getText());
time=Integer.parseInt(daotime.getText().toString());
new Thread(new Runnable() {
@Override
public void run() {
try {
while (time > 0) {
handler.sendEmptyMessage(time);
time--;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg=handler.obtainMessage();
msg.obj="倒计时完成";
handler.sendMessage(msg);
}
}).start();
break;
}
- 接收消息,等到时间倒数完毕,就会显示倒计时完成
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.obj!=null){
String result= (String) msg.obj;
start_time.setText(result);
}else {
start_time.setText(msg.what + "秒");
}
}
};
效果图:
我输的是十秒,十秒之后的效果图:
如果有什么不懂,下面是完整代码
package com.example.myapplication;
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 DownLoadActivity extends AppCompatActivity implements View.OnClickListener {
private TextView zhunbei_tv, start_time;
private Button start_btn;
private EditText daotime;
private int time;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.obj!=null){
String result= (String) msg.obj;
start_time.setText(result);
}else {
start_time.setText(msg.what + "秒");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_down_load);
bindID();
}
private void bindID() {
daotime = findViewById(R.id.daotime);
start_time = findViewById(R.id.start_time);
zhunbei_tv = findViewById(R.id.zhunbei_tv);
zhunbei_tv.setOnClickListener(this);
start_btn = findViewById(R.id.start_btn);
start_btn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.start_btn:
start_time.setText(daotime.getText());
time=Integer.parseInt(daotime.getText().toString());
new Thread(new Runnable() {
@Override
public void run() {
try {
while (time > 0) {
handler.sendEmptyMessage(time);
time--;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg=handler.obtainMessage();
msg.obj="倒计时完成";
handler.sendMessage(msg);
}
}).start();
break;
}
}
}