简介

进程保活对资讯类的App和即时通讯App的用处很大,但随着这一技术的滥用,各大手机厂商与谷歌都开始逐步收紧机制,进程保活也越来越难实现,可以说如今几乎无法实现100%保活(Android高版本特为尤甚),程序员能做的只是尽可能提升进程存活的几率(优先级)。当然,使用各种技巧提升进程存活几率的前提是对Android进程相关机制有一定的认知。

本文主要介绍一下目前网上主流的保活方案。

1像素保活

本方案主要是利用了安卓熄屏广播拉起仅有1个透明像素的OnePieceActivity来提升进程优先级以达到尽可能不被Kill的目的。

项目结构如下。

AndroidManifest.xml

<activityandroid:name=".OnePieceActivity"android:excludeFromRecents="true" //不在最近任务列表中展示android:finishOnTaskLaunch="false" //按Home去主页,再点击图标会进入MainActivity,且销毁本Activityandroid:launchMode="singleInstance"android:theme="@style/OnePieceTheme"> //使用自定义透明style
</activity>

res\values\styles.xml

<style name="OnePieceTheme" parent="AppTheme"><!-- 无背景(会使用主题默认背景) --><item name="android:windowBackground">@null</item><!-- 是否为透明窗口 --><item name="android:windowIsTranslucent">true</item>
</style>

KeepAliveManager :该类主要用于控制保活Activity的创建与销毁,并保证只有一个保活Activity实例。还提供了注册、注销接收熄屏、亮屏广播的工具方法。

package com.zyc.onepiece;import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;import java.lang.ref.WeakReference;public class KeepAliveManager {private static final KeepAliveManager instance = new KeepAliveManager();private WeakReference<OnePieceActivity> activity; //弱引用,防止内存泄漏private KeepAliveReceiver receiver;private KeepAliveManager() {}public static KeepAliveManager getInstance() {return instance;}public void setKeepLiveActivity(OnePieceActivity activity) {this.activity = new WeakReference<>(activity);}//开启保活Activitypublic void startOnePieceActivity(Context context) {Intent intent = new Intent(context, OnePieceActivity.class);context.startActivity(intent);}//关闭保活Activitypublic void finishOnePieceActivity() {if (activity != null && activity.get() != null) {activity.get().finish();}}//注册广播public void registerKeepLiveReceiver(Context context) {Log.d(MainActivity.TAG, "KeepAliveReceiver已注册");receiver = new KeepAliveReceiver();IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_SCREEN_OFF);filter.addAction(Intent.ACTION_SCREEN_ON);context.registerReceiver(receiver, filter);}//注销广播public void unregisterKeepLiveReceiver(Context context) {Log.d(MainActivity.TAG, "KeepAliveReceiver已注销");if (receiver != null) {context.unregisterReceiver(receiver);}}
}

KeepAliveReceiver :接收熄屏、亮屏广播后拉起、结束保活Activity。

package com.zyc.onepiece;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;public class KeepAliveReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {Log.d(MainActivity.TAG, "屏幕关闭,准备拉起OnePieceActivity");KeepAliveManager.getInstance().startOnePieceActivity(context);} else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {Log.d(MainActivity.TAG, "屏幕开启,准备关闭OnePieceActivity");KeepAliveManager.getInstance().finishOnePieceActivity();}}
}

OnePieceActivity :保活Activity,无界面,看上去是透明的。

