这个话题,网上很多,并且列出了一大堆,把各种可能猜测都搞了一遍,结果结论不明确,很多都是不了了之,我们要的是一个确实可以实现的方案,说那么多,到最后贴出来的代码很容易就被杀了,没用的,本文我只讲我觉得可以作为实际运用的方案,其他的就不多提了,最后会给出demo供大家验证。
先说下现有的有哪些方案:
1、将Service设置为前台进程 2、在service的onStartCommand方法里返回 STATR_STICK 3、添加Manifest文件属性值为android:persistent=“true” 4、覆写Service的onDestroy方法 5、添加广播监听android.intent.action.USER_PRESENT事件以及其他一些可以允许的事件 6、服务互相绑定 7、设置闹钟,定时唤醒 8、账户同步,定时唤醒 9、开启一个1像素activity 前台保活 10、native进程
提前说明下,以下方案只对部分8.0以上手机有效,8.0以下基本都可以。
基本上就这些,当然还有些说是通过native 层的代码实现的,但是弊端很明显,跟厂商,和系统版本关系太大,而且还不靠谱。
1.项目不需要在后台常驻进程,只是不要那么容易被系统回收
2.后台需要有个进程常驻,长时间的去工作发现进程被杀后把他拉起来。
1.如果说只是要提高进程级别,当然进程oom_adj 值越小就越不容易被杀。我觉得上边的1、9就可以满足需求。但是前台进程会在8.0及以上状态栏留下一个通知,这个好像不太好,这个看需求,如果应用有通知类的东西或许会好点。
在讲之前先讲几个adb命令
adb shell ps 查看所有进程信息
adb shell "ps|grep 包名" 查看特定进程信息
adb shell "cat /proc/ppid号/oom_adj 查看进程优先级 8.0不可用了
adb shell am force-stop 包名 杀进程
1.开启一个1像素activity 前台保活,这个是能提高进程的优先级的
LiveService.java
public class LiveService extends Service {
private static final String TAG = "LiveService";
public static void toLiveService(Context pContext) {
Intent intent = new Intent(pContext, LiveService.class);
pContext.startService(intent);
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
final ScreenManager screenManager = ScreenManager.getInstance(LiveService.this);
ScreenBroadcastListener listener = new ScreenBroadcastListener(this);
listener.registerListener(new ScreenBroadcastListener.ScreenStateListener() {
@Override
public void onScreenOn() {
Log.d(TAG, "onScreenOn: ");
screenManager.finishActivity();
}
@Override
public void onScreenOff() {
Log.d(TAG, "onScreenOff: ");
screenManager.startActivity();
}
});
return START_REDELIVER_INTENT;
}
}
ScreenBroadcastListener
public class ScreenBroadcastListener {
Context mContext;
ScreenBroadcastReceiver mScreenReceiver;
ScreenStateListener mListener;
public ScreenBroadcastListener(Context context) {
mContext = context.getApplicationContext();
mScreenReceiver = new ScreenBroadcastReceiver();
}
interface ScreenStateListener {
void onScreenOn();
void onScreenOff();
}
private class ScreenBroadcastReceiver extends BroadcastReceiver {
String action = null;
@Override
public void onReceive(Context context, Intent intent) {
action = intent.getAction();
if (intent.ACTION_SCREEN_ON.equals(action)) {
mListener.onScreenOn();
} else if (intent.ACTION_SCREEN_OFF.equals(action)) {
mListener.onScreenOff();
}
}
}
public void registerListener(ScreenStateListener listener) {
mListener = listener;
registerListener();
}
private void registerListener(){
IntentFilter filter=new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mScreenReceiver,filter);
}
}
ScreenManager
public class ScreenManager {
static ScreenManager gDefualt;
WeakReference mActivityWref;
Context mContext;
public ScreenManager(Context mContext) {
this.mContext = mContext;
}
public static ScreenManager getInstance(Context context) {
if (gDefualt==null) {
gDefualt=new ScreenManager(context.getApplicationContext());
}
return gDefualt;
}
public void setActivity(Activity pActivity){
mActivityWref = new WeakReference<>(pActivity);
}
public void startActivity(){
LiveActivity.actionToLiveActivity(mContext);
}
public void finishActivity(){
if (mActivityWref!=null) {
Activity activity=mActivityWref.get();
if (activity!=null) {
activity.finish();
}
}
}
}
2.开启前台服务用来提高优先级
KeepLiveService.java
public class KeepLiveService extends Service {
public static final int NOTIFICATION_ID=0x11;
public KeepLiveService() {
}
@Override
public IBinder onBind(Intent intent) {
return (IBinder) new UnsupportedOperationException("not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT
这个我尝试了很多种方式,觉得最靠谱的其实还是双进程守护,这里尝试了两种方式
1.应用内通过自身开启一个公有进程,注意了不是 “ :” 开头的,否则在手动杀进程时会被杀掉。
MyTestService.java
public class MyTestService extends Service {
private static final String TAG = "MyTestService";
int number=0;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
while (true){
number++;
Log.d(TAG, "run: "+number );
}
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
}
AndroidManifest注册
Activity 内启动服务进程,使用 adb shell ps 查看机器正在运行的进程
Intent intent = new Intent(); intent.setAction("android.liveservice.front"); intent.setPackage("com.example.servicelive1"); startService(intent);
这里我们看到有三个进程一个主进程,一个私有服务进程,一个公有服务进程,现在我们在设备上将应用进程杀掉。
三个进程全部被干掉了,因为我的设备是zuk 8.0的,再换成 小米pad2 5.1 也是全部被干掉了 换成nexus_5_25
竟然还活着。换成nexus_5_26 同样进程还活着,这说明我们的国产厂商对系统做了优化。
2.应用开启其他应用中的一个服务,用于对此应用进程进行守护。
nexus_5_26 首先安装一个应用服务公有(csdnactivity),启动自身应用(servicelive1)发现启动不了个了
Not allowed to start service Intent { act=android.liveservice pkg=com.example.csdnactivity }: app is in background uid null
直接告诉我不允许启动,因为被启动的服务所属的应用进程还未启动,用户不知道,没有相应的pid,看来8.0是真滴不行啊。
nexus_5_25 同理在这试下。
非常顺利,现在查看
u0_a84 31371 1371 1397788 48336 SyS_epoll_ 00000000 S com.example.servicelive1
u0_a83 31386 1371 1406656 47620 SyS_epoll_ 00000000 S com.example.csdnactivity
两个进程,其中一个就是另一个应用的进程了。
同理,杀进程。
u0_a83 31386 1371 1406656 47668 SyS_epoll_ 00000000 S com.example.csdnactivity
还在后台运行。此方法可行。仅限于8.0以下。
现在试下小米pad 5.1 的
首先保证两个应用都安装了,然后杀掉所有进程,然后启动 servicelive1 ,一切顺利,csdnactivity 服务被启动了,是一个后台服务,现在杀进程。
adb shell ps 看到有一个进程还在后台运行,现在设备上已经没有可杀进程了。
u0_a749 1211 2434 976340 45548 ffffffff 00000000 S com.example.csdnactivity
这样实现进程保活方案是可行的,通过那个后台进程,监听应用进程是否被杀,如果杀了,就把它拉起。但是没有找到一种万全的不被啥的方案,并且8.0以上不行。这里我只是简单讲了几种我觉得可以在项目中去使用的方案,还有其他一些尝试性的我都在demo中了。
如果你有更好的方案欢迎留言和吐槽。
进程常驻demo