編輯:關於Android編程
數據業務中的短連接,是一種為了滿足業務需求,臨時建立起來的連接。當業務完成通信需求後,這種數據連接會被框架釋放掉。與之相對,長連接一旦撥號成功就會一直存在下去,除非用戶主動關閉或者終端受到網絡等因素的影響導致連接不可用。
一種比較常見的例子,就是發送彩信時,終端將建立短連接;當彩信發送結束時,短連接將被釋放掉。
在這篇博客中,我們就從彩信入手,看看Android中是如何建立和釋放短連接的。
1 MmsService
Android中彩信相關的應用為MmsService,我們看看它AndroidManifest.xml中的部分片段:
容易看出MmsService是運行在Phone進程中的。在這篇博客中,我們不深入研究彩信服務的啟動和收發彩信的過程,主要看看彩信如何建立和釋放短連接。
在MmsService.java中,每一次發送彩信均會形成一個MmsRequest(抽象類,實現類為SendRequest和DownloadRequest),並將其加入到運行隊列中:
private void addToRunningRequestQueueSynchronized(final MmsRequest request) { ........... final int queue = request.getQueueType(); //判讀queue值的有效性 ............ mRunningRequestCount++; mCurrentSubId = request.getSubId(); mRunningRequestExecutors[queue].execute(new Runnable() { @Override public void run() { try { //MmsRequest執行 request.execute(MmsService.this, getNetworkManager(request.getSubId())); } finally { synchronized (MmsService.this) { mRunningRequestCount--; if (mRunningRequestCount <= 0) { //將位於pending隊列中的請求,加入到running隊列中 movePendingSimRequestsToRunningSynchronized(); } } } } } }
在上面對代碼中,利用getNetworkManager創建了MmsRequest專屬的MmsNetworkManger:
//subId對應於發送彩信的卡 private MmsNetworkManager getNetworkManager(int subId) { synchronized (mNetworkManagerCache) { MmsNetworkManager manager = mNetworkManagerCache.get(subId); if (manager == null) { manager = new MmsNetworkManager(this, subId); mNetworkManagerCache.put(subId, manager); } return manager; } }
先看看MmsNetworkManager的構造函數:
public MmsNetworkManager(Context context, int subId) { ............ //構造出申請網絡的request //注意Capability指定為MMS,同時NetworkSpecifier指定為對應的sub id mNetworkRequest = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS) .setNetworkSpecifier(Integer.toString(mSubId)) .build(); }
接下來,看看MmsRequest的execute方法:
public void execute(Context context, MmsNetworkManager networkManager) { .......... //檢查是否做好准備工作 if (!ensureMmsConfigLoaded()) { ........ } else if (!prepareForHttpRequest()){ ........ } else { long retryDelaySecs = 2; for (int i = 0; i < RETRY_TIMES; i++) { try { //利用MmsNetworkManager建立短連接 networkManager.acquireNetwork(requestId); try { //進行實際的發送或接收 ............ } finally { //利用MmsNetworkManager釋放段連接 networkManager.releaseNetwork(requestId); } } .........//進行捕獲異常等操作 } //處理發送結果 processResult(context, result, response, httpStatusCode); } }
2 建立短連接
MmsNetworkManager中的acquireNetwork負責申請網絡:
public void acquireNetwork(final String requestId) throws MmsNetworkException { synchronized (this) { mMmsRequestCount += 1; //若之前已經申請過網絡,則不重新申請 if (mNetwork != null) { return; } if (mNetworkCallback == null) { //申請網絡 startNewNetworkRequestLocked(); } //定義申請網絡所應耗費的時間 final long shouldEnd = SystemClock.elapsedRealtime() + NETWORK_ACQUIRE_TIMEOUT_MILLIS; long waitTime = NETWORK_ACQUIRE_TIMEOUT_MILLIS; while (waitTime > 0) { try { //等待;申請網絡後,ConnectivityService將調用MmsNetworkManager的回調函數,在回調函數進行通知 this.wait(waitTime); } catch (InterruptedException e) { ............ } if (mNetwork != null) { //成功獲取到網絡 return; } waitTime = shouldEnd - SystemClock.elapsedRealtime(); } //獲取網絡失敗,釋放請求 releaseRequestLocked(mNetworkCallback); throw new MmsNetworkException("Acquiring network timed out"); } }
順著流程,看看MmsNetworkManager中的startNewNetworkRequestLocked:
private void startNewNetworkRequestLocked() { final ConnectivityManager connectivityManager = getConnectivityManager(); //定義回調函數 mNetworkCallback = new NetworkRequestCallback(); //利用ConnectivityManager向ConnectivityService發送請求,並注冊回調函數 connectivityManager.requestNetwork( mNetworkRequest, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS); }
在進入connectivityManager前,先看看MmsNetworkManager中的回調函數:
private class NetworkRequestCallback extends ConnectivityManager.NetworkCallback { @Override public void onAvailable(Network network) { super.onAvailable(network); synchronized (MmsNetworkManager.this) { //網絡可用時,存儲並通知 //於是acquireNetwork函數,可以返回結果 mNetwork = network; MmsNetworkManager.this.notifyAll(); } } //網絡斷開和不可用時的回調函數 ............ }
現在我們可以看看connectivityManager的requestNetwork函數:
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, int timeoutMs) { requestNetwork(request, networkCallback, timeoutMs, //根據網絡能力返回type;在之前的版本中會返回TYPE_MOBILE_MMS,android7.0中返回TYPE_NONE inferLegacyTypeForNetworkCapabilities(request.networkCapabilities)); } public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, int timeoutMs, int legacyType) { sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, REQUEST, legacyType); } private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback networkCallback, int timeoutSec, int action, int legacyType) { ................... try { .............. synchronized(sNetworkCallback) { if (action == LISTEN) { //這種類型的Request僅用來監聽網絡的變化,無法申請實際的網絡 ............. } else { //調用ConnectivityService的requestNetwork函數 networkCallback.networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType); } } } catch(RemoteException e) { ............ } .............. }
從上面的代碼,可以看到申請網絡還是需要依靠ConnectivityService:
@Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, Messenger messenger, int timeoutMs, IBinder binder, int legacyType) { //檢查參數有效性 ............ //利用參數構造networkRequest及NetworkRequestInfo NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId()); NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, type); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri)); .................... }
ConnectivityService將調用handleRegisterNetworkRequestWithIntent處理EVENT_REGISTER_NETWORK_REQUEST:
private void handleRegisterNetworkRequestWithIntent(Message msg) { //檢查msg攜帶Request的有效性 ............ handleRegisterNetworkRequest(nri); } private void handleRegisterNetworkRequest(NetworkRequestInfo nri) { mNetworkRequests.put(nri.request, nri); if (!nri.isRequest()) { //非申請網絡的request ................ } //重新匹配所有的NetworkAgent和NetworkRequest rematchAllNetworksAndRequests(null, 0); //默認建立的長連接對應的NetworkAgent,其capabilities不包含MMS,可以看看DataConnection中的makeNetworkCapabilities函數 //因此無論數據業務開關是否打開,此時mNetworkForRequestId.get(nri.request.requestId)返回值均為null if (nri.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) { //發送request給所有的NetworkFactory,分數為0 sendUpdatedScoreToFactories(nri.request, 0); } }
與之前介紹數據撥號准備工作的博客中描述的一樣,請求MMS網絡的NetworkRequest將同時發送給TelephonyNetworkFactory和PhoneSwitcher中創建的NetworkFactory。
這裡需要注意的是,TelephonyNetworkFactory申明自己的網絡能力時,指定匹配對應卡的subId;
PhoneSwitcher中創建的NetworkFactory申明自己的網絡能力時,指定匹配所有的subId。
於是,不匹配MMS NetworkRequest對應subId的TelephonyNetworkFactory,將無法處理該NetworkRequest。
此外,當TelephonyNetworkFactory處於激活態時,才能處理NetworkRequest。
綜上所述,我們可以得出結論:
當具有數據能力的Phone發送彩信時,對應的TelephonyNetworkFactory可以直接處理;
當不具有數據能力的Phone發送彩信時,沒有TelephonyNetworkFactory可以處理MMS Request;此時,必須先通過PhoneSwitcher切換Phone的數據能力,然後再進行處理,這個過程也被成為DDS。
我們來看看DDS的過程。在PhoneSwitcher中,負責處理NetworkRequest的函數為onRequestNetwork:
private void onRequestNetwork(NetworkRequest networkRequest) { //之前分析過,將NetworkRequest封裝成DcRequest時,按照xml中的配置,定義了優先級 final DcRequest dcRequest = new DcRequest(networkRequest, mContext); if (mPrioritizedDcRequests.contains(dcRequest) == false) { mPrioritizedDcRequests.add(dcRequest); //MMS request的優先級高於default,於是會排在最前面 Collections.sort(mPrioritizedDcRequests); onEvaluate(REQUESTS_CHANGED, "netRequest"); } } private void onEvaluate(boolean requestsChanged, String reason) { .............. if (diffDetected) { ListnewActivePhones = new ArrayList (); int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest); if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; if (newActivePhones.contains(phoneIdForRequest)) continue; //MMS NetworkRequest的phone id加入到newActivePhones newActivePhones.add(phoneIdForRequest); if (newActivePhones.size() >= mMaxActivePhones) break; } ............. for (int phoneId = 0; phoneId < mNumPhones; phoneId++) { if (newActivePhones.contains(phoneId) == false) { //注銷不匹配MMS NetworkRequest subId的phone的數據能力 //通過RIL下發命令,完成後通知觀察者 deactivate(phoneId); } } for (int phoneId : newActivePhones) { //賦予MMS Request subId對應phone的數據能力 //通過RIL下發命令,完成後通知觀察者,TelephonyNetworkFactory activate(phoneId); } } }
不論TelephonyNetworkFactory可以直接處理MMS NetworkRequest,還是DDS後才能進行處理,最終TelephonyNetworkFactory將調用DcTracker的requestNetwork函數。
之後,框架就會激活彩信對應的APN,並用彩信APN進行撥號。這部分流程與之前博客描述的,數據撥號的准備工作及數據長連接撥號基本一致,不再贅述。
3 釋放短連接
MmsNetworkManager中的releaseRequestLocked負責釋放短連接:
private void releaseRequestLocked(ConnectivityManager.NetworkCallback callback) { if (callback != null) { final ConnectivityManager connectivityManager = getConnectivityManager(); try { connectivityManager.unregisterNetworkCallback(callback); } catch (IllegalArgumentException e) { ............ } } ............ }
進入到ConnectivityManager的unregisterNetworkCallback函數:
public void unregisterNetworkCallback(NetworkCallback networkCallback) { ............. try { mService.releaseNetworkRequest(networkCallback.networkRequest); } catch(RemoteException e) { ............ } ............ }
隨著流程進入到ConnectivityService:
@Override public void releaseNetworkRequest(NetworkRequest networkRequest) { mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest)); } private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent, int callingUid) { NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent); if (nri != null) { handleReleaseNetworkRequest(nri.request, callingUid); } } private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) { NetworkRequestInfo nri = mNetworkRequests.get(request); if (nri != null) { ............. if (nri.isRequest()) { ............... for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { //釋放每個NetworkAgent中對networkRequest的記錄 if (nai.networkRequests.get(nri.request.requestId) != null) { nai.networkRequests.remove(nri.request.requestId); ........... //該NetworkAgent不再是任何NetworkRequest的最優匹配對象 if (unneeded(nai)) { //注銷掉該NetworkAgent,調用AsyncChannel的disconnect函數,與之前博客所述的長連接去撥號流程一致 //MMS建立的NetworkAgent將在此處被注銷 teardownUnneededNetwork(nai); } else { .............. } } } //移除ConnectivityService中NetworkAgentInfo的記錄信息 NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); if (nai != null) { mNetworkForRequestId.remove(nri.request.requestId); } ............ for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { //移除NetworkFactory中關於該NetworkRequest的記錄 //該消息將通知給PhoneSwitcher中創建的NetworkFactory和TelephonyNetworkFactory nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, nri.request); } } else { ......... } ....... } }
當PhoneSwitcher中創建的NetworkFactory收到CMD_CANCEL_REQUEST消息後,最終將調用onReleaseNetwork進行處理:
private void onReleaseNetwork(NetworkRequest networkRequest) { final DcRequest dcRequest = new DcRequest(networkRequest, mContext); //移除MMS對應的DcRequest if (mPrioritizedDcRequests.remove(dcRequest)) { //重新評估所有的DcRequest //此時,由於高優先級的MMS DcRequest被移除了,於是Default data request成為了最優先的request //如果之前為了發送彩信進行過DDS,那麼此時將重新進行DDS,將數據能力切換到原來的default subId對應的phone onEvaluate(REQUESTS_CHANGED, "netReleased"); } }
當TelephonyNetworkFactory收到CMD_CANCEL_REQUEST消息後,同樣會調用自己的onReleaseNetworkFor函數進行處理:
private void onReleaseNetworkFor(Message msg) { //移除對NetworkRequest的記錄 ............. if (mIsActive && isApplicable) { String s = "onReleaseNetworkFor"; localLog.log(s); log(s + " " + networkRequest); //調用DcTracker的releaseNetwork函數,將去激活MMS對應APN,同時移除MMS對應的dataConnection mDcTracker.releaseNetwork(networkRequest, localLog); } else { String s = "not releasing - isApp=" + isApplicable + ", isAct=" + mIsActive; localLog.log(s); log(s + " " + networkRequest); } }
注意到TelephonyNetworkFactory調用DcTracker的releaseNetwork函數有個前提條件,那就是該TelephonyNetworkFactory處於active狀態。
考慮到ConnectivityService也發送過CMD_CANCEL_REQUEST給PhoneSwitcher,
因此可能由於時序上的原因,導致TelephonyNetworkFactory在執行onReleaseNetworkFor之前,PhoneSwitcher先進行了DDS。
此時TelephonyNetworkFactory將無法通過onReleaseNetworkFor來完成releaseNetwork的操作。
為了避免這個問題,TelephonyNetworkFactory監聽了DDS變化的動作:
private void onActivePhoneSwitch() { final boolean newIsActive = mPhoneSwitcher.isPhoneActive(mPhoneId); if (mIsActive != newIsActive) { mIsActive = newIsActive; if (mIsDefault) { applyRequests(mDefaultRequests, (mIsActive ? REQUEST : RELEASE), logString); } applyRequests(mSpecificRequests, (mIsActive ? REQUEST : RELEASE), logString); } } private void applyRequests(HashMaprequestMap, boolean action, String logStr) { for (NetworkRequest networkRequest : requestMap.keySet()) { if (action == REQUEST) { mDcTracker.requestNetwork(networkRequest, localLog); } else { //同樣調用了releaseNetwork mDcTracker.releaseNetwork(networkRequest, localLog); } } }
綜上所述我們可以看到:
若PhoneSwitcher先進行了DDS,那麼TelephonyNetworkFactory將通過applyRequests來調用releaseNetwork;然後在onReleaseNetworkFor中僅進行清除NetworkRequest的記錄;
若TelephonyNetworkFactory先調用onReleaseNetworkFor,那麼將清除NetworkRequest的記錄並執行releaseNetwork;發生DDS後,由於已經清除過NetworkRequest的記錄,於是不會重復執行releaseNetwork。
結束語
我們從MMS入手,分析了數據業務中短連接的建立和斷開過程。
可以看到短連接主要利用ConnectivityManager的接口,來完成建立和斷開的操作,同時在必要的時候利用PhoneSwitcher完成DDS。
之後短連接將依賴於長連接的建立和斷開流程,完成實際的操作。
一、什麼是Activity?簡單的說:Activity就是布滿整個窗口或者懸浮於其他窗口上的交互界面。在一個應用程序中通常由多個Activity構成,都會在Manifes
本文實例為大家分享了Android創建發送和接收短信應用的簡單實現過程,供大家參考,具體內容如下一、接收短信項目的結構如下:一個簡單的接收和發送短信的功能1、定義一個接收
一,工程圖。二,效果圖。三,代碼。RootViewController.h#import <UIKit/UIKit.h>@interface RootView
因為最近的開發涉及到了網絡讀取數據,那麼自然少不了的就是下拉刷新的功能,搜索的方法一般是自己去自定義ListView或者RecyclerView來重寫OnTouch或者O