Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 再談Android App

再談Android App

編輯:關於Android編程

如何讓你的app一直在運行狀態呢?

默認情況下,不做任何跨進程部署配置的話,每個android app運行在單獨一個虛擬機上,每個虛擬機對應一個進程。當app被系統回收或者是被用戶主動殺掉(通過app管理軟件),進程就徹底退出了。

在有些場景,app所在的進程退出了,我們希望還能做一些操作。比如,app被卸載後(卸載會先退出運行),我們希望跳轉浏覽器做一些卸載原因的調查問卷;或者為了優化體驗,提高app熱啟動速度,再比如,有些監控的app,我們希望一直在運行,否則可能數據不准確,這就需要app所在進程退出後能自我啟動。

為了做到app停止運行狀態的監控和執行回調,典型的解決方案就是多進程相互守護。

去你的手機上瞧瞧,設置-應用管理-運行中 ,有沒有發現,支付寶,QQ,微信等等App同時都有兩個進程在運行,有木有。

雙進程相互守護

雙進程A和B相互守護,當A進程檢測到B進程退出時,A進程重新啟動B進程,同理當A進程退出時,B重新啟動A進程。進程的運行狀態的檢測是實現進程守護的關鍵點,比較容易想到的方案有:

  1. 通過輪訓的方式查詢遠端進程的狀態

  2. 通過發送心跳包來看是否能接受應答,無應答,遠端進程可能已經退出

  3. 在Application生命周期終止時,也就是onTerminate方法裡發送廣播通知對方自己即將滅亡

通過輪訓的方式,只要通過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原生進程守護

介紹幾個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路徑

  1. 執行shell命令:dd if= path+lib/xxx.so of=path/xxx ;//拷貝到app路徑下,重命名為xxx

  2. 賦可執行權限 chmod 777 path/xxx;

  3. 運行可執行文件 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是否會被重新啟動。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved