展开说说:Android之常用的延时执行策略

总结了以下六种常用的Android延时执行策略,以此记录:

1、 TimerTask

2、 Handler.postDelayed

3、 Handler.sendEnptyMessageDelayeed

4、 Thread.sleep 线程休眠 - 需要在子线程

5、 使用 AlarmManager- 全 局 定时器或者闹钟

6、Wait

首先定义一个时间常量:

复制代码
public static final long DELAYTIME = 2000L;

1、TimerTask

复制代码
TimerTask mTimerTask = new TimerTask() {
        @Override
        public void run() {
            Log.e(TAG, "TimerTask -run:");
        }
    };
//timer可以复用,timerTask不可复用,否则闪退
if (mTimer ==null){
    mTimer = new Timer();
}
mTimer.schedule(mTimerTask,DELAYTIME);

Handler.postDelayed
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        Log.e(TAG, "handler.postDelayed :");
    }
},DELAYTIME);

注意:以上用法timer 可以复用, timerTask 不可复用。

第二种和第三中都依赖Handler,先定义一个handler

复制代码
public static class MyHandler extends Handler{

    private final WeakReference<Activity> activityWeak;

    public MyHandler(Activity activity){
        activityWeak = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        TimerActivity activity = (TimerActivity) activityWeak.get();
        switch (msg.what){
            case TimerActivity.MESSAGE1:
                Log.e(TAG, "handleMessage: what="+msg.what );
                break;
            default:
                Log.e(TAG, "handleMessage:      default      -what="+msg.what );
                break;
        }
    }
}

2、Handler.postDelayed

复制代码
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        Log.e(TAG, "handler.postDelayed :");
    }
},DELAYTIME);

3、Handler.sendEnptyMessageDelayeed

复制代码
Log.e(TAG, "handler.sendEmptyMessageAtTime :start");
handler.sendEmptyMessageDelayed(MESSAGE1,DELAYTIME);
Log.e(TAG, "handler.sendEmptyMessageAtTime :");

4、Thread.sleep 线程休眠 -需要在子线程, sleep 是线程的方法,他休眠中不会释放锁

复制代码
Log.e(TAG, "Thread.sleep:  onClick" );
Object lock = new Object();
new Thread() {
    @Override
    public void run() {
        super.run();
        try {
            synchronized (lock){
                Log.e(TAG, "Thread.sleep:  子线程start" );
                Thread.sleep(20000);
                Log.e(TAG, "Thread.sleep:  子线程end" );
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}.start();
try {
    Log.e(TAG, "Thread.sleep:  onClick-1" );
    Thread.sleep(200);//此行代码影响甚大,需灵活注释,
虽然上面new Thread耗时很短,但是也是有一定开销足以让它在主线程顺序之后执行
    Log.e(TAG, "Thread.sleep:  主线程执行-准备获取锁" );
    synchronized (lock){
        Log.e(TAG, "Thread.sleep:  主线程已获得锁" );
    }
    Log.e(TAG, "Thread.sleep:  主线程已释放锁锁" );
} catch (InterruptedException e) {
    e.printStackTrace();
}

主线程没有 200毫秒延时也就是注释Thread.sleep (200)就会先执行主线程然后进入子线程,虽然上面 new Thread 耗时很短,但是也是有一定开销足以让它在主线程顺序之后执行,以上代码测试发现从new Thread到子线程内第一行代码执行耗时不足1毫秒。以下是运行日志:
2023-12-11 15:12:26.944 18617-18617/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: onClick
2023-12-11 15:12:26.945 18617-18617/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: onClick-1
2023-12-11 15:12:26.945 18617-18617/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: 主线程执行 - 准备获取锁
2023-12-11 15:12:26.946 18617-18617/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: 主线程已获得锁
2023-12-11 15:12:26.946 18617-18919/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: 子线程 start
2023-12-11 15:12:46.947 18617-18919/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: 子线程 end

======================================

主线程有 200 毫秒延时也就是不注释Thread.sleep (200),就会先进入子线程执行完

然后进入主线程**。以下是运行日志:**
2023-12-11 15:09:40.785 17990-17990/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: onClick
2023-12-11 15:09:40.786 17990-17990/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: onClick-1
2023-12-11 15:09:40.786 17990-18231/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: 子线程 start
2023-12-11 15:09:40.987 17990-17990/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: 主线程执行 - 准备获取锁
2023-12-11 15:10:00.787 17990-18231/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: 子线程 end
2023-12-11 15:10:00.787 17990-17990/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.sleep: 主线程已获得锁

5、使用 AlarmManager- 全 局 定时器或者闹钟。适用一直在后台运行的定时任务,此处放在一个 service 执行 :

复制代码
Intent intent = new Intent();
intent.setAction("short");
ComponentName component = new ComponentName(TimerActivity.this, MyReceiver.class);
intent.setComponent(component);
//这里除了启动广播也可以换成启动Activity和service
PendingIntent sender = PendingIntent.getBroadcast(TimerActivity.this,0,intent,0);

Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND,5);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),sender);

6、Wait- 必须使用 synchronized ,他是 Object 的方法,他休眠会释放锁

复制代码
 Object lock = new Object();
                new Thread(){
                    @Override
                    public void run() {
                        super.run();
                        try {
//                            synchronized (lock){  //没有synchronized锁就会报错:java.lang.IllegalMonitorStateException: object not locked by thread before wait()
                                Log.e(TAG, "Thread.wait:  start" );
				//以下两行需灵活注释
                                lock.wait(2000);
//                                Thread.sleep(2000);
                                Log.e(TAG, "Thread.wait:  end" );
//                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        Log.e(TAG, "Thread.wait:  子线程第二条" );
                    }
                }.start();

                try {
                    Thread.sleep(200);
                    Log.e(TAG, "Thread.wait:  主线程开始获取锁" );
                    synchronized (lock){
                        Log.e(TAG, "Thread.wait:  主线程已获得锁" );
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

**lock.wait(2000)放开,**Thread.sleep(2000)注释,日志如下:
2023-12-11 14:21:34.371 9385-9748/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: start
2023-12-11 14:21:34.571 9385-9385/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: 主线程开始获取锁
2023-12-11 14:21:34.571 9385-9385/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: 主线程已获得锁
2023-12-11 14:21:36.372 9385-9748/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: end
2023-12-11 14:21:36.372 9385-9748/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: 子线程第二条

Thread.sleep(2000) **放开,lock.wait(2000)**注释,日志如下:

sleep 是线程的方法,他休眠中不会释放锁
2023-12-11 14:24:27.519 10346-10750/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: start
2023-12-11 14:24:27.719 10346-10346/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: 主线程开始获取锁
2023-12-11 14:24:29.520 10346-10750/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: end
2023-12-11 14:24:29.520 10346-10750/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: 子线程第二条
2023-12-11 14:24:29.520 10346-10346/com.example.testdemo3 E/com.example.testdemo3.activity.TimerActivity: Thread.wait: 主线程已获得锁

相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui