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里面加上权限


开启白名单

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无障碍模式自动加白,如果有人做出来了,可以分享一下。

你可能感兴趣的:(android程序保活(2020))