监听自身应用卸载,并在卸载之后,使用libcurl三方库进行一次三方请求,或者启动网页。

该博客分享了如何监听Android应用卸载的过程,通过检测内部存储器中应用文件夹的删除。作者提供了一个sample,当应用被卸载时,利用libcurl库执行HTTP请求。文章详细介绍了监听代码、curl库的集成和使用,以及Android.mk配置和在MainActivity中的调用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近研究了一下如何监听自身应用卸载,最后,做了一个sample。

sample 下载地址:http://download.youkuaiyun.com/detail/keanbin/6991979

主要代码:

1. 监听本应用卸载,其实是监听应用在内部存储器中的文件夹是否被删除。

ListenUninstallProcess.c

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/inotify.h>

#include "AndroidLog.h"
#include "CurlHttpOperate.h"

/* 宏定义begin */
//清0宏
#define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)

//LOG宏定义
#define LOG_INFO(tag, msg) __android_log_write(ANDROID_LOG_INFO, tag, msg)
#define LOG_DEBUG(tag, msg) __android_log_write(ANDROID_LOG_DEBUG, tag, msg)
#define LOG_WARN(tag, msg) __android_log_write(ANDROID_LOG_WARN, tag, msg)
#define LOG_ERROR(tag, msg) __android_log_write(ANDROID_LOG_ERROR, tag, msg)

//#define FEEDBACK_URL "http://www.baidu.com"
//#define LISTEN_FILE_PATH "/data/data/com.keanbin.app"

/* 内全局变量begin */
static jboolean b_IS_COPY = JNI_TRUE;

/*
 * 启动进程,监听指定文件,如果文件被删除,就启动网页。
 *
 * listenFilePathJStr: 监听的文件路径
 * urlJStr: 网址
 */
void Java_com_keanbin_testlistenselfuninstall_ListenUninstallProcess_startBrowser(
		JNIEnv* env, jobject thiz, jstring listenFilePathJStr, jstring urlJStr,
		jint androidSdkVersion) {

	const char *listenFilePath;
	const char *url;

	//初始化log
	LOGI("jni startBrowser()");

	listenFilePath = (*env)->GetStringUTFChars(env, listenFilePathJStr, NULL);
	if (listenFilePath == NULL) {
		return;
	}

	url = (*env)->GetStringUTFChars(env, urlJStr, NULL);
	if (url == NULL) {
		return;
	}

	LOGI("jni startBrowser() listenFilePath = %s , url = %s",
			listenFilePath, url);

	//fork子进程,以执行轮询任务
	pid_t pid = fork();
	if (pid < 0) {
		//出错log
		LOGI("fork failed !!!");
	} else if (pid == 0) {
		//子进程注册"/data/data/pym.test.uninstalledobserver"目录监听器
		int fileDescriptor = inotify_init();
		if (fileDescriptor < 0) {
			LOGI("inotify_init failed !!!");

			exit(1);
		}

		int watchDescriptor;
		watchDescriptor = inotify_add_watch(fileDescriptor, listenFilePath,
				IN_DELETE);
		if (watchDescriptor < 0) {
			LOGI("inotify_add_watch failed !!!");

			exit(1);
		}

		//分配缓存,以便读取event,缓存大小=一个struct inotify_event的大小,这样一次处理一个event
		void *p_buf = malloc(sizeof(struct inotify_event));
		if (p_buf == NULL) {
			LOGI("malloc failed !!!");
			exit(1);
		}

		//开始监听
		LOGI("start observer");
		size_t readBytes = read(fileDescriptor, p_buf,
				sizeof(struct inotify_event));

		//read会阻塞进程,走到这里说明收到目录被删除的事件,注销监听器
		free(p_buf);
		inotify_rm_watch(fileDescriptor, IN_DELETE);

		//目录不存在log
		LOGI("uninstalled");

		LOGI("Android Sdk Version: %d \n", androidSdkVersion);

		if (androidSdkVersion < 17) {
			//执行命令am start -a android.intent.action.VIEW -d http://www.baidu.com
			execlp("am", "am", "start", "-a", "android.intent.action.VIEW",
					"-d", url, (char *) NULL);
		} else {
			//4.2以上的系统由于用户权限管理更严格,需要加上 --user 0
			execlp("am", "am", "start", "--user", "0", "-a",
					"android.intent.action.VIEW", "-d", url, (char *) NULL);
		}

	} else {
		//父进程直接退出,使子进程被init进程领养,以避免子进程僵死
	}
}

/*
 * 启动进程,监听指定文件,如果文件被删除,就进行http请求。
 *
 * listenFilePathJStr: 监听的文件路径
 * urlJStr: 网址
 * paramsStr: 参数。参数格式:变量名=值,多个变量用"&"连接起来,比如“key1=value1&key2=value2”。
 * httpMethod:请求方式。get方式:1,post方式:2。
 */
