Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android7.0 數據業務中的短連接

Android7.0 數據業務中的短連接

編輯:關於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) {
        List newActivePhones = 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(HashMap requestMap, 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。
之後短連接將依賴於長連接的建立和斷開流程,完成實際的操作。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved