zoukankan      html  css  js  c++  java
  • 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手机卫士的反馈地址,仅供大家学习、测试使用~~~

    技术方案有一个硬伤,就是每1s就需要轮询目录是否存在,对资源消耗较大,第二版将会解决这一问题。思路是今天想到了一个Android自API1就有的一个类FileObserver,这个类用于监听某个文件的变化状态,如果是目录,这个类还可以监听其子目录及子目录文件的变化状态,通过阅读FileObserver源码,发现其实现利用了Linux内核中一个重要的机制inotify,它是一个内核用于通知用户空间程序文件系统变化的机制,详情可参考http://en.wikipedia.org/wiki/Inotify,里面对inotify有比较详细的说明。

      使用inotify的好处就在于不需要每1s的轮询,这样就不会无谓地消耗系统资源,使用inotify时会用read()方法阻塞进程,直到收到IN_DELETE通知,此时进程重新被唤醒,执行反馈处理流程。

      由于Activity代码和Android.mk文件和第一版并无实质性区别,这里就不贴代码了,直接贴出C端进程的实现代码吧。

    核心——native方法头文件:

    复制代码
     1 /* 头文件begin */
     2 #include <jni.h>
     3 #include <stdlib.h>
     4 #include <stdio.h>
     5 #include <string.h>
     6 #include <android/log.h>
     7 #include <unistd.h>
     8 #include <sys/inotify.h>
     9 #include <fcntl.h>
    10 /* 头文件end */
    11 
    12 /* 宏定义begin */
    13 //清0宏
    14 #define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)
    15 
    16 //LOG宏定义
    17 #define LOG_INFO(tag, msg) __android_log_write(ANDROID_LOG_INFO, tag, msg)
    18 #define LOG_DEBUG(tag, msg) __android_log_write(ANDROID_LOG_DEBUG, tag, msg)
    19 #define LOG_WARN(tag, msg) __android_log_write(ANDROID_LOG_WARN, tag, msg)
    20 #define LOG_ERROR(tag, msg) __android_log_write(ANDROID_LOG_ERROR, tag, msg)
    21 /* 宏定义end */
    22 
    23 #ifndef _Included_main_activity_UninstalledObserverActivity
    24 #define _Included_main_activity_UninstalledObserverActivity
    25 #ifdef __cplusplus
    26 extern "C" {
    27 #endif
    28 
    29 #undef main_activity_UninstalledObserverActivity_MODE_PRIVATE
    30 #define main_activity_UninstalledObserverActivity_MODE_PRIVATE 0L
    31 #undef main_activity_UninstalledObserverActivity_MODE_WORLD_READABLE
    32 #define main_activity_UninstalledObserverActivity_MODE_WORLD_READABLE 1L
    33 #undef main_activity_UninstalledObserverActivity_MODE_WORLD_WRITEABLE
    34 #define main_activity_UninstalledObserverActivity_MODE_WORLD_WRITEABLE 2L
    35 #undef main_activity_UninstalledObserverActivity_MODE_APPEND
    36 #define main_activity_UninstalledObserverActivity_MODE_APPEND 32768L
    37 #undef main_activity_UninstalledObserverActivity_MODE_MULTI_PROCESS
    38 #define main_activity_UninstalledObserverActivity_MODE_MULTI_PROCESS 4L
    39 #undef main_activity_UninstalledObserverActivity_BIND_AUTO_CREATE
    40 #define main_activity_UninstalledObserverActivity_BIND_AUTO_CREATE 1L
    41 #undef main_activity_UninstalledObserverActivity_BIND_DEBUG_UNBIND
    42 #define main_activity_UninstalledObserverActivity_BIND_DEBUG_UNBIND 2L
    43 #undef main_activity_UninstalledObserverActivity_BIND_NOT_FOREGROUND
    44 #define main_activity_UninstalledObserverActivity_BIND_NOT_FOREGROUND 4L
    45 #undef main_activity_UninstalledObserverActivity_BIND_ABOVE_CLIENT
    46 #define main_activity_UninstalledObserverActivity_BIND_ABOVE_CLIENT 8L
    47 #undef main_activity_UninstalledObserverActivity_BIND_ALLOW_OOM_MANAGEMENT
    48 #define main_activity_UninstalledObserverActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
    49 #undef main_activity_UninstalledObserverActivity_BIND_WAIVE_PRIORITY
    50 #define main_activity_UninstalledObserverActivity_BIND_WAIVE_PRIORITY 32L
    51 #undef main_activity_UninstalledObserverActivity_BIND_IMPORTANT
    52 #define main_activity_UninstalledObserverActivity_BIND_IMPORTANT 64L
    53 #undef main_activity_UninstalledObserverActivity_BIND_ADJUST_WITH_ACTIVITY
    54 #define main_activity_UninstalledObserverActivity_BIND_ADJUST_WITH_ACTIVITY 128L
    55 #undef main_activity_UninstalledObserverActivity_CONTEXT_INCLUDE_CODE
    56 #define main_activity_UninstalledObserverActivity_CONTEXT_INCLUDE_CODE 1L
    57 #undef main_activity_UninstalledObserverActivity_CONTEXT_IGNORE_SECURITY
    58 #define main_activity_UninstalledObserverActivity_CONTEXT_IGNORE_SECURITY 2L
    59 #undef main_activity_UninstalledObserverActivity_CONTEXT_RESTRICTED
    60 #define main_activity_UninstalledObserverActivity_CONTEXT_RESTRICTED 4L
    61 #undef main_activity_UninstalledObserverActivity_RESULT_CANCELED
    62 #define main_activity_UninstalledObserverActivity_RESULT_CANCELED 0L
    63 #undef main_activity_UninstalledObserverActivity_RESULT_OK
    64 #define main_activity_UninstalledObserverActivity_RESULT_OK -1L
    65 #undef main_activity_UninstalledObserverActivity_RESULT_FIRST_USER
    66 #define main_activity_UninstalledObserverActivity_RESULT_FIRST_USER 1L
    67 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_DISABLE
    68 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_DISABLE 0L
    69 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_DIALER
    70 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_DIALER 1L
    71 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SHORTCUT
    72 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SHORTCUT 2L
    73 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SEARCH_LOCAL
    74 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
    75 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SEARCH_GLOBAL
    76 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
    77 
    78 /*
    79  * Class:     main_activity_UninstalledObserverActivity
    80  * Method:    init
    81  * Signature: ()V
    82  */
    83 JNIEXPORT void JNICALL Java_main_activity_UninstalledObserverActivity_init(JNIEnv *, jobject);
    84 
    85 #ifdef __cplusplus
    86 }
    87 #endif
    88 #endif
    复制代码

    核心——native方法实现:

    复制代码
     1 /* 头文件begin */
     2 #include "main_activity_UninstalledObserverActivity.h"
     3 /* 头文件end */
     4 
     5 #ifdef __cplusplus
     6 extern "C" {
     7 #endif
     8 
     9 /* 内全局变量begin */
    10 static char c_TAG[] = "UninstalledObserverActivity.init";
    11 static jboolean b_IS_COPY = JNI_TRUE;
    12 /* 内全局变量 */
    13 
    14 /*
    15  * Class:     main_activity_UninstalledObserverActivity
    16  * Method:    init
    17  * Signature: ()V
    18  */
    19 JNIEXPORT void JNICALL Java_main_activity_UninstalledObserverActivity_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 failed !!!"), &b_IS_COPY));
    34     }
    35     else if (pid == 0)
    36     {
    37         //子进程注册"/data/data/pym.test.uninstalledobserver"目录监听器
    38         int fileDescriptor = inotify_init();
    39         if (fileDescriptor < 0)
    40         {
    41             LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
    42                     , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_init failed !!!"), &b_IS_COPY));
    43 
    44              exit(1);
    45         }
    46 
    47         int watchDescriptor;
    48         watchDescriptor = inotify_add_watch(fileDescriptor, "/data/data/pym.test.uninstalledobserver", IN_DELETE);
    49         if (watchDescriptor < 0)
    50         {
    51             LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
    52                     , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_add_watch failed !!!"), &b_IS_COPY));
    53 
    54             exit(1);
    55         }
    56 
    57         //分配缓存,以便读取event,缓存大小=一个struct inotify_event的大小,这样一次处理一个event
    58         void *p_buf = malloc(sizeof(struct inotify_event));
    59         if (p_buf == NULL)
    60         {
    61             LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
    62                     , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "malloc failed !!!"), &b_IS_COPY));
    63 
    64             exit(1);
    65         }
    66         //开始监听
    67         LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
    68                     , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "start observer"), &b_IS_COPY));
    69         size_t readBytes = read(fileDescriptor, p_buf, sizeof(struct inotify_event));
    70 
    71         //read会阻塞进程,走到这里说明收到目录被删除的事件,注销监听器
    72         free(p_buf);
    73         inotify_rm_watch(fileDescriptor, IN_DELETE);
    74 
    75         //目录不存在log
    76         LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
    77                     , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "uninstalled"), &b_IS_COPY));
    78 
    79         //执行命令am start -a android.intent.action.VIEW -d http://shouji.360.cn/web/uninstall/uninstall.html
    80         execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d", "http://shouji.360.cn/web/uninstall/uninstall.html", (char *)NULL);
    81     }
    82     else
    83     {
    84         //父进程直接退出,使子进程被init进程领养,以避免子进程僵死
    85     }
    86 }
    87 
    88 #ifdef __cplusplus
    89 }
    90 #endif
    复制代码

    http://www.cnblogs.com/zealotrouge/p/3157126.html

    http://www.cnblogs.com/zealotrouge/p/3159772.html

  • 相关阅读:
    Mac下eclipse安装SVN插件
    python中函数参数*args和**kw的区别
    Linux下安装JDK1.8
    SpringBoot Profiles特性
    一张图讲解单机FastDFS图片服务器安装步骤(修订版)
    一张图讲解最少机器搭建FastDFS高可用分布式集群安装说明
    Zookeeper作为配置中心使用说明
    一张图秒懂微服务网络架构
    TestNG的静态方法mock的步骤
    Java中indexOf的用法
  • 原文地址:https://www.cnblogs.com/shanzei/p/3160705.html
Copyright © 2011-2022 走看看