package com.zyc.onepiece;import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;import androidx.appcompat.app.AppCompatActivity;public class OnePieceActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d(MainActivity.TAG, "OnePieceActivity onCreate");//左上角显示Window window = getWindow();window.setGravity(Gravity.START | Gravity.TOP);//设置为1像素大小WindowManager.LayoutParams params = window.getAttributes();params.x = 0;params.y = 0;params.width = 1;params.height = 1;window.setAttributes(params);//KeepAliveManager中的保活Activity初始化为本ActivityKeepAliveManager.getInstance().setKeepLiveActivity(this);}@Overrideprotected void onStart() {Log.d(MainActivity.TAG, "OnePieceActivity onStart");super.onStart();}@Overrideprotected void onRestart() {Log.d(MainActivity.TAG, "OnePieceActivity onRestart");super.onRestart();}@Overrideprotected void onStop() {Log.d(MainActivity.TAG, "OnePieceActivity onStop");super.onStop();}@Overrideprotected void onDestroy() {Log.d(MainActivity.TAG, "OnePieceActivity onDestroy");super.onDestroy();}
}

MainActivity :主要用于控制广播的注册与注销。

package com.zyc.onepiece;import android.os.Bundle;
import android.util.Log;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {public static String TAG = "MyLog";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d(MainActivity.TAG, "MainActivity onCreate");KeepAliveManager.getInstance().registerKeepLiveReceiver(this);}@Overrideprotected void onStart() {Log.d(MainActivity.TAG, "MainActivity onStart");super.onStart();}@Overrideprotected void onRestart() {Log.d(MainActivity.TAG, "MainActivity onRestart");super.onRestart();}@Overrideprotected void onStop() {Log.d(MainActivity.TAG, "MainActivity onStop");super.onStop();}@Overrideprotected void onDestroy() {KeepAliveManager.getInstance().unregisterKeepLiveReceiver(this);super.onDestroy();}
}

运行,发现在笔者的一加6T(Android 10)下应用内熄屏可以拉起1像素保活Activity,但按Home回到主页后,再熄屏虽然可以接到广播,但无法拉起Activity:

设置前台Service

本方案原理是拥有前台Service的进程将拥有更高的优先级,也就更难被回收掉。注意:部分Android版本前台Service会在顶部通知栏露出“马脚”:

可以试着在前台Service创建通知后立马清除它来隐藏自己。利用相同id前台Service会使用同一条通知的特性,创建一个与前台Service有相id的“替罪羊”前台Service,然后结束它,这样通知会被随之清除,但原本的Service可以继续工作。

项目结构如下。

AndroidManifest.xml

//不加该权限会报错:java.lang.SecurityException: Permission Denial: startForeground from pid=XXX, uid=XXX requires android.permission.FOREGROUND_SERVICE
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />...<serviceandroid:name=".ScapegoatService"android:enabled="true"android:exported="true" />
<serviceandroid:name=".ForegroundService"android:enabled="true"android:exported="true" />

ForegroundService :模拟工作的(前台)线程,如有必要会开启ScapegoatService来清除通知栏通知。由于缺乏更多高版本系统样本,Android 8.0以上部分可能不靠谱,不过推荐还是老老实实按谷歌要求发出通知为妙。

package com.zyc.foregroundservice;import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;import androidx.annotation.RequiresApi;import java.util.Timer;
import java.util.TimerTask;public class ForegroundService extends Service {private static final int SERVICE_ID = 1;private Timer timer;private TimerTask timerTask;public static int count;public ForegroundService() {}@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();Log.d(MainActivity.TAG, "创建前台服务");}@RequiresApi(api = Build.VERSION_CODES.O)@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {startTask();//判断版本if (Build.VERSION.SDK_INT < 18) {//Android4.3以下版本//直接调用startForeground即可,不会在通知栏创建通知startForeground(SERVICE_ID, new Notification());} else if (Build.VERSION.SDK_INT < 24) {//Android4.3 - 7.0之间Intent scapegoatIntent = new Intent(this, ScapegoatService.class);startService(scapegoatIntent);} else {//Android 8.0以上//经测试,本人的一加6T(Android 10)这样写并不会在通知栏创建通知,其他机型与版本效果仍需考证startForeground(SERVICE_ID, new Notification());}return START_STICKY;}/*** 开启定时任务,count每秒+1*/private void startTask() {timer = new Timer();timerTask = new TimerTask() {@Overridepublic void run() {Log.d(MainActivity.TAG, "服务运行中,count: " + count);count++;}};timer.schedule(timerTask, 0, 1000);}/*** 结束定时任务*/private void stopTask() {if (timer != null) {timer.cancel();timer = null;}if (timerTask != null) {timerTask.cancel();timerTask = null;}count = 0;}@Overridepublic void onDestroy() {stopTask();Log.d(MainActivity.TAG, "销毁前台服务");super.onDestroy();}
}

