近期这段时间比较忙,这次有点时间就继续把这个问题写完。
通过上篇博客的记录,可以知道采用子进程的方式来做存在着一些问题。那如何来解决呢?既然主进程存在这样的问题,那是否可以考虑不使用主进程,而考虑其他和程序相关的进程,并且不会影响到主进程。这样自然就考虑到了service.
为了尽可能的减少用户感知的变化,采用如下的方式:新建一个service,这个service只是用来启动监听的子进程,当监听的子进程启动完成以后,关闭sercie。这样从用户的感知来看,程序原有的进程与服务没有什么变化。
这样处理后,今天的子进程就会被系统init进程接管。这样就存在一个问题,这个监听进程何时会被系统回收以及如何判断是否已经被系统回收掉而需要重新创建的问题。
由于监听子进程是一个sercie的子进程,因此其进程名与service的进程命相同,在代码中调用startService(intent)启动服务时,由于监听子进程的存在,service并不会再次执行oncreate(),再结合锁文件的情况来判断,因此不会出现创建多个监听子进程的情况。可以通过在主activity中的onstart()中通过startservice来进行子进程是否存在或者如果不存在就再次创建的处理。当然也可在其他的地方进行检查,比如定时调用的地方进行处理等等。。
通过这样的方式基本上可以实现通过子进程对程序进行卸载监听的目的。
下边是lib库的卸载监听子进程的代码,供大家参考:
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/inotify.h>
#include <android/log.h>
//清0宏
#define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)
static char c_TAG[] = "myunistall_init";
const char* datapath;
const char* apppath;
const char* httpuri;
const char* obserfile;
const char* lockfile;
int version = 0;
int deletedata = 0;
static jboolean b_IS_COPY = JNI_TRUE;
//LOG宏定义
#define LOG_INFO(tag, msg) __android_log_write(ANDROID_LOG_INFO, tag, msg)
#define LOG_DEBUG(tag, msg) __android_log_write(ANDROID_LOG_DEBUG, tag, msg)
#define LOG_WARN(tag, msg) __android_log_write(ANDROID_LOG_WARN, tag, msg)
#define LOG_ERROR(tag, msg) __android_log_write(ANDROID_LOG_ERROR, tag, msg)
int Java_com_uninstallcheck_UninstallCheck_init(JNIEnv* env,
jobject context, jstring AppPath, jstring DataPath, jstring HttpUri,
jstring ObserFile, jstring Lockfile, int VersionInt) {
int time = 0;
jstring tag = (*env)->NewStringUTF(env, c_TAG);
pid_t ppid = getpid();
//=======================进行版本的判断针对4.0以上机型=====================================================
// if (VersionInt >= 14) {
datapath = (*env)->GetStringUTFChars(env, DataPath, NULL);
obserfile = (*env)->GetStringUTFChars(env, ObserFile, NULL);
apppath = (*env)->GetStringUTFChars(env, AppPath, NULL);
httpuri = (*env)->GetStringUTFChars(env, HttpUri, NULL);
lockfile = (*env)->GetStringUTFChars(env, Lockfile, NULL);
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, datapath), &b_IS_COPY));
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, apppath), &b_IS_COPY));
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, httpuri), &b_IS_COPY));
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, obserfile), &b_IS_COPY));
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, lockfile), &b_IS_COPY));
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "init OK"), &b_IS_COPY));
version = VersionInt;
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "observed by child process"), &b_IS_COPY));
pid_t pid = fork();
if (pid < 0) {
exit(1);
} else if (pid == 0) {
FILE *p_LFile = fopen(lockfile, "r");
if (p_LFile == NULL) {
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "lockfile doesn't exist"), &b_IS_COPY));
p_LFile = fopen(lockfile, "w");
}else{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "lockfile exist"), &b_IS_COPY));
}
fclose(p_LFile);
// 创建锁文件,通过检测加锁状态来保证只有一个卸载监听进程
int lockFileDescriptor = open(lockfile, O_RDONLY);
if (lockFileDescriptor == -1) {
// //文件无法读取或不存在,如果存在则创建,如果存在则标识文件有锁,即有监听进程存在
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "can not read lockfile"), &b_IS_COPY));
deletedata = 1;
// lockFileDescriptor = open(lockfile, O_CREAT);
}
int lockRet = flock(lockFileDescriptor, LOCK_EX | LOCK_NB);
if (lockRet == -1) {
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "observed by another process 1"), &b_IS_COPY));
exit(0);
}
biaoji:
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "fork success!!!"), &b_IS_COPY));
//===========================================================================================================
// 若被监听文件不存在,创建文件
FILE *p_observedFile = fopen(obserfile, "r");
if (p_observedFile == NULL) {
p_observedFile = fopen(obserfile, "w");
}else{
if(deletedata == 1){
//执行了清理数据的操作
exit(0);
}
}
fclose(p_observedFile);
//======================================================================================================================
// 子进程注册"/data/data/pym.test.uninstalledobserver"目录监听器
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "data check!!!"), &b_IS_COPY));
int fileDescriptor = inotify_init();
if (fileDescriptor < 0) {
exit(1);
}
int watchDescriptor;
watchDescriptor = inotify_add_watch(fileDescriptor, datapath,
IN_DELETE);
if (watchDescriptor < 0) {
exit(1);
}
//分配缓存,以便读取event,缓存大小=一个struct inotify_event的大小,这样一次处理一个event
void *p_buf = malloc(sizeof(struct inotify_event));
if (p_buf == NULL) {
exit(1);
}
//开始监听
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "start read!!!"), &b_IS_COPY));
size_t readBytes = read(fileDescriptor, p_buf,
sizeof(struct inotify_event));
//read会阻塞进程,走到这里说明收到目录被删除的事件,注销监听器
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "DataPath delete event!!!"), &b_IS_COPY));
free(p_buf);
inotify_rm_watch(fileDescriptor, IN_DELETE);
//=====================================================================================================================
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "check obserfile!!!"), &b_IS_COPY));
while (time <= 15) {
//==============由于APK文件在进行升级和覆盖安装的时候会进行替换无法对这两种操作进行判断,改为标记文件的方式=============================
FILE *p_file = fopen(obserfile, "r");
if (p_file != NULL) {
fclose(p_file);
LOG_DEBUG( (*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "obserfile exist"), &b_IS_COPY));
sleep(1);
time = time + 1;
} else {
//目录不存在log
LOG_DEBUG( (*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "obserfile delete"), &b_IS_COPY));
//==============清空数据时会将标记文件删除,此时检查APK文件是否存在,如存在则不是删除,重建标记文件并跳出===============================
LOG_DEBUG( (*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "check apkfile"), &b_IS_COPY));
FILE *app_file = fopen(apppath, "r");
if (app_file != NULL) {
fclose(app_file);
LOG_DEBUG( (*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "apkfile exist"), &b_IS_COPY));
sleep(1);
time = time + 1;
//重建标记文件
if (time >= 12) {
FILE *observed_File = fopen(obserfile, "r");
if (observed_File == NULL) {
LOG_DEBUG(
(*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "create obserfile"), &b_IS_COPY));
observed_File = fopen(obserfile, "w");
}
fclose(p_observedFile);
}
} else {
//==================通过版本判断执行不同的am指令弹出卸载推荐网页=================================================================
time = 16;
if (version < 17) {
LOG_DEBUG(
(*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "am start1"), &b_IS_COPY));
//针对4.2以下版本的系统执行命令am start -a android.intent.action.VIEW -d https://www.google.com
execlp("am", "am", "start", "-a",
"android.intent.action.VIEW", "-d", httpuri,
(char *) NULL);
} else {
LOG_DEBUG(
(*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "am start2"), &b_IS_COPY));
//4.2以上的系统由于用户权限管理更严格,需要加上 --user 0
execlp("am", "am", "start", "--user", "0", "-a",
"android.intent.action.VIEW", "-d", httpuri,
(char *) NULL);
}
}
}
}
time = 0;
goto biaoji;
// exit(0);
} else {
//父进程直接退出,使子进程被init进程领养,以避免子进程僵死
}
// 释放string,避免内存泄漏
(*env)->ReleaseStringUTFChars(env, DataPath, datapath);
(*env)->ReleaseStringUTFChars(env, AppPath, apppath);
(*env)->ReleaseStringUTFChars(env, HttpUri, httpuri);
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "Release String !!"), &b_IS_COPY));
// exit(0);
return ppid;
}
int Java_com_uninstallcheck_UninstallCheck_check(JNIEnv* env,
jobject context, jstring Lockfile) {
const char* lockfile;
jstring tag = (*env)->NewStringUTF(env, c_TAG);
lockfile = (*env)->GetStringUTFChars(env, Lockfile, NULL);
FILE *p_LFile = fopen(lockfile, "r");
if (p_LFile == NULL) {
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "check lockfile doesn't exist"), &b_IS_COPY));
p_LFile = fopen(lockfile, "w");
return 0;
} else {
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "check lockfile exist"), &b_IS_COPY));
}
fclose(p_LFile);
// 创建锁文件,通过检测加锁状态来保证只有一个卸载监听进程
int lockFileDescriptor = open(lockfile, O_RDONLY);
int lockRet = flock(lockFileDescriptor, LOCK_EX | LOCK_NB);
if (lockRet == -1) {
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY),
(*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "check file locked"), &b_IS_COPY));
return 1;
}
flock(lockFileDescriptor, LOCK_UN | LOCK_NB);
return 0;
}
以下是用来启动子进程监听的service的代码,供大家参考:
import java.util.List;
import com..uninstallcheck.UninstallCheck;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class UninstallService extends Service{
String httpuri = "https://www.google.com";
String httpuri1 = "https://www.baidu.com";
String TAG = "UninstallService";
@Override
public IBinder onBind(Intent arg0) {
// TODO 自动生成的方法存根
Log.d(TAG, "ServiceDemo onBind");
return null;
}
@Override
public void onCreate() {
Log.d(TAG, "ServiceDemo onCreate");
Log.d(TAG,this.getPackageCodePath());
Log.d(TAG,this.getPackageName());
// new Thread() {
// public void run() {
// Ipid = UninstallCheck.StardAD(UninstallService.this, httpuri);
// }
// }.start();
MainActivity.Ipid = UninstallCheck.StardAD(UninstallService.this, httpuri);
Log.d("myunistall_init", "MainActivity.Ipid = " + MainActivity.Ipid);
super.onCreate();
}
@SuppressWarnings("deprecation")
@Override
public void onStart(Intent intent, int startId) {
Log.d(TAG, "ServiceDemo onStart");
super.onStart(intent, startId);
stopSelf();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "ServiceDemo onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d("myunistall_init", "UninstallService.ondestory");
// TODO 自动生成的方法存根
super.onDestroy();
Log.d("myunistall_init", "android.os.Process.myPid() = " + android.os.Process.myPid());
// Process.killProcess(MainActivity.Ipid);
}
}