AlarmManager+ Service + BroadCastReceiver解决持续定位问题

AlarmManager+ Service + BroadCastReceiver解决持续定位问题

tips: 除了白名单,任何方法都不能保证进程不被系统杀死。本篇文章,也只是尽量不被系统杀死,但是会导致CPU不能休眠,会相当耗电。慎重选择。

  • Service部分
    定位我就不多说了,bat大厂都提供了。首先我们启动一个Service 注意启动的时候注意8.0权限问题,同时为了保证8.0连续定位问题,我们需要启动前台服务,如下:
    创建通知栏
private final String CHANNEL_ONE_ID = "1";

private final String CHANNEL_ONE_NAME = "定位采集";

Intent nfIntent = new Intent(this, MainActivity.class);
        Notification.Builder builder = new Notification.Builder(this.getApplicationContext())
                .setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
                .setSmallIcon(R.mipmap.login_logo) // 设置状态栏内的小图标
                .setContentTitle(getResources().getString(R.string.app_name))
                .setContentTitle("轨迹采集服务")
                .setContentText("定位采集正在运行") // 设置上下文内容
                .setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
        //----------------  新增代码 --------------------------------------
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            //修改安卓8.1以上系统报错
            NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ONE_ID, CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_MIN);
            notificationChannel.enableLights(false);//如果使用中的设备支持通知灯,则说明此通知通道是否应显示灯
            notificationChannel.setShowBadge(false);//是否显示角标
            notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            manager.createNotificationChannel(notificationChannel);
            builder.setChannelId(CHANNEL_ONE_ID);
        }

        Notification notification;// 获取构建好的Notification
        if (android.os.Build.VERSION.SDK_INT >= 16) {
            notification = builder.build();
        } else {
            notification = builder.getNotification();
        }
        notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
        startForeground(1001, notification);

启动定位service:

/**
     * 启动定位service
     */
    private void startLocateService(Context context) {
        Intent intent = new Intent(context, LocateService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //android8.0以上通过startForegroundService启动service
            context.startForegroundService(intent);
        } else {
            context.startService(intent);
        }
    }
  • AlarmManager

为了保证连续定位,我们选择AlarmManager, 之所以选择它,是因为,他能够在CPU休眠的时候,继续运转,而Timer和Hander 在息屏后,都无法保证继续运转.

AlarmManager概述
AlarmManager是Android的全局定时器。就是在指定时间做一个事情(封装在PendingIntent)。通过PendingIntent的getActivity()、getService()或getBroadcast()来执行。听起来AlarmManager和Timer很类似,但是Timer有可能因为手机休眠而被杀掉服务,但是AlarmManager可以做到唤醒手机。

AlarmManager提供了对系统定时服务的访问接口,使得开发者可以安排在未来的某个时间运行应用。当到达闹铃设定时间,系统就会广播闹铃之前注册的Intent。如果此时目标应用没有被启动,系统还会帮你自动启动目标应用。 即使设备已经进入睡眠已注册的闹铃也会被保持,只有当设备关闭或是重启的时候会被清除.

tips: 具体使用请参考下面两偏文章:

AlarmManager相关API使用一
AlarmManager相关API使用二

怎么实现呢看代码:

alarmManager = (AlarmManager) this.context.getSystemService(Context.ALARM_SERVICE);

Intent intent;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                intent = new Intent();
                //定义广播类型
                intent.setAction("com.*****.alarmLoop");
                intent.setComponent(new ComponentName(context.getPackageName(), "com.*****.LoopBroadCast"));
                /* intent.setComponent(new ComponentName("com.examplehq.forhelp","com.examplehq.forhelp.MyReceiver"));*/
            } else {
                intent = new Intent(context, LoopBroadCast.class);
                intent.setAction("com.*******");
            }
            sender = PendingIntent.getBroadcast(context, 0, intent, 0);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL, sender);
                } else {
                    alarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL, sender);
                }
            } else {
                if (flag == 0) {    //一次性闹钟
                    alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL, sender);
                } else {            //重复闹钟
                    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), INTERVAL, sender);
                }
            }

