android任务,android任务管理器的设计与实现

Android的任务栈

任务是指在执行特定作业(任务 task)时与用户交互的activity。这些Activity按照各自打开的顺序 排列在堆栈(返回栈)中。设备的屏幕 桌面的图标(launcher)是多数据任务的的起点。当用户触摸应用启动器中的图标时,该用户的任务将出现在前台。如果应用不存在任务(应用未曾使用),则会创建一个新的任务,并且该应用的"主Activity将作为堆栈中的根Activity打开"。

萨嘎网站制作公司哪家好,找创新互联!从网页设计、网站建设、微信开发、APP开发、响应式网站设计等网站项目制作,到程序开发,运营维护。创新互联于2013年成立到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选创新互联

当Activity启动另一个Activity时,该新Activity会被推送到堆栈的顶部,如果当前app只有一个栈 且app处于前台,那么这个栈顶的Activity用户应该是可见的,而启动栈顶这个Activity(启动者)仍然保留在堆栈中,但是是处于停止状态。Activity停止时系统会保留其用户信息 在Activity onSaveInstanceSate、 onRestoreInstanceState(Bundle savedInstanceState) 方法 具体可以查看这两个方法。当用户按"返回"按钮时,当前Activity(所谓当前Activity一定是用户可以看到的在栈顶的)会从堆栈顶部弹出(Activity被销毁 走onDestory方法)这里说销毁也不太严格 因为正常情况下会被销毁 在不存在内泄漏导致Activity回收不掉的情况。栈顶的Activity被销毁 它下面的Activity就会恢复执行(下面这个Activity露头你可以看到它了)。堆栈中的Activity永远不会重新排序,我们操作app的功能时 仅仅是 进入页面(压栈)退出页面(弹栈),因此返回栈(弹栈)以 "后进先出"的对象结构运行。

任务是一个有机整体,当用户开始新任务或通过点击"home键" 这个时候应用会切换到“后台”,在后台时该任务中的所有Activity全部停止,但是任务的返回栈仍旧不变,也就是说,当另一个任务发生时,该任务仅是失败焦点而已

如何更通过api查看当前应用中的任务

发现当前只有一个任务

同时通过这个任务我们可以查看当前栈顶的activity如下图

Android 后台任务执行

参考:

手机休眠引发的“血案”

使设备保持唤醒

目的为了后台能够执行定时任务,避免因为设备息屏等操作导致CPU进入睡眠状态,定时任务被暂停,这就需要能够唤醒CPU,使CPU能够起来工作

具有唤醒CPU功能, 唤醒CPU与唤醒屏幕非同一功能。

AlarmManager是安卓系统封装的用于管理RTC 模块,RTC(实时时钟)是一个独立的硬件时钟,可以在CPU休眠时正常运行,在预设的时间到达时,通过中断唤醒CPU。这意味着,如果我们用AlarmManager来定时执行任务,CPU可以正常的休眠,只有在需要运行任务时醒来一段很短的时间。

AlarmManager 定时任务测试:

MI8 UD:

测试1: 创建一个 Service, Service 中启动一个 AlarmManager 定时任务

息屏后会继续打印Log,但息屏超过1min 后,log 停止输出:

测试2: 创建一个前台通知Service

Service + StartForground + 前台通知 方式,

MI8 UD 息屏后仍继续打印log.

MI 8 + MI 10 经过测试,在长时间息屏状态下, AlarmManager 也会存在不工作情况。

另外,设备处于低电耗模式下, AlarmManager 会停止工作或延迟工作,解决办法: AlarmManager 利弊

手机长时间不操作,CPU 就会进入睡眠状态,会导致 Timer 中的定时任务无法正常运行。

息屏后,TimerTask 停止工作,再次亮屏后,继续工作

同样会由于息屏导致CPU睡眠, Handler 停止工作

太“重”了,使用起来。 影响设备耗电量。

WorkManager 也可以运行后台任务,用于在APP进程被kill后,系统依然可以运行的任务,不要用于APP被杀后,后台服务即停止的任务。

总结:

Timer并不太适用于那些需要长期在后台运行的定时任务。为了能让电池更加耐用,每种手机都会有自己的休眠策略,Android 手机就会在长时间不操作的情况下自动让 CPU 进入到睡眠状态,这就有可能导致 Timer 中的定时任务无法正常运行。

Alarm具有唤醒 CPU 的功能,即可以保证每次需要执行定时任务的时候 CPU 都能正常工作。

AlarmManager 定时任务最小间隔5S, 如何设置间隔 5s, 也是按照 5s 间隔执行。

Android DozeMode

Android 实现定时任务的五种方式

