• Best Practices for Background Jobs_3 Managing Device Awake State之电源锁、Alarm、WakefulBroadcastReceiver


    http://developer.android.com/training/scheduling/index.html

    当静置一个设备的时候,先会屏幕变暗,然后关闭屏幕,最后关闭CPU,以省电。但有的时候有这样的需求: .比如游戏或者电影,需要屏幕一直亮着。 .有些app不要求屏幕亮着,但是要求CPU一直运行,直到完成某项工作。

    1.保持屏幕变亮

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    只能用于activity,不能用于其它组件。

    public class MainActivity extends Activity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    }

    或者在activity的 layout文件某个布局或view上设置:android:keepScreenOn="true",会自动给其相关的Window设置上述的属性。

    设置这个属性不需要特殊的权限,不需要手动维护和释放。系统会根据当前activity是在后台还是前台来自动切换状态,界面在前台时才会起作用。 若要清除标记,可调用:

    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON).

    2.保持CPU唤醒

    阻止系统休眠,耗电明显,却有必须的时候才用,而且时间要尽量的短。 一般是后台服务里面用,如果是Activity应该用上面的 FLAG_KEEP_SCREEN_ON.

    使用 PowerManager的电源锁需要如下的权限:

    <uses-permission android:name="android.permission.WAKE_LOCK" />


    一定要记得释放,而且要在完成工作后尽快的释放。

    //申请电源锁:
    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
    Wakelock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag");
    wakeLock.acquire();
    
    
    //释放电源锁:
    wakelock.release()

    3.使用 WakefulBroadcastReceiver

    在 support.v4 兼容包中,用来启动一个service(通常是一个IntentService)。因为它维护一个PARTIAL_WAKE_LOCK电源锁,所以需要上面的"android.permission.WAKE_LOCK" 权限。解决在Service启动之前系统就休眠的问题。

    有如下方法:

    startWakefulService(context, intent);

    //它会申请一个电源锁,启动service,并将电源锁通过intent传入到 Service中,在onStart、onStartCommand中接收intent。

    completeWakefulIntent(Intent intent);

    //如果service中的工作完成了,调用这个方法释放 receiver 中传入的电源锁。 释放成功返回true,失败返回false.

    public class MyWakefulReceiver extends WakefulBroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
    
            // Start the service, keeping the device awake while the service is
            // launching. This is the Intent to deliver to the service.
            Intent service = new Intent(context, MyIntentService.class);
            startWakefulService(context, service);
        }
    }
    public class MyIntentService extends IntentService {
        public static final int NOTIFICATION_ID = 1;
        private NotificationManager mNotificationManager;
        NotificationCompat.Builder builder;
        public MyIntentService() {
            super("MyIntentService");
        }
        @Override
        protected void onHandleIntent(Intent intent) {
            Bundle extras = intent.getExtras();
            // Do the work that requires your app to keep the CPU running.
            // ...
            // Release the wake lock provided by the WakefulBroadcastReceiver.
            MyWakefulReceiver.completeWakefulIntent(intent);
        }
    }

    4.Alarm

    Alarm的运行独立于app进程的生命周期之外。基于AlarmManager。 如果是在app运行期间发生的定时操作,应该用Handler,Timer和Thread,更轻量和易控制。

    功能:

    .能在特定的时间或以特定频率发出intent 。

    可以结合 Receiver 来启动 Service,或者执行其他功能 。

    .独立于app进程的生命周期,可以在app没有运行的时候触发事件,即使设备是休眠状态。

    .最小化app资源需求,可以定时执行任务而不依赖于Timers或者后台持久的service。

    定时重复的alarm相对简单,但是灵活性较小,如果是触发的网络操作,若设计不好可能会很快耗光电量,或者加重服务器的负载。 一个常用的应用场景,是在app退出时和服务器同步数据,可以用重复的alarm实现,但是如果访问数据的服务器是自己的,推荐使用  Google Cloud Messaging(GCM) 结合 sync adapter。

    建议:

    .对定时重复的alarm,如果是触发网络操作,应该用一个随机的时间点,而不是固定在同一个时间点,以免服务器负载过重。

    .将alarm的频率尽量降低 .如非必要不要唤醒设备 。

    .不用让alarm触发时间过度的精确,除非有必要 使用setInexactRepeating() 替换 setRepeating(),使用前者,系统会将多个app的alarm同步起来一起发出,以便减少系统被唤醒的总次数,减少耗电。从Android4.4 (API Level 19), 开始,所有的重复alarm都是inexact的。

    .尽量不要让alarm基于时钟时间,用 ELAPSED_REALTIME。

    重复alarm的构成:

    .alarm类型。

    .触发的时间,如果时间已经过去了,会立刻触发本alarm。

    .重复的间隔。

    .Intent,alarm触发时发出,如果重复设置的alarm的intent是一样的,则前一个alarm会被替换掉。

    时间的类型:

    .elapsed real time       从系统启动作为起始时间,开始计时,和日期的时间无关,不受时区,地区影响。 适用于以固定间隔重复的alarm,比如没半小时触发一次

    .real time clock(RTC)  就是手机平时的时钟的时间,用户可以修改,受时区,地区影响. 适用于在一天某个特定的时间点触发的alarm

    Alarm的类型:

    如果不是唤醒设备的类型,Alarm会在系统下次被唤醒的时候发出.

    .ELAPSED_REALTIME                  从系统启动开始计算的时间,包括系统休眠的时间,不会唤醒设备

    .ELAPSED_REALTIME_WAKEUP   同上,会唤醒设备

    .RTC                                             基于时钟时间,不会唤醒设备

    .RTC_WAKEUP                             基于时钟时间,唤醒设备

    reboot处理:
    默认情况下,所有的alarm在关机后都会被cancel掉。
    可以接收 "android.intent.action.BOOT_COMPLETED" 广播,在广播接收器中重设Alarm。

    函数原型:

    public void setInexactRepeating(int type, long triggerAtTime, long interval,PendingIntent operation) 
    
    public void setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation) 
    
    public void set(int type, long triggerAtTime, PendingIntent operation)

    使用setInexactRepeating时,interval参数只能设为指定的几个值,否则和setRepeating是一样的效果。

    指定的值为:

    AlarmManager.INTERVAL_FIFTEEN_MINUTES,

    AlarmManager.INTERVAL_HALF_HOUR,

    AlarmManager.INTERVAL_HOUR,

    AlarmManager.INTERVAL_HALF_DAY,

    AlarmManager.INTERVAL_DAY

    实例:

    //1. ELAPSED_REALTIME_WAKEUP类型的Alarm,半小时重复一次
    // Hopefully your alarm will have a lower frequency than this!
    alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
            AlarmManager.INTERVAL_HALF_HOUR,
            AlarmManager.INTERVAL_HALF_HOUR, 
            alarmIntent);
    
    
    //2. ELAPSED_REALTIME_WAKEUP类型的Alarm,单次的,一小时后触发
    private AlarmManager alarmMgr;
    private PendingIntent alarmIntent;
    ...
    alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, AlarmReceiver.class);
    alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
    
    alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime() + 60 * 1000, alarmIntent);
    
    
    //3. RTC_WAKEUP类型, 2:00 p.m触发,每天重复:
    // Set the alarm to start at approximately 2:00 p.m.
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 14);
    
    // With setInexactRepeating(), you have to use one of the AlarmManager interval
    // constants--in this case, AlarmManager.INTERVAL_DAY.
    alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
            AlarmManager.INTERVAL_DAY, alarmIntent);
    
    
    
    //4. RTC_WAKEUP类型 8:30 a.m.触发, 每隔20分钟重复一次:
    private AlarmManager alarmMgr;
    private PendingIntent alarmIntent;
    ...
    alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, AlarmReceiver.class);
    alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
    
    // Set the alarm to start at 8:30 a.m.
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 8);
    calendar.set(Calendar.MINUTE, 30);
    
    // setRepeating() lets you specify a precise custom interval--in this case,
    // 20 minutes.
    alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
            1000 * 60 * 20, alarmIntent);
    
    
    //取消Alarm:
    if (alarmMgr!= null) {
        alarmMgr.cancel(alarmIntent);
    }
  • 相关阅读:
    Test Double
    测试金字塔
    windows 10安装 db2
    漫谈系列
    SOA 和 微服务的几篇文章
    JavaScript JQuery
    【原创】单测代码生成工具Evosuite试用
    [转载]Linux进程调度原理
    [转载]Java 应用性能调优实践
    [转载]Java进程物理内存远大于Xmx的问题分析
  • 原文地址:https://www.cnblogs.com/zijianlu/p/3628295.html
Copyright © 2020-2023  润新知