ScapegoatService :拥有和工作线程相同id,启动后立马自行停止并销毁,也就替ForegroundService 消除了通知。

package com.zyc.foregroundservice;import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;public class ScapegoatService extends Service {private static final int SERVICE_ID = 1; //后台ForegroundService的SERVICE_ID相同public ScapegoatService() {}@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();Log.d(MainActivity.TAG, "创建前台服务替身");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {startForeground(SERVICE_ID, new Notification());stopForeground(true);//移除通知栏消息stopSelf();return START_STICKY;}@Overridepublic void onDestroy() {Log.d(MainActivity.TAG, "销毁前台服务替身");super.onDestroy();}
}

MainActivity :启动/停止Service而已,不多赘述。

package com.zyc.foregroundservice;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;
import android.os.Bundle;public class MainActivity extends AppCompatActivity {public static String TAG = "MyLog";private Intent intent;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);intent = new Intent(this, ForegroundService.class);startService(intent);}@Overrideprotected void onDestroy() {stopService(intent);super.onDestroy();}
}

运行,在Android 5.1.1下可以以此消除通知栏Service提示。

单进程广播守护

本方案主要是通过Service销毁发出广播通知BroadcastReceiver“复活”自己实现进程保活。
项目结构如下。

AndroidManifest.xml

<receiverandroid:name=".KeepAliveReceiver"android:enabled="true"android:exported="true" /><serviceandroid:name=".KeepAliveService"android:enabled="true"android:exported="true" />

KeepAliveReceiver :为啥用BroadcastReceiver 而不直接在 Service的onDestroy()方法中重启服务?因为这种方式启动的Service仍旧和原进程"在一起",会被一并杀死,而BroadcastReceiver 接到广播启动的则与原进程有隔离性,可以存活。

package com.zyc.demo;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;public class KeepAliveReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Log.d(MainActivity.LOG_TAG, "收到保活广播");if (!MainActivity.isServiceRunning(context.getApplicationContext(), KeepAliveService.class)) {Log.d(MainActivity.LOG_TAG, "检测到服务未在运行,启动服务");Intent serviceIntent = new Intent(context, KeepAliveService.class);context.startService(serviceIntent);} else {Log.d(MainActivity.LOG_TAG, "检测到服务正在运行,无需再次启动");}}}

KeepAliveService :该Service会在创建后会对变量count 执行 +1/秒 定时任务,在服务终止/创建时会保存/读取count ,以保证count 的状态不会丢失。保活手段体现在:

  • onStartCommand()返回 START_STICKY,Service所在进程被杀死后系统会尝试再次启动,但具体启动时机由系统决定。
  • onDestroy()方法发送出Broadcast,等KeepAliveReceiver 收到后来启动自己。
package com.zyc.demo;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;import java.util.Timer;
import java.util.TimerTask;public class KeepAliveService extends Service {private Timer timer;private TimerTask timerTask;public static int count;@Overridepublic void onCreate() {Log.d(MainActivity.LOG_TAG, "服务创建了");int save = SharedPreferenceTool.getInstance(getApplicationContext()).getInt("count", -1);if (save == -1) {this.count = 0;Log.d(MainActivity.LOG_TAG, "count首次启动,从0开始计数");} else {this.count = save;Log.d(MainActivity.LOG_TAG, "count从上次保存的 " + save + " 开始计数");}}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {startTask();Log.d(MainActivity.LOG_TAG, "服务启动了");return START_STICKY;}/*** 开启定时任务,count每秒+1*/private void startTask() {timer = new Timer();timerTask = new TimerTask() {@Overridepublic void run() {Log.d(MainActivity.LOG_TAG, "服务运行中,count: " + count);count++;}};timer.schedule(timerTask, 0, 1000);}/*** 结束定时任务*/private void stopTask() {if (timer != null) {timer.cancel();timer = null;}if (timerTask != null) {timerTask.cancel();timerTask = null;}count = 0;}@Overridepublic void onDestroy() {stopTask();Log.d(MainActivity.LOG_TAG, "服务停止了");Intent intent = new Intent(this, KeepAliveReceiver.class);sendBroadcast(intent);Log.d(MainActivity.LOG_TAG, "发送保活广播");}@Overridepublic IBinder onBind(Intent intent) {return null;}
}

