最近做项目需要知道自己何时被卸载了,开始就想到监听程序被卸载的广播。但是这样并不行,想了想当程序被卸载时,首先系统会退出程序在内存的进程,进程都没有了,广播当然也收不到了。
网上搜索了下,发现几篇博客很有用。。。
项目中会用到一些jni文件,项目github地址:
download下来的项目有这几个重要的文件
MyActivity:
public class MyActivity extends Activity {
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initPackageRemovedListener();
}
private void initPackageRemovedListener() {
Intent intent = new Intent(this, SDCardListenSer.class);
startService(intent);
init();
}
private native void init();
static {
Log.d("onEvent", "load jni lib");
System.loadLibrary("hello-jni");
}
}
SDCardListenSer:
这就是监听文件状态的serveice类
注意/data/data/com.example.untitled 这个报名要换成自己的包名;
public class SDCardListenSer extends Service {
SDCardListener[] listenners;
@Override
public void onCreate() {
SDCardListener[] listenners = {new SDCardListener("/data/data/com.example.untitled", this),
new SDCardListener(Environment.getExternalStorageDirectory() + File.separator + "1.txt", this)};
this.listenners = listenners;
Log.i("onEvent", "=========onCreate============");
for (SDCardListener listener : listenners) {
listener.startWatching();
}
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "1.txt");
Log.i("onEvent", "dddddddddddddddddddddd nCreate============");
if (file.exists())
file.delete();
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy() {
for (SDCardListener listener : listenners) {
listener.stopWatching();
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
class SDCardListener extends FileObserver {
private String mPath;
private final Context mContext;
public SDCardListener(String parentpath, Context ctx) {
super(parentpath);
this.mPath = parentpath;
this.mContext = ctx;
}
@Override
public void onEvent(int event, String path) {
int action = event & FileObserver.ALL_EVENTS;
switch (action) {
case FileObserver.DELETE:
Intent intent = new Intent();
//exeShell("am start -a android.intent.action.VIEW -d http:aoi.androidesk.com");
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
Log.i("onEvent", "打包请关闭****DELETE 删除 " + mPath + File.separator + path);
//openBrowser();
break;
case FileObserver.MODIFY:
Log.i("onEvent", "打包请关闭****MODIFY 修改 " + mPath + File.separator + path);
break;
case FileObserver.CREATE:
Log.i("onEvent", "打包请关闭****CREATE 创建 " + mPath + File.separator + path);
break;
default:
break;
}
}
protected void openBrowser() {
Uri uri = Uri.parse("http://aoi.androidesk.com");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
mContext.startActivity(intent);
}
public void exeShell(String cmd){
try{
Runtime.getRuntime().exec(cmd);
/*BufferedReader in = new BufferedReader(
new InputStreamReader(
p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
Log.i("exeShell",line);
}*/
}
catch(Throwable t)
{
t.printStackTrace();
}
}
}
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
include $(BUILD_SHARED_LIBRARY)
hello-jni.c:
1. 记住如果是要在自己项目中添加了jni文件,这个类名“Java_com_example_untitled_MyActivity_init”要换成自己的哟。
2. "/data/data/com.example.untitled" 自己的包名也要改哟。
jstring
Java_com_example_untitled_MyActivity_init( JNIEnv* env,
jobject thiz )
{
jstring tag = (*env)->NewStringUTF(env, c_TAG);
//初始化log
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "init OK"), &b_IS_COPY));
//fork子进程,以执行轮询任务
pid_t pid = fork();
if (pid < 0)
{
//出错log
LOG_ERROR((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "fork failed !!!"), &b_IS_COPY));
}
else if (pid == 0)
{
//子进程注册"/data/data/pym.test.uninstalledobserver"目录监听器
int fileDescriptor = inotify_init();
if (fileDescriptor < 0)
{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_init failed !!!"), &b_IS_COPY));
exit(1);
}
int watchDescriptor;
watchDescriptor = inotify_add_watch(fileDescriptor, "/data/data/com.example.untitled", IN_DELETE);
if (watchDescriptor < 0)
{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_add_watch failed !!!"), &b_IS_COPY));
exit(1);
}
//分配缓存,以便读取event,缓存大小=一个struct inotify_event的大小,这样一次处理一个event
void *p_buf = malloc(sizeof(struct inotify_event));
if (p_buf == NULL)
{
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "malloc failed !!!"), &b_IS_COPY));
exit(1);
}
//开始监听
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "start observer"), &b_IS_COPY));
size_t readBytes = read(fileDescriptor, p_buf, sizeof(struct inotify_event));
//read会阻塞进程,走到这里说明收到目录被删除的事件,注销监听器
free(p_buf);
inotify_rm_watch(fileDescriptor, IN_DELETE);
//目录不存在log
LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
, (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "uninstalled"), &b_IS_COPY));
//执行命令am start -a android.intent.action.VIEW -d http://shouji.360.cn/web/uninstall/uninstall.html
// execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d", "http://shouji.360.cn/web/uninstall/uninstall.html", (char *)NULL);
//4.2以上的系统由于用户权限管理更严格,需要加上 --user 0
execlp("am", "am", "start","--user", "0" ,"-a", "android.intent.action.VIEW", "-d", "https://www.google.com", (char *)NULL);
}
else
{
//父进程直接退出,使子进程被init进程领养,以避免子进程僵死
}
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
libhello-jni.so:就是c文件编译的so文件。