1、普通线程sleep的方式,可用于一般的轮询Polling

  new Thread(new Runnable() {

        @Override

        public void run() {

            while (true) {

                //todo

              try {

                    Thread.sleep(iDelay);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    }).start();

优点:非常简单的实现,逻辑清晰明了,也是最常见的写法

缺点:在sleep结束后,并不能保证竞争到cpu资源,这也就导致了下次执行时间必定=iDelay,存在时间精度问题

2、Timer定时器

//Timer + TimerTask结合的方法

private final Timer timer = new Timer();

private TimerTask timerTask = new TimerTask() {

    @Override

    public void run() {

        //todo

    }

};

启动定时器方法:

timer.schedule(TimerTask task, long delay, long period)

立即执行

timer.schedule(timerTask, 0, 1000); //立刻执行,间隔1秒循环执行

延时执行

timer.schedule(timerTask, 2000, 1000); //等待2秒后再执行,间隔1秒循环执行

关闭定时器方法:timer.cancel();

优点:纯正的定时任务,纯java SDK,单独线程执行,比较安全,而且还可以在运行过程中取消执行

缺点:基于单线程执行,多个任务之间会相互影响,多个任务的执行是串行的,性能较低,而且timer也无法保证时间精确度,是因为手机休眠的时候,无法唤醒cpu,不适合后台任务的定时

3、ScheduledExecutorService

private Runnable runnable2 = new Runnable() {

    @Override

    public void run() {

        //todo

    }

};

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

executor.scheduleAtFixedRate(runnable2, 0, 1, TimeUnit.SECONDS);

关于scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 方法说明:

command:需要执行的线程

initialDelay:第一次执行需要延时的时间,如若立即执行,则initialDelay = 0

period:固定频率,周期性执行的时间

unit:时间单位,常用的有MILLISECONDS、SECONDS和MINUTES等,需要注意的是,这个单位会影响initialDelay和period,如果unit = MILLISECONDS,则initialDelay和period传入的是毫秒,如果unit = SECONDS,则initialDelay和period传入的是秒

补充一下: 还有一个方法跟上面的很相似:scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit),这个也是带延迟时间的调度,并且也是循环执行,唯一的不同就是固定延迟时间循环执行,上面的是固定频率的循环执行。那这两者的区别?

举例子:

使用scheduleAtFixedRate,任务初始延迟3秒,任务执行3秒,任务执行间隔为5秒:

    ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

    Log.e(TAG, "schedule just start! time =" +  simpleDateFormat.format(System.currentTimeMillis()));

    executor.scheduleAtFixedRate(new Runnable() {

        @Override

        public void run() {

            SystemClock.sleep(3000L);

            Log.e(TAG, "runnable just do it! time =" +  simpleDateFormat.format(System.currentTimeMillis()));

        }

    }, 3, 5, TimeUnit.SECONDS);

执行结果截图:

使用scheduleWithFixedDelay,任务初始延迟3秒,任务执行3秒,任务执行延迟为5秒:

    ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

    Log.e(TAG, "schedule just start! time =" +  simpleDateFormat.format(System.currentTimeMillis()));

    executor.scheduleWithFixedDelay(new Runnable() {

        @Override

        public void run() {

            SystemClock.sleep(3000L);

            Log.e(TAG, "runnable just do it! time =" +  simpleDateFormat.format(System.currentTimeMillis()));

        }

    }, 3, 5, TimeUnit.SECONDS);

执行结果截图:

从这两者的运行结果就可以看到区别了:scheduleAtFixedRate是相对于任务执行的开始时间,而scheduleWithFixedDelay是相对于任务执行的结束时间。

优点:ScheduledExecutorService是一个线程池,其内部使用的延迟队列,本身就是基于等待/唤醒机制实现的,所以CPU并不会一直繁忙。解决了TimerTimerTask存在的问题,多任务处理时效率高

缺点:取消时需要打断线程池的运行,而且和外界的通信不太好处理

4、使用Handler中的postDelayed方法

private Handler mHandler = new Handler();

private Runnable runnable = new Runnable() {

    @Override

    public void run() {

        //todo

        mHandler.postDelayed(this, iDelay);

    }

};

mHandler.post(runnable); //立即执行

mHandler.postDelayed(runnable, iDelay); //延时执行

mHandler.removeCallbacks(runnable); //取消执行

优点:比较简单的android实现,适用UI线程

缺点:没想到,手动捂脸。。。。我估计是使用不当会造成内存泄露吧

5、Service + AlarmManger + BroadcastReceiver

Android 任务栈

任务是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。设备主屏幕是大多数任务的起点。当用户触摸应用启动器中的图标(或主屏幕上的快捷方式)时,该应用的任务将出现在前台。 如果应用不存在任务(应用最近未曾使用),则会创建一个新任务,并且该应用的“主”Activity 将作为堆栈中的根 Activity 打开。

当前 Activity 启动另一个 Activity 时,该新 Activity 会被推送到堆栈顶部,成为焦点所在。 前一个 Activity 仍保留在堆栈中,但是处于停止状态。Activity 停止时,系统会保持其用户界面的当前状态。 用户按“返回”按钮时,当前 Activity 会从堆栈顶部弹出(Activity 被销毁),而前一个 Activity 恢复执行(恢复其 UI 的前一状态)。 堆栈中的 Activity 永远不会重新排列,仅推入和弹出堆栈:由当前 Activity 启动时推入堆栈;用户使用“返回”按钮退出时弹出堆栈。 因此,返回栈以“后进先出”对象结构运行。

上述文字摘自 Android开发者官网

默认行为的场景 :当前的task包含4个activity–当前activity下面有3个activity。当用户按下HOME键返回到程序启动器(application launcher)后,选择了一个新的应用程序(事实上是一个新的task),当前的task就被转移到后台,新的task中的根activity将被显示在屏幕上。过了一段时间,用户按返回键回到了程序启动器界面,选择了之前运行的程序(之前的task)。那个task,仍然包含着4个activity。当用户再次按下返回键时,屏幕不会显示之前留下的那个activity(之前的task的根activity),而显示当前activity从task栈中移出后栈顶的那个activity。


本文标题:android任务,android任务管理器的设计与实现
文章路径:http://pwwzsj.com/article/phchoe.html