公司的一个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();
}
}
会弹出提示框,点击允许即可。
可以判断是否已经开启了白名单:
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");
}
}
勾选允许自启动和后台活动即可。
第一步和第二部的详细代码如下:(来自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无障碍模式自动加白,如果有人做出来了,可以分享一下。