Android Service後臺程序守護
看了很多資料,說是在android java層實現服務自啟動,但是親測,無論怎麼做都達不到自啟動。
後面看了網上一些資料,從linux層面著手,感覺可行,不過很多資料都採用輪詢的方法去啟動服務,個人感覺輪詢從耗電等各方面考慮並不是最優方案,因為如果可以檢測到服務掛了然後直接啟動服務可能更節省效能,而android本身提供了這樣的機制,service死掉後會呼叫生命週期中的onDestroy進行銷燬。因此,我們就可以通過這樣的方法在依靠linux下孤立的子程序存活的情況下去呼叫啟動服務命令。
基本流程:
父程序fork後產生孤立的子程序A, A啟動服務程序s, s發現自己將要銷燬時依靠A程序呼叫啟動s的命令。
c關鍵程式碼:
#include <stdio.h> #include <string.h> #include <jni.h> #include <unistd.h> #include <stdlib.h> #include <sys/resource.h> #include <dirent.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/wait.h>#include <pthread.h> #define PROC_DIRECTORY "/proc/" #define CASE_SENSITIVE 1 #define CASE_INSENSITIVE 0 #define EXACT_MATCH 1 #define INEXACT_MATCH 0 #define MAX_LINE_LEN 5 #include <android/log.h> #define TAG "daemon" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__) #define LOG "Helper" void thread(char* srvname) { check_and_restart_service(srvname); } char* a; char* srvn; /** * srvname 服務名 * sd 之前建立子程序的pid寫入的檔案路徑 */ int start(int argc, char* srvname, char* sd) { pthread_t id; int ret; struct rlimit r; srvn = srvname; /** * 第一次fork的作用是讓shell認為本條命令已經終止,不用掛在終端輸入上。 * 還有一個作用是為後面setsid服務。setsid的呼叫者不能是程序組組長(group leader)。 * 此時父程序是程序組組長。 */ int pid = fork(); LOGI("fork pid: %d", pid); if (pid < 0) { LOGI("first fork() error pid %d,so exit", pid); exit(0); } else if (pid != 0) { LOGI("first fork(): I'am father pid=%d", getpid()); //exit(0); } else { // 第一個子程序 umask(0); //使用umask修改檔案的遮蔽字,為檔案賦予跟多的許可權,因為繼承來的檔案可能某些許可權被遮蔽,從而失去某些功能,如讀寫 int pid = fork(); if (pid == 0) { // 第二個子程序 FILE *fp; sprintf(sd,"%s/pid",sd); if((fp=fopen(sd,"a"))==NULL) {//開啟檔案 沒有就建立 LOGI("%s檔案還未建立!",sd); ftruncate(fp, 0); lseek(fp, 0, SEEK_SET); } fclose(fp); fp=fopen(sd,"rw"); if(fp>0){ char buff1[6]; int p = 0; memset(buff1,0,sizeof(buff1)); fseek(fp,0,SEEK_SET); fgets(buff1,6,fp); //讀取一行 LOGI("讀取的程序號:%s",buff1); if(strlen(buff1)>1){ // 有值 kill(atoi(buff1), SIGTERM); LOGI("殺死程序,pid=%d",atoi(buff1)); } } fclose(fp); fp=fopen(sd,"w"); char buff[100]; int k = 3; if(fp>0){ sprintf(buff,"%lu",getpid()); fprintf(fp,"%s\n",buff); // 把程序號寫入檔案 LOGI("寫入。。。。。"); } fclose(fp); fflush(fp); LOGI("step 2 I'am child-child pid=%d", getpid()); //step 4:修改程序工作目錄為根目錄,chdir(“/”). chdir("/"); LOGI("after step 4 ...11133.", ""); //step 5:關閉不需要的從父程序繼承過來的檔案描述符。LOGI("after step 5 ...1111.", ""); umask(0); // 關閉不必要的檔案描述符 close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); ret = pthread_create(&id, NULL, (void *) thread, srvname); LOGI("after step 5 ....222", ""); if (ret != 0) { printf("Create pthread error!\n"); exit(1); } int stdfd = open ("/dev/null", O_RDWR); dup2(stdfd, STDOUT_FILENO); dup2(stdfd, STDERR_FILENO); } else { exit(0); } } return 0; } /** * 執行命令 */ void ExecuteCommandWithPopen(char* command, char* out_result, int resultBufferSize) { FILE * fp; out_result[resultBufferSize - 1] = '\0'; fp = popen(command, "r"); if (fp) { fgets(out_result, resultBufferSize - 1, fp); out_result[resultBufferSize - 1] = '\0'; pclose(fp); } else { LOGI("popen null,so exit"); exit(0); } } /** * 檢測服務,如果不存在服務則啟動. * 通過am命令啟動一個laucher服務,由laucher服務負責進行主服務的檢測,laucher服務在檢測後自動退出 */ void check_and_restart_service(char* service) { LOGI("current process pid=",getpid()); char cmdline[200]; sprintf(cmdline, "am startservice --user 0 -n %s", service); char tmp[200]; sprintf(tmp, "cmd=%s", cmdline); ExecuteCommandWithPopen(cmdline, tmp, 200); LOGI( tmp, LOG); } jstring stoJstring(JNIEnv* env, const char* pat) { jclass strClass = (*env)->FindClass(env, "Ljava/lang/String;"); jmethodID ctorID = (*env)->GetMethodID(env, strClass, "<init>", "([BLjava/lang/String;)V"); jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat)); (*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*) pat); jstring encoding = (*env)->NewStringUTF(env, "utf-8"); return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding); } /** * 判斷是否是數字 */ int IsNumeric(const char* ccharptr_CharacterList) { //LOGI("IsNumeric: test.cpp/main: argc=%d",ccharptr_CharacterList); for (; *ccharptr_CharacterList; ccharptr_CharacterList++) if (*ccharptr_CharacterList < '0' || *ccharptr_CharacterList > '9') return 0; // false return 1; // true } //intCaseSensitive=0大小寫不敏感 int strcmp_Wrapper(const char *s1, const char *s2, int intCaseSensitive) { if (intCaseSensitive) return !strcmp(s1, s2); else return !strcasecmp(s1, s2); } //intCaseSensitive=0大小寫不敏感 int strstr_Wrapper(const char* haystack, const char* needle, int intCaseSensitive) { if (intCaseSensitive) return (int) strstr(haystack, needle); else return (int) strcasestr(haystack, needle); } /** * 通過程序名稱獲取pid */ pid_t GetPIDbyName_implements(const char* cchrptr_ProcessName, int intCaseSensitiveness, int intExactMatch) { char chrarry_CommandLinePath[100]; char chrarry_NameOfProcess[300]; char* chrptr_StringToCompare = NULL; pid_t pid_ProcessIdentifier = (pid_t) - 1; struct dirent* de_DirEntity = NULL; DIR* dir_proc = NULL; int (*CompareFunction)(const char*, const char*, int); if (intExactMatch) CompareFunction = &strcmp_Wrapper; else CompareFunction = &strstr_Wrapper; dir_proc = opendir(PROC_DIRECTORY); if (dir_proc == NULL) { perror("Couldn't open the " PROC_DIRECTORY " directory"); return (pid_t) - 2; } while ((de_DirEntity = readdir(dir_proc))) { if (de_DirEntity->d_type == DT_DIR) { if (IsNumeric(de_DirEntity->d_name)) { strcpy(chrarry_CommandLinePath, PROC_DIRECTORY); strcat(chrarry_CommandLinePath, de_DirEntity->d_name); strcat(chrarry_CommandLinePath, "/cmdline"); FILE* fd_CmdLineFile = fopen(chrarry_CommandLinePath, "rt"); //open the file for reading text if (fd_CmdLineFile) { LOGI("chrarry_NameOfProcess %s", chrarry_NameOfProcess); fscanf(fd_CmdLineFile, "%s", chrarry_NameOfProcess); //read from /proc/<NR>/cmdline fclose(fd_CmdLineFile); //close the file prior to exiting the routine chrptr_StringToCompare = chrarry_NameOfProcess; if (CompareFunction(chrptr_StringToCompare, cchrptr_ProcessName, intCaseSensitiveness)) { pid_ProcessIdentifier = (pid_t) atoi( de_DirEntity->d_name); LOGI("processName=%d, pid=%d",cchrptr_ProcessName,pid_ProcessIdentifier); closedir(dir_proc); return pid_ProcessIdentifier; } } } } } LOGI("processName=%d, pid=%d",cchrptr_ProcessName,pid_ProcessIdentifier); closedir(dir_proc); return pid_ProcessIdentifier; } /** * 檢測服務,如果不存在服務則啟動 */ void check_and_restart_activity(char* service) { char cmdline[200]; sprintf(cmdline, "am start -n %s", service); char tmp[200]; sprintf(tmp, "cmd=%s", cmdline); ExecuteCommandWithPopen(cmdline, tmp, 200); LOGI( tmp, LOG); } /** * 返回ABI給Java */ jstring Java_com_daemonprocess_fork_NativeRuntime_stringFromJNI(JNIEnv* env, jobject thiz) { #if defined(__arm__) #if defined(__ARM_ARCH_7A__) #if defined(__ARM_NEON__) #define ABI "armeabi-v7a/NEON" #else #define ABI "armeabi-v7a" #endif #else #define ABI "armeabi" #endif #elif defined(__i386__) #define ABI "x86" #elif defined(__mips__) #define ABI "mips" #else #define ABI "unknown" #endif return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI "."); } /** * jstring 轉 String */ char* jstringTostring(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = (*env)->FindClass(env, "java/lang/String"); jstring strencode = (*env)->NewStringUTF(env, "utf-8"); jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); jsize alen = (*env)->GetArrayLength(env, barr); jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE); if (alen > 0) { rtn = (char*) malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } (*env)->ReleaseByteArrayElements(env, barr, ba, 0); return rtn; } /** * 查詢程序 */ pid_t JNICALL Java_com_daemonprocess_fork_NativeRuntime_findProcess(JNIEnv* env, jobject thiz, jstring cchrptr_ProcessName) { char * rtn = jstringTostring(env, cchrptr_ProcessName); LOGI("Java_com_daemonprocess_fork_NativeRuntime_findProcess run....ProcessName:%s", rtn); //return 1; return GetPIDbyName_implements(rtn, 0, 0); //大小寫不敏感 sub串匹配 } /** * 啟動Service */ void Java_com_daemonprocess_fork_NativeRuntime_startService(JNIEnv* env, jobject thiz, jstring cchrptr_ProcessName, jstring sdpath) { char * rtn = jstringTostring(env, cchrptr_ProcessName); // 得到程序名稱 char * sd = jstringTostring(env, sdpath); LOGI("Java_com_daemonprocess_fork_NativeRuntime_startService run....ProcessName:%s", rtn); a = rtn; start(1, rtn, sd); } /** * 關閉Service */ JNIEXPORT jint JNICALL Java_com_daemonprocess_fork_NativeRuntime_stopService(JNIEnv* env, jobject thiz) { LOGI("========exit", ""); exit(0); } /** * service已經斷開,重啟服務 */ JNIEXPORT void JNICALL Java_com_daemonprocess_fork_NativeRuntime_serviceHasDisconnection (JNIEnv* env, jobject thiz){ if(srvn != NULL){ LOGI("========serviceHasDisconnection serviceprocess--%s", srvn); check_and_restart_service(srvn); } } /** * 啟動Activity */ void Java_com_daemonprocess_fork_NativeRuntime_startActivity(JNIEnv* env, jobject thiz, jstring activityName) { char * rtn = jstringTostring(env, activityName); LOGI("check_and_restart_activity run....activityName:%s", rtn); check_and_restart_activity(rtn); } JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { return result; } LOGI("JNI_OnLoad ......"); return JNI_VERSION_1_4; }
相關推薦
Android Service後臺程序守護
看了很多資料,說是在android java層實現服務自啟動,但是親測,無論怎麼做都達不到自啟動。 後面看了網上一些資料,從linux層面著手,感覺可行,不過很多資料都採用輪詢的方法去啟動服務,個人感覺輪詢從耗電等各方面考慮並不是最優方案,因為如果可以檢測到服務掛了然後直接
Android SERVICE後臺服務程序的守護
Service元件在android開發中經常遇到,其經常作為後臺服務,需要始終保持執行,負責處理一些必要(見不得人)的任務。而一些安全軟體,如360等,會有結束程序的功能,如果不做Service的保持,就會被其殺掉。在早些時候,我們可以通過在1. service中重寫onS
Android實現雙程序守護
如何保證Service不被Kill (1)onStartCommand方法,返回START_STICKY @Override public int onStartCommand(Intent intent, int flags, int star
Android NDK雙程序守護(Socket)
NDK雙程序守護(單工機制) 最近在系統的學習Android NDK開發於是想著把剛學完的一個知識點總結寫一篇部落格,就是我今天要說的NDK程序守護。目前市面上的應用,貌似除了微信和手Q都會比較擔心被使用者或者系統(廠商)殺死的問題。而最近學的雙程序守護就能很
Android Service後臺多執行緒壓縮並提交圖片及資料
手機端發帖,多張圖片上傳是個問題.最近重構專案程式碼,正好碰到這個,這裡把解決的方案整理,以備後用. 方案原理: 建立上傳任務表, 帖子內容釋出的時候將資料存放到任務表中,並傳遞資料到service中. 啟動服務,遍
android 判斷App程序是否存在,處在前臺,後臺,前後臺切換,service是否執行,設定為系統app
1.判斷程序是否存在 //判斷是否在主程序,這個方法判斷程序名或者pid都可以,如果程序名一樣那pid肯定也一樣 //true:當前程序是主程序 false:當前程序不是主程序 public boolean isUIProcess() { ActivityManager
對Android程序守護、鬧鐘後臺被殺死的研究
最近公司要求要做一個提醒功能,一說到提醒,那肯定就和鬧鐘差不多的意思,那麼肯定就要用到AlarmManager。 但是,我們知道,android系統很坑爹,不同的廠商對rom的定製,導致對程序的管理都
Android雙程序守護service保活
package com.guardservice; import com.guardservice.aidl.GuardAidl; import android.app.Notification; import android.app.PendingIntent; import android.app.Se
android 使用Service進行雙程序守護,防止程序被殺
public class MyService extends Service { String msg; public MyService() { msg = "Msg from MyService"; } @Override public IBind
如何讓你的App永遠在後臺存活:對Android程序守護、鬧鐘後臺被殺死的研究
相關閱讀: 公眾號:Java和Android架構 關注回覆:Android,iOS,PHP,js,HTML5,Python,機器學習 ,AI,大資料,Hadoop,c++,J2EE等關鍵字就能免費獲取學習資料視訊 最近公司要求要做一個提醒
Android 通過JNI實現守護程序,使Service服務不被殺死
開發一個需要常住後臺的App其實是一件非常頭疼的事情,不僅要應對國內各大廠商的ROM,還需要應對各類的安全管家... 雖然不斷的研究各式各樣的方法,但是效果並不好,比如工作管理員把App幹掉,服務就起不來了... 網上搜尋一番後,主要的方法有以下幾種方法,但其實也都
雙程序守護保證Service後臺任務一直執行不被殺死
本來想實現一個一直執行的Service,使用alarmManger定時開啟Service實現重新整理功能,可是發現在android5.0之後會在清理後臺任務時殺死,在android5.0之前的如果使用360軟體也能被殺死。我用的是小米6.0,和華為5.0測試的都會被系統自帶
Android學習筆記11-Service後臺服務(2)
Android學習筆記11-Service後臺服務(二)-非同步訊息處理機制和AsyncTask 一,訊息機制的簡介 在Android中使用訊息機制,首先想到的是Handler,Handler是Android訊息機制的上層介面,Handler的使用方法很簡單,通過它可以把一個
Android學習筆記10-Service後臺服務(1)
Android學習筆記10-Service後臺服務(一)-Android 多執行緒程式設計 1,Service簡介 Service時Android程式中的四大元件之一,它和Activity都是Context的子類,只不過Service沒有UI介面,是在後臺執行的元件。 Servi
Android NDK(C++) 雙程序守護
雙程序守護如果從程序管理器觀察會發現新浪微博、支付寶和QQ等都有兩個以上相關程序,其中一個就是守護程序,由此可以猜到這些商業級的軟體都採用了雙程序守護的辦法。 什麼是雙程序守護呢?顧名思義就是兩個程序互相監視對方,發現對方掛掉就立刻重啟!不知道應該把這樣的一對程序是叫做相依為命呢還是難兄難弟好呢,但總之
UNIX中後臺程序與守護程序
守護程序最重要的特性是後臺執行。在這一點上DOS下的常駐記憶體程式TSR與之相似。其次,守護程序必須與其執行前的環境隔離開來。這些環 境包括未關閉的檔案描述符,控制終端,會話和程序組,工作目錄以及檔案建立掩模等。這些環境通常是守護程序從執行它的父程序(特別是shell)中繼承下 來的。最後,守護程序的啟動方式
Android程序守護,讓APP在系統記憶體中常駐(一)
其實我們開發者並不想讓自己做的應用變成流氓軟體,但是沒辦法, 你的老闆需要,你要不想讓你的應用常駐,那咱就常駐不了了。。。所以說,言歸正傳。。。 第一篇準備使用系統的服務保活。如果想看提高app的程序等級來實現應用保活,可以直接進行點選Androi
Android程序守護,讓APP在系統記憶體中常駐(二)
昨天晚上寫了用系統服務等方法來實現應用保活。今天寫一下用提高app的程序等級來實現應用保活。想看直接呼叫系統方法保活應用的可以點選Android程序守護,讓APP在系統記憶體中常駐(一)進行跳轉。 一:第一種實現思路,建立廣播接收者來監聽系統關屏亮屏
探討Android實現後臺(Service)按鍵監聽的功能
一般來說需要做這個功能的大多數是機頂盒或者智慧電視,也就是AndroidTv。剛好這兩天公司的機頂盒有這麼一個需求,針對遙控器某些特殊按鍵,實現按鍵的監聽,並實現相應的功能。研究了一下,大概有這麼一些辦法。 第一種辦法是廣播,一般來說系統對於音量鍵和Hom
go lang 後臺(守護程序)執行(daemon)
引用:https://bitbucket.org/kardianos/service service will install / un-install, start / stop, and run a program as a service (daemon).