这篇文章主要介绍了android监听应用是否被卸载的方法,具有一定借鉴价值,需要的朋友可以参考下。如下资料是关于android实现监听的详细步骤内容。
一 效果演示
打开应用效果图:
图1
点击卸载后提示,如下图:
图2
然后退出应用,卸载程序,会发现当应用被卸载以后,会弹出调用浏览器的提示,这里随便放了一个搜狐浏览页面,在自己的应用中应该调用的一般都是调查页面。如下图:
图3
ok,效果前面已经演示了,现在需要讨论一下其具体实现了。
首先,通过adb shell进入手机,然后第一次进入应用,像图1一样,不点击按钮,通过 ps | busybox grep ubuntu 看这个应用的进程信息,如下图:
这个时候只有
u0_a108 2953 124 490956 47792 ffffffff 40052a40 S com.example.ubuntuforandroid
2953 这一个进程
点击 卸载后提示 按钮再次,执行刚才执行的ps命令,发现已经有两个进程了如下图:
其实新产生的进程是通过程序调用jni接口
public static native int Reguninstall(String path,String url);
这个接口fork了一个进程,而新fork的进程负责监听本应用是否被卸载了
二 源码分析
java层的代码如下,很简单,就是调用一下jni接口
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initInjectFunction("testfile"); test = (TextView)this.findViewById(R.id.testview); test.setText("点击卸载后提示按钮,你的应用在卸载以后会调用浏览器,然后调用你需要的页面。"); btn = (Button)this.findViewById(R.id.testbtn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub String directory = MainActivity.this.getFilesDir().getAbsolutePath(); String url = "http://www.sohu.com/"; JniExec.Reguninstall(directory,url); test.setText("现在可以退出应用,然后卸载应用,看看是否有效果"); } }); }
一目了然,不用多言了
现在就分析
package com.example.ubuntuforandroid; public class JniExec { static { System.loadLibrary("uninstall"); } public static native int Reguninstall(String path,String url); }
Reguninstall 这个jni接口里面做了什么事情,能够达到监听本身应用卸载的效果。
native代码分析
jint Java_com_example_ubuntuforandroid_JniExec_Reguninstall(JNIEnv* env, jobject thiz, jstring path, jstring url) { LOGI("Java_com_example_ubuntuforandroid_JniExec_Reguninstall"); char *listenpath = (char*) (*env)->GetStringUTFChars(env,path, 0); char *jumpurl = (char*) (*env)->GetStringUTFChars(env,url, 0); LOGI("notify path is %s",listenpath); LOGI("jumpurl is %s",jumpurl); pid_t pid; pid = fork(); if(pid == 0) { //子进程 inotify_main(listenpath,jumpurl); } //父进程不阻塞调用 waitpid ok 子进程变成了孤儿进程,被init进程收养了 pid = waitpid(-1,0,1); LOGI("father bye bye"); return 0; }
这个接口里面最关键的是调用了 inotify_main 这个函数。如果看这段代码比较费力的话,建议先弄清楚linux 下的fork机制,搞清楚 孤儿进程 僵尸进程这些如何产生的情况。
下面看 inotify_main 这个函数
void inotify_main(char *path,char *url) { struct pollfd poll_list[2]; poll_list[0].fd = inotify_init(); poll_list[0].events = POLLIN; int wd = inotify_add_watch(poll_list[0].fd, path, IN_DELETE | IN_CREATE); if(wd < 0) { fprintf(stderr, "could not add watch for %s, %s\n", path, strerror(errno)); return ; } int retval; while(1) { retval = poll(poll_list,(unsigned long)1,-1); /* retval 总是大于0或为-1,因为我们在阻塞中工作 */ LOGI("retval = %d\n",retval); if(retval < 0) { fprintf(stderr,"poll错误: %s/n",strerror(errno)); return; } if((poll_list[0].revents & POLLIN) == POLLIN) { LOGI("poll_list[0].revents&POLLIN\n"); inotify_handle(poll_list[0].fd,url); } } inotify_rm_watch(poll_list[0].fd,wd); }
这个函数,这里面用到了 inotify_init inotify_add_watch inotify_rm_watch 这几个linux接口,这几个接口主要的作用就是监听指定的目录,其配合poll函数,能够监听目录下的任何改动,当要监听的目录有任何改动的时候,会触发poll函数的 POLLIN
有可读数据到来事件。
在本应用中,监听的是 /data/data/com.example.ubuntuforandroid/files/ 这个目录,因为在卸载的时候卸载程序会删除监听目录的,而fork出来的守护进程当发现自身应用的目录被卸载程序删除了也就是卸载了,这个时候调用 inotify_handle
这个函数,然后调用 am命令启动浏览器,调用自己需要调用界面。
当然,在调用am指令以后,记得自身守护进程的使命也完成了,需要exit退出一下。
inotify接口的用处很多,扩展一下,也可以用于守护进程,比如A进程和B进程是共生共死的,这里有一种实现的方式就是 A进程用inotify 监听 /proc/B进程id目录,当B进程结束的时候,A进程就能知道B进程不在了,从而结束自己。
上文描述的就是android监听应用是否被卸载的方法,具体使用情况还需要大家自己动手实验使用过才能领会。如果想了解更多相关内容,欢迎关注亿速云行业资讯频道!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。