android守護進程
⑴ AndroidFramework 之啟動 ServiceManager
本文源碼基於 Android 10 ,涉及相關源碼如下。
ServiceManagaer 是 Binder 的守護進程,在 Binder 機制中起著重要的作用。本文將從源碼的角度對其進行分析,整體流程如下:
時序圖如下。
先來看看 ServiceManager 是如何啟動的:
在 Zygote 一文中說過, init 進程啟動的第二階段會解析 init.rc 文件。
在這之後會觸發 trigger init 。
結合 init.rc 看看 action init 做了什麼。
當觸發 trigger init 後,會啟動 servicemanager 服務,其聲明如下。
對應的執行文件為 /system/bin/servicemanager ,在編譯前位於 frameworks/native/cmds/servicemanager 下,來看看 Android.bp 。
其對應的源碼為 service_manager.c 和 binder.c ,入口函數 main() 位於 servicemanager.c 。
啟動完 ServiceManager 後會打開 Binder 驅動。
在 main() 中首先調用 binder_open() 。
binder_open() 主要做了如下事情:
給結構體 binder_state 分配內存。
系統調用 open() 打開 /dev/binder ,如果打開驅動失敗,則執行 fail_open 釋放內存。
簡單的解釋一下什麼是系統調用?
由於需要限制不同的程序之間的訪問能力,防止程序獲取別的程序的內存數據, CPU 劃分出兩個許可權等級, 用戶態 和 內核態 。
所有的用戶程序都是運行在用戶態,但有時需要做一些內核態的事情,而唯一可以做這些事情的就是操作系統,所以程序需要向操作系統發起請求,以程序的名字來執行這些操作。這時就需要一個從用戶態切換到內核態但不能控制內核態中執行的機制,這種機制就是 系統調用 。
系統調用 ioctl() 傳入 BINDER_VERSION 命令獲取 Binder 驅動版本,對比版本是否一致,不一致則執行 fail_open 釋放內存。
系統調用 mmap() 映射 128kb 的內存空間,即把 Binder 驅動文件的 128kb 映射到內存空間供 ServiceManager 使用,內存映射失敗則執行 fail_map ,關閉 fd 並釋放內存。
ServiceManager 進程 mmap 的內存大小可以通過 adb shell 命令查看。
可以看到內存映射地址為 0xf10f8000 ~ 0xf1118000 ,差為 0x20000 即十進制的 128kb 。
打開 Binder 驅動後會將 ServiceManager 設置為上下文管理者。
調用 binder_become_context_manager() 。
android 10 新增 BINDER_SET_CONTEXT_MGR_EXT 命令來設置安全的上下文管理者,如果設置失敗,則使用原有的 BINDER_SET_CONTEXT_MGR 命令來設置上下文管理者,兩者區別在於是否攜帶參數。
最後會進入循環,從 Binder 驅動讀取和解析數據。
調用 binder_loop() 進入循環,不斷地通過系統調用 ioctl() 從 Binder 驅動讀取數據,並通過 binder_parse() 進行數據解析。
注意這里調用 binder_loop() 傳入的 svcmgr_handler() ,後面會使用到。
binder_write() 會封裝 struct binder_write_read ,並通過系統調用 ioctl() 將對應的命令傳遞給 Binder 驅動。
binder_parse() 用來解析從 Binder 驅動讀取到的數據,然後根據不同的命令執行對應的操作。
因為 cmd 命令可能有多個,所以通過 while 循環每次處理一個 cmd 命令,多 cmd 的結構大致如下圖所示。
這里重點看下 BR_TRANSACTION 命令。
BR_TRANSACTION 是 Binder 驅動向 Server 端發送請求數據。
binder_transaction_data 的結構如下,其表明了 transcation 傳輸的具體語義,語義碼記錄在 code 中,不同語義碼攜帶的數據是不同的,這些數據由 data 指定。
在解析完 binder_transaction_data 的具體語義後,會調用前面傳給 binder_loop() 的 svcmgr_handler() ,其實就是 switch case 語義碼做不同的事情。
ServiceManager 的功能其實很簡單:
至此 ServiceManager 就分析完了。
⑵ Android 日誌系統分析(二):logd
logd 守護進程是日誌系統的管家,內部維持三個日誌 Socket : logd、logdr、logdw 來與客戶端進行通信。同時負責維護幾個環形緩沖區,用於存放系統中的各種日誌,緩沖區包含 main、system、events、radio、crash、kernel ;但是在 Android 5.0 之前, logd 進程並不存在,日誌是保留在 /dev/log/main、/dev/log/system、/dev/log/radio、/dev/log/event 等節點中,但是這樣面臨的一個問題就是當 Android 系統大版本升級時, linux kernel 需要升級對應的日誌驅動,因此在後續的版本中就有了 logd 進程。
在 Android 日誌系統分析(一):概述 一文中,總結了整個日誌讀寫的主要流程,因此對於 logd 進程是如何同外界溝通進而讀寫日誌的過程不再贅述,而著重於 logd 本身的一些知識點,這里先看一下 logd 的系統框圖:
知識點:
① logd 是日誌系統的核心進程,由 init 啟動,是屬於守護進程常駐後台
② logd 維護各個日誌節點緩存隊列,提供 socket 介面進行讀、寫、控制功能
③ logd 進程啟動後,分別啟動 LogReader、LogListener、CommandListener 三個線程,監聽並處理來自三個 socket 的消息。在收到消息後,會通過 LogBuffer 類保存日誌到對應的 RAM buffer 中
④ LogAudit 模塊用於接收 Kernel selinux 信息,即可以在用戶空間列印 selinux 日誌信息
⑤ LogKlog 用於接收 kernel 日誌信息,通過設置 property ,可以通過 logcat 命令讀取內核日誌
⑥ LogStatistics 是日誌統計模塊,默認開啟統計數據較少,僅能以 pid/uid 緯度統計列印日誌的數量。如果設置了 logd.statistic = true 。會列印更多緯度的統計信息,包括哪些 pid/uid/tid/TAG 日誌量比較大,可用於日誌裁剪相關
在 main 函數中,會打開 /dev/kmsg 來讀取內核日誌,通過 LogKlog 來進行存儲;若是配置了 ro.logd.kernel 屬性,則打開 /proc/kmsg 讀取內核日誌;
logd 作為 Native Service ,系統啟動時會讀取 init.rc 腳本去啟動,它的相關屬性被定義在 logd.rc 文件中:
這里主要分為兩部分: 啟動 logd 服務 和 啟動 logd-reinit 服務 (在Android 10 上添加了 logd-auditctl 服務,目的是為了限制 selinux denia列印日誌為5秒一次);先來看一下 啟動 logd 服務 的同時做了些什麼:
① 創建 logd、logdr、logdw 這三個 socket 為後面的通信做准備
② logdw 定義為 dgram 類型的 socket ,類似與 UDP類型的 Socket ,這么做的原因是考慮到性能問題,在多個進程同時寫日誌的情況下, write 函數寫入到 socket 的 buffer 中即可返回,這樣不會 block 業務邏輯太長時間。如果是 TCP 類型的 Socket ,客戶端需要等到 TCP 收到 ACK 響應才能返回,這樣就會過多的消耗性能和資源;
啟動 logd-reinit 服務:
這個服務的主要作用是重新初始化 logd 的 LogBuffer,在配置中 oneshot 表示開機只啟動一次。在上面的 main.cpp 中的 main 函數內, logd 在啟動後,會創建一個線程 reinit_thread_start () ,當 logd-reinit 傳入參數 reinit 後,進行功能執行:
① 如果 reinit 啟動後,並且 /deg/kmsg 打開成功,把 logd.daemon: renit 寫入 kmsg
② 重新初始化各個 log buffer 的大小,以及其他參數的初始化,但不會重新生成 LogBuffer 對象
main.cpp##main
main.cpp#reinit_thread_start()
[ 1 ] 深入理解安卓日誌系統(logcat / liblog / logd)
[ 2 ] Android10.0 日誌系統分析(二)-logd、logcat架構分析及日誌系統初始化
⑶ Android保活系列之——雙進程守護
近期跳槽到玩加電競,加之英雄聯盟雲頂之弈排位模式的推出,導致個人精力有限沒有時間和心情去寫相關的博客。
在Context中提供了bindService的方法
綁定服務是客戶端--伺服器介面中的伺服器。組件和服務進行綁定後,可以發送請求、接收響應、執行進程間通信(IPC)。這里的伺服器模型不同於網路C-S模型而是針對於Android應用不同的功能進行進程劃分,例如提供視頻播放的進程我們可以把它當做視頻播放伺服器,我們UI層屬於客戶端,客戶端想要調用視頻播放,需要用IPC方式通過bind服務的方式調用視頻播放服務。(PS:如果大家對IPC不太熟悉可以參考我的其他文章 Android跨進程通信技術的使用及原理 )客戶端可以通過調用bindService()綁定到服務。調用時,必須提供ServiceConnection的實現,後者會監控與服務的連接及銷毀。
藉助bindService的機制,我們可以在主進程創建時創建一個守護進程,並監聽守護進程的連接及銷毀,再守護進程bindService中綁定主進程,這樣即使進程因為鎖屏、內存等問題殺掉後,也會被守護進程拉起,這就是Android中雙進程守護的概念。當然早在PC時期,網吧的計時系統就使用了雙進程守護機制,防止被惡意殺掉。
實現方式就不寫了,網路上一搜一堆,這里給出一篇文章
Android 雙進程守護
寫的中規中矩,如果不清楚如何實現可以看一下。
谷歌官方8.0變更
當版本>8.0時,如果需要在後台啟動服務需要調用startForegroundService。並且在serivce中onCreate方法必須在5秒內調用startForeground ,向通知欄發一個通知告知用戶你的App正在後台運行,否則就會拋出異常
8.0通知適配
保活分兩種:拉活、保活
拉活和保活是相輔相成的。在6.0版本以後的機型上,系統殺應用是按照進程組殺的,會直接導致雙進程守護失效。那麼因此就不使用雙進程了么?
1.低版本雙進程守護是依然親測好使。
2.雙進程守護可以和後面講到的 賬號同步、外部PUSH、JobScheler
相結合,可以規避開系統殺進程組的問題。使雙進程守護功能可以兼容高版本。
講的有些籠統,我尋思的發Dome、寫例子,但這樣解決不了根本問題,只有懂得了思路,了解了什麼是雙進程守護,才能在開發中隨機應變。只能說Android水太深,大家需要細心細心再細心。
⑷ Android 守護進程的實現方式
在我們進行應用開發時,會遇到上級的各種需求,其中有一條 剛需: 後台保活 ,更有甚者:
我要我們的應用永遠活在用戶的手機後台不被殺死 —— 這都 TM 的扯淡
除了系統級別的應用能持續運行,所有三方程序都有被殺死的那一天!當然 QQ/微信/陌陌 等會好一些,因為他們已經深入設備的 心 ;
我們能做的只是通過各種手段盡量讓我們的程序在後台運行的時間長一些,或者在被幹掉的時候,能夠重新站起來,而且這個也不是每次都有效的,也是不能在所有的設備的上都有效的;要做到後台進程保活,我們需要做到兩方便:
要實現實現上邊所說,通過下邊幾點來實現,首先我們需要了解下進程的優先順序劃分:
Process Importance 記錄在 ActivityManager.java 類中:
了解進程優先順序之後,我們還需要知道一個進程回收機制的東西;這里參考 AngelDevil 在博客園上的一篇文章:
Android 的 Low Memory Killer 基於 Linux 的 OOM 機制,在 Linux 中,內存是以頁面為單位分配的,當申請頁面分配時如果內存不足會通過以下流程選擇bad進程來殺掉從而釋放內存:
在 Low Memory Killer 中通過進程的 oom_adj 與佔用內存的大小決定要殺死的進程, oom_adj 越小越不容易被殺死;
Low Memory Killer Driver 在用戶空間指定了一組內存臨界值及與之一一對應的一組 oom_adj 值,當系統剩餘內存位於內存臨界值中的一個范圍內時,如果一個進程的 oom_adj 值大於或等於這個臨界值對應的 oom_adj 值就會被殺掉。
下邊是表示 Process State (即老版本里的 OOM_ADJ )數值對照表,數值越大,重要性越低,在新版SDK中已經在 android 層去除了小於0的進程狀態
Process State (即老版本的 OOM_ADJ )與 Process Importance 對應關系,這個方法也是在 ActivityManager.java 類中,有了這個關系,就知道可以知道我們的應用處於哪個級別,對於我們後邊優化有個很好地參考
一般情況下,設備端進程被幹掉有一下幾種情況
由以上分析,我們可以可以總結出,如果想提高我們應用後台運行時間,就需要提高當前應用進程優先順序,來減少被殺死的概率
分析了那麼多,現在對Android自身後台進程管理,以及進程的回收也有了一個大致的了解,後邊我們要做的就是想盡一切辦法去提高應用進程優先順序,降低進程被殺的概率;或者是在被殺死後能夠重新啟動後台守護進程
第一種方式就是利用系統漏洞,使用 startForeground() 將當前進程偽裝成前台進程,將進程優先順序提高到最高(這里所說的最高是服務所能達到的最高,即1);
這種方式在 7.x 之前都是很好用的,QQ、微信、IReader、Keep 等好多應用都是用的這種方式實現;因為在7.x 以後的設備上,這種偽裝前台進程的方式也會顯示出來通知欄提醒,這個是取消不掉的,雖然 Google 現在還沒有對這種方式加以限制,不過這個已經能夠被用戶感知到了,這種方式估計也用不了多久了
下邊看下實現方式,這邊這個 VMDaemonService 就是一個守護進程服務,其中在服務的 onStartCommand() 方法中調用 startForeground() 將服務進程設置為前台進程,當運行在 API18 以下的設備是可以直接設置,API18 以上需要實現一個內部的 Service ,這個內部類實現和外部類同樣的操作,然後結束自己;當這個服務啟動後就會創建一個定時器去發送廣播,當我們的核心服務被幹掉後,就由另外的廣播接收器去接收我們守護進程發出的廣播,然後喚醒我們的核心服務;
當我們啟動這個守護進程的時候,就可以使用以下 adb 命令查看當前程序的進程情況(需要 adb shell 進去設備),
為了等下區分進程優先順序,我啟動了一個普通的後台進程,兩外兩個一個是我們啟動的守護進程,一個是當前程序的核心進程,可以看到除了後台進程外,另外兩個進程都帶有 isForeground=true 的屬性:
然後我們可以用下邊的命令查看 ProcessID
有了 ProcessID 之後,我們可以根據這個 ProcessID 獲取到當前進程的優先順序狀態 Process State ,對應 Linux 層的 oom_adj
可以看到當前核心進程的級別為 0 ,因為這個表示當前程序運行在前台 UI 界面,守護進程級別為 1 ,因為我們利用漏洞設置成了前台進程,雖然不可見,但是他的級別也是比較高的,僅次於前台 UI 進程,然後普通後台進程級別為 4 ;當我們退到後台時,可以看到核心進程的級別變為 1 了,這就是因為我們利用 startForeground() 將進程設置成前台進程的原因,這樣就降低了進程被系統回收的概率了;
可以看到這種方式確實能夠提高進程優先順序,但是在一些國產的設備上還是會被殺死的,比我我測試的時候小米點擊清空最近運行的應用進程就別幹掉了;當把應用加入到設備白名單里就不會被殺死了,微信就是這樣,人家直接裝上之後就已經在白名單里了,我們要做的就是在用戶使用中引導他們將我們的程序設置進白名單,將守護進程和白名單結合起來,這樣才能保證我們的應用持續或者
Android系統在5.x以上版本提供了一個 JobSchele 介面,系統會根據自己實現定時去調用改介面傳遞的進程去實現一些操作,而且這個介面在被強制停止後依然能夠正常的啟動;不過在一些國產設備上可能無效,比如小米;
下邊是 JobServcie 的實現:
我們要做的就是在需要的時候調用 JobSchele 的 schele 來啟動任務;剩下的就不需要關心了, JobSchele 會幫我們做好,下邊就是我這邊實現的啟動任務的方法:
在實現 Service 類時,將 onStartCommand() 返回值設置為 START_STICKY ,利用系統機制在 Service 掛掉後自動拉活;不過這種方式只適合比較原生一些的系統,像小米,華為等這些定製化比較高的第三方廠商,他們都已經把這些給限制掉了;
這種方式在以下兩種情況無效:
事事沒有絕對,萬物總有一些漏洞,就算上邊的那些方式不可用了,後邊肯定還會出現其他的方式;我們不能保證我們的應用不死,但我們可以提高存活率;
其實最好的方式還是把程序做好,讓程序本身深入人心,別人喜歡你了,就算你被幹掉了,他們也會主動的把你拉起來,然後把你加入他們的白名單,然後我們的目的就實現了不是 😁 ~
⑸ Android 使用MarsDaemon進程常駐
在特定的業務場景中,我們可能會需要app在後台做一些事情,比如上傳數據之類的操作,並且希望這種操作及時在程序退出之後依然可以繼續進行。因此也就理所當然的想到了使用Service進行處理。 但是 ,在特定條件(app進入後台+設備內存不足+進程佔用的內存足夠大)的情況下,Service會非常容易在幾分鍾內被系統幹掉,因此提高Service的存活率至關重要。
此方法企圖利用Service是生命周期去調用其本身,事實證明這種方法是無效的,在進程被殺死時,Service根本不會執行onDestroy就被直接清出內存了,因此靠自身的力量提高存活率的方式也就不可行了。
導入項目之後
之後不要忘記導入mole
此處將process1作為主要進程,process2作為守護進程。MainService中執行主要的業務邏輯,Receiver1、GuardService、Receiver2都是額外創建的,裡面不要做任何事情,都是空實現就好。
由於我們的Application一般都會集成其他的Application,因此需要在attachBaseContext中初始化DaemonClient,然後調用onAttachBaseContext即可實現
使用Marsdaemon提高Service存活率的方式雖然有一定效果,但是在Android5.0之後的版本中,並不可靠,並且還有如下幾個缺陷。
因此,Marsdaemon不應是大家頻繁使用的功能,特殊情況下可以應急即可。
⑹ 在Android設備上怎麼調試守護進程
其實網上有很多類似的文章,但是你會發現幾乎都不可重現,要麼是細節沒講清楚,要麼是壓根自己沒有真正去試過。這里,我僅給出自己用gdb和gdbserver調試android native code的實際過程,希望對大家有用。
註:以調試mediaserver進程為例.
第一步:你需要下載android,以debug方式編譯,並以生成的image起模擬器或者設備。
第二步:你需要從「http://developer.download.nvidia.com/tegra/files/tegra-gdb-20100430.zip」下載一個gdb,覆蓋到android源碼中gdb對應的位置。
第三步:adb shell到設備,並起gdbserver偵聽目標進程:
adb shell
gdbserver :5039 /system/bin/mediaserver
第四步: 建立pc機和設備的消息連接:
adb forward tcp:5039 tcp:5039
第五步: 使用gdb調試目標進程:
cd android_src
prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gdb out/debug/target/proct/generic/symbols/system/bin/mediaserver
第六步: 設置符號表:
set solib-absolute-prefix /your_android_src_path/out/debug/target/proct/generic/symbols
set solib-search-path /your_android_src_path/out/debug/target/proct/generic/symbols/system/lib
第七步: 使gdb和gdb server建立連接:
target remote :5039
第八步: ok. 現在可以使用gdb的命令進行調試,譬如next\break\step\info thread等.
⑺ Android-保活
Low Memory Killer
打開的應用越多,後台緩存的進程也越多。因為系統出於體驗和性能上的考慮,app在退到後台時系統並不會真正的kill掉這個進程,而是將其緩存起來。於是在系統內存不足的情況下,系統開始依據自身的一套進程回收機制來判斷要kill掉哪些進程,以騰出內存來供給需要的app, 這套殺進程回收內存的機制就叫 Low Memory Killer。
進程的優先順序(by:https://developer.android.google.cn/guide/components/activities/process-lifecycle?hl=zh-cn)
前台進程
用戶正在使用的程序,一般系統是不會殺死前台進程的,除非用戶強制停止應用或者系統內存不足等極端情況會殺死。
可見進程
用戶正在使用,看得到,但是摸不著,沒有覆蓋到整個屏幕,只有屏幕的一部分可見進程不包含任何前台組件,一般系統也是不會殺死可見進程的,除非要在資源吃緊的情況下,要保持某個或多個前台進程存活施。
服務進程
在內存不足以維持所有前台進程和可見進程同時運行的情況下,服務進程會被殺死。
後台進程
系統可能隨時終止它們,回收內存。
空進程
某個進程不包含任何活躍的組件時該進程就會被置為空進程,完全沒用,殺了它只有好處沒壞處,第一個干它。
內存閾值
內存閾值在不同的手機上不一樣,一旦低於該值,Android便會殺死對應優先順序的進程。一旦低於該值,Android便開始按逆序關閉進程。即優先順序從最高的空進程開始,逆序關閉,直到內存足夠。
如何判斷進程的優先順序?
通過 oom_adj 值,判斷進程的優先順序,不同手機的oom_adj 值可能不一樣。
我們了解這個有什麼用呢?PS:了解這個你才能想辦法保證自己怎麼不被殺掉。
網上的一些方案和自己認為有用的方案
1 開啟一個像素Activity(偽前台進程)
在鎖屏的時候在本進程開啟一個Activity,為了欺騙用戶,讓這個Activity的大小是1像素,並且透明無切換動畫,在開屏幕的時候,把這個Activity關閉掉,所以這個就需要監聽系統鎖屏廣播。
我們的應用就始終和前台進程是一樣的優先順序了,為了省電,系統檢測到鎖屏事件後一段時間內會殺死後台進程,如果採取這種方案,就可以避免了這個問題,但是還是有被殺掉的可能。
Android5.0以下:
Process.killProcessQuiet(pid);
Android5.0以後:
Process.killProcessQuiet(app.pid);
Process.killProcessGroup(app.info.uid, app.pid);
應用退出後,ActivityManagerService不僅把主進程給殺死,另外把主進程所屬的進程組一並殺死,這樣一來,由於子進程和主進程在同一進程組,子進程在做的事情,也就停止了。
2 相互喚醒(廣播喚醒)
相互喚醒的意思就是,假如你手機里裝了支付寶、淘寶、天貓、UC等阿里系的app,那麼你打開任意一個阿里系的app後,有可能就順便把其他阿里系的app給喚醒了。這個完全有可能的。此外,開機,網路切換、拍照、拍視頻時候,利用系統產生的廣播也能喚醒app,不過Android N已經將這三種廣播取消了。
3 JobSheler機制保活(不推薦)
JobSheler是作為進程死後復活的一種手段,native進程方式最大缺點是費電, Native 進程費電的原因是感知主進程是否存活有兩種實現方式,在 Native 進程中通過死循環或定時器,判斷主進程是否存活,當主進程不存活時進行拉活。其次5.0以上系統不支持。 但是JobSheler可以替代在Android5.0以上native進程方式,這種方式即使用戶強制關閉,部分廠商手機(如:華為)也能被拉起來,但AndroidN失效。
4 粘性服務&與系統服務捆綁()
這個是系統自帶的,onStartCommand方法必須具有一個整形的返回值,這個整形的返回值用來告訴系統在服務啟動完畢後。Service的onStartCommand方法里返回 STATR_STICK,onDestory中start自啟(准確的將算不上進程拉活,只能算service自啟,force_stop後不能正常拉活)。
5 監聽第三方app開放的靜態廣播(同2)
需要大量反編譯app去找開放的靜態廣播,而且不保證長期有效,可能第三方開放廣播在版本升級時改為私有廣播,如果自己公司有多個app,可廣播互相拉起。
6 NDK+Socket通過fork實現進程保活方案()
實現進程守護實際是守護app的主要服務,當app主進程被系統kill時,主要服務也會殺死,守護進程將其喚醒。
實現原理圖:
進程保活方案調研結果
未能實現真正意義上的進程保活。
光從保活這一點來說,綁定一個像素activity和循環一個無聲的聲音這種方法比較好,但是對用戶來說太流氓了,不推薦。 對於有硬性需求的,可以引導用戶加入白名單。至於推送, 可以嘗試集成多個推送方案,小米,華為等都有推送sdk,在對應手機上可以確保收到消息, 然後像網路這種是多app公用通道的,也就是手機中有一個使用網路推送的app被允許後台啟動,就能讓其他app收到推送。隨著Android版本的不斷更新及國內廠商對ROM的不斷優化,如何最大可能的對進程保活,是Android一道需要長期學習/鑽研的學問,也是Android開發者不得不面對的問題。
引援:https://www.jianshu.com/p/1c353edf73ba
⑻ Android應用開發需要具備哪些知識
l 熟練運用Android下的自定義控制項。x0dx0al 熟練掌握Android系統架構,對Android的各個層次的開發有一定的認識。x0dx0al 熟練掌握android下的XML,JSON,HTML的解析,熟練掌握各種數據的存儲方式,能使用MVC獨立開發客戶端程序,熟悉安卓下的GPS定位。x0dx0al 熟悉android 的JNI 開發,通過JNI實現JAVA與C/C++程序間的調用及回調。x0dx0al 熟練掌握UI設計、常用布局、動畫特效。熟悉安卓下的消息推送機制原理。x0dx0al 熟悉Android下的安全機制。如獲取系統最高許可權使得不能停止服務,利用守護進程保護服務不被停止,清理內存等。x0dx0al 熟悉Android下網路通信機,對Socket通信、TCP、Http有較深刻的了解和經驗。x0dx0al 熟練應用Mysql,SQLServer,及安卓下的SQLite資料庫操作及編碼。x0dx0al 熟練掌握HTML,DIV/CSS,熟悉JavaScript/Ajax/jquery能實現靜態頁面的開發。x0dx0al 了解HTML5,了解PhoneGAP框架,WebSevice。x0dx0a熟練使用Eclipse/Myeclipse,CVS/SVN/GIT等開發工具, 對數據結構有深入了解,有C/C++基礎x0dx0a當然你java基礎也必須要好 演算法什麼的
⑼ Android系統內存管理
部分內容出至林學森的Android內核設計思想。
Android官網內存管理
部分出至 https://www.jianshu.com/p/94d1cd553c44
Android本質是Linux所以先從Linux說起。
Linux的內存管理為系統中所有的task提供可靠的內存分配、釋放和保護機制。
核心:
虛擬內存
內存分配與釋放
內存保護
將外存儲器的部分空間作為內存的擴展,如從硬碟劃出4GB大小。
當內存資源不足時,系統按照一定演算法自動條形優先順序低的數據塊,並把他們存儲到硬碟中。
後續如果需要用到硬碟中的這些數據塊,系統將產生「缺頁」指令,然後把他們交換回內存中。
這些都是由操作系統內核自動完成的,對上層應用」完全透明「。
每個進程的邏輯地址和物理地址都不是直接對應的,任何進程都沒辦法訪問到它管轄范圍外的內存空間——即刻意產生的內存越界與非法訪問,操作系統也會馬上阻止並強行關閉程序,從而有力的保障應用程序和操作系統的安全和穩定。
一旦發現系統的可用內存達到臨界值,機會按照優先順序順序,匆匆低到高逐步殺掉進程,回收內存。
存儲位置:/proc/<PID>/oom_score
優先順序策略:
進程消耗的內存
進程佔用的CPU時間
oom_adj(OOM權重)
Android平台運行的前提是可用內存是浪費的內存。它試圖在任何時候使用所有可用的內存。例如,系統會在APP關閉後將其保存在內存中,以便用戶可以快速切換回它們。出於這個原因,Android設備通常運行時只有很少的空閑內存。在重要系統進程和許多用戶應用程序之間正確分配內存內對存管理是至關重要。
Android有兩種主要的機制來處理低內存的情況:內核交換守護進程(kernel swap daemon)和低內存殺手(low-memory killer)。
當用戶在APP之間切換時,Android會在最近使用的(LRU)緩存中保留不在前台的APP,即用戶看不到的APP,或運行類似音樂播放的前台服務。如果用戶稍後返回APP,系統將重用該進程,從而使APP切換更快。
如果你的APP有一個緩存進程,並且它保留了當前不需要的內存,那麼即使用戶不使用它,你的APP也會影響系統的整體性能。由於系統內存不足,它會從最近使用最少的進程開始殺死LRU緩存中的進程。該系統還負責處理佔用最多內存的進程,並可以終止這些進程以釋放RAM。
當系統開始終止LRU緩存中的進程時,它主要是自底向上工作的。系統還考慮哪些進程消耗更多的內存,從而在終止時為系統提供更多的內存增益。你在LRU列表中消耗的內存越少,你就越有可能留在列表中並能夠快速恢復。
為了滿足RAM的所有需求,Android嘗試共享RAM來跨進程通信。它可以做到以下方式:
Android設備包含三種不同類型的內存:RAM、zRAM和storage。
注意:CPU和GPU都訪問同一個RAM。
內存被拆分成頁。通常每頁有4KB的內存。
頁面被認為是空閑的或已使用的。
空閑頁是未使用的RAM。
已使用頁是系統正在積極使用的RAM,分為以下類別:
干凈的頁面(Clean pages)包含一個文件(或文件的一部分)的一份精確副本存在存儲器上。當一個干凈的頁面不再包含一個精確的文件副本(例如,來自應用程序操作的結果)時,它就變成了臟頁。可以刪除干凈的頁,因為它們始終可以使用存儲中的數據重新生成;不能刪除臟頁(Dirty pages),否則數據將丟失。
內核跟蹤系統中的所有內存頁。
當確定一個應用程序正在使用多少內存時,系統必須考慮shared pages。APP訪問相同的服務或庫將可能共享內存頁。例如,Google Play Services 和一個游戲APP可能共享一個位置服務。這使得很難確定有多少內存屬於這個服務相對於每個APP。
當操作系統想要知道所有進程使用了多少內存時,PSS非常有用,因為頁面不會被多次計數。PSS需要很長時間來計算,因為系統需要確定哪些頁面是共享的,以及被有多少進程。RSS不區分共享頁面和非共享頁面(使計算速度更快),更適合於跟蹤內存分配的更改。
內核交換守護進程(kswapd)是Linux內核的一部分,它將使用過的內存轉換為空閑內存。當設備上的空閑內存不足時,守護進程將變為活動狀態。Linux內核保持低和高的可用內存閾值。當空閑內存低於低閾值時,kswapd開始回收內存。當空閑內存達到高閾值,kswapd將停止回收內存。
kswapd可以通過刪除干凈的頁面來回收干凈的頁面,因為它們有存儲器支持並且沒有被修改。如果進程試圖定址已刪除的干凈頁,則系統會將該頁從存儲器復制到RAM。此操作稱為請求分頁。
kswapd將緩存的私有臟頁(private dirty pages)和匿名臟頁(anonymous dirty pages)移動到zRAM進行壓縮。這樣做可以釋放RAM中的可用內存(空閑頁)。如果進程試圖觸摸zRAM中臟頁,則該頁將被解壓縮並移回RAM。如果與壓縮頁關聯的進程被終止,則該頁將從zRAM中刪除。
如果可用內存量低於某個閾值,系統將開始終止進程。
lmkd實現源碼要在system/core/lmkd/lmkd.c。
lmkd會創建名為lmkd的socket,節點位於/dev/socket/lmkd,該socket用於跟上層framework交互。
小結:
LMK_TARGET: AMS.updateConfiguration() 的過程中調用 updateOomLevels() 方法, 分別向/sys/mole/lowmemorykiller/parameters目錄下的minfree和adj節點寫入相應信息;
LMK_PROCPRIO: AMS.applyOomAdjLocked() 的過程中調用 setOomAdj() 向/proc/<pid>/oom_score_adj寫入oom_score_adj後直接返回;
LMK_PROCREMOVE: AMS.handleAppDiedLocked 或者 AMS.() 的過程,調用remove(),目前不做任何事,直接返回;
為了進一步幫助平衡系統內存並避免終止APP進程,可以Activity類中實現ComponentCallbacks2介面。提供的onTrimMemory()回調方法允許APP在前台或後台偵聽與內存相關的事件,然後釋放對象以響應應用程序生命周期或表明系統需要回收內存的系統事件。
onTrimMemory()回調是在Android 4.0(API級別14)中添加的。
對於早期版本,可以使用onLowMemory(),它大致相當於TRIM_MEMORY_COMPLETE事件。
一個專門的驅動。(Linux Kernel 4.12 已移除交給kswapd處理)。
很多時候,kswapd無法為系統釋放足夠的內存。在這種情況下,系統使用onTrimMemory()通知APP內存不足,應該減少其分配。如果這還不夠,內核將開始終止進程以釋放內存,它使用低內存殺手(LMK)來完成這個任務。
為了決定要終止哪個進程,LMK使用一個名為oom_adj_score的「out of memory」分數來確定運行進程的優先順序,高分的進程首先被終止。
後台應用程序首先被終止,系統進程最後被終止。
下表列出了從高到低的LMK評分類別。第一排得分最高的項目將首先被殺死:
Android Runtime(ART)和Dalvik虛擬機使用分頁(Paging)和內存映射(mmapping)來管理內存。應用程序通過分配新對象或觸摸已映射頁面來修改內存都將保留在RAM中,並且不能被調出。應用程序釋放內存的唯一方式是垃圾收集器。
⑽ 解決Android Studio內存佔用過大問題
筆者在Ubuntu電腦上面運行Android Studio,發現內存佔用非常大,主要是Gradle守護進程在執行任務之後不釋放導致,參考Gradle官方文檔,可以使用命令來清理,不用每次都重啟Android Studio 守護進程會在閑置3小時後自動終止.如果想在這之前停止守護進程,也可以通過操作系統運行gradle --stop命令終止後台進程.--stop選項會要求所有運行相同版本的守護進程終止.