這個話題,網上很多,並且列出了一大堆,把各種可能猜測都搞了一遍,結果結論不明確,很多都是不了了之,我們要的是一個確實可以實現的方案,說那麼多,到最後貼出來的代碼很容易就被殺了,沒用的,本文我只講我覺得可以作爲實際運用的方案,其他的就不多提了,最後會給出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.開啓一個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<Activity> 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<Build.VERSION_CODES.JELLY_BEAN_MR2 ){
startForeground(NOTIFICATION_ID,new Notification());
}else{
//18以上
Notification.Builder builder=new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(NOTIFICATION_ID,builder.build());
startService(new Intent(this,InnerService.class));
}
}
public class InnerService extends Service{
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Notification.Builder builder=new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(NOTIFICATION_ID,builder.build());
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
stopForeground(true);
NotificationManager manager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
stopSelf();
}
},100);
}
}
}
1.常駐進程
這個我嘗試了很多種方式,覺得最靠譜的其實還是雙進程守護,這裏嘗試了兩種方式
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註冊
<service android:name=".MyTestService" android:process="com.MyTestService"> <intent-filter> <action android:name="android.liveservice.front"/> </intent-filter> </service>
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中了。
如果你有更好的方案歡迎留言和吐槽。