• 2023Android白名单保活(后台定位)分享


    Android 系统已经更新到13了,各个Rom厂商也控制越来越严格了,还能做保活App。答案肯定是可以的,然而路线是很艰难的。

    最近接到一个项目,需要安装一次app后,就需要一直获取定位。随着Android系统的不断完善,厂商rom的不断优化,想要实现后台不断定位的功能,要面临的问题很多。先总结下此次开发中遇到的难点和要点。首先确认的是一点,用白名单保活的方式来实现,如果是可以实现黑名单保活的大牛,此文请忽略。

    需要解决的主要问题有以下几项:

    1.白名单保活核心:权限-自启动管理 要求用户手动设置打开自启动和后台运行权限。

    2.电池优化:关闭电池优化。或者说耗电管理

    3.进程复活:即使开启了自启动权限也要有自身拉活机制。

    4.维持cpu唤醒状态:此项很重要,某些机器不一定需要,但是某些机器不这么处理,程序百分之百死掉。比如华为的某些pad。

    5.比保活还重要的是定位权限:如果是做后台定位的话,熄屏后失去定位权限的解决方案。

    解决方案:

    如果做白名单保活,那么在做权限获取的时候需要两部分来完成,软件自动调整权限设置页或者引导用户手动开启权限设置。

    这套方案目前在大多数手机或pad中可以保证服务不死定位不停,个别设备需要单独进行设置或处理才行,文章末尾会说。

    先看代码上能做的处理有哪些:

    1.关闭电池优化

    我的所有处理都是从MainActivity开始的,

    int checkTime = 0;
    在onCreate中执行
    //忽略电池优化
    
    1. KeepAliveUtils.Checkbattery(this, 0);
    2. ​​​​​​​checkTime++;
    1. public static void Checkbattery(Activity activity, int time) {
    2. if (time > 1)
    3. return;
    4. PowerManager manager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
    5. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    6. boolean ignoringBatteryOptimizations = manager.isIgnoringBatteryOptimizations(activity.getPackageName());
    7. if (!ignoringBatteryOptimizations) {
    8. Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
    9. intent.setData(Uri.parse("package:" + activity.getPackageName()));
    10. activity.startActivityForResult(intent, 110);
    11. }
    12. }
    13. }

    然后在MainActivity中处理

    1. @Override
    2. protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    3. super.onActivityResult(requestCode, resultCode, data);
    4. if (resultCode == 110) {
    5. KeepAliveUtils.Checkbattery(this, checkTime);
    6. }
    7. }

    这段代码会弹出提示框提示用户是否关闭电池优化功能。

    2.提示框提示用户手动打开自启动权限管理

    int checkGoTime = 0;

    在onCreate中执行

    1. checkGoTime = KvUtils.getInstance().getInt("checkGoTime", 0);
    2. if (checkGoTime == 0) {
    3. KvUtils.getInstance().put("checkGoTime", 1);
    4. //打开自启动
    5. new XPopup.Builder(this)
    6. .isDestroyOnDismiss(true)
    7. .asCustom(new ConfirmCenterPop(this, "为保证App正常运行,请打开应用自启动权限和后台弹窗权限", new ConfirmCenterPop.OnSubmitListenner() {
    8. @Override
    9. public void onClick() {
    10. // CheckPermissionUtils.startToAutoStartSetting(MainActivity.this);
    11. JumpPermissionManagement.GoToSetting(MainActivity.this);
    12. }
    13. }))
    14. .show();
    15. }

    这段加了次数管理,避免弹出多次。

    3.检查并申请定位权限和通知并开启服务

    1. //请求权限开启定位
    2. XXPermissions.with(this)
    3. // 申请单个权限
    4. .permission(Permission.ACCESS_FINE_LOCATION)
    5. .permission(Permission.ACCESS_COARSE_LOCATION)
    6. .permission(Permission.ACCESS_BACKGROUND_LOCATION)
    7. // 申请多个权限
    8. // 设置权限请求拦截器(局部设置)
    9. //.interceptor(new PermissionInterceptor())
    10. // 设置不触发错误检测机制(局部设置)
    11. //.unchecked()
    12. .request(new OnPermissionCallback() {
    13. @Override
    14. public void onGranted(@NonNull List permissions, boolean allGranted) {
    15. if (!allGranted) {
    16. ToastUtils.showShort("获取部分权限成功,但部分权限未正常授予");
    17. return;
    18. }
    19. startService(new Intent(MainActivity.this, AliveService.class));
    20. startService(new Intent(MainActivity.this, SAliveService.class));
    21. }
    22. @Override
    23. public void onDenied(@NonNull List permissions, boolean doNotAskAgain) {
    24. if (doNotAskAgain) {
    25. ToastUtils.showShort("被永久拒绝授权,请手动开启定位权限");
    26. // 如果是被永久拒绝就跳转到应用权限系统设置页面
    27. XXPermissions.startPermissionActivity(MainActivity.this, permissions);
    28. } else {
    29. ToastUtils.showShort("获取定位权限失败");
    30. }
    31. }
    32. });
    33. //获取通知权限,后台持续定位
    34. XXPermissions.with(this)
    35. // 申请单个权限
    36. .permission(Permission.POST_NOTIFICATIONS)
    37. // 申请多个权限
    38. // 设置权限请求拦截器(局部设置)
    39. //.interceptor(new PermissionInterceptor())
    40. // 设置不触发错误检测机制(局部设置)
    41. //.unchecked()
    42. .request(new OnPermissionCallback() {
    43. @Override
    44. public void onGranted(@NonNull List permissions, boolean allGranted) {
    45. if (!allGranted) {
    46. ToastUtils.showShort("获取部分权限成功,但部分权限未正常授予");
    47. return;
    48. }
    49. startService(new Intent(MainActivity.this, AliveService.class));
    50. startService(new Intent(MainActivity.this, SAliveService.class));
    51. }
    52. @Override
    53. public void onDenied(@NonNull List permissions, boolean doNotAskAgain) {
    54. if (doNotAskAgain) {
    55. ToastUtils.showShort("被永久拒绝授权,请手动开启定位权限");
    56. // 如果是被永久拒绝就跳转到应用权限系统设置页面
    57. XXPermissions.startPermissionActivity(MainActivity.this, permissions);
    58. } else {
    59. ToastUtils.showShort("获取定位权限失败");
    60. }
    61. }
    62. });

    之所以把定位权限和通知权限放在一起,是因为做后台定位的时候需要发送通知,将定位变成前台服务才能保证后台持续定位。

    接下来就是核心中的核心

    AliveService.class和SAliveService
    双进程保活互相拉起,维持服务不死和定位权限不失的核心内容都在这两个服务里边处理

    4.AliveService.class和SAliveService

    1. public class AliveService extends Service {
    2. private MyBinder myBinder;
    3. /**
    4. * 连接对象
    5. */
    6. private Connection connection;
    7. @Nullable
    8. @Override
    9. public IBinder onBind(Intent intent) {
    10. return myBinder;
    11. }
    12. private static final int NOTIFICATION_ID = 1;
    13. private Runnable runnable;
    14. private Handler handler;
    15. PowerManager pm;
    16. @SuppressLint("InvalidWakeLockTag")
    17. PowerManager.WakeLock wakeLock;
    18. @Override
    19. public void onCreate() {
    20. super.onCreate();
    21. //创建NotificationChannel
    22. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    23. startForeground(1, LocationUtils.buildNotification());
    24. }
    25. myBinder = new MyBinder();
  • 相关阅读:
    国民经济核算
    游戏平台能否进行定制开发?
    python基于django学生成绩管理系统o8mkp
    [附源码]计算机毕业设计springboot海滨学院学生大创项目申报与审批系统
    echarts 柱状图及折线图常用设置
    ubuntu无法virtualenv创建python虚拟环境的解决
    格式化硬盘时提示“此驱动器正在使用”解决占用问题
    TortoiseGit使用教程
    基于springboot的鲜花管理系统
    [acwing周赛复盘] 第 69 场周赛20220917
  • 原文地址:https://blog.csdn.net/z690798364/article/details/132663604