1. 程式人生 > >Android應用如何監聽自己是否被解除安裝及解除安裝反饋功能的實現

Android應用如何監聽自己是否被解除安裝及解除安裝反饋功能的實現

  一個應用被使用者解除安裝肯定是有理由的,而開發者卻未必能得知這一重要的理由,畢竟使用者很少會主動反饋建議,多半就是用得不爽就卸,如果能在被解除安裝後獲取到使用者的一些反饋,那對開發者進一步改進應用是非常有利的。目前據我所知,國內的Android應用中實現這一功能的只有360手機衛士、360平板衛士,那麼如何實現這一功能的?

  我們可以把實現解除安裝反饋的問題轉化為監聽自己是否被解除安裝,只有得知自己被解除安裝,才可以設計相應的反饋處理流程。以下的列表是我在研究這一問題的思路:

  1,註冊BroadcastReceiver,監聽"android.intent.action.PACKAGE_REMOVED"系統廣播

  結果:NO。未寫程式碼,直接分析,解除安裝的第一步就是退出當前應用的主程序,而此廣播是在已經解除安裝完成後才發出的,此時主程序都沒有了,去哪onReceive()呢?

  2,若能收到"將要解除安裝XX包"的系統廣播,在主程序被退出之前就搶先進行反饋處理就好了,可惜沒有這樣的系統廣播,不過經過調研,倒是發現了一個辦法,讀取系統log,當日志中包含"android.intent.action.DELETE"和自己的包名時,意味著自己將要被解除安裝。

  結果:NO。除錯時發現此方法有兩個缺陷,(1)點選設定中的解除安裝按鈕即發出此Intent,此時使用者尚未在彈框中確認解除安裝;(2)pm命令解除安裝不出發此Intent,意味著被諸如手機安全管家,豌豆莢等軟體解除安裝時,無法提前得知解除安裝意圖。

  3,由於時間點不容易把控,所以乾脆不依賴系統廣播或log,考慮到解除安裝過程會刪除"/data/data/包名"目錄,我們可以用執行緒直接輪詢這個目錄是否存在,以此為依據判斷自己是否被解除安裝。

  結果:NO。同方法1,主程序退出,相應的執行緒必定退出,執行緒還沒等到判斷目錄是否存在就已經被銷燬了。

  4,改用C端程序輪詢"/data/data/包名"目錄是否存在

  結果:YES。藉助Java端程序fork出來的C端程序在應用被解除安裝後不會被銷燬。

  OK,上程式碼!

Activity啟動時fork出C端程序輪詢目錄:

 1 package main.activity;
2 3 import pym.test.uninstalledmoniter.R; 4 import android.app.Activity; 5 import android.os.Bundle; 6 import android.util.Log; 7 8 /** 9 * @author pengyiming 10 * @note 監聽此應用是否被解除安裝,若被解除安裝則彈出解除安裝反饋 11 * 12 */ 13 14 public class UninstalledMoniterActivity extends Activity 15 { 16 /* 資料段begin */ 17 private static final String TAG = "UninstalledMoniterActivity"; 18 /* 資料段end */ 19 20 /* 函式段begin */ 21 private native void init(); 22 static 23 { 24 Log.d(TAG, "load libuninstalled_moniter"); 25 System.loadLibrary("uninstalled_moniter"); 26 } 27 28 @Override 29 public void onCreate(Bundle savedInstanceState) 30 { 31 super.onCreate(savedInstanceState); 32 Log.d(TAG, "onCreate"); 33 34 setContentView(R.layout.uninstalled_moniter_layout); 35 36 init(); 37 } 38 /* 函式段end */ 39 }

核心——native方法標頭檔案:

 1 /* 標頭檔案begin */
 2 #include <jni.h>
 3 #include <stdio.h>
 4 #include <string.h>
 5 #include <android/log.h>
 6 #include <unistd.h>
 7 /* 標頭檔案end */
 8 
 9 /* 巨集定義begin */
10 //清0巨集
11 #define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)
12 
13 //LOG巨集定義
14 #define LOG_INFO(tag, msg) __android_log_write(ANDROID_LOG_INFO, tag, msg)
15 #define LOG_DEBUG(tag, msg) __android_log_write(ANDROID_LOG_DEBUG, tag, msg)
16 #define LOG_WARN(tag, msg) __android_log_write(ANDROID_LOG_WARN, tag, msg)
17 #define LOG_ERROR(tag, msg) __android_log_write(ANDROID_LOG_ERROR, tag, msg)
18 /* 巨集定義end */
19 
20 #ifndef _Included_main_activity_UninstalledMoniterActivity
21 #define _Included_main_activity_UninstalledMoniterActivity
22 #ifdef __cplusplus
23 extern "C" {
24 #endif
25 
26 #undef main_activity_UninstalledMoniterActivity_MODE_PRIVATE
27 #define main_activity_UninstalledMoniterActivity_MODE_PRIVATE 0L
28 #undef main_activity_UninstalledMoniterActivity_MODE_WORLD_READABLE
29 #define main_activity_UninstalledMoniterActivity_MODE_WORLD_READABLE 1L
30 #undef main_activity_UninstalledMoniterActivity_MODE_WORLD_WRITEABLE
31 #define main_activity_UninstalledMoniterActivity_MODE_WORLD_WRITEABLE 2L
32 #undef main_activity_UninstalledMoniterActivity_MODE_APPEND
33 #define main_activity_UninstalledMoniterActivity_MODE_APPEND 32768L
34 #undef main_activity_UninstalledMoniterActivity_MODE_MULTI_PROCESS
35 #define main_activity_UninstalledMoniterActivity_MODE_MULTI_PROCESS 4L
36 #undef main_activity_UninstalledMoniterActivity_BIND_AUTO_CREATE
37 #define main_activity_UninstalledMoniterActivity_BIND_AUTO_CREATE 1L
38 #undef main_activity_UninstalledMoniterActivity_BIND_DEBUG_UNBIND
39 #define main_activity_UninstalledMoniterActivity_BIND_DEBUG_UNBIND 2L
40 #undef main_activity_UninstalledMoniterActivity_BIND_NOT_FOREGROUND
41 #define main_activity_UninstalledMoniterActivity_BIND_NOT_FOREGROUND 4L
42 #undef main_activity_UninstalledMoniterActivity_BIND_ABOVE_CLIENT
43 #define main_activity_UninstalledMoniterActivity_BIND_ABOVE_CLIENT 8L
44 #undef main_activity_UninstalledMoniterActivity_BIND_ALLOW_OOM_MANAGEMENT
45 #define main_activity_UninstalledMoniterActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
46 #undef main_activity_UninstalledMoniterActivity_BIND_WAIVE_PRIORITY
47 #define main_activity_UninstalledMoniterActivity_BIND_WAIVE_PRIORITY 32L
48 #undef main_activity_UninstalledMoniterActivity_BIND_IMPORTANT
49 #define main_activity_UninstalledMoniterActivity_BIND_IMPORTANT 64L
50 #undef main_activity_UninstalledMoniterActivity_BIND_ADJUST_WITH_ACTIVITY
51 #define main_activity_UninstalledMoniterActivity_BIND_ADJUST_WITH_ACTIVITY 128L
52 #undef main_activity_UninstalledMoniterActivity_CONTEXT_INCLUDE_CODE
53 #define main_activity_UninstalledMoniterActivity_CONTEXT_INCLUDE_CODE 1L
54 #undef main_activity_UninstalledMoniterActivity_CONTEXT_IGNORE_SECURITY
55 #define main_activity_UninstalledMoniterActivity_CONTEXT_IGNORE_SECURITY 2L
56 #undef main_activity_UninstalledMoniterActivity_CONTEXT_RESTRICTED
57 #define main_activity_UninstalledMoniterActivity_CONTEXT_RESTRICTED 4L
58 #undef main_activity_UninstalledMoniterActivity_RESULT_CANCELED
59 #define main_activity_UninstalledMoniterActivity_RESULT_CANCELED 0L
60 #undef main_activity_UninstalledMoniterActivity_RESULT_OK
61 #define main_activity_UninstalledMoniterActivity_RESULT_OK -1L
62 #undef main_activity_UninstalledMoniterActivity_RESULT_FIRST_USER
63 #define main_activity_UninstalledMoniterActivity_RESULT_FIRST_USER 1L
64 #undef main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_DISABLE
65 #define main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_DISABLE 0L
66 #undef main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_DIALER
67 #define main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_DIALER 1L
68 #undef main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_SHORTCUT
69 #define main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_SHORTCUT 2L
70 #undef main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_SEARCH_LOCAL
71 #define main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
72 #undef main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_SEARCH_GLOBAL
73 #define main_activity_UninstalledMoniterActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
74 
75 /*
76  * Class:     main_activity_UninstalledMoniterActivity
77  * Method:    init
78  * Signature: ()V
79  */
80 JNIEXPORT void JNICALL Java_main_activity_UninstalledMoniterActivity_init(JNIEnv *, jobject);
81 
82 #ifdef __cplusplus
83 }
84 #endif
85 #endif

核心——native方法實現:

 1 /* 標頭檔案begin */
 2 #include "main_activity_UninstalledMoniterActivity.h"
 3 /* 標頭檔案end */
 4 
 5 #ifdef __cplusplus
 6 extern "C" {
 7 #endif
 8 
 9 /* 內全域性變數begin */
10 static char c_TAG[] = "UninstalledMoniterActivity.init";
11 static jboolean b_IS_COPY = JNI_TRUE;
12 /* 內全域性變數 */
13 
14 /*
15  * Class:     main_activity_UninstalledMoniterActivity
16  * Method:    init
17  * Signature: ()V
18  */
19 JNIEXPORT void JNICALL Java_main_activity_UninstalledMoniterActivity_init(JNIEnv *env, jobject obj)
20 {
21     jstring tag = (*env)->NewStringUTF(env, c_TAG);
22 
23     //初始化log
24     LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
25             , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "init OK"), &b_IS_COPY));
26 
27     //fork子程序,以執行輪詢任務
28     pid_t pid = fork();
29     if (pid < 0)
30     {
31         //出錯log
32         LOG_ERROR((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
33                 , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "fork error !!!"), &b_IS_COPY));
34     }
35     else if (pid == 0)
36     {
37         //子程序輪詢"/data/data/pym.test.uninstalledmoniter"目錄是否存在,若不存在則說明已被解除安裝
38         while (1)
39         {
40             FILE *p_file = fopen("/data/data/pym.test.uninstalledmoniter", "r");
41             if (p_file != NULL)
42             {
43                 fclose(p_file);
44 
45                 //目錄存在log
46                 LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
47                             , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "I'm OK !!!"), &b_IS_COPY));
48 
49                 sleep(1);
50             }
51             else
52             {
53                 //目錄不存在log
54                 LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
55                             , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "I'm NOT OK !!!"), &b_IS_COPY));
56 
57                 //執行命令am start -a android.intent.action.VIEW -d http://shouji.360.cn/web/uninstall/uninstall.html
58                 execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d", "http://shouji.360.cn/web/uninstall/uninstall.html", (char *)NULL);
59             }
60         }
61     }
62     else
63     {
64         //父程序直接退出,使子程序被init程序領養,以避免子程序僵死
65     }
66 }
67 
68 #ifdef __cplusplus
69 }
70 #endif

注1:為了除錯方便,包含<android/log.h>,使得so在執行過程中也可以像Java端一樣方便得打出log。相應的mk檔案需要加上以下兩句宣告

LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog

注2:程式碼中引用了360手機衛士的反饋地址,僅供大家學習、測試使用~~~

相關推薦

Android應用如何自己是否解除安裝解除安裝反饋功能實現(第二版)

原文地址為: Android應用如何監聽自己是否被解除安裝及解除安裝反饋功能的實現(第二版)   昨天發了一篇有關監聽自己是否被解除安裝和解除安裝反饋功能實現的部落格,地址如下:http://www.cnblogs.com/zealotrouge/p/3157126.html,發出去後收

Android應用如何自己是否解除安裝解除安裝反饋功能實現

  一個應用被使用者解除安裝肯定是有理由的,而開發者卻未必能得知這一重要的理由,畢竟使用者很少會主動反饋建議,多半就是用得不爽就卸,如果能在被解除安裝後獲取到使用者的一些反饋,那對開發者進一步改進應用是非常有利的。目前據我所知,國內的Android應用中實現這一功能的只有360手機衛士、360平板衛士,那麼如

Android應用如何自己是否解除安裝解除安裝反饋功能實現(第三版)

1 /* 標頭檔案begin */ 2 #include "main_activity_UninstalledObserverActivity.h" 3 /* 標頭檔案end */ 4 5 #ifdef __cplusplus 6 extern "C" 7 { 8 #en

Android APP自己解除安裝以及解除安裝後的邏輯處理

兩個月前發了兩篇有關監聽自己是否被解除安裝和解除安裝反饋功能實現的部落格,第二版的地址如下:http://www.cnblogs.com/zealotrouge/p/3159772.html,感謝@whiletrue_童鞋發現了我的程式碼在4.2.2系統上無法實現解除安裝反饋,經過除錯,有了問題的解決方案

