Android 通過JNI實現守護程式

發表於2016-10-25

開發一個需要常住後臺的App其實是一件非常頭疼的事情,不僅要應對國內各大廠商的ROM,還需要應對各類的安全管家…  雖然不斷的研究各式各樣的方法,但是效果並不好,比如工作管理員把App幹掉,服務就起不來了…

網上搜尋一番後,主要的方法有以下幾種方法,但其實也都治標不治本:

1、提高Service的優先順序:這個,也只能說在系統記憶體不足需要回收資源的時候,優先順序較高,不容易被回收,然並卵…

2、提高Service所在程式的優先順序:效果不是很明顯

3、在onDestroy方法裡重啟service:這個倒還算挺有效的一個方法,但是,直接幹掉程式的時候,onDestroy()方法都進不來,更別想重啟了

4、broadcast廣播:和第3種一樣,沒進入onDestroy(),就不知道什麼時候發廣播了,另外,在Android4.4以上,程式完全退出後,就不好接收廣播了,需要在發廣播的地方特定處理

5、放到System/app底下作為系統應用:這個也就是平時玩玩,沒多大的實際意義。

6、Service的onStartCommand()方法,返回START_STICKY,這個主要是針對系統資源不足而導致的服務被關閉,還是有一定的道理的。

在這裡推薦一篇文章:【騰訊Bugly】Android 程式保活招式大全

應對的方法是有,實現起來都比較繁瑣,而且穩定性也不好。如果你自己可以定製ROM,那就有很多種辦法了,比如把你的應用加入白名單,或是多安裝一個沒有圖示的app作為守護程式… 不過這個思想大部分程式都不適用。

那麼,有沒有辦法在一個APP裡面,開啟一個子執行緒,在主執行緒被幹掉了之後,子執行緒通過監聽、輪詢等方式去判斷服務是否存在,不存在的話則開啟服務。答案自然是肯定的,通過JNI的方式,fork()出一個子執行緒作為守護程式,輪詢監聽服務狀態。守護程式(Daemon)是執行在後臺的一種特殊程式。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。而守護程式的會話組和當前目錄,檔案描述符都是獨立的。後臺執行只是終端進行了一次fork,讓程式在後臺執行,這些都沒有改變。

那麼我們先來看看Android4.4的原始碼,ActivityManagerService(原始碼/frameworks/base/services/core/Java/com/Android/server/am/ActivityManagerService.java)是如何關閉在應用退出後清理記憶體的:

應用退出後,ActivityManagerService就把主程式給殺死了,但是,在Android5.0中,ActivityManagerService卻是這樣處理的:

雖只差了一句程式碼,差別卻很大。Android5.0在應用退出後,ActivityManagerService不僅把主程式給殺死,另外把主程式所屬的程式組一併殺死,這樣一來,由於子程式和主程式在同一程式組,子程式在做的事情,也就停止了…要不怎麼說Android5.0在安全方面做了很多更新呢…

那麼,有沒有辦法讓子程式脫離出來,不要受到主程式的影響,當然也是可以的。那麼,在C/C++層是如何實現的呢?先上關鍵程式碼:

這裡有幾個重點需要理解一下:

1、為什麼要fork兩次?第一次fork的作用是為後面setsid服務。setsid的呼叫者不能是程式組組長(group leader),而第一次呼叫的時候父程式是程式組組長。第二次呼叫後,把前面一次fork出來的子程式退出,這樣第二次fork出來的子程式,就和他們脫離了關係。

2、setsid()作用是什麼?setsid() 使得第二個子程式是會話組長(sid==pid),也是程式組組長(pgid == pid),並且脫離了原來控制終端。故不管控制終端怎麼操作,新的程式正常情況下不會收到他發出來的這些訊號。

3、umask(0)的作用:由於子程式從父程式繼承下來的一些東西,可能並未把許可權繼承下來,所以要賦予他更高的許可權,便於子程式操作。

4、chdir (“/”);作用:程式活動時,其工作目錄所在的檔案系統不能卸下,一般需要將工作目錄改變到根目錄。

5、程式從建立它的父程式那裡繼承了開啟的檔案描述符。如不關閉,將會浪費系統資源,造成程式所在的檔案系統無法卸下以及引起無法預料的錯誤。所以在最後,記得關閉掉從父程式繼承過來的檔案描述符。

然後,在上面的程式碼中開啟執行緒後做的事,就是迴圈去startService(),程式碼如下:

這兩個啟動服務的函式,裡面就涉及到一些Android和Linux的命令了,這裡我就不細說了。特別是am,挺強大的功能的,不僅可以開啟服務,也可以開啟廣播等等…然後呼叫ndk-build命令進行編譯,生成so庫。

20160119155020137

C/C++端關鍵的部分主要是以上這些,接下來就是Java端呼叫。

首先來看一下so庫的載入類,以及C++函式的呼叫:

然後,我們在收到開機廣播後,啟動該服務。

Service服務裡面,就可以做該做的事情。

當然,也不要忘記在Manifest.xml檔案配置receiver和service:

run起來,在程式應用裡面,結束掉這個程式,不一會了,又自動起來了~~~~跟流氓軟體一個樣,沒錯,就是這麼賤…

20160119161147528

這邊是執行在谷歌的原生系統上,Android版本為5.0… 在其他系統下穩定性還遠遠不足,但是要真正做到殺不死服務幾乎是不可能的。 總結一下就是:服務常駐要應對的不是各種難的技術,而是各大ROM。QQ為什麼不會被殺死,是因為國內各大ROM不想讓他死…

本文主要提供的是一個思路,實現還有諸多不足之處,菜鳥之作,不喜勿噴。

最後附上本例的原始碼:Android 通過JNI實現雙守護程式

相關文章