MainActivity :主要用于开启Service,并提供了判断指定Service是否运行的工具方法。count放在MainActivity 而不是Service是因为Service的onDestroy()在异常结束时不一定被调用。

package com.zyc.demo;import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {public static String LOG_TAG = "MyLog";private Intent serviceIntent;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (!isServiceRunning(getApplicationContext(), KeepAliveService.class)) {Log.d(LOG_TAG, "检测到服务未在运行,启动服务");serviceIntent = new Intent(this, KeepAliveService.class);startService(serviceIntent);} else {Log.d(LOG_TAG, "检测到服务正在运行,无需再次启动");}}/*** 判断某个Service是否在运行* @param context* @param serviceClass 需要查看的Service的Class* @return*/public static boolean isServiceRunning(Context context, Class serviceClass) {ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);for (ActivityManager.RunningServiceInfo runningServiceInfo : activityManager.getRunningServices(Integer.MAX_VALUE)) {if (serviceClass.getName().equals(runningServiceInfo.service.getClassName())) {return true;}}return false;}@Overrideprotected void onDestroy() {if (serviceIntent != null) {stopService(serviceIntent);}SharedPreferenceTool.getInstance(getApplicationContext()).putInt("count", KeepAliveService.count);Log.d(LOG_TAG, "count保存了");super.onDestroy();}
}

SharedPreferenceTool :一个用于保存例中count的SharedPreference工具类。切记用context.getApplicationContext()传入Context ,因为这里的instance是一个static且强引用的,如果随处使用XXXActivity.this传入Context ,这些Activity会随着instance的全局存在而难以回收,最终将造成内存泄漏。