这里我要做一个循环的闹铃 ,所以每次铃响,都发送个广播,然后继续开启闹铃.
具体我们看下一部分.需要注意的是,广播,在8.0以后,需要判断处理一下.8.0以上必须要加上/* intent.setComponent(new ComponentName(“com.examplehq.forhelp”,“com.examplehq.forhelp.MyReceiver”));*/

  • BroadCastRecevier
    这部分主要是监听闹铃响起以后,要做的事情,所以,我们在这里启动服务,还有唤醒屏幕.
class LoopBroadCast extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if ("com.*******".equals(intent.getAction())) {
            Log.w("AlarmMangerUtils", "拉起广播");
            AlarmMangerUtils.getInstance().setPendingBroadCast();
            startLocateService(context);

            boolean contextNull = WakeUpUitls.getInstance().isContextNull();
            if (contextNull) {
                WakeUpUitls.getInstance().init(context);
            }
            //获取cpu
            WakeUpUitls.getInstance().acquireWakeLock();
            //点亮屏幕
            WakeUpUitls.getInstance().wakeupAndUnLock();
        }
    }
 }
  • WakeUp

这里面我门主要主力CPU, 是为了保证CPU持续运行service

//获取电源锁,保持该服务在屏幕熄灭时仍然获取CPU时,保持运行
    public void acquireWakeLock() {
        Log.e("LoopBroadCast", "屏幕熄灭时仍然获取CPU");
        if (null == keepCpu) {
            keepCpu = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, this.getClass().getSimpleName());
            if (null != keepCpu) {
                keepCpu.acquire();
            }
        } else {
            keepCpu.acquire();
        }
    }

同时保证CPU休眠的中间,唤醒,打断持续休眠状态

Log.e("LoopBroadCast", "点亮屏幕");
        if (null == wakeLock) {
            //屏锁管理器
            KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
            KeyguardManager.KeyguardLock kl = km.newKeyguardLock("unLock");
            //解锁
            kl.disableKeyguard();

            //获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值
            wakeLock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP |
                    PowerManager.SCREEN_DIM_WAKE_LOCK, this.getClass().getSimpleName());
            //点亮屏幕
            if (null != wakeLock) {
                wakeLock.acquire(1);
            }
        } else {
            wakeLock.acquire(1);
        }

就这样,尽量保持定位service 不被系统杀掉 .
亲测:手里有一部华为荣耀的手机,坚持了一个小时,还在继续定位,但是手里的vivo最多的时候挺到30分钟,还有很多10多分钟就被杀掉的情况.

希望对您有帮助!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
安卓的BroadcastReceiver可以被用来接收系统发送的广播消息,而AlarmManager则可以用来触发在特定时间触发的定时任务。然而,在某些情况下,我们可能会遇到AlarmManager无法将广播发送给BroadcastReceiver的问题。 出现这种情况的原因可能有多个。一种可能性是未正确注册BroadcastReceiver。在AndroidManifest.xml文件中,需要添加正确的intent-filter来指定BroadcastReceiver接收的广播类型。如果没有正确添加intent-filter,那么AlarmManager发送的广播就无法被BroadcastReceiver接收到。 另一种可能性是未正确设置AlarmManager的PendingIntent。在设置AlarmManager时,我们需要为PendingIntent指定BroadcastReceiver的类。如果未正确设置PendingIntent,那么AlarmManager发送的广播就无法正确地被指定的BroadcastReceiver接收到。 此外,还有可能是由于权限问题导致BroadcastReceiver无法接收AlarmManager发送的广播。当AlarmManager发送广播时,需要确保应用程序具有相应的权限。如果没有正确配置权限,那么BroadcastReceiver将无法接收到AlarmManager发送的广播。 解决这个问题的步骤包括: 1. 在AndroidManifest.xml文件中确保正确注册BroadcastReceiver,并添加正确的intent-filter。 2. 在设置AlarmManager时,确保为PendingIntent正确指定BroadcastReceiver的类。 3. 确认应用程序具有相应的权限来接收AlarmManager发送的广播。 通过以上步骤的检查和调整,应该能够解决AndroidBroadcastReceiver收不到AlarmManager发送的广播的问题。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值