從上面的鏈接裏面找到了一些資料:html
若是一開始就對Android手機的硬件架構有必定的瞭解,設計出的應用程序一般不會成爲待機電池殺手,而要設計出正確的通訊機制與通訊協議也並不困難。但若是不去了解而盲目設計,可就沒準了。java
首先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
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。app
從耗費的電量、流量和數據送達的及時性來講,Push 都會有明顯的優點,但 Push 的實現和維護成本相對較高。在移動無線網絡下維護長鏈接,相對也有一些技術上的難度。本文試圖給你們介紹一下咱們極光推送在 Android 平臺上是如何維護長鏈接。less
移動無線網絡的特色
由於 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萬個長鏈接。
http://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中這樣描述:
public static final String WIFI_SLEEP_POLICY
Since: API Level 3
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.
Constant Value:"wifi_sleep_policy"
那麼咱們在開啓應用時,首先取出這個值,上代碼:
private void setWifiDormancy()
{
int value = Settings.System.getInt(getContentResolver(), Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
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);
}
}
在應用退出咱們需將這個設置的值還原:
private void restoreWifiDormancy()
{
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);
}
這樣初始設置,退出還原咱們可讓應用一直處於wifi模式下(不過前提是有wifi網絡哦)。
from: http://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的休眠狀態是不同的。並且不一樣的機器也有不一樣的表現,好比有的機器,插着數據線就會充電,有的不會,有的機器的設置的充電時屏幕不變暗 等等,把本身都搞暈了。其實搞明白這個休眠機制,一切都好說了。