前言
一、常见的应用保活方法
1、 监听广播方式
2、 提高Service的优先级
3、 双service拉起
3、双进程拉起
二、多进程音频保活方案
综上所述,上面的方法只是提高了APP后台运行存留能力,在用户不主动清理或强杀的情况下,测试APP的保活效果还是非常不错的。但是,"咕咚"在点击一键清理时奇妙的活了下来,原因是在后台循环播放一段无声音乐。如下图:
代码如下:
public class PlayerMusicService extends Service {
private final static String TAG = "PlayerMusicService";
private MediaPlayer mMediaPlayer;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.silent);
mMediaPlayer.setLooping(true);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
startPlayMusic();
}
}).start();
return START_STICKY;//注释1
}
private void startPlayMusic(){
if(mMediaPlayer != null){
mMediaPlayer.start();
}
}
private void stopPlayMusic(){
if(mMediaPlayer != null){
mMediaPlayer.stop();
}
}
@Override
public void onDestroy() {
super.onDestroy();
stopPlayMusic();
// 注释2
Intent intent = new Intent(getApplicationContext(),PlayerMusicService.class);
startService(intent);
}
}
AndroidManifest.xml
<service android:name=".service.PlayerMusicService"
android:enabled="true"
android:exported="true"
android:process=":music_service"/>//多进程
开启后台播放音频服务,此方案在各机型测试情况如下:
1、 华为Mate8(7.0) ,一键清理依然存活,在置于后台的黑屏模式下存活12小时以上;但是如果用户只选择清理此应用也会被杀死,这与"咕咚"保活效果一致。
2、三星C9(6.0),一键清理最近应用,成功保活;
3、华为4X(6.0):一键清理最近应用,成功保活;
4、三星Note4(5.0):一键清理最近应用,成功保活;
三、通过JobScheduler方案
概述
原理
新建类:AliveJobService.java
@TargetApi(21)
public class AliveJobService extends JobService {
private static final int MESSAGE_ID_TASK = 0x01;
// 告知编译器,这个变量不能被优化
private volatile static Service mKeepAliveService = null;
public static boolean isJobServiceAlive(){
return mKeepAliveService != null;
}
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if(SystemUtils.isAPPALive(getApplicationContext(), Contants.PACKAGE_NAME)){
//APP活着的
}else{
Intent intent = new Intent(getApplicationContext(), SportsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
//APP被杀死后重启
}
jobFinished( (JobParameters) msg.obj, false ); // 通知系统任务执行结束
return true;
}
});
@Override
public boolean onStartJob(JobParameters params) {
mKeepAliveService = this;
Message msg = Message.obtain(mHandler, MESSAGE_ID_TASK, params);
mHandler.sendMessage(msg);
// 返回false,系统假设这个方法返回时任务已经执行完毕;
// 返回true,系统假定这个任务正要被执行
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
mHandler.removeMessages(MESSAGE_ID_TASK);
return false;
}
}
JobScheduler管理类
执行系统任务:
JobSchedulerManager.java
public class JobSchedulerManager {
private static final int JOB_ID = 1;
private static JobSchedulerManager mJobManager;
private JobScheduler mJobScheduler;
private static Context mContext;
private JobSchedulerManager(Context ctxt){
this.mContext = ctxt;
mJobScheduler = (JobScheduler)ctxt.getSystemService(Context.JOB_SCHEDULER_SERVICE);
}
public final static JobSchedulerManager getJobSchedulerInstance(Context ctxt){
if(mJobManager == null){
mJobManager = new JobSchedulerManager(ctxt);
}
return mJobManager;
}
@TargetApi(21)
public void startJobScheduler(){
if(AliveJobService.isJobServiceAlive() || isBelowLOLLIPOP()){
return; // 如果JobService已经启动或API<21,返回
}
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID,new ComponentName(mContext, AliveJobService.class));
builder.setPeriodic(3000); // 设置每3秒执行一下任务
builder.setPersisted(true); // 设置设备重启时,执行该任务
builder.setRequiresCharging(true); // 当插入充电器,执行该任务
JobInfo info = builder.build();
mJobScheduler.schedule(info);
}
@TargetApi(21)
public void stopJobScheduler(){
if(isBelowLOLLIPOP())
return;
mJobScheduler.cancelAll();
}
private boolean isBelowLOLLIPOP(){
return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; // API< 21
}
}
记得添加权限: android:permission="android.permission.BIND_JOB_SERVICE"
Doze休眠模式
测试结果
三星C9(6.0):一键清理和强制停止(force stop)都能够唤醒APP;
三星Note4(5.0):一键清理和强制停止(force stop)都能够唤醒APP;
华为荣耀4X(6.0):一键清理和强制停止(force stop)都能够唤醒APP;
华为Mate8(7.0):失效(可能被华为屏蔽了);
四、华为推送SDK
MyHwPushReceiver.java
public class MyHwPushReceiver extends PushEventReceiver{
private final static String TAG = "MyHwPushReceiver";
@Override
public void onToken(Context context, String token, Bundle bundle) {
Log.i(TAG,"连接到华为推送服务器,token="+token);
}
@Override
public boolean onPushMsg(Context context, byte[] msgBytes, Bundle bundle) {
Log.i(TAG,"接收透传消息:"+new String(msgBytes,"UTF-8"));
// 启动应用
return false;
}
@Override
public void onPushState(Context context, boolean connectState) {
Log.i(TAG,"是否连接到华为推送服务器:"+(connectState?"connected":"disconnected"));
}
@Override
public void onEvent(Context context, Event event, Bundle bundle) {
//点击打开通知栏
super.onEvent(context, event, bundle);
}
}
HwPushManager.java
public class HwPushManager {
private static HwPushManager mPushManager;
private Context mContext;
private HwPushManager(Context mContext){
this.mContext = mContext;
}
public static HwPushManager getInstance(Context mContext){
if(mPushManager == null){
mPushManager = new HwPushManager(mContext);
}
return mPushManager;
}
public void startRequestToken(){
//向服务器请求Token
PushManager.requestToken(mContext);
}
//是否接收服务器传递过来的透传消息
public void isEnableReceiveNormalMsg(boolean isEnable){
PushManager.enableReceiveNormalMsg(mContext,isEnable);
}
//是否接收自呈现消息
public void isEnableReceiverNotifyMsg(boolean isEnable){
PushManager.enableReceiveNotifyMsg(mContext,isEnable);
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- 第三方相关 :接收Push消息(注册、Push消息、Push连接状态、标签,LBS上报结果)广播 -->
<receiver android:name=".receiver.MyHwPushReceiver" >
<intent-filter>
<action android:name="com.huawei.android.push.intent.REGISTRATION" />
<action android:name="com.huawei.android.push.intent.RECEIVE" />
<action android:name="com.huawei.android.push.intent.CLICK" />
<action android:name="com.huawei.intent.action.PUSH_STATE" />
<action android:name="com.huawei.android.push.plugin.RESPONSE" />
</intent-filter>
<meta-data android:name="CS_cloud_ablitity" android:value="@string/hwpush_ability_value"/>
</receiver>
<!-- PushSDK:PushSDK接收外部请求事件入口 -->
<receiver
android:name="com.huawei.android.pushagent.PushEventReceiver"
android:process=":pushservice" >
<intent-filter>
<action android:name="com.huawei.android.push.intent.REFRESH_PUSH_CHANNEL" />
<action android:name="com.huawei.intent.action.PUSH" />
<action android:name="com.huawei.intent.action.PUSH_ON" />
<action android:name="com.huawei.android.push.PLUGIN" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver
android:name="com.huawei.android.pushagent.PushBootReceiver"
android:process=":pushservice" >
<intent-filter>
<action android:name="com.huawei.android.push.intent.REGISTER" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
<meta-data
android:name="CS_cloud_version"
android:value="\u0032\u0037\u0030\u0035" />
</receiver>
<!-- PushSDK:Push服务 -->
<service
android:name="com.huawei.android.pushagent.PushService"
android:process=":pushservice" >
</service>
<!-- locale|layoutDirection 切换语言后不重新创建activity -->
<activity
android:name="com.huawei.android.pushselfshow.richpush.RichPushActivity"
android:process=":pushservice"
android:theme="@style/hwpush_NoActionBar"
android:configChanges="orientation|screenSize|locale|layoutDirection"
android:screenOrientation="portrait">
<meta-data android:name="hwc-theme"
android:value="androidhwext:style/Theme.Emui"/>
<intent-filter>
<action android:name="com.huawei.android.push.intent.RICHPUSH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name="com.huawei.android.pushselfshow.permission.RequestPermissionsActivity"
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:configChanges="orientation|screenSize|locale|layoutDirection"
android:exported="false">
</activity>
测试结果
参考链接:
https://blog.csdn.net/andrexpert/article/details/75174586
https://blog.csdn.net/andrexpert/article/details/75045678