package com.zyc.demo;import android.content.Context;
import android.content.SharedPreferences;public class SharedPreferenceTool {public static SharedPreferenceTool instance;private SharedPreferences sharedPreferences;private SharedPreferences.Editor editor;private SharedPreferenceTool(Context context) {sharedPreferences = context.getSharedPreferences("preferences", Context.MODE_PRIVATE);editor = sharedPreferences.edit();}public static SharedPreferenceTool getInstance(Context context) {if (instance == null) {synchronized (SharedPreferenceTool.class) {if (instance == null) {// 使用双重同步锁instance = new SharedPreferenceTool(context);}}}return instance;}/*** 往SharedPreference存放整型数据*/public void putInt(String key, int value) {editor.putInt(key, value);editor.commit();}/*** 从SharedPreference取出整型数据*/public int getInt(String key, int defaultValue) {return sharedPreferences.getInt(key, defaultValue);}
}

运行,虽然有时Service重启并不及时,但在Android 5.1.1总体而言count被很大程度保留在后台运转。但此方法无法对抗 设置–应用程序–强制停止,也无法在Android 10下拉起服务。

AIDL双进程守护

本方案主要是两个(不同进程)Service通过AIDL“互相拉起”用于实现进程保活。

项目结构如下。

AndroidManifest.xml

<!-- 进程名remote,冒号开头表示私有进程 -->
<serviceandroid:name=".RemoteService"android:enabled="true"android:exported="true"android:process=":remote" /><serviceandroid:name=".LocalService"android:enabled="true"android:exported="true" />

AIDL

package com.zyc.aidlkeepalive;interface IKeepAliveAidlInterface {}

LocalService:与进程RemoteService相互绑定,当RemoteService被终止导致onServiceDisconnected()方法被触发时再次将其启动。

package com.zyc.aidlkeepalive;import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;import java.util.Timer;
import java.util.TimerTask;public class LocalService extends Service {private IBinder binder;private ServiceConnection serviceConnection;private Timer timer;private TimerTask timerTask;private int count = 0;public LocalService() {}@Overridepublic void onCreate() {super.onCreate();Log.d(MainActivity.TAG, "LocalService onCreate");binder = new LocalServiceBinder();serviceConnection = new LocalServiceConnection();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(MainActivity.TAG, "LocalService onStartCommand");startTask();wakeService();return START_STICKY;}@Overridepublic IBinder onBind(Intent intent) {Log.d(MainActivity.TAG, "LocalService onBind");return binder;}@Overridepublic void onDestroy() {stopTask();stopSelf();unbindService(serviceConnection);Log.d(MainActivity.TAG, "LocalService onDestroy");super.onDestroy();}private void wakeService() {if (!MainActivity.isServiceRunning(this, RemoteService.class)) {startService(new Intent(this, RemoteService.class));}bindService(new Intent(this, RemoteService.class), serviceConnection, Context.BIND_IMPORTANT);}class LocalServiceConnection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.d(MainActivity.TAG, "触发LocalService onServiceConnected");}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.d(MainActivity.TAG, "触发LocalService onServiceDisconnected");wakeService();}}class LocalServiceBinder extends IKeepAliveAidlInterface.Stub {}/*** 开启定时任务,count每秒+1*/private void startTask() {count = 0;timer = new Timer();timerTask = new TimerTask() {@Overridepublic void run() {Log.d(MainActivity.TAG, "服务运行中,count: " + count);count++;}};timer.schedule(timerTask, 0, 1000);}/*** 结束定时任务*/private void stopTask() {if (timer != null) {timer.cancel();timer = null;}if (timerTask != null) {timerTask.cancel();timerTask = null;}count = 0;}
}

RemoteService:与LocalService作用基本相同,不多赘述。

package com.zyc.aidlkeepalive;import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;import java.util.Timer;
import java.util.TimerTask;public class RemoteService extends Service {private IBinder binder;private ServiceConnection serviceConnection;private Timer timer;private TimerTask timerTask;private int count = 0;public RemoteService() {}@Overridepublic void onCreate() {super.onCreate();Log.d(MainActivity.TAG, "RemoteService onCreate");binder = new RemoteServiceBinder();serviceConnection = new RemoteServiceConnection();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(MainActivity.TAG, "RemoteService onStartCommand");startTask();wakeService();return START_STICKY;}@Overridepublic IBinder onBind(Intent intent) {Log.d(MainActivity.TAG, "RemoteService onBind");return binder;}@Overridepublic void onDestroy() {stopTask();stopSelf();unbindService(serviceConnection);Log.d(MainActivity.TAG, "RemoteService onDestroy");super.onDestroy();}class RemoteServiceConnection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.d(MainActivity.TAG, "触发RemoteService onServiceConnected");}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.d(MainActivity.TAG, "触发RemoteService onServiceDisconnected");wakeService();}}private void wakeService() {if (!MainActivity.isServiceRunning(this, LocalService.class)) {startService(new Intent(this, LocalService.class));}bindService(new Intent(this, LocalService.class), serviceConnection, Context.BIND_IMPORTANT);}class RemoteServiceBinder extends IKeepAliveAidlInterface.Stub {}/*** 开启定时任务,count每秒+1*/private void startTask() {count = 0;timer = new Timer();timerTask = new TimerTask() {@Overridepublic void run() {Log.d(MainActivity.TAG, "服务运行中,count: " + count);count++;}};timer.schedule(timerTask, 0, 1000);}/*** 结束定时任务*/private void stopTask() {if (timer != null) {timer.cancel();timer = null;}if (timerTask != null) {timerTask.cancel();timerTask = null;}count = 0;}
}

