這幾天,一位做Android的朋友和我探討了一個問題:因為業務需求的原因,在自己的App長時間不使用被kill掉之后,如何讓它再重新運行起來。
雖然,我本身很排斥這種做法,有點類似“流氓軟件”的行為,但是還是查詢了資料,大概想了一個實現的方式,和大家一起分享。
其實,這個問題可以簡單的看作:如何編寫一個守護進程。
使用C/C++編寫一個守護進程的.so程序,Android端通過JNI調用。該進程監聽當前的目標程序進程,如果目標程序被kill掉了,再重新start一下,大概的思路就是這樣。偽代碼如下:
int pid = fork(); if(pid <0) { printf("Error\n"); exit(0); } else if(pid > 0) { printf("this is father\n"); exit(0); } else if(pid == 0) // child1 { setsid(); int pid = fork(); if(pid == 0) // child2 { while(1) { if(App is killed) start App again; } } }
這其中,缺少了:
①unmask(),因為從父進程繼承下來了一些東西,所以需要設置一定的權限進行操作。
②chdir("/")。
③關閉從父進程繼承而來的不需要的文件描述符,否則就可能造成資源的浪費。
為什么要進行兩次fork()?
在這段偽代碼中,進行了兩次fork()操作,其實很多文章在描述守護進程時都只是僅僅的進行了一次fork操作就結束了,這里我想仔細講一講原因。
首先,setsid()函數的作用。一般而言,父進程和子進程都處在一個session當中,父進程是session的領頭進程。如果當父進程(領頭進程)被殺死之后,那么同一個session中的所有進程都會被殺死,或者成為孤兒進程而被init托管。所以,我們需要讓子進程調用setsid(),創建一個新的session並將自己設置為該session的領頭進程(若領頭進程調用setsid()則沒有任何效果)。這樣,如果父進程被kill掉,因為他們並不在一個session中,所以子進程仍然可以繼續執行。由於session對控制終端的獨占性,進程同時與控制終端脫離。
session中包含了很多東西,如:控制終端、進程組等等。如果我們只fork()一次,將第一個由父進程創建出的子進程分離出來作為守護進程,一般情況下也是沒有什么問題的。但是,如果此時通過什么方式通過此進程創建出了一個與自己的session相關聯的控制終端,那么則會產生一定的影響。所以,則有了第二次fork()的意義。第二次fork使用子進程創建出了一個“孫子進程”,並且我們還是使用第一個父進程創建出的子進程進行setsid()但它並不是守護進程,該孫子進程進行守護進程相關的操作。這就有效的避免了上文提到的問題的產生,因為子進程創建出了一個新的session,並且作為該session的領頭進程,同時這個session包含了該孫子進程且它並不是領頭進程。那么該孫子進程就永遠無法創建出一個控制終端,也就沒有任何影響。
存在的問題
代碼中使用輪詢的方式來查詢,App進程是否被kill了,這樣效率十分低下,會導致手機的電量損耗很快。是否可以通過進程間通信的方式,如:socket等等,進行相關操作。