MAX ?;罘治?br> 可以引入module,和使用打包好的AAR形式。使用方法:

注意:如果targetSdk為31 需要將module AndroidManifest.xml中AutoBootReceiver 增加android:exported="true"
首先講一下使用文件鎖來保活進程的思路,首先windows,linux是有一套文件的進程同步機制,一個進程可以給一個文件上鎖,在操作完成之后再進行解鎖,其他進程如果訪問這個文件,會先檢查一下這個文件是否有鎖,如果有鎖的話,代表別人正在操作,就會對文件延遲處理,那么當一個進程掛掉,他給文件加的鎖也自然會消失。

按照這個機制,可以想到進程A給文件A1加鎖,進程B給進程B1加鎖,然后讓進程A去阻塞讀取進程B1文件,進程B去阻塞讀取A1文件,那么如果有進程掛掉那么另一個進程就會讀取到被釋放的文件,通過這個方式監(jiān)聽到對方掛掉。
這個方法JAVA層是無法做到的,所以需要使用C,在native層來實現(xiàn)。
思路是這樣的,但是系統(tǒng)在殺死應用對應進程的時候時間非???,只有幾十毫秒時間,而發(fā)送一個Intent時間較長,它使用的是ActivityManagerService的一個代理類,通過Binder將Intent傳給系統(tǒng),執(zhí)行完時間在百毫秒,很難發(fā)出去,所以要做到用最短的時間來啟動。
我們發(fā)送intent的時候會初始化一個Parcel,通過binder transcate過去。
時間消耗在了創(chuàng)建Parcel上面,這里需要我們在進程開始的時候就要創(chuàng)建好Parcel,再拿到Binder直接發(fā)送出去,看看競品是怎么做的。
p = Parcel.obtain();
p.writeInterfaceToken("android.app.IActivityManager");
p.writeStrongBinder(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
p.writeInt(1);
}
entity.intent.writeToParcel(p, 0);
p.writeString(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
p.writeInt(0);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
p.writeString(entity.intent.getComponent().getPackageName());
}
p.writeInt(0);
try {
Class<?> cls = Class.forName("android.app.ActivityManagerNative");
Object invoke = cls.getMethod("getDefault", new Class[0]).invoke(cls, new Object[0]);
Field field = invoke.getClass().getDeclaredField("mRemote");
field.setAccessible(true);
binder = (IBinder) field.get(invoke);
field.setAccessible(false);
Logger.v(Logger.TAG, "initAmsBinder: mRemote == iBinder " + binder);
} catch (Throwable th) {
binderManager.thrown(th);
}
if (binder == null) {
try {
binder = (IBinder) Class.forName("android.os.ServiceManager").getMethod(
"getService", new Class[]{String.class}).invoke(null,
new Object[]{"activity"});
} catch (Throwable th) {
binderManager.thrown(th);
}
}
Pacel是在進程初始化的時候拿到的,而Binder是通過反射拿到的。
大概思路是這樣的,當然在實現(xiàn)中還會有很多問題,比如:
1.binder transcate 無法啟動Service,需要在廣播中去啟動,還需要創(chuàng)建一個parcel來啟動廣播,使用廣播來拉起進程
2.在Native層中直接傳遞parcel,會導致監(jiān)聽不到進程被殺;改成傳輸u8數(shù)據(jù)解決了
現(xiàn)在我們來從頭梳理一下,這個項目到底在Application中都做了什么?
在DaemonHolder中attach方法做了哪些事?
public void attach(Context base, Application app) {
app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(final Activity activity, Bundle savedInstanceState) {
Logger.v(Logger.TAG, String.format("====> [%s] created", activity.getLocalClassName()));
ServiceHolder.getInstance().bindService(activity, DaemonService.class,
new ServiceHolder.OnServiceConnectionListener() {
@Override
public void onServiceConnection(ServiceConnection connection, boolean isConnected) {
if (isConnected) {
connCache.put(activity, connection);
}
}
});
}
..........
}
可以看到監(jiān)聽了ActivityLifecycleCallbacks,在onActivityCreated通過BindService方式去啟動了DaemonService。
public class DaemonService extends DaemonBaseService {
@Override
public IBinder onBind(Intent intent) {
return super.onBind(intent);
}
@Override
public void onCreate() {
try {
ContextCompat.startForegroundService(this,
new Intent().setClassName(getPackageName(), NotifyResidentService.class.getName()));
} catch (Throwable th) {
Logger.e(Logger.TAG, "failed to start foreground service: " + th.getMessage());
}
Intent intent2 = new Intent();
intent2.setClassName(getPackageName(), AssistService1.class.getName());
startService(intent2);
Intent intent3 = new Intent();
intent3.setClassName(getPackageName(), AssistService2.class.getName());
startService(intent3);
super.onCreate();
}
}
DaemonService 中啟動了前臺通知NotifyResidentService,和AssistService1,AssistService2。
NotifyResidentService是一個前臺通知Service,而AssistService1,AssistService2就是在不同進程上的常規(guī)Service,這里不過多介紹。
再接著attach方法中往下看
JavaDaemon.getInstance().fire(
base,
new Intent(base, DaemonService.class),
new Intent(base, DaemonReceiver.class),
new Intent(base, DaemonInstrumentation.class)
);
接著往下分析JavaDaemon中都干了什么,捋下來發(fā)現(xiàn)創(chuàng)建了三空文件,
private void fire(Context context, DaemonEnv env, String[] strArr) {
Logger.i(Logger.TAG, "############################################## !!! fire(): " +
"env=" + env + ", strArr=" + Arrays.toString(strArr));
boolean z;
String processName = Utils.getProcessName();
Logger.e(Logger.TAG, "processName: " + processName);
if (processName.startsWith(context.getPackageName()) && processName.contains(COLON_SEPARATOR)) {
String substring = processName.substring(processName.lastIndexOf(COLON_SEPARATOR) + 1);
List<String> list = new ArrayList();
if (strArr != null) {
z = false;
for (String str : strArr) {
if (str.equals(substring)) {
z = true;
} else {
list.add(str);
}
}
} else {
z = false;
}
if (z) {
Logger.v(Logger.TAG, "app lock file start: " + substring);
NativeKeepAlive.lockFile(context.getFilesDir() + "/" + substring + "_d");
Logger.v(Logger.TAG, "app lock file finish");
String[] strArr2 = new String[list.size()];
for (int i = 0; i < strArr2.length; i++) {
strArr2[i] = context.getFilesDir() + "/" + list.get(i) + "_d";
Logger.e(Logger.TAG, "processName strarr2: " + strArr2[i]);
}
futureScheduler.scheduleFuture(new AppProcessRunnable(env, strArr2, "daemon"), 0);
}
} else if (processName.equals(context.getPackageName())) {
ContextCompat.startForegroundService(context, new Intent(context, DaemonService.class));
}
}
除此之外還有一段核心代碼:
@Override
public void run() {
DaemonEntity entity = new DaemonEntity();
entity.str = str;
entity.strArr = strArr;
entity.intent = env.intent;
entity.intent2 = env.intent2;
entity.intent3 = env.intent3;
List<String> list = new ArrayList();
list.add("export CLASSPATH=$CLASSPATH:" + env.publicSourceDir);
if (env.nativeLibraryDir.contains("arm64")) {
list.add("export _LD_LIBRARY_PATH=/system/lib64/:/vendor/lib64/:" + env.nativeLibraryDir);
list.add("export LD_LIBRARY_PATH=/system/lib64/:/vendor/lib64/:" + env.nativeLibraryDir);
list.add(String.format("%s / %s %s --application --nice-name=%s &",
new Object[]{new File("/system/bin/app_process").exists() ?
"app_process" : "app_process", DaemonMain.class.getName(),
entity.toString(), str}));
} else {
list.add("export _LD_LIBRARY_PATH=/system/lib/:/vendor/lib/:" + env.nativeLibraryDir);
list.add("export LD_LIBRARY_PATH=/system/lib/:/vendor/lib/:" + env.nativeLibraryDir);
list.add(String.format("%s / %s %s --application --nice-name=%s &",
new Object[]{new File("/system/bin/app_process32").exists() ?
"app_process32" : "app_process", DaemonMain.class.getName(),
entity.toString(), str}));
}
Logger.i(Logger.TAG, "cmds: " + list);
File file = new File("/");
String[] strArr = new String[list.size()];
for (int i = 0; i < strArr.length; i++) {
strArr[i] = list.get(i);
}
ShellExecutor.execute(file, null, strArr);
}
DaemonEntity 儲存了context.getApplicationInfo()中的信息,publicSourceDir,nativeLibraryDir,都是由ApplicationInfo對象獲取的。
然后通過ProcessBuilder對象以命令行的形式,來執(zhí)行l(wèi)ist中的內(nèi)容。android 底層是linux,也就是在Linux中去執(zhí)行命令。
我們查看一下list中的數(shù)據(jù):
export CLASSPATH=$CLASSPATH:/data/app/com.daemon.keepalive2-yTOyxGcJe4jXr41qdEREFg==/base.apk,
export _LD_LIBRARY_PATH=/system/lib64/:/vendor/lib64/:/data/app/com.daemon.keepalive2-yTOyxGcJe4jXr41qdEREFg==/lib/arm64,
export LD_LIBRARY_PATH=/system/lib64/:/vendor/lib64/:/data/app/com.daemon.keepalive2-yTOyxGcJe4jXr41qdEREFg==/lib/arm64,
app_process / com.keepalive.daemon.core.DaemonMain AgAAADIAAAAvAGQAYQB0AGEALwB1AHMAZQByAC8AMAAvAGMAbwBtAC4AZABhAGUAbQBvAG4ALgBrAGUAZQBwAGEAbABpAHYAZQAyAC8AZgBpAGwAZQBzAC8AYQBzAHMAaQBzAHQAMQBfAGQAAAAAADIAAAAvAGQAYQB0AGEALwB1AHMAZQByAC8AMAAvAGMAbwBtAC4AZABhAGUAbQBvAG4ALgBrAGUAZQBwAGEAbABpAHYAZQAyAC8AZgBpAGwAZQBzAC8AYQBzAHMAaQBzAHQAMgBfAGQAAAAAAAYAAABkAGEAZQBtAG8AbgAAAAAAAQAAAP////8AAAAA//////////8AAAAA/////xUAAABjAG8AbQAuAGQAYQBlAG0AbwBuAC4AawBlAGUAcABhAGwAaQB2AGUAMgAAADEAAABjAG8AbQAuAGsAZQBlAHAAYQBsAGkAdgBlAC4AZABhAGUAbQBvAG4ALgBjAG8AcgBlAC4AYwBvAG0AcABvAG4AZQBuAHQALgBEAGEAZQBtAG8AbgBTAGUAcgB2AGkAYwBlAAAAAAAAAAAAAAAAAAAAAAAAAP7/////////AAAAAAAAAAAAAAAAAAAAAAEAAAD/////AAAAAP//////////AAAAAP////8VAAAAYwBvAG0ALgBkAGEAZQBtAG8AbgAuAGsAZQBlAHAAYQBsAGkAdgBlADIAAAAyAAAAYwBvAG0ALgBrAGUAZQBwAGEAbABpAHYAZQAuAGQAYQBlAG0AbwBuAC4AYwBvAHIAZQAuAGMAbwBtAHAAbwBuAGUAbgB0AC4ARABhAGUAbQBvAG4AUgBlAGMAZQBpAHYAZQByAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v////////8AAAAAAAAAAAAAAAAAAAAAAQAAAP////8AAAAA//////////8AAAAA/////xUAAABjAG8AbQAuAGQAYQBlAG0AbwBuAC4AawBlAGUAcABhAGwAaQB2AGUAMgAAADkAAABjAG8AbQAuAGsAZQBlAHAAYQBsAGkAdgBlAC4AZABhAGUAbQBvAG4ALgBjAG8AcgBlAC4AYwBvAG0AcABvAG4AZQBuAHQALgBEAGEAZQBtAG8AbgBJAG4AcwB0AHIAdQBtAGUAbgB0AGEAdABpAG8AbgAAAAAAAAAAAAAAAAAAAAAAAAD+/////////wAAAAAAAAAAAAAAAAAAAAA= --application --nice-name=daemon &]
那這些數(shù)據(jù)到底是什么意思?前三條好理解,就是配置了linux 環(huán)境變量,對應的數(shù)據(jù)分別是publicSourceDir,nativeLibraryDir,nativeLibraryDir,那第四條實在是看不懂,讓我大膽猜測一下,通過下文代碼得知,會不會是將DaemonMain類在底層去運行呢,而DaemonMain是和進程每個進程單獨持有的,也就是說每個進程都聲明到了底層,然后具體類通過這個類去實現(xiàn)。
有了猜測去實驗,發(fā)現(xiàn)果然,通過debug方式發(fā)現(xiàn)代碼無法走到DaemonMain方法中,而查看log發(fā)現(xiàn)DaemonMain中是運行的?。?/p>
那DaemonMain就是關鍵了啊,那這個保活方案和上文中我們提到的文件鎖?;钤硎遣皇且恢履?,繼續(xù)往下看:
public class DaemonMain {
private IBinderManager binderManager = new IBinderManager();
public DaemonEntity entity;
private Parcel p;
private Parcel p2;
private Parcel p3;
private IBinder binder;
private static volatile FutureScheduler futureScheduler;
private DaemonMain(DaemonEntity entity) {
this.entity = entity;
}
public static void main(String[] strArr) {
if (futureScheduler == null) {
synchronized (DaemonMain.class) {
if (futureScheduler == null) {
futureScheduler = new SingleThreadFutureScheduler(
"daemonmain-holder",
true
);
}
}
}
DaemonEntity entity = DaemonEntity.create(strArr[0]);
if (entity != null) {
new DaemonMain(entity).execute();
}
Logger.e(Logger.TAG, "DaemonMain.main Process.killProcess" + Process.myPid());
Process.killProcess(Process.myPid());
}
private void execute() {
try {
initAmsBinder();
assembleParcel();
NativeKeepAlive.nativeSetSid();
try {
Logger.v(Logger.TAG, "setArgV0: " + entity.str);
Process.class.getMethod("setArgV0", new Class[]{String.class}).invoke(null,
new Object[]{entity.str});
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 1; i < entity.strArr.length; i++) {
futureScheduler.scheduleFuture(new DaemonRunnable(this, i), 0);
}
Logger.v(Logger.TAG, "[" + entity.str + "] wait file lock start: " + entity.strArr[0]);
NativeKeepAlive.waitFileLock(entity.strArr[0]);
Logger.v(Logger.TAG, "[" + entity.str + "] wait file lock finish");
startService();
broadcastIntent();
startInstrumentation();
Logger.v(Logger.TAG, "[" + entity.str + "] start android finish");
} catch (Throwable th) {
binderManager.thrown(th);
}
}
public void startInstrumentation() {
Logger.i(Logger.TAG, "call startInstrumentation(): " + p3);
if (p3 != null) {
try {
binder.transact(binderManager.startInstrumentation(), p3, null, 1);
} catch (Throwable th) {
binderManager.thrown(th);
}
}
}
public void broadcastIntent() {
Logger.i(Logger.TAG, "call broadcastIntent(): " + p2);
if (p2 != null) {
try {
binder.transact(binderManager.broadcastIntent(), p2, null, 1);
} catch (Throwable th) {
binderManager.thrown(th);
}
}
}
public void startService() {
Logger.i(Logger.TAG, "call startService(): " + p);
if (p != null) {
try {
binder.transact(binderManager.startService(), p, null, 1);
} catch (Throwable th) {
binderManager.thrown(th);
}
}
}
private void assembleParcel() {
assembleServiceParcel();
assembleBroadcastParcel();
assembleInstrumentationParcel();
}
private void assembleServiceParcel() {
p = Parcel.obtain();
p.writeInterfaceToken("android.app.IActivityManager");
p.writeStrongBinder(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
p.writeInt(1);
}
entity.intent.writeToParcel(p, 0);
p.writeString(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
p.writeInt(0);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
p.writeString(entity.intent.getComponent().getPackageName());
}
p.writeInt(0);
}
@SuppressLint("WrongConstant")
private void assembleBroadcastParcel() {
p2 = Parcel.obtain();
p2.writeInterfaceToken("android.app.IActivityManager");
p2.writeStrongBinder(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
p2.writeInt(1);
}
entity.intent2.setFlags(32);
entity.intent2.writeToParcel(p2, 0);
p2.writeString(null);
p2.writeStrongBinder(null);
p2.writeInt(-1);
p2.writeString(null);
p2.writeInt(0);
p2.writeStringArray(null);
p2.writeInt(-1);
p2.writeInt(0);
p2.writeInt(0);
p2.writeInt(0);
p2.writeInt(0);
}
private void assembleInstrumentationParcel() {
p3 = Parcel.obtain();
p3.writeInterfaceToken("android.app.IActivityManager");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
p3.writeInt(1);
}
entity.intent3.getComponent().writeToParcel(p3, 0);
p3.writeString(null);
p3.writeInt(0);
p3.writeInt(0);
p3.writeStrongBinder(null);
p3.writeStrongBinder(null);
p3.writeInt(0);
p3.writeString(null);
}
private void initAmsBinder() {
try {
Class<?> cls = Class.forName("android.app.ActivityManagerNative");
Object invoke = cls.getMethod("getDefault", new Class[0]).invoke(cls, new Object[0]);
Field field = invoke.getClass().getDeclaredField("mRemote");
field.setAccessible(true);
binder = (IBinder) field.get(invoke);
field.setAccessible(false);
Logger.v(Logger.TAG, "initAmsBinder: mRemote == iBinder " + binder);
} catch (Throwable th) {
binderManager.thrown(th);
}
if (binder == null) {
try {
binder = (IBinder) Class.forName("android.os.ServiceManager").getMethod(
"getService", new Class[]{String.class}).invoke(null,
new Object[]{"activity"});
} catch (Throwable th) {
binderManager.thrown(th);
}
}
}
class DaemonRunnable implements Runnable {
private WeakReference<DaemonMain> thiz;
private int index;
private DaemonRunnable(DaemonMain thiz, int index) {
this.thiz = new WeakReference<>(thiz);
this.index = index;
}
@Override
public void run() {
Logger.v(Logger.TAG, "[Thread] wait file lock start: " + index);
NativeKeepAlive.waitFileLock(thiz.get().entity.strArr[index]);
Logger.v(Logger.TAG, "[Thread] wait file lock finished");
thiz.get().startService();
thiz.get().broadcastIntent();
thiz.get().startInstrumentation();
Logger.v(Logger.TAG, "[Thread] start android finish");
}
}
}
大致一看發(fā)現(xiàn),果然通過反射的方式拿到了Binder,也初始化了三個Parcel,分別是服務,廣播,還有Instrumentation,乍一看果然和文件鎖的原理差不多啊,但是別輕易下結論,它是對文件進行鎖定操作了,但是目前局勢并不明朗。
這里Instrumentation 不做詳細詳解,可以去搜索相關源碼進行了解
http://developer.android.com/intl/zh-cn/reference/android/app/Instrumentation.html