MainActivity

package com.zyc.aidlkeepalive;import androidx.appcompat.app.AppCompatActivity;import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;public class MainActivity extends AppCompatActivity {public static final String TAG = "MyLog";private Intent intentLocal;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);intentLocal = new Intent(this, LocalService.class);startService(intentLocal);}public static boolean isServiceRunning(Context context, Class serviceClass) {ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);for (ActivityManager.RunningServiceInfo runningServiceInfo : activityManager.getRunningServices(Integer.MAX_VALUE)) {if (runningServiceInfo.service.getClassName().equals(serviceClass.getName())) {return true;}}return false;}
}

运行,在Android 5.1.1两个进程可以在一定程度上相互拉起(不是100%),但无法抵抗应用–强制终止。

NDK双进程守护

由于NDK进程优先级非常高,所以我们可以把上面的Java双进程守护放到NDK实现。本方案主要是使用C++ fork()一个子进程来保护APP进程,两者通过socket连接,一旦APP进程被杀死,socket连接断开,守护进程就知道该启动APP进程了。PS:采用socket方案要比轮询更节省资源。

项目结构如下。

AndroidManifest.xml:

<serviceandroid:name=".WorkService"android:enabled="true"android:exported="true">
</service>

CMakeLists.txt:

cmake_minimum_required(VERSION 3.4.1)add_library(native-libSHAREDnative-lib.cpp)find_library(log-liblog)target_link_libraries(native-lib${log-lib})

Guard:声明JNI方法。

package com.zyc.doubleguard;public class Guard {static {System.loadLibrary("native-lib");}public native void create(String userId);public native void connect();
}

WorkService:模拟APP要被守护的工作服务,不停打印Log。

package com.zyc.doubleguard;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.util.Log;import java.util.Timer;
import java.util.TimerTask;public class WorkService extends Service {private static int count = 0;private Guard guard;private Timer timer;private TimerTask timerTask;public WorkService() {}@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();Log.d("MyLog", "服务创建");timer = new Timer();timerTask = new TimerTask() {@Overridepublic void run() {Log.i("MyLog", "服务进行中 count=" + count);count++;}};guard = new Guard();guard.create(String.valueOf(Process.myUid()));guard.connect();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d("MyLog", "服务启动");startWork();return START_STICKY;}@Overridepublic void onDestroy() {stopWork();Log.d("MyLog", "服务被销毁");super.onDestroy();}private void startWork() {timer.schedule(timerTask, 0, 3000);}private void stopWork() {if (timerTask != null) {timerTask.cancel();timerTask = null;}if (timer != null) {timer.cancel();timer = null;}}
}

MainActivity:只用来启动服务,不多赘述。

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);intent = new Intent(this, WorkService.class);startService(intent);
}

native-lib.cpp:提供了开启子进程、子进程开启socket服务端、客户端连接socket方法。

