android程序保活(2020)

公司的一个app,需要接收付款通知,用百度TTS播报。但是程序在后台,灭屏+不充电的情况下,几分钟后就无法响应了。
要想app一直在后台存活,需要做保活操作。
以前的一些保活措施已经失效了,找了一篇靠谱的文章:
https://juejin.im/post/6844904023955341319
http://www.52im.net/thread-3033-1-1.html
这篇文章里,提供了两种方法。
1:开启系统电池白名单。
2:引导用户在设置里,将app加入后台运行白名单。
加了这两种方法后,经过测试,也只能维持一个小时,在别的app里,看到还加入了一项措施,开启前台服务。
试了下加入前台服务后,基本上能一直存活了。
3:加入前台服务。

以下实现这3种操作。
1:开启系统电池白名单。
屏幕灭屏的时候,会将后台的一些进程杀死,让电池耐用,所以需要加入这个白名单。
先在manifest里面加上权限

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

开启白名单

public void requestIgnoreBatteryOptimizations() {
        try {
            Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
            intent.setData(Uri.parse("package:" + getContext().getPackageName()));
            startActivityForResult(intent,1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
111111.png

会弹出提示框,点击允许即可。
可以判断是否已经开启了白名单:

private boolean isIgnoringBatteryOptimizations() {
        boolean isIgnoring = false;
        PowerManager powerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
        if (powerManager != null) {
            isIgnoring = powerManager.isIgnoringBatteryOptimizations(getContext().getPackageName());
        }
        return isIgnoring;
    }

2:引导用户在设置里,将app加入后台运行白名单。
这一步,对用户来说比较复杂,需要引导用户去设置里,让app允许在后台运行。
因为不同的厂商,进入后台白名单的界面不一样,需要单独定制。
以华为为例:

public void goHuaweiSetting() {
        try {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
        } catch (Exception e) {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.optimize.bootstart.BootStartActivity");
        }
    }
222222.png

勾选允许自启动和后台活动即可。

第一步和第二部的详细代码如下:(来自https://juejin.im/post/6844904023955341319
http://www.52im.net/thread-3033-1-1.html

public class MobileButlerUtil {

    private Context mContext;

    public MobileButlerUtil(Context context){
        this.mContext = context;
    }

    public boolean isHuawei() {
        if (Build.BRAND == null) {
            return false;
        } else {
            return Build.BRAND.toLowerCase().equals("huawei") || Build.BRAND.toLowerCase().equals("honor");
        }
    }

    public boolean isXiaomi() {
        return Build.BRAND != null && Build.BRAND.toLowerCase().equals("xiaomi");
    }

    public boolean isOPPO() {
        return Build.BRAND != null && Build.BRAND.toLowerCase().equals("oppo");
    }

    public boolean isVIVO() {
        return Build.BRAND != null && Build.BRAND.toLowerCase().equals("vivo");
    }

    public boolean isMeizu() {
        return Build.BRAND != null && Build.BRAND.toLowerCase().equals("meizu");
    }

    public boolean isSamsung() {
        return Build.BRAND != null && Build.BRAND.toLowerCase().equals("samsung");
    }

    public boolean isLeTV() {
        return Build.BRAND != null && Build.BRAND.toLowerCase().equals("letv");
    }

    public boolean isSmartisan() {
        return Build.BRAND != null && Build.BRAND.toLowerCase().equals("smartisan");
    }


    public void goHuaweiSetting() {
        try {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
        } catch (Exception e) {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.optimize.bootstart.BootStartActivity");
        }
    }

    public void goXiaomiSetting() {
        showActivity("com.miui.securitycenter",
                "com.miui.permcenter.autostart.AutoStartManagementActivity");
    }

    public void goOPPOSetting() {
        try {
            showActivity("com.coloros.phonemanager");
        } catch (Exception e1) {
            try {
                showActivity("com.oppo.safe");
            } catch (Exception e2) {
                try {
                    showActivity("com.coloros.oppoguardelf");
                } catch (Exception e3) {
                    showActivity("com.coloros.safecenter");
                }
            }
        }
    }

    public void goVIVOSetting() {
        showActivity("com.iqoo.secure");
    }

    public void goMeizuSetting() {
        showActivity("com.meizu.safe");
    }

    public void goSamsungSetting() {
        try {
            showActivity("com.samsung.android.sm_cn");
        } catch (Exception e) {
            showActivity("com.samsung.android.sm");
        }
    }

    public void goLetvSetting() {
        showActivity("com.letv.android.letvsafe",
                "com.letv.android.letvsafe.AutobootManageActivity");
    }

    public void goSmartisanSetting() {
        showActivity("com.smartisanos.security");
    }

    /**
     * 跳转到指定应用的首页
     */
    public void showActivity(@NonNull String packageName) {
        Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(packageName);
        mContext.startActivity(intent);
    }

    /**
     * 跳转到指定应用的指定页面
     */
    public void showActivity(@NonNull String packageName, @NonNull String activityDir) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(packageName, activityDir));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(intent);
    }


}
public class PermissionSetting extends BaseFragment implements View.OnClickListener {


    @BindView(R.id.battery_white)
    TextView batteryWhite;

    @BindView(R.id.operation_permission)
    TextView operationPermission;

    @BindView(R.id.iv_goback)
    TextView goBack;

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.battery_white:
                //加入电池白名单
                requestIgnoreBatteryOptimizations();
                break;
            case R.id.operation_permission:
                checkPermission();//设置允许后台活动
                break;
            case R.id.iv_goback:
                pop();
                break;
        }

    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    private boolean isIgnoringBatteryOptimizations() {
        boolean isIgnoring = false;
        PowerManager powerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
        if (powerManager != null) {
            isIgnoring = powerManager.isIgnoringBatteryOptimizations(getContext().getPackageName());
        }
        return isIgnoring;
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    public void requestIgnoreBatteryOptimizations() {
        try {
            Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
            intent.setData(Uri.parse("package:" + getContext().getPackageName()));
            startActivityForResult(intent,1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1) {
            if(isIgnoringBatteryOptimizations()){
                batteryWhite.setText("已开启");
                batteryWhite.setBackground(null);
                batteryWhite.setEnabled(false);
            }
        }
    }

    public void checkPermission(){
        MobileButlerUtil mobileButlerUtil = new MobileButlerUtil(getContext());
        final PromptPopup promptPopup = new PromptPopup(getContext())
                .setContent("为了接收到账通知,请去设置易掌管app允许在后台活动")
                .withConfirmClick(v -> {
                    if(mobileButlerUtil.isHuawei()){
                        mobileButlerUtil.goHuaweiSetting();
                    }else if(mobileButlerUtil.isXiaomi()){
                        mobileButlerUtil.goXiaomiSetting();
                    }else if(mobileButlerUtil.isOPPO()){
                        mobileButlerUtil.goOPPOSetting();
                    }else if(mobileButlerUtil.isVIVO()){
                        mobileButlerUtil.goVIVOSetting();
                    }else if(mobileButlerUtil.isMeizu()){
                        mobileButlerUtil.goMeizuSetting();
                    }else if(mobileButlerUtil.isSamsung()){
                        mobileButlerUtil.goSamsungSetting();
                    }else if(mobileButlerUtil.isLeTV()){
                        mobileButlerUtil.goLetvSetting();
                    }else if(mobileButlerUtil.isSmartisan()){
                        mobileButlerUtil.goSmartisanSetting();
                    }
                }, true);
        promptPopup.showPopupWindow();
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onLazyInitView(@Nullable Bundle savedInstanceState) {
        super.onLazyInitView(savedInstanceState);
        ImmersionBar.with(this).titleBar(R.id.ly_title_bar).autoDarkModeEnable(true).statusBarColor(R.color.bar_grey).init();
        batteryWhite.setOnClickListener(this);
        operationPermission.setOnClickListener(this);
        goBack.setOnClickListener(this);

        if(isIgnoringBatteryOptimizations()){
            batteryWhite.setText("已开启");
            batteryWhite.setBackground(null);
            batteryWhite.setEnabled(false);
        }
    }



    @Override
    protected int getLayoutId() {
        return R.layout.permission_setting;
    }
}

关键的第三步:开启前台服务。

//开启前台服务
        intent = new Intent(MainActivity.this, WhiteService.class);
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {//8.0以上的开启方式不同
            startForegroundService(intent);
        } else {
            startService(intent);
        }

注意在onDestroy时,关闭服务:

//关闭前台服务
        stopService(intent);

服务:

public class WhiteService extends Service {

    private static final String TAG = WhiteService.class.getSimpleName();
    private static final int NOTIFICATION_FLAG =0X11;
    private static final String CHANNEL_ONE_ID = "NOTIFY_ID";
    private static final String CHANNEL_ONE_NAME = "PUSH_NOTIFY_NAME";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 在Android进行通知处理,首先需要重系统哪里获得通知管理器NotificationManager,它是一个系统Service。
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        // 设置点击通知跳转的Intent
        Intent nfIntent = new Intent(this, MainActivity.class);
        // 设置 延迟Intent
        // 最后一个参数可以为PendingIntent.FLAG_CANCEL_CURRENT 或者 PendingIntent.FLAG_UPDATE_CURRENT
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, nfIntent, 0);

        //构建一个Notification构造器
        Notification.Builder builder = new Notification.Builder(this.getApplicationContext());

        //适配8.0service
        NotificationChannel mChannel = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            mChannel = new NotificationChannel(CHANNEL_ONE_ID, CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_HIGH);
            manager.createNotificationChannel(mChannel);
            builder.setChannelId(CHANNEL_ONE_ID);
        }

        builder.setContentIntent(pendingIntent)   // 设置点击跳转界面
                .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
                        R.drawable.login_logo)) // 设置下拉列表中的图标(大图标)
                .setTicker("您有一个notification")// statusBar上的提示
                .setContentTitle("xxxapp正在后台运行") // 设置下拉列表里的标题
                .setSmallIcon(R.drawable.login_logo) // 设置状态栏内的小图标24X24
                .setContentText("为了能正常收到付款通知,请不要结束应用。") // 设置详细内容
                .setContentIntent(pendingIntent) // 设置点击跳转的界面
                .setWhen(System.currentTimeMillis()) // 设置该通知发生的时间
                .setDefaults(Notification.DEFAULT_VIBRATE) //默认震动方式
                .setPriority(Notification.PRIORITY_HIGH);   //优先级高

        Notification notification = builder.build(); // 获取构建好的Notification
        notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
        notification.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明当通知被用户点击时,通知将被清除。
        notification.flags |= Notification.FLAG_ONGOING_EVENT; //将此通知放到通知栏的"Ongoing"即"正在运行"组中
        notification.flags |= Notification.FLAG_NO_CLEAR; //表明在点击了通知栏中的"清除通知"后,此通知不清除,常与FLAG_ONGOING_EVENT一起使用


        //manager.notify(NOTIFICATION_FLAG, notification);
        // 启动前台服务
        // 参数一:唯一的通知标识;参数二:通知消息。
        startForeground(NOTIFICATION_FLAG, notification);// 开始前台服务
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 停止前台服务--参数:表示是否移除之前的通知
        stopForeground(true);
        Log.d(TAG, "onDestroy");
    }

}

参考自:
https://blog.csdn.net/weixin_37577039/article/details/80041624
https://blog.csdn.net/o279642707/article/details/82352431
https://www.jianshu.com/p/cb8426620e74

实现的以上三步,程序应该能在后台存活很久了。

另外,针对第二步,引导用户操作,对用户而言过于复杂,看到有人提到可以用android无障碍模式自动加白,如果有人做出来了,可以分享一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容