void Java_com_keanbin_testlistenselfuninstall_ListenUninstallProcess_httpRequest(
		JNIEnv* env, jobject entryObject, jstring listenFilePathJStr,
		jstring urlJStr, jstring paramsStr, jint httpMethod) {

	const char *listenFilePath;

	//初始化log
	LOGI("jni httpRequest()");

	listenFilePath = (*env)->GetStringUTFChars(env, listenFilePathJStr, NULL);
	if (listenFilePath == NULL) {
		return;
	}

	LOGI("jni httpRequest() listenFilePath = %s", listenFilePath);

	//fork子进程,以执行轮询任务
	pid_t pid = fork();
	if (pid < 0) {
		//出错log
		LOGI("fork failed !!!");
	} else if (pid == 0) {
		//子进程注册"/data/data/pym.test.uninstalledobserver"目录监听器
		int fileDescriptor = inotify_init();
		if (fileDescriptor < 0) {
			LOGI("inotify_init failed !!!");

			exit(1);
		}

		int watchDescriptor;
		watchDescriptor = inotify_add_watch(fileDescriptor, listenFilePath,
				IN_DELETE);
		if (watchDescriptor < 0) {
			LOGI("inotify_add_watch failed !!!");

			exit(1);
		}

		//分配缓存,以便读取event,缓存大小=一个struct inotify_event的大小,这样一次处理一个event
		void *p_buf = malloc(sizeof(struct inotify_event));
		if (p_buf == NULL) {
			LOGI("malloc failed !!!");
			exit(1);
		}

		//开始监听
		LOGI("start observer");
		size_t readBytes = read(fileDescriptor, p_buf,
				sizeof(struct inotify_event));

		//read会阻塞进程,走到这里说明收到目录被删除的事件,注销监听器
		free(p_buf);
		inotify_rm_watch(fileDescriptor, IN_DELETE);

		//目录不存在log
		LOGI("uninstalled");

		curlHttp(env, entryObject, urlJStr, paramsStr, httpMethod);
	} else {
		//父进程直接退出,使子进程被init进程领养,以避免子进程僵死
	}
}

ListenUninstallProcess.java:

package com.keanbin.testlistenselfuninstall;

import android.content.Context;

/**
 * C层启动另一个进程监听本应用卸载。
 * 
 * @author keanbin
 * 
 */
public class ListenUninstallProcess {

	private Context mContext;

	static {
		System.loadLibrary("listen-uninstall-process-jni");
	}

	public ListenUninstallProcess(Context context) {
		mContext = context;
	}

	/**
	 * 使用方法:在监听开机启动广播的接收器中执行,还有安装完后执行,可以保存个配置信息来标记。
	 * 
	 * @param androidSdkVersion
	 */
	private native void startBrowser(String listenFilePath, String url,
			int androidSdkVersion);

	/**
	 * 使用方法:在监听开机启动广播的接收器中执行,还有安装完后执行,可以保存个配置信息来标记。
	 * 
	 * @param url
	 * @param parames
	 * @param httpMethod
	 */
	private native void httpRequest(String listenFilePath, String url,
			String parames, int httpMethod);

	/**
	 * 启动进程监听本应用卸载,卸载之后启动浏览器。 使用方法:在监听开机启动广播的接收器中执行,还有安装完后执行,可以保存个配置信息来标记。
	 * 
	 * @param url
	 * @param androidSdkVersion
	 */
	public void startProcessStartBrowser(String url, int androidSdkVersion) {

		startBrowser(getListenFilePath(), url, androidSdkVersion);
	}

	/**
	 * 启动进程监听本应用卸载,卸载之后发起HTTP请求。 使用方法:在监听开机启动广播的接收器中执行,还有安装完后执行,可以保存个配置信息来标记。
	 * 
	 * @param url
	 * @param parames
	 *            参数。参数格式:变量名=值,多个变量用"&"连接起来,比如“key1=value1&key2=value2”。
	 * @param httpMethod
	 *            请求方式。get方式:1,post方式:2。
	 */
	public void startProcessHttpRequest(String url, String parames,
			int httpMethod) {

		httpRequest(getListenFilePath(), url, parames, httpMethod);
	}

	private String getListenFilePath() {
		return mContext.getFilesDir().getAbsolutePath();
	}
}

2. 使用curl三方库来进行http请求:

下载curl三方库的源码,放进jni目录下新建的curl目录中。

封装使用curl三方库的函数(CurlHttpOperate.c):

#include "CurlHttpOperate.h"
#include "AndroidLog.h"

static size_t HTTPData(void *buffer, size_t size, size_t nmemb, void *userData) {
	int len = size * nmemb;
	pageInfo_t *page = (pageInfo_t *) userData;

	if (buffer && page->data && (page->len + len < (16 * 1024))) {
		memcpy(&page->data[page->len], buffer, len);
		page->len += len;
	}
	return len;
}

