求推荐语言反馈齐全的阿里云 用户反馈馈平台哦?

Android NDK开发(八)——应用监听自身卸载,弹出用户反馈调查
时间: 18:30:10
&&&& 阅读:327
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&转载请注明出处:
监听卸载情景和原理分析
1,情景分析
& & & & 在上上篇博客中我写了一下NDK开发实践项目,使用开源的LAME库转码MP3,作为前面几篇基础博客的加深理解使用的,但是这样的项目用处不大,除了练练NDK功底。这篇博客,我将讲述一下一个各大应用中很常见的一个功能,同样也是基于JNI开发的Android应用小Demo,看完这个之后,不仅可以加深对NDK开发的理解,而且该Demo也可以使用在实际的开发中。不知道大家在使用一个Android应用的时候,当我们卸载这个应用后,设备上会弹出一个“用户反馈调查”的网页出来,也许很多人没有留意过或者直接忽视了,那么从现在开始请留意,大家不妨下载一下“豌豆荚”“360”之类的应用装上,然后卸载,看看设备上有没有弹出浏览器,浏览器上打开的“XXX用户反馈”?上面写了一些HTML表单,问我们“你为毛要卸载我们这么好的应用啊?”“我们哪里得罪你了?”“卸载之后,你丫的还装不?”,呵呵,开个玩笑,实际效果如下图:
& & & &好了,上面的图片是感觉似曾显示啊?那么这样的一个小功能是怎么实现的呢?我们先从Java层以我们有的Android基础分析一下:
1,监听系统的卸载广播,但是这个只能监听其他应用的卸载广播的动作,通过卸载广播监听自己是监听不到的:失败
2,系统配置文件,做一个标记应用是否卸载,判断标记来show用户反馈,显然这也是不合理的,因为应用卸载之后,配置文件也没有了。
3,静默安装另一个程序,监听自己的应用被卸载的动作。前提是要root,才能实现。但是市场绝大多数手机都是默认没有root权限的。
4,服务检测,只能是自己开启,当自身被卸载了,服务也一并被干掉了。
以上几点看起来都无法实现这个功能,确实如此啊,单纯的从Java层是做不到这一点的。
2,原理分析
& & & &上面情景分析后表明Java实现不了这样的一个功能,是否该考虑一下使用JNI了,用C在底层为我们实现这样一个打开内置浏览器加载用户反馈网页即可,在知道这个方法之前,我们有必要了解以下几个知识点。
1.通过c语言,c进程监视。
& & 既然Java做不到的话,我们试着使用C语言在底层实现好了,让C语言调用Android adb的命令去打开内置浏览器。
判断自己是否被卸载
andoird程序在被安装的时候会在/data/data/目录下生成一个以为包名为文件名的目录/data/data/包名
监听该目录是否还存在,如果不存在,就证明应用被卸载了。
2.c代码可以复制一个当前的进程作为自己的儿子,父进程销毁的时候,子进程还存在。
fork()函数:
& & & &&fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,两个进程可以做相同的事,相当于自己生了个儿子,如果初始参数或者传入的参数不一样,两个进程做的事情也不一样。当前进程调用fork函数之后,系统先给当前进程分配资源,然后再将当前进程的所有变量的值复制到新进程中(只有少数值不一样),相当于克隆了一个自己。
& & & &pid_t fpid = fork()被调用前,就一个进程执行该段代码,这条语句执行之后,就将有两个进程执行代码,两个进程执行没有固定先后顺序,主要看系统调度策略,fork函数的特别之处在于调用一次,但是却可以返回两次,甚至是三种的结果
(1)在父进程中返回子进程的进程id(pid)
(2)在子进程中返回0
(3)出现错误,返回小于0的负值
出现错误原因:(1)进程数已经达到系统规定&(2)内存不足,此时返回
3.在c代码的子进程中监视父进程是否被卸载,如果被卸载,通知Android系统打开一个url,卸载调查的网页。
& & & & Android系统提供的adb工具,在adb的基础上执行adb shell就可以直接对android系统执行shell命令
& & & & am命令:在Android系统中通过adb shell 启动某个Activity、Service、拨打电话、启动浏览器等操作Android的命令。
& & & & am命令的源码在Am.java中,在shell环境下执行am命令实际是启动一个线程执行Am.java中的主函数(main方法),am命令后跟的参数都会当做运行时参数传递到主函数中,主要实现在Am.java的run方法中。
& & & & am命令可以用start子命令,和带指定的参数,start是子命令,不是参数
常见参数:-a:表示动作,-d:表示携带的数据,-t:表示传入的类型,-n:指定的组件名
例如,我们现在在命令行模式下进入adb shell下,使用这个命令去打开一个网页
类似的命令还有这些:
命令:am start -a android.intent.action.CALL -d tel:电话号码
示例:am start -a android.intent.action.CALL -d tel:10086
打开一个网页
命令:am start -a android.intent.action.VIEW -d &网址
示例:am start -a android.intent.action.VIEW -d &http://www.baidu.com&
启动一个服务
命令:am startservice &服务名称&
示例:am startservice -n com.android.music/com.android.music.MediaPlaybackService
execlp()函数
& & & & & execlp函数简单的来说就是C语言中执行系统命令的函数
& & & & & execlp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名, 找到后便执行该文件, 然后将第二个以后的参数当做该文件的argv[0], argv[1], ..., 最后一个参数必须用空指针(NULL)作结束.
& & & & & android开发中,execlp函数对应android的path路径为system/bin/目录下
调用格式:
execlp(&am&,&am&,&start&,&--user&,&0&,&-a&,&android.intent.action.VIEW&,&-d&,&http://shouji.360.cn/web/uninstall/uninstall.html&,(char*)NULL);
===================================================================================================================
编写代码实现
1,Java层定义native方法
& & & &在Java层定义一个native方法,提供在Java端和C端调用
public native void uninstall(String packageDir, int sdkVersion);
该方法需要传递应用的安装目录和当前设备的版本号,在Java代码中获取,传递给C代码处理。
2,使用javah命令生成方法签名头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include &jni.h&
/* Header for class com_example_appuninstall_MainActivity */
#ifndef _Included_com_example_appuninstall_MainActivity
#define _Included_com_example_appuninstall_MainActivity
#ifdef __cplusplus
extern &C& {
com_example_appuninstall_MainActivity
* Signature: (Ljava/lang/S)V
JNIEXPORT void JNICALL Java_com_example_appuninstall_MainActivity_uninstall
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
& & & &方法签名生成好之后,工程上右键 --& Android Tools --& Add Native Support,在弹出的对话框中输入编辑的C/C++的文件名,确定之后,在工程的自动生成的jni目录下找到cpp后缀名的文件修改为.c后缀名的文件,因为本案例是基于C语言上实现的,然后同样修改Android.mk文件中的LOCAL_SRC_FILES为.c的C文件,最后将上面生成好的.h方法签名文件拷贝到jni目录下。
3,编写C语言代码
& & & & 正如上面原理分析的那样,我们在实现这样一个功能的时候用Java是无法实现的,只能在C中克隆出一个当前App的子进程,让这个子进程去监听应用本身的卸载。那么实现这样的功能我们需要哪些步骤呢?下面就是编写代码的思路:
1,将传递过来的java的包名转为c的字符串
2,创建当前进程的克隆进程
3,根据返回值的不同做不同的操作
4,在子进程中监视/data/data/包名这个目录
5,目录被删除,说明被卸载,执行打开用户反馈的页面
#include &stdio.h&
#include &jni.h&
#include &malloc.h&
#include &string.h&
#include &strings.h&
#include &stdlib.h&
#include &unistd.h&
#include &com_example_appuninstall_MainActivity.h&
#include &android/log.h&
#define LOG_TAG &System.out.c&
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
* 返回值 char* 这个代表char数组的首地址
* Jstring2CStr 把java中的jstring的类型转化成一个c语言中的char 字符串
char* Jstring2CStr(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = (*env)-&FindClass(env, &java/lang/String&); //String
jstring strencode = (*env)-&NewStringUTF(env, &GB2312&); // 得到一个java字符串 &GB2312&
jmethodID mid = (*env)-&GetMethodID(env, clsstring, &getBytes&,
&(Ljava/lang/S)[B&); //[ String.getBytes(&gb2312&);
jbyteArray barr = (jbyteArray) (*env)-&CallObjectMethod(env, jstr, mid,
strencode); // String .getByte(&GB2312&);
jsize alen = (*env)-&GetArrayLength(env, barr); // byte数组的长度
jbyte* ba = (*env)-&GetByteArrayElements(env, barr, JNI_FALSE);
if (alen & 0) {
rtn = (char*) malloc(alen + 1); //&\0&
memcpy(rtn, ba, alen);
rtn[alen] = 0;
(*env)-&ReleaseByteArrayElements(env, barr, ba, 0); //
JNIEXPORT void JNICALL Java_com_example_appuninstall_MainActivity_uninstall(
JNIEnv * env, jobject obj, jstring packageDir, jint sdkVersion) {
// 1,将传递过来的java的包名转为c的字符串
char * pd = Jstring2CStr(env, packageDir);
// 2,创建当前进程的克隆进程
pid_t pid = fork();
// 3,根据返回值的不同做不同的操作,&0,&0,=0
if (pid & 0) {
// 说明克隆进程失败
LOGD(&current crate process failure&);
} else if (pid & 0) {
// 说明克隆进程成功,而且该代码运行在父进程中
LOGD(&crate process success,current parent pid = %d&, pid);
// 说明克隆进程成功,而且代码运行在子进程中
LOGD(&crate process success,current child pid = %d&, pid);
// 4,在子进程中监视/data/data/包名这个目录
while (JNI_TRUE) {
FILE* file = fopen(pd, &rt&);
if (file == NULL) {
// 应用被卸载了,通知系统打开用户反馈的网页
LOGD(&app uninstall,current sdkversion = %d&, sdkVersion);
if (sdkVersion &= 17) {
// Android4.2系统之后支持多用户操作,所以得指定用户
execlp(&am&, &am&, &start&, &--user&, &0&, &-a&,
&android.intent.action.VIEW&, &-d&,
&http://www.baidu.com&, (char*) NULL);
// Android4.2以前的版本无需指定用户
execlp(&am&, &am&, &start&, &-a&,
&android.intent.action.VIEW&, &-d&,
&http://www.baidu.com&, (char*) NULL);
// 应用没有被卸载
LOGD(&app run normal&);
}& & & & 上述代码就如上述的步骤一样,用C代码实现了,首先注意的一点就是Android的版本问题,众所周知,Android是基于Linux的非常优秀的操作系统,而且在Android4.2版本以后支持多用户操作,但是这也给我们这个小小的项目中带来了不便之处,因为在多用户情况下执行am命令的时候强制指定一个用户和一个编号,在Android4.2之前的版本这些参数是没有必要的,所以我们在编写C代码的时候需要区别Android系统版本,分别执行相应的am命令,关于获取Android系统版本可以在Java层实现,然后将其作为参数传递给C代码中,C代码根据Android版本为判断条件执行am命令。
& & & & 注意:为了简便起见,我在C代码监视应用是否被卸载的时候,使用了一个While(true)的死循环,并且是每隔1毫秒执行一次监视检测,这样写的代码是“不环保的”,想想这样的结果是程序被不停的执行,LOG被不停的打印,造成cpu计算资源浪费和耗电是难免的。最好的解决方案是,使用Android给我们提供的FileObserve文件观察者,FileObserve使用到的是Linux系统下的inotify进程,用来监视文件目录的变化的,本实例中如果需要优化就需要使用这个API,但是需要的知识就更加多了,我现在为了简单的演示起见,暂时用了while(true)死循环,关于后期的优化版本,等我写出来,再一起公布一下!
4,编译.so动态库
& & & &正如上篇博客写的那样,我们编写好了C源码之后,就需要使用ndk-build命令来编译成.so文件了,具体编译的过程也是非常简单的,在Eclipse中切换到C/C++编辑的手下,找到“小锤子”按钮,点击一下就开始编译了,如果代码没有出现错误的情况,编译之后的结果是这样的:
5,编写Java代码,传递数据 ,加载链接库
& & & & 上面的工作做好了,剩下的就是在Java中加载这个链接库,和调用这个本地方法了。首先,要获取本应用安装的目录/data/data/包名,然后获取当前设备的版本号,一起传给本地方法中,最后调用这个方法。
public class MainActivity extends Activity {
System.loadLibrary(&uninstall&);
public native void uninstall(String packageDir, int sdkVersion);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String packageDir = &/data/data/& + getPackageName();
int sdkVersion = android.os.Build.VERSION.SDK_INT;
uninstall(packageDir, sdkVersion);
& & & &好了,应用是做完了,我们clean一下工程,然后启动一个基于ARM的模拟器,运行这个程序,回到桌面,点击应用图片——卸载掉这个应用,看看效果:
好了,大家看看效果吧,实际上打开的网页应该是用户反馈调查页面,由于我暂时没有服务器,所以将网址定向到了百度首页了,大家在开发的时候,可以将execlp函数里的参数网址改成自己的服务器网址,这样就大功告成了。检查一下Log日志的输出:
看到了,LOG输入日志跟代码流程是一致的,好了,源码在下面的链接下,有兴趣的朋友可以下载研究,欢迎你给我提出宝贵意见,大家一起学习一起进步!
经过查询资料,我已经了解不使用while(true)轮询方式,改用Linux的Inotify机制监听应用安装目录的实现方法了,关于最新优化版本的案例已经做完,请点击这里查看实现原理和代码:
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&原文地址:http://blog.csdn.net/allen315410/article/details/
&&国之画&&&& &&&&chrome插件
版权所有 京ICP备号-2
迷上了代码!产品帮助与支持 | 三星电子 售后服务 CN
产品帮助与支持
产品帮助与支持
您需要帮助吗?
{"Expand":"展开"
,"Collapse":"隐藏"}
{"Expand":"展开"}
我不知道我的产品型号
No Product Image
Now Loading
三星服务 官方微信
线上技术服务,解决产品使用中的问题
专业技术人员通过远程查看和控制您的设备,直接帮您解决产品问题。
如需技术支持或者咨询常见问题,您可以发送邮件,我们会在工作日24小时内回复。
有意见或建议? 您可以发送E-mail联系CEO办公室。我们会在工作日24小时内回复。
手机预约维修,尊享优先服务 !
周一至周五 8:00~18:00
周末及法定节假日 8:00~17:00
*打印机相关咨询请访问
查找三星服务中心
查询三星服务中心
为了保障您的权益,请选择三星授权服务中心。
如何查找产品型号?
需要帮助查找产品型号?请从下方菜单中选择您的产品,
我们将告知您产品型号的所在位置。
Now Loading404 Not Found
404 Not Found注册 | 登录
萌不可以当饭吃,但实力可以。
从零开始学运营,10年经验运营总监亲授,2天线下集训+1年在线学习,做个有竞争力的运营人。
说起做用户反馈那点事儿吧,以前我们团队说做客服的都是折翼的天使,哈哈哈,我估计前前后后加起来,可能折翼了有一年半以上吧,不知道以后还有没有机会做这个凝聚着汗水和泪水的工作,把自己感悟到的那点东西跟大家分享讨论下。
一、先准备一颗强大的“钻石心”,我们再说其他。
嗯,在我工作的第一年就接触到用户反馈,那时做一个积分产品,遇到为了1分钱将我祖宗N代以及全家都问候到了的用户。当然有时候也会遇到态度较好的,然而大部分都是来势汹汹破口大骂的,那个负能量就不用说了,要不支付宝客服还有每个月500块的“被骂补贴”呢(我以前室友是支付宝客服,我听缩滴。。。)。但是作为基础的运营,刚入行的运营,其实做这块是特别有好处的,能站在用户的角度去体验,甚至能够预计到用户的问题,这对产品设计的初期指导是十分有意义的。
用户反馈工作琐碎而意义重大,最好有强大的“钻石心”;当然也欢迎希望锻炼自己的玻璃心。
二、用户反馈的收集渠道,此处以App为例。
1、App内置的反馈入口。
这个是最常见的,现在的互联网产品越来越重视用户反馈,大部分都会在产品里内置一个反馈入口,常见的是放在“设置”这个功能下面。
联系用户方便。
缺点是:能找到这个不太明显的入口来反馈的,都是核心用户真爱粉,so,流失的那些用户的心声,你未必知道。
2、应用市场评论。
这个也是很常见的,就是你用app的时候也经常会遇到弹出小窗口,鼓励你去市场给他们评分的(其实我个人特别不喜欢那小弹窗,总是残忍滴拒绝。。hiahia~~)。我是很懒的用户,如果不是真的体验好到爆,我很少主动去给好评,就算淘宝卖家说好评返2元,我也还是懒。。。
优点是:比app内的反馈用户范围更广一些,因为很多产品的冷启动都是靠自己公司的产品带量,所以用户属性都很接近公司旗下产品,是属于很精准的用户,留存率通常比市场都要高。
不方便联系用户,很多应用市场都用自己的accout体系,有些就是匿名评论,你是无法联系到该用户的;也有些市场的开发者后台提供回复功能,但是依然不方便。
收集起来很麻烦,市场实在是很多,有些没有PC端,所以复制黏贴也不是很方便。也有些公司的开发比较给力的,会把各市场用户反馈接入友盟后台。
信息不全。app内置的反馈你可以要求开发做成保留机型等等,然而应用市场的反馈是不会把这些暴露给你的。也有小部分的市场,在开发者后台能看到信息较全面的反馈,不普遍。
3、客服电话,线上客服等各种客服渠道。
比如我现在的公司,是做在线教育的,所以早就有成套的成熟的呼叫中心客服。虽然我所在的业务不是销售课程的,但是很多被教育过的用户,都还是会找到客服电话,来反馈各个产品线的问题。所以也常常会收到客服同学转过来的用户反馈。
优点是:通常能转到你这边来,肯定是客服已经安抚过用户了,所以一般到你这的时候,用户火气已经不那么大了;客服能够找到你这里,肯定用户也是有过比较清晰的描述,这样就筛掉了一批特别傻的反馈,啰嗦半天也说不清自己什么问题(别问我是怎么知道的。。。
缺点是:通常客服也有自己的KPI,问题解决率什么的,所以有时候你想直接跟用户沟通解决问题的时候,客服会不屈不饶,非要从中传话,那你懂的,多一个人传话就多一份沟通难度和成本,尤其这个传话的人还是不懂你业务的人。
4、调查问卷。
这是一种产品的主动获取用户反馈的方式。我们通常用在产品立项的前期调研,或者重大版本更新决策之前,或者大版本大功能上线之初(灰度、核心用户内测)以及之后。
优点是:你能通过问题的设计,有针对性的获取你想要的反馈。后期的反馈呈现,能够数据化。而日常的用户反馈,用户都是描述性的语言,而且很多不清晰的,要追问。
对设计问卷的人自身的要求比较高,我们通常是运营来设计(当然,你公司如果给你们配专门的用研团队,那你就是幸糊的),有时候呢就会出现问卷发出去了,然而忽然觉得哪里没考虑周全。正因为是主动获取反馈的一种手段,因此很容易主观判断,甚至出到一些诱导用户回答的问题。
样本量的获取。尤其是对产品立项前期的调研,这个时候,产品还没有用户,那么你的样本就要完全靠你去定位用户,然后去获取,这个真的很辛苦,尤其是你的领导跟你说没有调研预算的时候。/(ㄒoㄒ)/~~
当样本量不足够大的时候,好像并没有什么卵用。
5、核心用户体验团。
这个是很多产品普遍采用的灰度测试方法。当产品有一定量用户后,从中筛选or招募一些对产品好感度较高热情较大的用户,用QQ群等圈起来,由运营去维护。
优点是:反馈速度快,质量高,易触达。
缺点是:正因为是好感度高的用户,所以反馈有可能偏颇,情人眼里出西施么,你懂的。他不能代表小白用户的。
6、搜索引擎、社交平台等外部公开平台。
这个建议就是有余力的情况,可以做起来。例如以前我们会在百度知道啊,搜索结果啊,微博大号啊,微信公众号啊什么的,去看看产品相关关键词的结果里,都在说些什么。
优点是:你能知道潜在用户和已流失用户是怎么评价你家产品的。
缺点是:海量信息里去刨除和你有关的,成本也是有点大。微博微信大号还好一些,搜索引擎这种就真的有点累。而且搜索引擎这种也不适合全新的产品,因为没有用户量么,在搜索引擎那边权重也不高,所以能找到的有效信息比较少。
7、线下活动之类的获取的反馈。
例如说我们公司每年的校招啊,会去线下搞一些宣讲,可以派一个人跟过去,也可以设计好问卷之类的让人力资源的同事帮忙发一发,或者夹在笔试题目里之类的。
优点是:一下子就能获得好多反馈啊,不用你辛辛苦苦去找人来填问卷了好吗
缺点是:成本大啊,不是所有公司都有线下那么大的号召力;如果是通过送礼品的手段获取的反馈,反馈的可参考程度也要打一点折扣的,人家也许没有好好填。
8、你公司的同事啊,最好不是同一个业务线的。
这个适合中型、大型的公司,找那些对你业务根本不了解的人去做,比较好。
优点是:容易触达,尤其是找到行政通知这种能一下子@all的手段;同一个公司的嘛,大家也会给点面子,好好体验一下产品给反馈。
缺点是:成也萧何败也萧何,正因为是同一家公司,可能有千丝万缕的利益联系,一般不会说的很难听,还是会给点人情分。
三、你不是反馈的搬运工,你是用户运营。
用户反馈工作是用户运营工作中的一部分。我始终觉得,做用户反馈工作,不是简单的把从各个渠道搜集来的反馈甩产品经理一脸这样,运营是要经过自己的整理和理解,去给到产品经理一些建议的。
我通常会给产品经理以下几样东西:
1、整理过的原始反馈表。
也就是将这些琐碎的,描述性的,格式各异的反馈,通过你的筛选整理,提取一两个关键词(例如:类型,描述关键词),我称之为“反馈点”,这样你在整理数据的时候就不用重复的一遍遍看所有的反馈,瞄一眼关键词你就知道这是个啥了。
2、反馈点分布。
整理好反馈点后,我会做一个反馈点分布饼图or excel表之类的,这个是为了让看你反馈报告的人,一眼知道这个反馈最强烈的部分是什么。
3、优化建议。
这个部分,我觉得可以根据运营自己的理解,去给到产品经理建议,因为你是离用户最近的人,你是最经常跟用户厮混在一起的人,所以你可能会更了解用户。
当然这部分也不是必须的,这个对于刚入行的新人来说会比较难。一个靠谱的建议,通常会需要对产品比较深的理解,和对用户比较全面的把握。
授权发布于人人都是产品经理 ,未经许可,禁止转载。
收藏已收藏 | 150赞已赞 | 12
萌不可以当饭吃,但实力可以。
产品经理群
运营交流群
数据分析群
文案交流群
Axure交流群
关注微信公众号
大家都在问
31个回答32人关注
4个回答4人关注
10个回答18人关注
18个回答20人关注
19个回答22人关注
26个回答45人关注}

我要回帖

更多关于 阿里云 用户反馈 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信