android 如何自身應用解除安裝

   前段時間有個同事問我android應用在解除安裝以後,如何能夠通知一下伺服器,讓使用者填寫一下解除安裝的原因,以求為將來的應用修改積累資料。當時他是有段原始碼的,但是有點小問題,我只是幫他定位一下了程式碼的問題,具體細節沒有研究。又加上最近工作比較繁忙,所以就放下來了,今天稍微有點空,就自己

Android 主程序

當按多工鍵時,然後清除所有程式或者殺死單個程式時,如果要監聽這個動作的話,可以在一個service裡監聽。 效果如下: 可以看到,在多工視窗中,左滑結束程序和清除所有程序後,都會自己再開啟應用,程式碼如下: public class KeepLifeService

Android如何第三方應用的啟動

個人總結的監聽第三方應用啟動的方法有以下幾種: 1.Root狀態下Shell監聽ActivityManager的log,或者ps程序 2.參考程式鎖的實現,監聽後臺執行的程序 3.深入framework,通過反射等方式注入hook 第一種方式監聽應用的啟動,restart

android 通過edittext實現button的點擊事件

監聽 edittext 如果你沒有接第三方的輸入設備,那麽點擊按鈕只需找到你的button然後:button.performClick();就可以了那麽如果你用到第三方輸入法,有些時候監聽就沒有這麽好使了:以下場景為:外接輸入服務,直接給我丟一串字符,我拿到字符後執行按鈕事件代碼如下: editTe

android 真正軟鍵盤得彈起和收

 在開發中我們有時需要監聽鍵盤得變化情況  Softkeyboardlistener.class 工具類   public class Softkeyboardlistener { private View rootView;//activity的根

Android如何藍芽耳機的按鍵事件

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android 手勢demo

package com.shengfang.demo.testdemo; import android.content.Context; import android.view.GestureDetector; import android.view.MotionEvent; import and

AndroidScrollView滑動停止和滑動到底部

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

如何實現android來電並生成懸浮窗體提示

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android事件

案例一 事件監聽(三種寫法) 1.1 標籤上直接繫結監聽方法 public void xxx(View view) //第一種 public void xxx(View view) 1.2 建立監聽器物件,元件再繫結監聽器物件 1.2.1 匿名內部類 1.2

Android實時軟鍵盤顯示或隱藏

搞了一個實時監聽軟鍵盤顯示或隱藏,當軟鍵盤隱藏了,做邏輯;軟鍵盤顯示出來了,做邏輯; 直接上程式碼了,下面是我寫的工具類,名字為 SoftKeyBoardListener : import android.app.Activity; import android.graphics.

Android動態網路變化

廣播接收器可以自由地對自己感興趣的廣播進行註冊,這樣當有相應的廣播接收時,廣播接收器就能收到該廣播,並在內部處理其相應的邏輯。廣播註冊的方法有兩種,在程式碼中註冊與在AndridManifest.xml中註冊,前者稱為動態註冊,後者為靜態註冊,接下來我們就說說關於動態註冊

Android App軟鍵盤按鍵的三種方式

前言: 我們在android手機上面有時候會遇到監聽手機軟鍵盤按鍵的時候,例如:我們在瀏覽器輸入url完畢後可以點選軟鍵盤右下角的“GO”按鍵載入url頁面;在點選搜尋框的時候,點選右下角的search符號鍵可以進行搜尋;或者在全部資料輸入完畢後,點選右下角的"done"

AndroidView的兩個指頭是放大和縮小

我們有時需要對view進行手勢監聽,通過兩個手指開始距離和結束距離,來判斷放大縮小,下面是實現程式碼,所有View通用。 private double nLenStart = 0;//監聽 WebView所用手勢 @Override public boolean on

xamarin android如何單擊事件

在xamarin android單擊事件是最基礎的事情,看過菜鳥上的android教程時,java寫的都是監聽事件,為一個按鈕,單選按鈕、多選按鈕的單擊事件有三種,前面兩種用的非常普遍,也很簡易,我這裡主要就是寫一下xamarin android中的監聽事件。 1.使用委託

AndroidEdittext控制元件於鍵盤中按下確認事件OnEditorActionListener

背景 Edittext作為一個常見控制元件,很多時候都有監聽鍵盤確認就執行指定邏輯的需求 實現 這個很簡單嘛,Edittext自帶有鍵盤按下事件監聽,就是OnEditorActionListener了,用就對了 問題 使用中發現,的確可以監聽鍵盤確認事