/*
 * http请求
 *
 * urlJStr: 网址
 * paramsStr: 参数。参数格式:变量名=值,多个变量用"&"连接起来,比如“key1=value1&key2=value2”。
 * httpMethod:请求方式。get方式:1,post方式:2。
 */
jstring curlHttp(JNIEnv* env, jobject entryObject, jstring urlJStr,
		jstring paramsStr, jint httpMethod) {
	pageInfo_t page;
	CURL *curl;
	CURLcode res;
	char *buffer;

	const char *url;
	const char *params;

	url = (*env)->GetStringUTFChars(env, urlJStr, NULL);
	if (url == NULL) {
		return NULL; /* OutOfMemoryError already thrown */
	}

	//api 参数
	params = (*env)->GetStringUTFChars(env, paramsStr, NULL);

	LOGI("jni curlHttp(): url = %s, params = %s", url, params);

	page.data = (char *) malloc(16 * 1024);
	page.len = 0;
	if (page.data)
		memset(page.data, 32, 16 * 1024);

	curl = curl_easy_init();

	if (curl) {
		if (httpMethod == HTTP_METHOD_GET) {
			char* tmp = "?";
			int urllen = strlen(url) + strlen(tmp) + strlen(params);

			char* getUrl = (char *) malloc(sizeof(char) * urllen + 1);
			memset(getUrl, 0, sizeof(char) * urllen + 1);

			strcat(getUrl, url);
			strcat(getUrl, tmp);
			strcat(getUrl, params);

			curl_easy_setopt(curl, CURLOPT_URL, getUrl);
			curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HTTPData);
			curl_easy_setopt(curl, CURLOPT_WRITEDATA, &page);
		} else {
			curl_easy_setopt(curl, CURLOPT_URL, url);
			curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HTTPData);
			curl_easy_setopt(curl, CURLOPT_WRITEDATA, &page);

			curl_easy_setopt(curl, CURLOPT_POSTFIELDS, params);
			curl_easy_setopt(curl, CURLOPT_POST, 1);
		}

		LOGI("jni curlHttp(): curl_easy_perform");
		res = curl_easy_perform(curl);
		curl_easy_cleanup(curl);

		LOGI("jni curlHttp(): res = %d", res);

		if (res == 0) {
			if (page.data) {
				LOGI("jni curlHttp(): result = %s", page.data);
				return (*env)->NewStringUTF(env, page.data);
			}
		}

		buffer = (char *) malloc(1024);
		sprintf(buffer, "jni curlHttp()", res);
		return (*env)->NewStringUTF(env, buffer);
	} else {
		LOGI("jni curlHttp(): Unable to init cURL");
		return (*env)->NewStringUTF(env, "Unable to init cURL");
	}
}


3.Android.mk:

LOCAL_PATH:= $(call my-dir)

CFLAGS := -Wpointer-arith -Wwrite-strings -Wunused -Winline \
 -Wnested-externs -Wmissing-declarations -Wmissing-prototypes -Wno-long-long \
 -Wfloat-equal -Wno-multichar -Wsign-compare -Wno-format-nonliteral \
 -Wendif-labels -Wstrict-prototypes -Wdeclaration-after-statement \
 -Wno-system-headers -DHAVE_CONFIG_H

include $(CLEAR_VARS)
include $(LOCAL_PATH)/curl/lib/Makefile.inc


LOCAL_SRC_FILES := $(addprefix curl/lib/,$(CSOURCES))
LOCAL_CFLAGS += $(CFLAGS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/curl/include/ $(LOCAL_PATH)/curl/lib

LOCAL_COPY_HEADERS_TO := libcurl
LOCAL_COPY_HEADERS := $(addprefix curl/include/curl/,$(HHEADERS))

LOCAL_MODULE:= libcurl

include $(BUILD_STATIC_LIBRARY)

# Build shared library now
# curltest

include $(CLEAR_VARS)

LOCAL_MODULE := listen-uninstall-process-jni
LOCAL_SRC_FILES := CurlHttpOperate.c ListenUninstallProcess.c
LOCAL_STATIC_LIBRARIES := libcurl
LOCAL_C_INCLUDES += $(LOCAL_PATH)/curl/include $(LOCAL_PATH)/curl/lib $(LOCAL_PATH)/include
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY) 

4. 使用代码(MainActivity.java):

package com.keanbin.testlistenselfuninstall;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		ListenUninstallProcess listenUninstallProcess = new ListenUninstallProcess(
				MainActivity.this);

		// listenUninstallProcess.startProcessStartBrowser(
		// "http://192.168.1.250/testpost.php",
		// android.os.Build.VERSION.SDK_INT);

		listenUninstallProcess.startProcessHttpRequest(
				"http://192.168.1.250/testpost.php",
				"name=kenabin&method=post", 2);
		//
		// listenUninstallProcess.startProcessHttpRequest(
		// "http://192.168.1.250/testget.php",
		// "name=keanbin&method=gethttp", 1);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值