参考博客:https://blog.csdn.net/Danny_llp/article/details/114932585

定时任务常常会依靠子线程,因为我们主线程里面不能进行耗时操作,所以我们可以开一个子线程,让子线程自已运行这,从而实现定时。

1 Handler +Thread.sleep(xxx)方法

这种方式说是java的实现多线程的方式

1.1 实现方式

大体来说就是我们就是开启一个线程,让线程while(true)一直执行这,运行一次之后让线程睡眠几秒,这样线程就会周期性的运行了。

  • 我们新建一个类来定义一个自己的方法。 ```java

public class TimeMethods {

  1. public static void getRandom(){
  2. new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. //开始一个死循环
  6. while (true){
  7. //这里是一个子线程,在这里创建一个方法
  8. Random random = new Random();
  9. int i = random.nextInt(100);
  10. Log.i("random", "随机数是 :"+Integer.toString(i));
  11. try {
  12. //运行之后睡眠两秒,其实意思就是每两秒执行一次这个程序
  13. Thread.sleep(2000);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }
  19. }).start();
  20. }

}

  1. - [ ] 然后我们在主活动里面调用这个方法,从而启动子线程。
  2. ```java
  3. public class MainActivity extends AppCompatActivity {
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_main);
  8. TimeMethods.getRandom();
  9. }
  10. }
  • 看一下效果

image.png

1.2 遗留的问题

可以看到我们在子线程里面可以实现两秒之后输出一下随机产生的数据,但是现在我们的数据是没有办法发给
主线程的,就是说我们目前是没有办法将子线程产生的随机数在界面上展示出来的。

方法就是使用handler机制。在子线程里面通过handler将子线程的数据发送给主线程,然后在主线程的Handler里面接收子线程发送过来的数据,并进行处理,主线程Handler里面的数据其实就是在主线程里面,可以直接进行UI操作。

在另一篇笔记里面进行详细介绍。

2 Handler的postDelayed(Runnable, long)

这种方式说是安卓自己的简单实现。
这里需要的一个参数是Runnable,
我们可以了解一下,Thread和Runnable的区别,其实Thread本质上也是实现了Runnable接口的一个实例

1.1 实现方式

首先我们还是先创建一个类,在这个类里面写一个方法,方法里面实现Runnable接口,返回一个Runnable对象,
因为在启动线程的时候需要这个Runnable对象

  • [x] 创建一个类,在类里面创建方法 ```java public class TimeMethods {

    public static Runnable postDelay(Handler handler){

    1. Runnable runnable = new Runnable(){
    2. @Override
    3. public void run() {
    4. //这里是一个子线程,在这里得到一个随机数
    5. Random random = new Random();
    6. int i = random.nextInt(100);
    7. Log.i("random", "随机数是 :"+Integer.toString(i));
    8. handler.postDelayed(this,2000);
    9. }
    10. };
    11. return runnable;

    }

}

  1. - [x] 在主活动里面创建两个按钮,点击一个按钮开始定时任务,点击第二个按钮关闭定时任务
  2. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1791947/1650266933274-ba5fe772-fe0f-4381-851d-594fb3ab262e.png#clientId=u014f30f8-9a4e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=283&id=ue18b3502&margin=%5Bobject%20Object%5D&name=image.png&originHeight=531&originWidth=1123&originalType=binary&ratio=1&rotation=0&showTitle=false&size=28462&status=done&style=none&taskId=u1083d168-4cfc-4777-8c30-9ccb115cf2c&title=&width=598.9333333333333)
  3. ```java
  4. public class MainActivity extends AppCompatActivity {
  5. /**
  6. * 这里来展示网上的几种安卓的定时任务
  7. * @param savedInstanceState
  8. */
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.activity_main);
  13. //TimeMethods.getRandom();
  14. Button starttimer = findViewById(R.id.StartTimer);
  15. starttimer.setOnClickListener(new View.OnClickListener() {
  16. @Override
  17. public void onClick(View view) {
  18. //开始调用我们创建的方法启动线程,这里的0其实没有什么作用
  19. handler.postDelayed(TimeMethods.postDelay(handler),0);
  20. }
  21. });
  22. Button stoptimer = findViewById(R.id.StopTimer);
  23. stoptimer.setOnClickListener(new View.OnClickListener() {
  24. @Override
  25. public void onClick(View view) {
  26. handler.removeCallbacks(TimeMethods.postDelay(handler));
  27. }
  28. });
  29. }
  30. }

1.2 问题一:无法关闭定时

点击一个按钮启动定时任务没有问题,但是点击取消按钮的时候,定时任务并没有取消。
而且和方法 一最大的不同的一点就是这种方式实现的定时任务里面没有看到死循环。

1.3 问题二: 多次点击开始按钮有问题

image.png

  • 第一次点开始按钮

首先就是开始点击开始按钮的时候确实是每2秒进行一次输出

  • 点击一次开始按钮,再点击一次开始按钮的时候

发现变为每一秒进行一次输出了,我猜测是因为有调用了一次方法,从而又创建了一个线程。导致两个子线程通输出数据了

  • 第三次点击开始按钮以后,发现出现了每秒打印两次 的情况。原因应该也是这样的。我猜测是因为有一次调用方法,从而又创建了一个线程。导致三个子线程通输出数据了

1.4 问题三:传递信息给主线程

我们其实还是仿照第一种方法将子线程的数据传递给主线程。

3 Handler与Timer及TimerTask结合的方法

就是先创建Timertask这个定时任务,然后再将定时任务给到定时器,最后在使用handler将数据发送出去

还是在祝活动里面创建两个按钮,点击一个开始定时任务,再点击一个关闭定时任务
代码

  1. public class MainActivity extends AppCompatActivity {
  2. private Handler handler = new Handler(Looper.myLooper()){
  3. @Override
  4. public void handleMessage(@NonNull Message msg) {
  5. Log.i("recMsg",(String) msg.obj);
  6. }
  7. };
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. setContentView(R.layout.activity_main);
  12. //TimeMethods.getRandom();
  13. // * 创建一个定时任务
  14. TimerTask timerTask = new TimerTask() {
  15. @Override
  16. public void run() {
  17. //给主线程发消息
  18. Message msg = new Message();
  19. msg.what = 33 ;
  20. msg.obj = "hello " ;
  21. //正式发送过去
  22. handler.sendMessage(msg);
  23. }
  24. };
  25. //创建定时器
  26. Timer timer = new Timer();
  27. Button starttimer = findViewById(R.id.StartTimer);
  28. starttimer.setOnClickListener(new View.OnClickListener() {
  29. @Override
  30. public void onClick(View view) {
  31. //启动定时器
  32. //将定时任务任务给到定时器
  33. timer.schedule(timerTask,2000,2000); //2秒发送一次
  34. }
  35. });
  36. Button stoptimer = findViewById(R.id.StopTimer);
  37. stoptimer.setOnClickListener(new View.OnClickListener() {
  38. @Override
  39. public void onClick(View view) {
  40. //关闭定时器
  41. timer.cancel();
  42. }
  43. });
  44. }
  45. }

效果
image.png
确实实现了定时输出的任务

3.2 分析

这个打印的数据,字符串hello,是我们在定时任务(子线程)里面发送出去的然后我们在主线程里面拿到这个数据,将数据打印出出来的
实现了线程之间的通信。