贴一个进程双守护,利用文件锁的特性来实现
2015-04-10 16:56
337 查看
/* * SSIDMonitor.h * * Created on: 2015-3-19 * Author: hgm */ #ifndef SSIDMONITOR_H_ #define SSIDMONITOR_H_ #include <jni.h> #ifdef __cplusplus extern "C" { #endif /* * Class: com_feizhang_ssidmonitor_MainActivity * Method: uninstall * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_feizhang_ssidmonitor_MainActivity_uninstall(JNIEnv *,jobject, jstring,jint,jint,jstring); #ifdef __cplusplus } #endif #endif
这个是一个典型的java调用c语言的接口,贴在这里不是贴错了,而是本来就是为了实现一个安卓app而做的。顺便回忆一下c++的对象模型,因为看着这个为java调用的c接口是十分非常的顺眼啊。
JNIEXPORT dllexport 不解释,告诉编译器该函数导出
JNICALL stdcall 不解释,告诉编译器该函数的参数压栈方式
void 函数返回值
Java_com_feizhang_ssidmonitor 包名
MainActivity需要扩展的java类
uninstall 需要扩展的java类成员函数
JNIENV*进程执行环境变量,可以理解为虚拟机句柄
jobject,扩展的java类实例
后面四个为成员函数参数
#include <stdio.h> #include <jni.h> #include <malloc.h> #include <string.h> #include <strings.h> #include <stdlib.h> #include <unistd.h> #include <android/log.h> #include "SSIDMonitor.h" #include <sys/socket.h> #include <sys/types.h> #include <sys/file.h> #include <sys/stat.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #define LOG_TAG "System.out.c" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) static const char* active_name = "com.feizhang.ssidmonitor/com.feizhang.ssidmonitor.MainActivity"; static const char* connect_path = "/data/data/com.feizhang.ssidmonitor/my.sock"; static int using_sdk_version = 0; /* * ret = 0 文件已经上锁 * ret = 1 文件上锁 * ret = -1 出错 * * flock锁的释放非常具有特色, * 即可调用LOCK_UN参数来释放文件锁, * 也可以通过关闭fd的方式来释放文件锁(flock的第一个参数是fd), * 意味着flock会随着进程的关闭而被自动释放掉。 * * 再此我们使用了这个函数的这个特性用来检测被监听进程是否还在存活 * 之所以如此是因为安卓系统更新后(包括各种管家升级后)在清除进程时 * 不再优雅清除而是直接kill掉,导致以前在析构函数中重启服务无法使 * 用 */ static int connected_fd = -1; static int connected_alive(int get_lock,int closefd){ int flag = get_lock ? (LOCK_EX|LOCK_NB) : (LOCK_UN); connected_fd = open(connect_path,O_RDWR|O_CREAT,0644); errno = 0; if(-1 == connected_fd){ LOGE("open file failed : %d(%s)",errno,strerror(errno)); return -1; } int ret = flock(connected_fd,flag); if(-1 == ret && EWOULDBLOCK != errno){ LOGE("process is alive : ... %d(%s)",errno,strerror(errno)); close(connected_fd); return -1; } LOGD("process locked file : ret = %d , errno = %d(%s)",ret,errno,strerror(errno)); if(closefd) close(connected_fd); return 0 == ret; } /* * 重新启动宿主进程 */ static void excute_active(){ int ret = 0; if(using_sdk_version < 17){ ret = execlp("am", "am", "start", "-n", active_name,(char*)NULL); }else{ ret =execlp("am", "am", "start", "--user", "0","-n",active_name,(char*)NULL); } if(-1 == ret){ LOGE("Execute a new active failed : %d(%s)",errno,strerror(errno)); } } /** * 返回值 char* 这个代表char数组的首地址 * Jstring2CStr 把java中的jstring的类型转化成一个c语言中的char 字符串 */ char* Jstring2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); //String jstring strencode = env->NewStringUTF("GB2312"); // 得到一个java字符串 "GB2312" jmethodID mid = env->GetMethodID(clsstring, "getBytes","(Ljava/lang/String;)[B"); //[ String.getBytes("gb2312"); jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode); // String .getByte("GB2312"); jsize alen = env->GetArrayLength(barr); // byte数组的长度 jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*) malloc(alen + 1); //"\0" memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); // return rtn; } /* * 扩展宿主进程的行为 * 因为用java写一个守护进程比较困难 * 所以在此使用ndk进行扩展,开启一个守护进程 * 来监视宿主进程是否被杀死或者被卸载掉 * */ extern "C" JNIEXPORT void JNICALL Java_com_feizhang_ssidmonitor_MainActivity_uninstall( JNIEnv * env, jobject obj, jstring packageDir, jint sdkVersion,jint sleepTime,jstring reportUrl) { // 1,将传递过来的java的包名转为c的字符串 char * pd = Jstring2CStr(env, packageDir); char * rp = Jstring2CStr(env,reportUrl); using_sdk_version = sdkVersion; /* * 本函数调用多次导致守护进程也启动多次 * 原理: * 当宿主进程第二次启动时,如果第一次 * */ if(connected_alive(1,0) <= 0){ LOGD("local port is using daemon process is running ..."); return; } LOGD("packagedDir = %s,sleepTime = %d,reportURL = %s",pd,sleepTime,rp); // 2,创建当前进程的克隆进程 pid_t pid = fork(); // 3,根据返回值的不同做不同的操作,<0,>0,=0 if (pid < 0) { // 说明克隆进程失败 LOGD("current crate process failure"); return; } if (pid > 0) { // 说明克隆进程成功,而且该代码运行在父进程中 LOGD("crate process success,current parent pid = %d", pid); free(pd); free(rp); return; } //释放有可能继承自文件句柄以及锁资源 close(connected_fd); // 说明克隆进程成功,而且代码运行在子进程中 LOGD("crate process success,current child pid = %d", pid); // 4,在子进程中监视/data/data/包名这个目录 for (; JNI_TRUE; sleep(sleepTime)) { FILE* file = fopen(pd, "rt"); if (file == NULL) { break; } fclose(file); LOGD("app run normal,will be checked service ... %d",sleepTime); if(1 == connected_alive(1,1)){ excute_active(); //重启应用,守护进程退出 return ; } } // 应用被卸载了,通知系统打开用户反馈的网页 LOGD("app uninstall,current sdkversion = %d,url = %s", sdkVersion,rp); if (using_sdk_version < 17) { // Android4.2以前的版本无需指定用户 execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d",rp, (char*) NULL); return; } // Android4.2系统之后支持多用户操作,所以得指定用户 execlp("am", "am", "start", "--user", "0", "-a","android.intent.action.VIEW", "-d", rp, (char*) NULL); }
注释已经很清楚了,就不解释了
相关文章推荐
- 在Linux下利用crond实现一个定时任务并完成一个守护(精灵)进程
- 自己动手实现一个守护进程,当控制台窗口关闭时还可以在后台运行。每隔一秒钟向my.log文件中插入一条记录
- 如何实现多进程写一个文件
- C#利用Mutex互斥量实现同时只有一个进程实例在运行
- 利用HTML5的一个重要特性 —— DeviceOrientation来实现手机网站上的摇一摇功能
- 利用HTML5的一个重要特性 —— DeviceOrientation来实现手机网站上的摇一摇功能
- 如何实现多进程写一个文件
- 利用MySQL的一个特性实现MySQL查询结果的分页显示
- 利用HTML5的一个重要特性 —— DeviceOrientation来实现手机网站上的摇一摇功能
- (转)linux用文件锁实现保证一个程序只能启动一个进程
- 利用Linux守护进程机制完成一个简单系统监控demo
- 利用HTML5的一个重要特性—DeviceOrientation来实现手机网站上的摇一摇功能(转)
- linux 进程(关于守护进程、检查一个进程是否活着、如何写一个进程号文件)
- linux用文件锁实现保证一个程序只能启动一个进程
- 利用IO的知识,实现一个程序列出某个目录下的所有的”.java”文件。
- 利用HTML5的一个重要特性 —— DeviceOrientation来实现手机网站上的摇一摇功能
- (1)知识准备【利用objective-c的runtime特性,结合FMDB实现一个轻量级的ORM】
- linux用文件锁实现保证一个程序只能启动一个进程
- linux 进程(关于守护进程、检查一个进程是否活着、如何写一个进程号文件)
- 利用HTML5的一个重要特性 —— DeviceOrientation来实现手机网站上的摇一摇功能