预备知识
- 默认情况下,同一应用的所有组件均在相同进程和线程(主线程)中运行
- 应用与android界面工具包组件几乎都在主线程中进行交互,因此有时也称为界面线程、UI线程。 不要阻塞UI线程
- 不要在UI线程外访问Android UI工具包
- 通过创建Thread类的子类来构造线程。Java定义了一个直接从根类Object中派生的Thread类。所有从这个类派生的子类或间接子类,均为线程。
- Android中创建的其他线程称为工作线程或后台线程
- 创建和执行一个线程需完成下列步骤
1. 创建一个Thread类的子类
2. 在Thread子类中重新定义自己的run()方法,在这个run()方法中包含了线程要实现的操作(通过Handler对象发送Message消息)
3. 用关键字new 创建一个线程对象
4. 调用线程对象的start()方法启动线程
5. 线程启动后当执行run()方法完毕时,会自然进入终止状态 - Android中的线程抛弃了Java线程中一些不安全的做法。例如,在Java中终止一个Thread线程,可以调用stop()、destroy()等方法来实现,但在Android中,这些方法都没有实现,故不能直接使用。
- 在Android的多线程中,把需要传递的数据称为消息。
- Message是一个描述消息的数据结构类,Message包含了很多成员变量和方法。
如下: - Android.os.Handler是Android中多个线程间消息传递和定时执行任务的“工具”类。Handler是消息的处理者,负责在多个线程之间发送Message和处理Message。
- Handler类在多线程中有两方面的应用:
- 发送消息,在不同的线程间传递数据,使用的方法为sendXXX();
- 定时执行任务,在指定的未来某时间执行某任务,使用的方法为postXXX()。
- 一个线程只能有一个Handler对象,通过该对象向所在线程发送消息。
- Handler类方法:
1 设计好页面布局
一个“启动线程”按钮:用于启动计时器线程
一个“停止线程”按钮:用于停止计时器线程按钮
一个用于显示计时结果的TextView控件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:text="启动线程" />
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:text="停止线程" />
</LinearLayout>
<TextView
android:id="@+id/viewOne"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="number"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
2 在MainActivity.java的类内设置私有全局变量
private int count = 0; // 保存计时的值
private boolean Stop = true; // 计时状态,如果为停止状态则Stop值为true
public TextView view1; // 获取显示计时值的TextView控件
private Button btn1; // 获取启动计时线程的Button控件
private Button btn2; // 获取停止计时线程的Button控件
private Handler handler = new Handler(); // 在此处MainActivity.java文件中,handler的作用是定时执行计时任务
3 实现Thread子类testThread,并通过handler进行周期性调用
实现Thread子类testThread
private class testThread extends Thread {
public void run() {
if(!Stop){count++;} // 更新计时的值
view1.setText(String.format(Locale.getDefault(),"%d",count)); // 更新textView内容
handler.postDelayed(this,1000); // 实现每隔1秒钟执行一次当前任务
}
}
在MainActivity.java的类的onCreate函数里,通过handler创建计时器线程
handler.post(new testThread());
4 实现两个按钮的点击事件
创建onStart和onEnd两个私有接口
private class onStart implements View.OnClickListener {
public void onClick(View view) {
Stop=false;
}
}
private class onEnd implements View.OnClickListener {
public void onClick(View view) {
Stop=true;
}
}
在MainActivity.java的类的onCreate函数里,分别传入onStart和onEnd
btn1.setOnClickListener(new onStart());
btn2.setOnClickListener(new onEnd());
5 自定义onDestroy()函数
protected void onDestroy(){
super.onDestroy();
Log.d("life circle","onDestroy; count: "+count+" stop: "+Stop);
}
6 调用onDestroy之前调用onSaveInstanceState(),保存当前状态到Bundle savedInstanceState中,在onCreate中取出保存状态。
调用onDestroy之前调用onSaveInstanceState(),保存当前状态(count和Stop)到Bundle savedInstanceState中
@Override
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt("count",count);
savedInstanceState.putBoolean("stop",Stop);
}
在MainActivity.java的类的onCreate函数里,取出被保存的状态
if(savedInstanceState!=null){
count = savedInstanceState.getInt("count");
Stop = savedInstanceState.getBoolean("stop");
}
7 实现按返回键不销毁当前Activity
// 下面该函数实现:Android 按返回键不销毁当前Activity
@Override
public boolean onKeyDown(int keyCode, KeyEvent event){
if(keyCode == KeyEvent.KEYCODE_BACK){
// 在activity中调用 moveTaskToBack (boolean nonRoot)方法即可将activity 退到后台,注意不是finish()退出。
// 参数为false——代表只有当前activity是task根,指应用启动的第一个activity时,才有效;
// 参数为true——则忽略这个限制,任何activity都可以有效。
Stop = true;
moveTaskToBack(true);
return true;
}
return super.onKeyDown(keyCode, event);
}
8 完整的MainActivity.java代码
包名这里不显示,不同项目包名不同
package XXX;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private int count = 0; // 保存计时的值
private boolean Stop = true; // 计时状态,如果为停止状态则Stop值为true
public TextView view1; // 获取显示计时值的TextView控件
private Button btn1; // 获取启动计时线程的Button控件
private Button btn2; // 获取停止计时线程的Button控件
private Handler handler = new Handler(); // 在此处MainActivity.java文件中,handler的作用是定时执行计时任务
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view1 = findViewById(R.id.viewOne);
btn1 = findViewById(R.id.button);
btn2 = findViewById(R.id.button2);
btn1.setOnClickListener(new onStart());
btn2.setOnClickListener(new onEnd());
if(savedInstanceState!=null){
count = savedInstanceState.getInt("count");
Stop = savedInstanceState.getBoolean("stop");
}
Log.d("life circle","onCreate;count: "+count+" stop: "+Stop);
handler.post(new testThread());
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt("count",count);
savedInstanceState.putBoolean("stop",Stop);
}
// 下面该函数实现:Android 按返回键不销毁当前Activity
@Override
public boolean onKeyDown(int keyCode, KeyEvent event){
if(keyCode == KeyEvent.KEYCODE_BACK){
// 在activity中调用 moveTaskToBack (boolean nonRoot)方法即可将activity 退到后台,注意不是finish()退出。
// 参数为false——代表只有当前activity是task根,指应用启动的第一个activity时,才有效;
// 参数为true——则忽略这个限制,任何activity都可以有效。
Stop = true;
moveTaskToBack(true);
return true;
}
return super.onKeyDown(keyCode, event);
}
protected void onDestroy(){
super.onDestroy();
Log.d("life circle","onDestroy; count: "+count+" stop: "+Stop);
}
private class onStart implements View.OnClickListener {
public void onClick(View view) {
Stop=false;
}
}
private class onEnd implements View.OnClickListener {
public void onClick(View view) {
Stop=true;
}
}
private class testThread extends Thread {
public void run() {
if(!Stop){count++;}
view1.setText(String.format(Locale.getDefault(),"%d",count));
handler.postDelayed(this,1000);
}
}
}