AlarmManager用法浅析

都0202年了,用AlarmManager作为一种后台唤醒手段好像有点落伍了。有如下原因:

  • 时间越往后,后台唤醒变得越来越难,不说谷歌官方的安卓6.0doze、8.0startService等限制,光国内厂商,也不会让你痛快地在后台搞事情,各种端外推送不得不接,华为杀后台等让你进程活不了多久。
  • 谷歌本身有推出过JobScheduler等组件。然而之前经测试,很多手机的JobScheduler功能不一,甚至有些小米手机似乎裁剪了JobScheduler的能力,使其无法使用,而AlarmManager反而较为稳定。

仍然要在后台进行一小些操作的需求以及AlarmManager的稳定性让我选择了它。

选择了AlarmManager自然要了解它的脾气:

  • 把定时时间设置得很短时,在后台时,系统为了在统一的“维护窗口”时间进行处理以节省电量,则会推迟他的时间。所以如果对性能有要求,可以在前后台时做不同的处理以最大限度地提升响应效率。
  • 有一些天然坑,如三星手机会出现alarm数超过限制的问题,原因是FLAG_CANCEL_CURRENT标记无法正确地取消alarm,可以使用FLAG_UPDATE_CURRENT标记来规避这种问题。

 

AlarmManager创建alarm在各个版本的行为不太一样但大致功能相同,封装如下:

    private static void schedulePendingIntent(Context ctx, PendingIntent pendingIntent, long delayMs){
        long nextAlarmInMilliseconds = System.currentTimeMillis() + delayMs;
        AlarmManager alarmManager = (AlarmManager) ctx.getSystemService(Service.ALARM_SERVICE);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds,
                    pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds,
                    pendingIntent);
        } else {
            alarmManager.set(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds,
                    pendingIntent);
        }
    }

下面测测Alarm的行为:

public class MainActivity extends AppCompatActivity {

    BroadcastReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("action");
        for (int i = 0; i<100; i++){
            String action = "action" + i;
            intentFilter.addAction(action);
        }
        registerReceiver(receiver = new TheReceiver(), intentFilter);

        scheduleAlarm();
    }

    private void scheduleAlarm(){
        for (int i = 0; i<5; i++){
            String action = "action" + i;
            Intent intent = new Intent().setAction(action).putExtra("v", i);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            schedulePendingIntent(this, pendingIntent, 1000*1);
        }
    }

    private static void schedulePendingIntent(Context ctx, PendingIntent pendingIntent, long delayMs){
        long nextAlarmInMilliseconds = System.currentTimeMillis() + delayMs;
        AlarmManager alarmManager = (AlarmManager) ctx.getSystemService(Service.ALARM_SERVICE);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds,
                    pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds,
                    pendingIntent);
        } else {
            alarmManager.set(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds,
                    pendingIntent);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
    }

    public class TheReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            int v = intent.getIntExtra("v", 0);

            log("action = " + action + " v = " + v);
        }
    }

    private void log(String log){
        Log.i("AlarmLog", log);
    }
}

执行结果如下:

2020-11-02 11:14:44.645 29772-29772/com.nykj.testalarm I/AlarmLog: action = action0 v = 0
2020-11-02 11:14:49.646 29772-29772/com.nykj.testalarm I/AlarmLog: action = action3 v = 3
2020-11-02 11:14:49.652 29772-29772/com.nykj.testalarm I/AlarmLog: action = action4 v = 4
2020-11-02 11:14:49.656 29772-29772/com.nykj.testalarm I/AlarmLog: action = action2 v = 2
2020-11-02 11:14:49.660 29772-29772/com.nykj.testalarm I/AlarmLog: action = action1 v = 1

requestCode唯一的情况下,action使用不同的值,FLAG_UPDATE_CURRENT不会影响到上一次alarm。

修改代码,让action固定:

private void scheduleAlarm(){
        for (int i = 0; i<5; i++){
            String action = "action";
            Intent intent = new Intent().setAction(action).putExtra("v", i);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            schedulePendingIntent(this, pendingIntent, 1000*1);
        }
    }

结果:

2020-11-02 19:05:39.937 2168-2168/com.nykj.testalarm I/AlarmLog: action = action v = 4

我们看到,我们提交了5次,最后只响应了1次,FLAG_UPDATE_CURRENT生效了,生效的条件是requestCode固定、action也固定。

我们让requestCode不固定呢?

    private void scheduleAlarm(){
        for (int i = 0; i<5; i++){
            String action = "action";
            Intent intent = new Intent().setAction(action).putExtra("v", i);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(this, i, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            schedulePendingIntent(this, pendingIntent, 1000*1);
        }
    }

结果:

2020-11-02 19:07:42.828 2539-2539/com.nykj.testalarm I/AlarmLog: action = action v = 0
2020-11-02 19:07:47.831 2539-2539/com.nykj.testalarm I/AlarmLog: action = action v = 3
2020-11-02 19:07:47.836 2539-2539/com.nykj.testalarm I/AlarmLog: action = action v = 4
2020-11-02 19:07:47.841 2539-2539/com.nykj.testalarm I/AlarmLog: action = action v = 2
2020-11-02 19:07:47.846 2539-2539/com.nykj.testalarm I/AlarmLog: action = action v = 1

可见,requestCode不同时,相当于这是5个PendingIntent去打开Alarm,它们互不影响,与不固定action且固定requestCode的情况相同。

这也印证了FLAG_UPDATE_CURRENT标记的用法:

    /**
     * Flag indicating that if the described PendingIntent already exists,
     * then keep it but replace its extra data with what is in this new
     * Intent. For use with {@link #getActivity}, {@link #getBroadcast}, and
     * {@link #getService}. <p>This can be used if you are creating intents where only the
     * extras change, and don't care that any entities that received your
     * previous PendingIntent will be able to launch it with your new
     * extras even if they are not explicitly given to it.
     */
    public static final int FLAG_UPDATE_CURRENT = 1<<27;

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值