人性的弱點在於習慣於學習精確的東西而不善於總體的把握。
寫在前面的話
Android程式最重要的模組就是網路部分,如何從網路上下載資料,如何將處理過的資料上傳至網路,往往是android程式的關鍵環節。前幾天偶一朋友遇到這麼一個問題:如何使用volley實現檔案上傳。最後問題解決了,小夥伴不禁有些飄飄然,大有一番天下之事皆逃不過我的魔掌的感覺。這時候coder君問了他幾個問題,大家可以一起思考下:
- TCP/IP協議、SOCKET、HTTP協議、HTTPS協議都是做什麼的,他們之間有關係嗎
- 你是如何管理網路使用情況的
- 常用的HTTP Client有哪些,該如何選擇
- 假如你需要修改的你網路請求框架,你要動多少程式碼
- 你是怎麼進行網路優化的
小夥伴支支吾吾,其實他熟悉的只是Android中網路相關的那些點中的一小部分,不免有些盲人摸象的感覺,你呢?
網路通訊機制
網路由下往上分為:物理層、資料鏈路層、網路層、傳輸層、會話層、表示層和應用層。
IP協議對應於網路層,TCP協議對應於傳輸層,而HTTP協議對應於應用層,三者從本質上來說沒有可比性,socket則是對TCP/IP協議的封裝和應用。也可以說,TPC/IP協議是傳輸層協議,主要解決資料如何在網路中傳輸,而HTTP是應用層協議,主要解決如何包裝資料。
TCP/IP協議
網路程式設計的目的就是直接或間接地通過網路協議與其他計算機進行通訊。
網路程式設計中有兩個主要的問題,一個是如何準確的定位網路上一臺或多臺主機;另一個就是找到主機後如何可靠高效的進行資料傳輸。目前使用最廣泛的因特網協議是TCP/IP協議。
在TCP/IP協議中IP層主要負責網路主機的定位,資料傳輸的路由,由IP地址可以唯一地確定Internet上的一臺主機。而TCP層則提供面向應用的可靠的或非可靠的資料傳輸機制,這是網路程式設計的主要物件,一般不需要關心IP層是如何處理資料的。
Socket
我們知道兩個程式如果需要進行通訊最基本的一個前提能能夠唯一的標示一個程式,在本地程式通訊中我們可以使用PID來唯一標示一個程式,但PID只在本地唯一,網路中的兩個程式PID衝突機率很大,這時候我們需要另闢它徑了,我們知道IP層的ip地址可以唯一標示主機,而TCP層協議和埠號可以唯一標示主機的一個程式,這樣我們可以利用ip地址+協議+埠號唯一標示網路中的一個程式。
能夠唯一標示網路中的程式後,它們就可以利用socket進行通訊了,什麼是socket呢?我們經常把socket翻譯為套接字,socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操作抽象為幾個簡單的介面供應用層呼叫已實現程式在網路中通訊。
Socket跟TCP/IP協議沒有必然的聯絡。Socket程式設計介面在設計的時候,就希望也能適應其他的網路協議。所以說,Socket的出現只是使得程式設計師更方便地使用TCP/IP協議棧而已,是對TCP/IP協議的抽象,
Http協議
HTTP協議即超文字傳送協議(Hypertext Transfer Protocol ),是Web聯網的基礎,也是手機聯網常用的協議之一,HTTP協議是建立在TCP協議之上的一種應用。
HTTP連線最顯著的特點是客戶端傳送的每次請求都需要伺服器回送響應,在請求結束後,會主動釋放連線。從建立連線到關閉連線的過程稱為“一次連線”。
HTTP提供了封裝或者顯示資料的具體形式。Socket提供了網路通訊的能力。
Https協議
HTTPS(全稱:Hypertext Transfer Protocol over Secure Socket Layer),是以安全為目標的HTTP通道,簡單講是HTTP的安全版。即HTTP下加入SSL層,HTTPS的安全基礎是SSL,因此加密的詳細內容就需要SSL。 它是一個URI scheme(抽象識別符號體系),句法類同http:體系。用於安全的HTTP資料傳輸。https:URL表明它使用了HTTP,但HTTPS存在不同於HTTP的預設埠及一個加密/身份驗證層(在HTTP與TCP之間)。被廣泛用於全球資訊網上安全敏感的通訊,例如交易支付方面。
Android網路許可權
1 2 |
<uses-permission android:name="android.permission.INTERNET" /><-- 允許應用程式開啟網路套接字 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><-- 允許應用程式訪問網路連線資訊 --> |
選擇一個HTTP Client
大多數連線網路的 Android app 會使用 HTTP 來傳送與接收資料。Android 提供了三種 HTTP client:HttpURLConnection、Apache HttpClient和okhttp。二者均支援 HTTPS、流媒體上傳和下載、可配置的超時、IPv6 與連線池(connection pooling)。
- Java.net包中的HttpURLConnection類HttpUrlConnection是JDK裡提供的聯網API,我們知道Android SDK是基於Java的,所以當然優先考慮HttpUrlConnection這種最原始最基本的API,其實大多數開源的聯網框架基本上也是基於JDK的HttpUrlConnection進行的封裝罷了
- HttpClientHttpClient是開源組織Apache提供的Java請求網路框架,其最早是為了方便Java伺服器開發而誕生的,是對JDK中的HttpUrlConnection各API進行了封裝和簡化,提高了效能並且降低了呼叫API的繁瑣,Android因此也引進了這個聯網框架,我們再不需要匯入任何jar或者類庫就可以直接使用,值得注意的是Android官方已經宣佈不建議使用HttpClient了,我們再開發的時候儘量少用吧,但是用了也無妨!
- okhttphttp是現在主流應用使用的網路請求方式, 用來交換資料和內容, 有效的使用HTTP可以使你的APP變的更快和減少流量的使用。OkHttp是一個很棒HTTP客戶端:
- 支援SPDY,可以合併多個到同一個主機的請求
- 使用連線池技術減少請求的延遲(如果SPDY是可用的話)
- 使用GZIP壓縮減少傳輸的資料量
- 快取響應避免重複的網路請求
當你的網路出現擁擠的時候,就是OKHttp大顯身手的時候,它可以避免常見的網路問題,如果你的服務是部署在不同的IP上面的,如果第一個連線失敗,OkHTtp會嘗試其他的連線。這對現在IPv4+IPv6中常見的把服務冗餘部署在不同的資料中心上也是很有必要的。OkHttp將使用現在TLS特性(SNI ALPN)來初始化新的連線,如果握手失敗,將切換到TLS 1.0。
使用OkHttp很容易,同時支援非同步阻塞請求和回撥.
如果你使用OkHttp ,你不用重寫你的程式碼, okhttp-urlconnection模組實現了 java.net.HttpURLConnection 中的API, okhttp-apache模組實現了HttpClient中的API
如何管理網路的使用情況
如果我們的程式需要執行大量網路操作,那麼應該提供使用者設定選項,來允許使用者控制程式的資料偏好。例如,同步資料的頻率,是否只在連線到 WiFi 才進行下載與上傳操作,是否在漫遊時使用套餐資料流量等等。這樣使用者才不大可能在快到達流量上限時,禁止我們的程式獲取後臺資料,因為他們可以精確控制我們的 app 使用多少資料流量。
檢查網路連線
在執行網路操作之前,檢查裝置當前連線的網路連線資訊是個好習慣。這樣可以防止我們的程式在無意間連線使用了非意向的網路頻道。如果網路連線不可用,那麼我們的應用應該優雅地做出響應。為了檢測網路連線,我們需要使用到下面兩個類:
- ConnectivityManager:它會回答關於網路連線的查詢結果,並在網路連線改變時通知應用程式。
- NetworkInfo:描述一個給定型別(行動網路或 Wi-Fi等)的網路介面狀態。
下面這個方法可以找到的第一個已連線的網路介面,如果返回null,則表示沒有已連線的網路介面(意味著網路連線不可用):
1 2 3 4 5 |
public boolean isOnline() { ConnectivityManager connMgr = (ConnectivityManager)getSystemServic(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); return (networkInfo != null & networkInfo.isConnected()); } |
管理網路的使用情況
我們可以實現一個偏好設定的選項,使使用者能直接設定程式對網路資源的使用情況。例如:
- 可以允許使用者僅在連線到 Wi-Fi 時上傳視訊。
- 可以根據諸如網路可用,時間間隔等條件來選擇是否做同步的操作。
檢測網路連線變化
要檢測網路連線的變化需要用到BroadcastReceiver 的子類:NetworkReceiver。當裝置網路連線改變時,NetworkReceiver 會監聽到 CONNECTIVITY_ACTION,這時需要判斷當前網路連線型別並相應的設定好 wifiConnected 與 mobileConnected。
這裡需要注意的是在使用BroadcastReceiver的時候,不必要的宣告註冊會浪費系統資源。最好在 onCreate()中註冊 BroadcastReceiver NetworkReceiver,在 onDestroy()中銷燬它。這樣做會比在 manifest 裡面宣告 更輕巧。
在一個單獨的執行緒中執行網路操作
網路操作會遇到不可預期的延遲。為了避免造成不好的使用者體驗,確保總是在 UI 執行緒之外單獨的執行緒中執行網路操作。關於Android中的執行緒和程式可以參考這裡。
封裝網路請求
有了Http Client,我們需要考慮的是如何優雅的使用這些Http Client進行網路請求。這時候我們可以對Http Clent進行封裝來滿足我們的需求。封裝的基本要點如下:
- 支援自定義請求的Header
- 支援Http的基本請求方法:GET、POST、PUT、DELETE等
- 支援檔案上傳和下載
- 可以載入圖片
- 支援多工網路請求操作
- 支援快取
- 支援請求回撥
- 支援session的保持
具體可以參考Volley原始碼分析、Retrofit分析等,看看這些成名已久的框架是如何封裝的。
網路請求框架
以下是曾經很火或者現在很多的網路請求框架:
- Android Async HTTP
- AndroidAsync
- Volley
- Retrofit
AsyncHttp是一個較高層的封裝,底層使用的是HttpClient。
Volley是Google推出的Android非同步網路請求框架和圖片載入框架。底層網路請求可以使用不同的網路庫來處理,比如OkHttp,HttpClient。
Retrofit是一個封裝比較好的,相對更面向開發者的rest請求庫,它的底層網路請求也可以使用不同的網路庫來處理。
網路請求框架再封裝
我在這些Tips讓你的App更容易維護 簡單提到過面向介面程式設計,這對於網路框架再封裝很實用。
我們可以定義一套我們自己的網路請求介面,介面規定我們需要有什麼能力的框架。呼叫的話我們可以直接呼叫介面方法就好了,這樣不管我們換什麼網路框架,只要這個框架有網路請求的能力就行。舉個例子,定義介面如下:
1 2 3 4 5 |
public interface ISender { void send(METHOD method, String url, SenderCallback callback); } |
我們寫一個類實現這個介面:
1 2 3 4 5 6 7 |
public class BaseSender implements ISender { @Override public synchronized void sender(METHOD method, final String url, final SenderCallback callback) { //volley,retrofit,balabala,用任意網路請求框架實現。 } } |
然後是我們的SendCallback
1 2 3 4 5 6 7 8 9 |
public interface SendCallback { //請求成功回撥 void onSucceeed(String t); //請求失敗回撥 void onError(String errorMsg); } |
最後是我們的呼叫:
1 2 3 4 5 6 7 8 |
private ISender mHttp; public void getWeahter(String tag) { //呼叫介面裡定義好的方法 mHttp.sender(); } |
這裡傳入我們需要的引數就好了。
資料解析
以下是比較流行的網路資料解析的庫:
- Gson
- Jackson
- FastJson
- HtmlPaser
- Jsoup
網路資料解析比較基礎,這裡就不過多描述了。
移動端網路優化
對於手機程式,網路操作相對來說是比較耗電的行為。優化網路操作能夠顯著節約電量的消耗。一個網路請求可以簡單分為連線伺服器和獲取資料兩個部分。其中連線伺服器前還包括 DNS 解析的過程;獲取資料後可能會對資料進行快取。那麼我們如果要進行網路優化需要從這兩個關鍵點入手。
以下是具體可以優化的點:
- 不用域名,用IP直連省去 DNS 解析過程,DNS 全名 Domain Name System,解析意指根據域名得到其對應的IP地址。
- 伺服器合理部署伺服器多運營商多地部署,一般至少含三大運營商、南中北三地部署。
- 根據當前的網路環境選擇當下最佳的策略主要的實施步驟有兩步:第1是檢測收集當前的網路環境資訊,第2是根據當前收集到的資訊進行網路請求行為的調整。
- 預取我們需要預先判斷使用者在此次操作之後,後續零散的請求是否很有可能會馬上被觸發,可以把後面幾分鐘有可能會使用到的零散請求都一次集中執行完畢。
- 連線複用節省連線建立時間,如開啟 keep-alive。
- 用捆綁批量訪問的方式來減少訪問的頻率預先判定那些可能馬上就會使用到的網路資源,捆綁一起集中進行網路請求。這點我們可以考慮按照提前預期後續1-2分鐘的資料作為基準。
- 分優先順序、延遲部分請求首先我們需要區分哪些網路請求是需要及時返回結果的,哪些是可以延遲執行的。我們可以有針對性的把請求行為捆綁起來,延遲到某個時刻統一發起請求。
- 對傳輸的資料進行壓縮網路傳輸資料量的大小主要由兩部分組成:圖片與序列化的資料,那麼我們需要做的就是減少這兩部分的資料傳輸大小。
- 多連線對於較大檔案,如大圖片、檔案下載可考慮多連線。 需要控制請求的最大併發量,畢竟移動端網路受限。
- 避免不必要的同步操作應用程式的一個基礎功能是能夠保持確保介面上呈現的資訊是即時最新的,例如呈現最新的新聞,天氣,資訊流等等資訊。但是,過於頻繁的促使手機客戶端應用去同步最新的伺服器資料會對效能產生很大的負面影響,不僅僅使得CPU不停的在工作,記憶體,網路流量,電量等等都會持續的被消耗,所以在進行網路請求操作的時候一定要避免多度同步操作。
- 做好網路資料的快取從手機的快取中直接讀取資料肯定比從網路上獲取資料要更加的便捷高效,特別是對於那些會被頻繁訪問到的資料,需要把這些資料快取到裝置上,以便更加快速的進行訪問。
參考連結:
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式