編輯:關於Android編程
如何讓你的app一直在運行狀態呢?
默認情況下,不做任何跨進程部署配置的話,每個android app運行在單獨一個虛擬機上,每個虛擬機對應一個進程。當app被系統回收或者是被用戶主動殺掉(通過app管理軟件),進程就徹底退出了。
在有些場景,app所在的進程退出了,我們希望還能做一些操作。比如,app被卸載後(卸載會先退出運行),我們希望跳轉浏覽器做一些卸載原因的調查問卷;或者為了優化體驗,提高app熱啟動速度,再比如,有些監控的app,我們希望一直在運行,否則可能數據不准確,這就需要app所在進程退出後能自我啟動。
為了做到app停止運行狀態的監控和執行回調,典型的解決方案就是多進程相互守護。
去你的手機上瞧瞧,設置-應用管理-運行中 ,有沒有發現,支付寶,QQ,微信等等App同時都有兩個進程在運行,有木有。
雙進程A和B相互守護,當A進程檢測到B進程退出時,A進程重新啟動B進程,同理當A進程退出時,B重新啟動A進程。進程的運行狀態的檢測是實現進程守護的關鍵點,比較容易想到的方案有:
通過輪訓的方式查詢遠端進程的狀態
通過發送心跳包來看是否能接受應答,無應答,遠端進程可能已經退出
通過輪訓的方式,只要通過ps命令查詢狀態,無需A和B做進程間通信;心跳包應答檢測的方式需要通過socket或者別的IPC機制來做通信。
守護進程可以是一個運行android app的進程(Dalvik進程),也可以是一個linux的原生進程,如果是Dalvik進程,當app被卸載時,進程會被退出,字節碼被移除,無法再運行任何邏輯比如跳轉浏覽器頁面。
如果守護進程是linux系統裡fork一個新的進程,與app不在同一個進程空間,當app被關閉或者殺掉或者卸載的時候,不會影響守護的運行狀態,也就是說守護還是處於運行狀態,可以執行相應操作。因此守護可以監控app的運行狀態,發現app停止運行時,可以發送命令啟動app進程,保證app的生存,這對某些系統監控的app來說至關重要。而且linux上的進程,通過android上的PackageManger獲取不到,也不會在app管理軟件的運行軟件之列,基本上不會被殺掉,守護本身可以相對可靠的生存。
本文介紹一下,如何在linux 上fork一個原生進程來守護Dalvik app的進程。
介紹幾個linux下的幾個api
int daemon (int nochdir, int noclose);
將程序將運行在後台,成為一個daemon程序,而linux下大多的服務都是以此方式運行的
int getopt_long(int argc, char * const argv[],
const char *optstring, const struct option *longopts, int *longindex);
參數解析的幫助函數,當命令參數多個而且有些課選參數時,如果只按順序接受參數容易混亂,如果按參數名來對應則便利很多,可以參考一下
FILE popen(const char command , const char *type );
popen()創建一個管道,fork一個新的進程執行shell命令,popen()的返回值是個標准I/O流,必須由pclose來終止。前面提到這個流是單向的(只能用於讀或寫)。向這個流寫內容相當於寫入該命令的標准輸入,命令的標准輸出和調用popen()的進程相同;與之相反的,從流中讀數據相當於讀取命令的標准輸出,命令的標准輸入和調用popen()的進程相同.
int system(const char * string);
函數說明
system()會調用fork()產生子進程,由子進程來調用/bin/sh-c string來執行參數string字符串所代表的命令,此命>令執行完後隨即返回原調用的進程。在調用system()期間SIGCHLD 信號會被暫時擱置,SIGINT和SIGQUIT 信號則會被忽略。
返回值
守護流程圖
守護流程圖
守護的程序代碼
int main( int argc, char* argv[] ) { signal(SIGTERM, SIG_IGN); const char *process_name = NULL; const char
*package_name = NULL;
const char *activity_name = NULL;
int interval_sec = 30;
struct option options[] = { { "process_name", required_argument, 0, 'p' }, { "package_name", required_argument, 0, 'a' }, { "activity_name", required_argument, 0, 'c' }, { "interval_sec", required_argument, 0, 'i' }, { 0, 0, 0, 0 } }; int c; for (;;) { c = getopt_long(argc, argv, "p:a:c:i:", options, NULL);
if (c == -1) { break; } switch (c) { case 'p': process_name = optarg;
break;
case 'a': package_name = optarg;
break;
case 'c': activity_name = optarg;
break;
case 'i': interval_sec = atoi(optarg);
break;
default:
exit(EXIT_FAILURE); } } if (process_name == NULL || package_name == NULL
|| activity_name == NULL)
exit(EXIT_FAILURE); daemon(1, 1); run_service(process_name, package_name, activity_name, 10); return 0; }
run_service的實現
int chk_process(const char *process_name){ FILE *stream; char *line = NULL; size_t len = 0; ssize_t read_len; stream = popen( "ps", "r" );
if (stream == NULL)
return -1;
int exists = 0;
while ( (
read_len = getline(&line, &len, stream)) != -1) { int len = strlen(line);
char *cmd = line + len;
while ( len >0 ) { len--;
if ( *cmd == ' ') { cmd++;
break; } cmd--; } if( strncmp(cmd, process_name, strlen(process_name)) == 0 ) { exists = 1;
break; } } pclose( stream );
if ( line != NULL )
free(line);
return exists; }
void run_service(const char *process_name, const char *package_name,
const char *activity_name, int interval_sec){
while (1) {
if ( chk_process(process_name) == 0) { char *pkg_activity_name = NULL; // 格式化命令 asprintf(&pkg_activity_name, "/system/bin/am start --user 0 -n %s/%s",
package_name, activity_name); system(pkg_activity_name);// 執行命令啟動app free(pkg_activity_name); } // sleep 指定時間間隔 sleep(interval_sec); }
return; }
編譯成功後生成xxx,重命名為xxx.so,把文件拷貝到libs下,這樣安裝後該文件會被同動態庫一起拷貝到data/data/app_package目錄下,編寫拷貝和chmod相關邏輯的代碼,大概流程如下
path = "/data/data/" +packageName; // 安裝後的app路徑
執行shell命令:dd if= path+lib/xxx.so of=path/xxx ;//拷貝到app路徑下,重命名為xxx
賦可執行權限 chmod 777 path/xxx;
運行可執行文件 path/xxx -p process_name -a pkgname ..(別的參數)
需要注意的一點:
這裡的操作都是通過執行shell來完成的,需要先cd到app 路徑下,才會有讀寫權限。
public static boolean execCommand(String command, String packageName) { Process process = null; try { process = Runtime.getRuntime().exec("sh"); //獲得shell. DataInputStream inputStream =
new DataInputStream(process.getInputStream()); DataOutputStream outputStream =
new DataOutputStream(process.getOutputStream());
//保證在command在自己的數據目錄裡執行,才有權限寫文件到當前目錄 outputStream.writeBytes("cd /data/data/" + packageName + "\n"); outputStream.writeBytes(command + " \n"); outputStream.writeBytes("exit\n"); outputStream.flush(); process.waitFor();
byte[] buffer = new byte[inputStream.available()]; inputStream.read(buffer); String s = new String(buffer); } catch (Exception e) {
return false; }
return true; }
編好代碼打包測試時,通過app管理界面停止app的運行,看看app是否會被重新啟動。
大家購買小米手環之後使用最多的大概就是來電提醒和檢測運動量這兩個功能了,但是很多朋友說小米手環經常抽風,來電不震動。那麼小米手環來電不震動這種情況是怎麼回事
本文為大家分享了兩段PHP分頁類,很實用,供大家參考,具體內容如下<?php class Page { private $total; //總記錄
Android多終端適配是我們在實際開發中必然會遇到也必然要解決的問題,解決多終端適配的方法有很多,比如使用百分比布局庫(percent-support-lib)、在re
在了解系統的activity,service,broadcastReceiver的啟動過程後,今天將分析下360 DroidPlugin是如何預注冊占坑的?本篇文章主要分