欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

小的计时器程序(多线程+生命周期)

程序员文章站 2022-05-31 10:06:46
...

预备知识

  1. 默认情况下,同一应用的所有组件均在相同进程和线程(主线程)中运行
  2. 应用与android界面工具包组件几乎都在主线程中进行交互,因此有时也称为界面线程、UI线程。 不要阻塞UI线程
  3. 不要在UI线程外访问Android UI工具包
  4. 通过创建Thread类的子类来构造线程。Java定义了一个直接从根类Object中派生的Thread类。所有从这个类派生的子类或间接子类,均为线程。
  5. Android中创建的其他线程称为工作线程或后台线程
  6. 创建和执行一个线程需完成下列步骤
    1. 创建一个Thread类的子类
    2. 在Thread子类中重新定义自己的run()方法,在这个run()方法中包含了线程要实现的操作(通过Handler对象发送Message消息)
    3. 用关键字new 创建一个线程对象
    4. 调用线程对象的start()方法启动线程
    5. 线程启动后当执行run()方法完毕时,会自然进入终止状态
  7. Android中的线程抛弃了Java线程中一些不安全的做法。例如,在Java中终止一个Thread线程,可以调用stop()、destroy()等方法来实现,但在Android中,这些方法都没有实现,故不能直接使用。
  8. 在Android的多线程中,把需要传递的数据称为消息。
  9. Message是一个描述消息的数据结构类,Message包含了很多成员变量和方法。
    如下:小的计时器程序(多线程+生命周期)
  10. Android.os.Handler是Android中多个线程间消息传递和定时执行任务的“工具”类。Handler是消息的处理者,负责在多个线程之间发送Message和处理Message。
  11. Handler类在多线程中有两方面的应用:
    1. 发送消息,在不同的线程间传递数据,使用的方法为sendXXX();
    2. 定时执行任务,在指定的未来某时间执行某任务,使用的方法为postXXX()。
  12. 一个线程只能有一个Handler对象,通过该对象向所在线程发送消息。
  13. 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);
        }
    }
}