大概意思是強大的跟蹤application及activity生命周期的功能,用于android 應用測試框架中,被做為基類使用。
可以看到在DaemonMain中聲明了Main方法,其中最顯眼的就是這個Process.killProcess(Process.myPid());這就令人奇怪了,為什么會手動結束進程呢,在結束之前又做了什么操作?我們一步一步分析。
execute方法中首先初始化Binder,再初始化了Parcel,緊接著調(diào)用Native方法中setId,調(diào)用setsid函數(shù)的進程成為新的會話的領頭進程,并與其父進程的會話組和進程組脫離
void keep_alive_set_sid(JNIEnv *env, jclass jclazz) {
setsid();
}
可以看見有一個waitFileLock 方法,這是個阻塞方法,也就是讀取文件狀態(tài)是否被鎖定,那么為什么在進程被殺死后還要
Process.killProcess(Process.myPid())呢,
原來Process.killProcess 最終是調(diào)用 linuxAPI kill() 發(fā)送 SIGKILL 信號,進行收到這個信息都會立即結束進程。
然而Android 下不同的是 ActivityManager 一直監(jiān)聽者進程狀態(tài)。如果發(fā)現(xiàn)進程被kill,會立即重啟進行,并重啟之前狀態(tài)對應的Activity、Service、ContentProvider等。
這就是為什么我們調(diào)用Process.killProcess后,發(fā)現(xiàn)程序是重啟了,而不是被kill了
也在最大程度保證了進程被重新啟動。