android設備休眠
從上麵的連接裏麵找到了一些資料:
如果一開始就對Android手機的硬件架構有一定的了解,設計出的應用程序通常不會成為待機電池殺手,而要設計出正確的通信機製與通信協議也並不困難。但如果不去了解而盲目設計,可就沒準了。
首先Android手機有兩個處理器,一個叫Application Processor(AP),一個叫Baseband Processor(BP)。AP是ARM架構的處理器,用於運行Linux+Android係統;BP用於運行實時操作係統(RTOS),通訊協議棧運行於BP的RTOS之上。非通話時間,BP的能耗基本上在5mA左右,而AP隻要處於非休眠狀態,能耗至少在50mA以上,執行圖形運算時會更高。另外LCD工作時功耗在100mA左右,WIFI也在100mA左右。一般手機待機時,AP、LCD、WIFI均進入休眠狀態,這時Android中應用程序的代碼也會停止執行。
Android為了確保應用程序中關鍵代碼的正確執行,提供了Wake Lock的API,使得應用程序有權限通過代碼阻止AP進入休眠狀態。但如果不領會Android設計者的意圖而濫用Wake Lock API,為了自身程序在後台的正常工作而長時間阻止AP進入休眠狀態,就會成為待機電池殺手。比如前段時間的某應用,比如現在仍然幹著這事的某應用。
首先,完全沒必要擔心AP休眠會導致收不到消息推送。通訊協議棧運行於BP,一旦收到數據包,BP會將AP喚醒,喚醒的時間足夠AP執行代碼完成對收到的數據包的處理過程。其它的如Connectivity事件觸發時AP同樣會被喚醒。那麼唯一的問題就是程序如何執行向服務器發送心跳包的邏輯。你顯然不能靠AP來做心跳計時。Android提供的Alarm Manager就是來解決這個問題的。Alarm應該是BP計時(或其它某個帶石英鍾的芯片,不太確定,但絕對不是AP),觸發時喚醒AP執行程序代碼。那麼Wake Lock API有啥用呢?比如心跳包從請求到應答,比如斷線重連重新登陸這些關鍵邏輯的執行過程,就需要Wake Lock來保護。而一旦一個關鍵邏輯執行成功,就應該立即釋放掉Wake Lock了。兩次心跳請求間隔5到10分鍾,基本不會怎麼耗電。除非網絡不穩定,頻繁斷線重連,那種情況辦法不多。
網上有說使用AlarmManager,因為AlarmManager 是Android 係統封裝的用於管理 RTC 的模塊,RTC (Real Time Clock) 是一個獨立的硬件時鍾,可以在 CPU 休眠時正常運行,在預設的時間到達時,通過中斷喚醒 CPU。
移動互聯網應用現狀
因為手機平台本身、電量、網絡流量的限製,移動互聯網應用在設計上跟傳統 PC 上的應用很大不一樣,需要根據手機本身的特點,盡量的節省電量和流量,同時又要盡可能的保證數據能及時到達客戶端。
為了解決數據同步的問題,在手機平台上,常用的方法有2種。一種是定時去服務器上查詢數據,也叫Polling,還有一種手機跟服務器之間維護一個 TCP 長連接,當服務器有數據時,實時推送到客戶端,也就是我們說的 Push。
從耗費的電量、流量和數據送達的及時性來說,Push 都會有明顯的優勢,但 Push 的實現和維護成本相對較高。在移動無線網絡下維護長連接,相對也有一些技術上的難度。本文試圖給大家介紹一下我們極光推送在 Android 平台上是如何維護長連接。
移動無線網絡的特點
因為 IP v4 的 IP 量有限,運營商分配給手機終端的 IP 是運營商內網的 IP,手機要連接 Internet,就需要通過運營商的網關做一個網絡地址轉換(Network Address Translation,NAT)。簡單的說運營商的網關需要維護一個外網 IP、端口到內網 IP、端口的對應關係,以確保內網的手機可以跟 Internet 的服務器通訊。
圖片源自 cisco.com.
NAT 功能由圖中的 GGSN 模塊實現。
大部分移動無線網絡運營商都在鏈路一段時間沒有數據通訊時,會淘汰 NAT 表中的對應項,造成鏈路中斷。
Android 平台上長連接的實現
為了不讓 NAT 表失效,我們需要定時的發心跳,以刷新 NAT 表項,避免被淘汰。
Android 上定時運行任務常用的方法有2種,一種方法用 Timer,另一種是AlarmManager。
Timer
Android 的 Timer 類可以用來計劃需要循環執行的任務,Timer 的問題是它需要用 WakeLock 讓 CPU 保持喚醒狀態,這樣會大量消耗手機電量,大大減短手機待機時間。這種方式不能滿足我們的需求。
AlarmManager
AlarmManager 是 Android 係統封裝的用於管理 RTC 的模塊,RTC (Real Time Clock) 是一個獨立的硬件時鍾,可以在 CPU 休眠時正常運行,在預設的時間到達時,通過中斷喚醒 CPU。
這意味著,如果我們用 AlarmManager 來定時執行任務,CPU 可以正常的休眠,隻有在需要運行任務時醒來一段很短的時間。極光推送的 Android SDK 就是基於這種技術實現的。
服務器設計
當有大量的手機終端需要與服務器維持長連接時,對服務器的設計會是一個很大的挑戰。
假設一台服務器維護10萬個長連接,當有1000萬用戶量時,需要有多達100台的服務器來維護這些用戶的長連接,這裏還不算用於做備份的服務器,這將會是一個巨大的成本問題。那就需要我們盡可能提高單台服務器接入用戶的量,也就是業界已經討論很久了的 C10K 問題。
C2000K
針對這個問題,我們專門成立了一個項目,命名為C2000K,顧名思義,我們的目標是單機維持200萬個長連接。最終我們采用了多消息循環、異步非阻塞的模型,在一台雙核、24G內存的服務器上,實現峰值維持超過300萬個長連接。
https://docs.jpush.cn/pages/viewpage.action?pageId=3309821
後記
穩定維護長連接是推送平台的一個基礎,極光推送團隊將會在這方麵長期投入,以保證用戶能有效的節省電量、流量,同時數據能實時送達。
以上是極光推送官方的文章,但是看了之後不免有幾個疑問。
1)他們號稱最高峰值可以達到300W的長連接,但是活躍鏈接的處理最高是多少呢?
2)消息的平均長度和限製各是多少?
3)消息的及時性怎麼樣,延時是多少?
4)不知道現在,極光推送的用戶大概有多少,所以這個峰值是在生產環境,還是測試環境的數值?
5)我想不通的是為什麼,客戶端要用AlarmManager來做推送消息的獲取?這個消息獲取還及時嗎?鄙人結識android也有N載。
6)我感興趣的是,不是極光方案一個月推送了幾百萬條數據,而是幾秒鍾或者一分鍾可以處理多少。
Android 消息推送方案
當我們開發需要與服務器交互的應用程序時,基本上都需要獲取服務器端的數據。要獲取服務器上不定時更新的信息,一般來說有兩種方法:第一種是客戶端使用pull(拉)的方式,隔一段時間就去服務器上獲取一下信息,看是否有更新的信息出現;第二種就是服務器使用push(推送)的方式,當服務器端有更新,則將最新的信息push到客戶端上,如此以來,客戶端就能自動地接收到消息。
雖然pull和push兩種方式都能實現獲取服務器端數據更新的功能,但pull方式的弊端很明顯:費流量、費電,需要我們的程序不停地去監測服務器端的更新。
首先可以來了解一下iOS和Android平台的推送機製:
iOS 係統的推送(APNS,即 Apple Push Notification Service)依托一個或幾個係統常駐進程運作,是全局的(接管所有應用的消息推送),所以可看作是獨立於應用之外,而且是設備和蘋果服務器之間的通訊,而非應用的提供商服務器。比如騰訊 QQ 的服務器(Provider)會給蘋果公司對應的服務器(APNs)發出通知,然後再中轉傳送到你的設備(Devices)之上。當你接收到通知,打開應用,才開始從騰訊服務器接收數據,跟你之前看到通知裏內容一樣,但卻是經由兩個不同的通道而來。
Android的推送有兩種形式,一種是後台Service,一種是GCM
不過這裏要指出的是,Android的這種形式和傳統電腦還是有區別的,電腦的後台是保持程序進程在後台運行,而Android是在程序後台Service區域添加一個新的程序專門接收數據。
要說的話,Android的後台推送機製更加複雜,但是因為可操控部分更多,加上GCM是在2.X之後才加上的而且有可能在係統中並不存在,所以很多軟件都是使用Service這種形式。
本質上,APNs 與 GCM 是類似的技術實現原理:即係統層有一個常駐的 TCP 長連接,一直保持的長連接,即使手機休眠的時候也在保持的長連接。這裏對於大部分人來說,最不理解的就是,休眠時候都保持在那裏的 TCP 長連接,不會耗電很厲害麼?
答案是:不會。這是手機的設計來做到的。TCP長連接有個心跳的時間,在國外可以很長比如30分鍾,在國內則因為網絡環境複雜一般10分鍾。客戶端發起的心跳,會短暫地消耗手機電能,但在這個心跳間隔期間,則消耗電能是很少的。當在心跳期間服務器端有推送信息過來時,客戶端可以收到並做處理。
這裏有篇文章以 Android 為例做原理解釋:
/jpush_wireless_push_principle/
極光推送技術原理:無線網絡長連接
再說 APNs 的設計成功處。
iOS 為了真正地為用戶體驗負責,不允許應用在後台活動。有了這個限製,但是對於終端設備,應用又是有必要“通知”到達用戶的,隨時與用戶主動溝通起來的(典型的如聊天應用)。
這就是 APNs 的邏輯所在:iOS 自己做個長駐後台保持連接。所有應用,有必要(申請)並且被允許(用戶可以改設置)的話,可以通過 APNs 中轉到達用戶。
Android 因為後台可以長駐,尤其是國內的 Android 的手機上 Google自家的推送服務 GCM 處於基本不可用的狀態。所以,各App各顯神通。聊天類應用的話,大多數直接借用 XMPP 規範裏的一些成果。少量如微信有IM底子的,自己開發協議。這些在實現原理上與 APNs / GCM 沒有本質的區別,但有一定的技術門檻。而大多數普遍應用,要使用推送的話,則使用輪詢的方式簡單實現。
其實,國外如 Urban Airship 自己實現了 Android 上的第三方提供的推送平台。國內如極光推送也實現了第三方的推送平台(技術與微信、GCM、APNs類似)。理論上,如果一個 Android 設備上多款應用都使用極光推送這種第三方推送平台的話,也可以如 APNs 一樣達到節省電量、流量消耗的效果。
另可參考Android實現推送方式解決方案
在開發Android的過程中,我們經常用到的WIFI在休眠情況下默認是會不連接的,這個時候當我們需要保持連接時,該如何解決
不少人說可以在係統設置的WIFI高級選項中將連接設為休眠保持連接,這個辦法的確可行,對於開發者來說很容易辦到,但是對於用戶來說他們一般不會知道這麼設置,這個時候該怎麼辦呢?可以使用如下代碼解決
[java]
public void WifiNeverDormancy(Context mContext)
{
ContentResolver resolver = mContext.getContentResolver();
int value = Settings.System.getInt(resolver, Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
final SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(mContext);
Editor editor = prefs.edit();
editor.putInt(mContext.getString(R.string.wifi_sleep_policy_default), value);
editor.commit();
if(Settings.System.WIFI_SLEEP_POLICY_NEVER != value)
{
Settings.System.putInt(resolver, Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_NEVER);
}
System.out.println("wifi value:"+value);
}
public void WifiNeverDormancy(Context mContext)
{
ContentResolver resolver = mContext.getContentResolver();
int value = Settings.System.getInt(resolver, Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
final SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(mContext);
Editor editor = prefs.edit();
editor.putInt(mContext.getString(R.string.wifi_sleep_policy_default), value);
editor.commit();
if(Settings.System.WIFI_SLEEP_POLICY_NEVER != value)
{
Settings.System.putInt(resolver, Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_NEVER);
}
System.out.println("wifi value:"+value);
}上麵這個函數,會自動修改我們WIFI設置中的高級選項,將其設置為一直保持連接。不用使用其他控件就可以解決。
需要注意的是此函數在調用時必須現在AndroidManifest.xml中聲明權限
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
轉】Android設置手機WIFI休眠策略
在開發Android應用中,有很大一部分app是要求手機一直處於網絡環境中的。在Android 設置--> WLAN -->點擊菜單鍵 選擇 高級 -->休眠狀態下保持WLAN連接的下拉列表{始終、僅限充電時、從不(會增加數據流量)}這個設置項中,很多用戶並不知道這個選項的存在,如果設置不為始終,那麼我們鎖屏休眠後,程序將會處於無網絡狀態,相應的app用戶會一直處於離線模式,那麼帶來的效果可想而知。
那麼我們開發中,應對這種情況,我們可以在程序首先開啟時,讀取這個值,保存起來,待程序退出時,將原先保存的值還原,這樣可以很好的解決上述這個問題。
在Android API中這樣描述:
The policy for deciding when Wi-Fi should go to sleep (which will in turn switch to using the mobile data as an Internet connection).
Set to one of WIFI_SLEEP_POLICY_DEFAULT, WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED, or WIFI_SLEEP_POLICY_NEVER.
{
final SharedPreferences prefs = getSharedPreferences(getString(R.string.wifi_sleep_policy), Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putInt(getString(R.string.wifi_sleep_policy_default), value);
editor.commit();
if(Settings.System.WIFI_SLEEP_POLICY_NEVER != value)
{
Settings.System.putInt(getContentResolver(), Settings.System.WIFI_SLEEP_POLICY, WIFI_SLEEP_POLICY_NEVER);
}
{
final SharedPreferences prefs = getSharedPreferences(getString(R.string.wifi_sleep_policy), Context.MODE_PRIVATE);
int defaultPolicy = prefs.getInt(getString(R.string.wifi_sleep_policy_default), Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
Settings.System.putInt(getContentResolver(), Settings.System.WIFI_SLEEP_POLICY, defaultPolicy);
}
from: https://blog.sina.com.cn/s/blog_a85b30ff0101ahzd.html
Android 關於休眠的幾個坑點
首先看一下Android Powermanager Class Overview,對Android的幾種不同的休眠模式有個大致了解。
如果不進行特別的設置,Android會在一定時間後屏幕變暗,在屏幕變暗後一定時間內,約幾分鍾,CPU也會休眠,大多數的程序都會停止運行,從而節省電量。但你可以在代碼中通過對Powmanager API的調用來設置不同的休眠模式。
Flag Value CPU Screen Keyboard
PARTIAL_WAKE_LOCK On* Off Off
SCREEN_DIM_WAKE_LOCK On Dim Off
SCREEN_BRIGHT_WAKE_LOCK On Bright Off
FULL_WAKE_LOCK On Bright Bright
如上表,最高等級的休眠是屏幕,鍵盤等,cpu都全部休眠。可以設置不同的模式,讓其產生不同的休眠,比如讓cpu保持運行。
設置代碼如下:
-
PowerManagerpm =(PowerManager)getSystemService(Context.POWER_SERVICE);
-
PowerManager.WakeLockwl =pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,"My Tag");
-
wl.acquire();
-
..screen will stay on during thissection..
-
wl.release();
我曾經遇到的幾個坑點及解決:
1.向服務器輪詢的代碼不執行。
曾經做一個應用,利用Timer和TimerTask,來設置對服務器進行定時的輪詢,但是發現機器在某段時間後,輪詢就不再進行了。查了很久才發 現是休眠造成的。後來解決的辦法是,利用係統的AlarmService來執行輪詢。因為雖然係統讓機器休眠,節省電量,但並不是完全的關機,係統有一部 分優先級很高的程序還是在執行的,比如鬧鍾,利用AlarmService可以定時啟動自己的程序,讓cpu啟動,執行完畢再休眠。
2.後台長連接斷開。
最近遇到的問題。利用Socket長連接實現QQ類似的聊天功能,發現在屏幕熄滅一段時間後,Socket就被斷開。屏幕開啟的時候需進行重連,但 每次看Log的時候又發現網絡是鏈接的,後來才發現是cpu休眠導致鏈接被斷開,當你插上數據線看log的時候,網絡cpu恢複,一看網絡確實是鏈接的, 坑。最後使用了PARTIAL_WAKE_LOCK,保持CPU不休眠。
3.調試時是不會休眠的。
讓我非常鬱悶的是,在調試2的時候,就發現,有時Socket會斷開,有時不會斷開,後來才搞明白,因為我有時是插著數據線進行調試,有時拔掉數據線,這 時Android的休眠狀態是不一樣的。而且不同的機器也有不同的表現,比如有的機器,插著數據線就會充電,有的不會,有的機器的設置的充電時屏幕不變暗 等等,把自己都搞暈了。其實搞明白這個休眠機製,一切都好說了。
最後更新:2017-04-03 05:39:44