#include <jni.h>
#include <string>
#include <filesystem>
#include <android/log.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <arpa/inet.h>#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "MyLog", __VA_ARGS__)void guard_start_work();
bool guard_create();
void guard_read_msg();const char *userId;/*** 用于创建一个守护进程*/
extern "C"
JNIEXPORT void JNICALL
Java_com_zyc_doubleguard_Guard_create(JNIEnv *env, jobject thiz, jstring user_id) {userId = env->GetStringUTFChars(user_id, 0);//开进程pid_t pid = fork();if (pid < 0) {LOGE("Guard开进程失败");} else if (pid == 0) {//子进程guard_start_work();} else if (pid > 0) {//父进程}env->ReleaseStringUTFChars(user_id, userId);
}/*** 守护进程 - 开始*/
void guard_start_work() {if (guard_create()) {guard_read_msg();}
}const char *PATH = "/data/data/com.zyc.doubleguard/guard.socket";
int server_lfd = -1;
int server_cfd = -1;/*** 守护进程 - 创建socket*/
bool guard_create() {server_lfd = socket(AF_LOCAL, SOCK_STREAM, 0);if (server_lfd < 0) {LOGE("Guard socket 初始化错误");return false;}unlink(PATH);//把之前连接的服务端清空struct sockaddr_un addr;bzero(&addr, sizeof(sockaddr_un));addr.sun_family = AF_LOCAL;strcpy(addr.sun_path, PATH);int bind_res = bind(server_lfd, (const sockaddr *) &addr, sizeof(sockaddr_un));if (bind_res < 0) {LOGE("Guard bind错误");return false;}listen(server_lfd, 5);//可以守护5个appLOGE("Guard 开始listen");while (true) {server_cfd = accept(server_lfd, NULL, NULL);if (server_cfd < 0) {if (errno == EINTR) {//client连接失败,重连continue;} else {LOGE("Guard 读取错误");return 0;}} else {LOGE("进程 %d 连接上了 Guard", server_cfd);break;}}return true;
}/*** 守护进程 - 阻塞读取*/
void guard_read_msg() {LOGE("guard_read_msg");fd_set set;struct timeval timeout = {3, 0};//3秒超时while (true) {FD_ZERO(&set);FD_SET(server_cfd, &set);int select_res = select(server_cfd + 1, &set, NULL, NULL, &timeout);LOGE("select_res:%d", select_res);if (select_res > 0) {if (FD_ISSET(server_cfd, &set)) {//保证是指定apk的连接LOGE("userId: %d 断开", userId);char temp[256] = {0};//read是阻塞的,客户端断开之后才会往后执行int res = read(server_cfd, temp, sizeof(temp));LOGE("准备启动服务");//执行启动服务execlp("am", "am", "startservice", "--user", userId,"com.zyc.doubleguard/com.zyc.doubleguard.WorkService", (char *) NULL);LOGE("启动服务完成");break;}}}
}/*** app连接守护进程*/
extern "C"
JNIEXPORT void JNICALL
Java_com_zyc_doubleguard_Guard_connect(JNIEnv *env, jobject thiz) {LOGE("app准备连接守护进程");int client_cfd;struct sockaddr_un addr;while (true) {client_cfd = socket(AF_LOCAL, SOCK_STREAM, 0);if (client_cfd < 0) {LOGE("app连接守护进程启动失败");return;}bzero(&addr, sizeof(sockaddr_un));addr.sun_family = AF_LOCAL;strcpy(addr.sun_path, PATH);int connect_res = connect(client_cfd, (const sockaddr *) &addr, sizeof(sockaddr_un));if (connect_res < 0) {//连不上就关闭后睡1秒重连LOGE("app连接守护进程失败");close(client_cfd);sleep(1);} else {LOGE("app连接守护进程成功");break;}}
}

运行,发现这一方案看似高大上,但是在诸多国产ROM机型/模拟器上都无法正常运作,不知道是笔者代码有误还是这一方案已经被封杀得很彻底,要么无法fork()出子进程,要么子进程很快变僵尸进程,要么execlp()无法启动应用/服务…

Android进程保活主流方案相关推荐

  1. Android进程保活方案

    自己曾经也在这个问题上伤过脑经,前几日刚好有一个北京的哥们在QQ说在做IM类的项目,问我进程保活如何处理比较恰当,决定去总结一下,网上搜索一下进程常驻的方案好多好多,但是很多的方案都是不靠谱的或者不是 ...

  2. Android 进程保活方案

    前言 Android 系统为了保持系统运行流畅,在内存吃紧的情况下,会将一些进程给杀掉,以释放一部分内存.然而,对于一些(如:QQ.微信等)比较重要的.我们希望能及时收到消息的App,需要保持进程持续 ...

  3. Android进程保活方案的几种方案

    自己曾经也在这个问题上伤过脑经,前几日刚好有一个北京的哥们在QQ说在做IM类的项目,问我进程保活如何处理比较恰当,决定去总结一下,网上搜索一下进程常驻的方案好多好多,但是很多的方案都是不靠谱的或者不是 ...

  4. 什么是Android进程(app)保活、进程保活的方案

    想了解什么是Android 进程.Android 进程的生命周期.Android 进程回收策略 可参照地址什么是Android 进程.Android 进程的生命周期.Android 进程回收策略_lm ...

  5. Android进程保活的一般套路

    自己曾经也在这个问题上伤过脑经,前几日刚好有一个北京的哥们在QQ说在做IM类的项目,问我进程保活如何处理比较恰当,决定去总结一下,网上搜索一下进程常驻的方案好多好多,但是很多的方案都是不靠谱的或者不是 ...

  6. android 进程保活6.0_Android 保活从入门到放弃:乖乖引导用户加白名单吧(附7大机型加白示例)...

    1.引言 IM在Android上的保活问题经常在即时通讯网的论坛和技术群里被讨论,自从Android 8.0后系统大大降低了后台运行应用的保活容忍度(详见<Android P正式版即将到来:后台 ...

  7. 关于 Android 进程保活,你所需要知道的一切

    早前,我在知乎上回答了这样一个问题:怎么让 Android 程序一直后台运行,像 QQ 一样不被杀死?.关于 Android 平台的进程保活这一块,想必是所有 Android 开发者瞩目的内容之一.你 ...

  8. Android 系统(265)----Android进程保活全攻略(上)

    Android进程保活全攻略(上) 对于每个公司的APP来说,当然都希望自己APP的进程尽量的不被杀死,于是乎,就有了一些列进程保活的方法出现,网上也有很多关于这类的文章,但网上很多资料往往只告诉了思 ...

  9. Android 系统(146)----Android进程保活招数概览

    Android进程保活招数概览 Android中的进程保活应该分为两个方面: 提高进程的优先级,减少被系统杀死的可能性 在进程已经被杀死的情况下,通过一些手段来重新启动应用进程 本文针对这两方面来进程 ...

最新文章

  1. Mongodb 添加删除分片与非分片表维护
  2. djano 模型查询
  3. 透明的WinForm窗体
  4. 用Python操作MySQL(pymysql)
  5. 从著名的list_head看linux内核中OO 从Unix分层内核栈以及中断处理看Linux内核的另类
  6. EndNote编辑毕业论文格式
  7. android 打开文件管理器选择文件
  8. 面向未来的大数据核心技术都有什么?
  9. antd tooltip 修改样式
  10. 中年男人,你如何自我救赎
  11. 几何画板用迭代法作图的技巧
  12. 浅谈Redis数据类型
  13. vue实现调查问卷一页一题,上一题下一题形式
  14. python反距离权重法_使用Python进行反距离加权(IDW)插值
  15. 亦师亦友——忆我在北邮四年中的几位老师(全)
  16. instagram图片大小_如何上传最好看的Instagram图片
  17. 【uniapp】悬浮球(floatball)全局组件——全局消息提醒功能
  18. SharePoint 16 规划大型文档存储库
  19. 算法高级(20)-集群容错算法
  20. CTF题库实验吧分道扬镳 (注意进入正确的流程,用最短的步骤走完迷宫。)

热门文章

  1. CentOS关机和重启命令
  2. 一线之间两重天--网络打印VS.共享打印 [转]
  3. 【flink 报错】Heartbeat of TaskManager is timed out
  4. 打造迪士尼梦幻体验的神奇魔法
  5. 中国人平均23岁初吻 消费文化与儒家文明碰撞
  6. 小程序直播,助力教育机构获客
  7. linux版本的RAR压缩软件
  8. 记录一个Xcode上传App报错的问题
  9. P2495 [SDOI2011]消耗战 虚树入门
  10. 判断点与圆的关系 点类与圆类 C++