Android 卸载监听详解
目前市场上比较多的应用在用户卸载后会弹出意见反馈界面,比如360手机卫士,腾讯手机管家,应用宝等等,虽然本人不太认同其交互方式,但是在技术实现上还是可以稍微研究下的。其实要实现这个功能,最主要的就是监听到自己被卸载,然后弹出一个网页,具体思路如下:
1. fork 监听进程
2. 创建监听文件
- 创建inotify实例:int fileDescriptor = inotify_init();
- 注册监听事件:int watchDescriptor = inotify_add_watch(fileDescriptor,path, IN_DELETE); 这个函数包含三个参数,分别是inotify实例,监听文件路径,以及事件掩码,在这里我们关注的是删除事件,所以用IN_DELETE;
- 调用read函数开始监听:size_t len = read(int, void *, size_t); read函数也有三个参数,分别是inotify实例,inotify_event 结构的数组指针,以及要读取的事件的总长度。
3. 打开网页
private String getUserSerial(Context context) {Object userManager = context.getSystemService("user");if (userManager == null) {return null;}try {Method myUserHandleMethod = android.os.Process.class.getMethod("myUserHandle", (Class<?>[]) null);Object myUserHandle = myUserHandleMethod.invoke(android.os.Process.class, (Object[]) null);Method getSerialNumberForUser = userManager.getClass().getMethod("getSerialNumberForUser", myUserHandle.getClass());long userSerial = (Long) getSerialNumberForUser.invoke(userManager,myUserHandle);return String.valueOf(userSerial);} catch (Exception e) {e.printStackTrace();}return null;}
JNIEXPORT int JNICALL Java_com_uninstall_browser_sdk_UninstallBrowserSDK_init(JNIEnv * env, jobject thiz, jstring arg0, jstring arg1,jstring userSerial) {const char *pkgName = (*env)->GetStringUTFChars(env, arg0, 0);const char *url = (*env)->GetStringUTFChars(env, arg1, 0);__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "init jni");// fork子进程,以执行轮询任务pid_t pid = fork();if (pid < 0) {__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "fork failed");} else if (pid == 0) {// 子进程注册目录监听器int fileDescriptor = inotify_init();if (fileDescriptor < 0) {__android_log_print(ANDROID_LOG_INFO, "JNIMsg","inotify_init failed");exit(1);}int watchDescriptor;watchDescriptor = inotify_add_watch(fileDescriptor,get_watch_file(pkgName), IN_DELETE);if (watchDescriptor < 0) {__android_log_print(ANDROID_LOG_INFO, "JNIMsg","inotify_add_watch failed");exit(1);}// 分配缓存,以便读取event,缓存大小=一个struct inotify_event的大小,这样一次处理一个eventvoid *p_buf = malloc(sizeof(struct inotify_event));if (p_buf == NULL) {__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "malloc failed");exit(1);}// 开始监听__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "start observer");while (1) {size_t readBytes = read(fileDescriptor, p_buf,sizeof(struct inotify_event));// read会阻塞进程,走到这里说明收到监听文件被删除的事件,但监听文件被删除,可能是卸载了软件,也可能是清除了数据FILE *p_appDir = fopen(pkgName, "r");// 已经卸载if (p_appDir == NULL) {__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "uninstalled");inotify_rm_watch(fileDescriptor, watchDescriptor);break;}// 未卸载,可能用户执行了"清除数据",重新监听else {__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "clean data");fclose(p_appDir);int watchDescriptor = inotify_add_watch(fileDescriptor,get_watch_file(pkgName), IN_DELETE);if (watchDescriptor < 0) {__android_log_print(ANDROID_LOG_INFO, "JNIMsg","inotify_add_watch failed");free(p_buf);exit(1);}}}free(p_buf);if (userSerial == NULL) {// 执行命令am start -a android.intent.action.VIEW -d $(url)execlp("am", "am", "start", "-a", "android.intent.action.VIEW","-d", url, (char *) NULL);} else {// 执行命令am start --user userSerial -a android.intent.action.VIEW -d $(url)const char *userSerialNumber = (*env)->GetStringUTFChars(env,userSerial, 0);execlp("am", "am", "start", "--user", userSerialNumber, "-a","android.intent.action.VIEW", "-d", url, (char *) NULL);(*env)->ReleaseStringUTFChars(env, userSerial, userSerialNumber);}execlp("am", "am", "start", "--user", "0", "-a","android.intent.action.VIEW", "-d", url, (char *) NULL);(*env)->ReleaseStringUTFChars(env, arg0, pkgName);(*env)->ReleaseStringUTFChars(env, arg1, url);} else {(*env)->ReleaseStringUTFChars(env, arg0, pkgName);(*env)->ReleaseStringUTFChars(env, arg1, url);return pid;}return -1;
}
问题一:监听哪个文件?
/*** 创建监听文件,避免覆盖安装被判断为卸载事件*/
char* get_watch_file(const char* package) {int len = strlen(package) + strlen("watch.tmp") + 1;char* watchPath = (char*) malloc(sizeof(char) * len);sprintf(watchPath, "%s/%s", package, "watch.tmp");FILE* file = fopen(watchPath, "r");if (file == NULL) {file = fopen(watchPath, "w+");chmod(watchPath, 0755);}fclose(file);__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "创建文件目录 : %s", watchPath);return watchPath;
}
问题二:如何判断监听进程是否存在?
/*** 设置软件卸载时弹出网页的URL*/public void setUninstallWebUrl(Context context, String url) {if (url == null || url.length() == 0) {return;}int mMonitorPid = ConfigDao.getInstance(context).getMonitorPid();if (mMonitorPid > 0 && !getNameByPid(mMonitorPid).equals("!")) {Log.i("stefanli", "监控进程存在");return;} else {int mPid = init("/data/data/" + context.getPackageName(), url, getUserSerial(context));Log.i("stefanli", "监控进程ID:" + mPid);Log.i("stefanli", "监控进程名称:" + getNameByPid(mPid));ConfigDao.getInstance(context).setMonitorPid(mPid);}}
JNIEXPORT jstring JNICALL Java_com_uninstall_browser_sdk_UninstallBrowserSDK_getNameByPid(JNIEnv * env, jobject thiz, jint pid) {char task_name[100];getPidName(pid, task_name);jsize len = strlen(task_name);jclass clsstring = (*env)->FindClass(env, "java/lang/String");jstring strencode = (*env)->NewStringUTF(env, "GB2312");jmethodID mid = (*env)->GetMethodID(env, clsstring, "<init>","([BLjava/lang/String;)V");jbyteArray barr = (*env)->NewByteArray(env, len);(*env)->SetByteArrayRegion(env, barr, 0, len, (jbyte*) task_name);return (jstring) (*env)->NewObject(env, clsstring, mid, barr, strencode);
}void getPidName(pid_t pid, char *task_name) {char proc_pid_path[BUF_SIZE];char buf[BUF_SIZE];sprintf(proc_pid_path, "/proc/%d/status", pid);FILE* fp = fopen(proc_pid_path, "r");if (NULL != fp) {if (fgets(buf, BUF_SIZE - 1, fp) == NULL) {fclose(fp);}fclose(fp);sscanf(buf, "%*s %s", task_name);}
}
Demo下载地址:http://download.csdn.net/detail/a378881925/8373409
Android 卸载监听详解相关推荐
- html学习 - jquery事件监听详解
html学习 - jquery事件监听详解 html学习 - jquery事件监听详解 监听方法 监听方法参数解释 click参数 事件自动执行问题解决 bind方法 live方法 监听方法 在jqu ...
- JavaScript 的addEventListener() 事件监听详解!
JavaScript 的addEventListener() 事件监听详解! addEventListener() 用于向指定元素添加事件. 可以向一个元素添加多次事件或者多次不同事件,后面的事件是 ...
- Vue watch监听详解,一篇文章彻底搞懂Vue2/Vue3中的watch监听
watch监听在vue2和vue3中的用法详解(全) Vue2中的watch用法 Vue3中的watch用法 首先写一个vue页面 <template><div><h1& ...
- 手动更改oracle监听,详解Oracle数据库手动注册监听的方法
本文主要介绍了为一个Oracle数据库(t02)配置多个监听(listener)的实验,过程有点小曲折.通过这个实验让我们来学习一下如何手动注册监听.以下是演示的过程. 1.新增两个测试的监听,lis ...
- Activiti工作流之事件监听详解-ActivitiEventListener
工作流程事件监听可用于任务提醒.超时提醒等的模块的设计.以下是相关事件的介绍 一.事件监听类型 ENGINE_CREATED 监听器监听的流程引擎已经创建完毕,并准备好接受API调用. ENGINE_ ...
- java监听上传文件,Springmvc文件上传监听详解
spring mvc CommonsMultipartResolver 文件上传监听. /** * 重写 parseRequest方法 监听 */ @Override protected Multip ...
- android listview ontouchlistener,Android ListView监听滑动事件的方法(详解)
ListView的主要有两种滑动事件监听方法,OnTouchListener和OnScrollListener 1.OnTouchListener OnTouchListener方法来自View中的监 ...
- android触摸滑动监听,Android 滑动监听的实例详解
Android 滑动监听的实例详解 摘要: ScollBy,ScollTo是对内容的移动,view.ScollyBy是对view的内容的移动 view,ScollTo是对内容的移动(移动到指定位置), ...
- Android Activity 生命周期详解及监听
前言 系列文章: Android Activity 与View 的互动思考 Android Activity 生命周期详解及监听 Android onSaveInstanceState/onResto ...
最新文章
- 推荐 正则表达式入门教程
- Python:使用ctypes库调用外部DLL 数据类型对应
- 【笔记】spring的注解回顾,springboot-restful项目结构介绍 springboot-freemarker ⼯程配置详解
- spark写入elasticsearch限流
- 如何腾出计算机内存,电脑C盘又飘红?教你这样清理内存,可以轻松腾出大量空间...
- create 执行存储过程报错出现符号_记一次数据库迁移的过程采坑过程
- tar包zip的拆分与合并
- mui获取css参数,Mui-获取时间-调用手机api
- MSC-VO: 基于曼哈顿和结构约束的视觉里程计(CVPR 2021)
- iOS获取UUID,并使用keychain存储,可用于封设备
- 用Session记录实现页面数据的关联
- Autovue v21.0.2的新功能
- 基于 移远 BC26 移柯BL620的NB模块调试流程
- DNS服务器可能不可用 win11解决方法
- python 在List中随机抽取n个元素
- 为什么要有无参构造方法,无参构造的运行原理
- 向量和矩阵梯度:标量Hesse矩阵和矢量Jacobian矩阵
- 读书 | 李开复自传:世界因你不同读后感1
- 【ECG理论篇】(1)AI实现心律失常判别:心电基础知识及利用算法判别心律失常的分析流程
- 打印unicode汉字编码字符串为乱码怎么办?
热门文章
- 计算机下方如何添加启动项,开机设置启动项,如何添加开机启动项
- Vue 2 项目和插件使用
- 网站安全狗安装时服务器名,解决网站安全狗安装后访问网站需要用户名密码的方法...
- android7.0调用CAMERA报异常 java.lang.SecurityException: Permission Denial: starting Intent...
- weblogic BEA-170011
- 面试谈薪资,别不好意思,4个技巧帮你勇敢谈薪
- chrome网页加载慢问题
- FPGA之FIFO详解,初识FIFO
- 大二(下)微机与接口技术 流光发生器的设计
- 使用Latex排版选择题试卷