1. 程式人生 > >網路連線評分機制之NetworkAgent

網路連線評分機制之NetworkAgent

http://blog.csdn.net/u010961631/article/details/48971651

前面介紹了開機過程中各個網路提供者的初始化過程,其實就是建立自己的NetworkFactory,並將其註冊到ConnectivityService。而在介紹NetworkFactory的時候,我們看到該類的官方註釋中有這麼一句描述:

  1. "A NetworkFactory is an entity that creates NetworkAgent objects."
        這說明NetworkFactory的主要作用還是用來建立NetworkAgent的,那麼NetworkAgent是什麼物件,而NetworkFactory與NetworkAgent又是什麼關係呢?

        我們接下來介紹NetworkAgent。

一、NetworkAgent介紹

        其實在上一個文件《Framework中的連線管理機制》中已經介紹過NetworkAgent相關知識了,現在我們來從另一個角度分析一下該物件的作用。
        從剛才NetworkFactory的註釋我們知道,NetworkAgent是被NetworkFactory建立的,這裡的建立並不是說在NetworkFactory內部建立NetworkAgent,而是說,在NetworkFactory這個環境就緒之後,網路提供者才可以建立NetworkAgent。並且在一個NetworkFactory中可以建立不同的NetworkAgent,他們擁有不同的Capabilities等引數。

        而他們之間還有一個區別就是,NetworkFactory是在系統初始化時就被建立,而NetworkAgent是在真正接入網路時才會建立。
        我們用運營商之間的關係來比喻他們的關係。
        NetworkFactory相當於不同的運營商,比如中國電信、鐵通、移動,他們具備聯通網際網路的能力,當用戶入網時就決定了自己的運營商(即完成NetworkFactory初始化)。但同時在每個運營商內部又建立各個不同的接入點,比如對於中國電信來說,還分為上海電信、河北電信等,只有當用戶開啟電腦真正上網的時候,才會被分配具體的接入點(即完成NetworkAgent初始化)。

        也就是說,同一個NetworkFactory可以在不同的時刻根據需要建立不同的NetworkAgent,比如使用資料上網時,會根據當前的需要(傳送MMS還是IMS,或者單純上網)來建立不同引數的NetworkAgent(不同的APN引數)物件,然後將其註冊到ConnectivityService中。

        下面我們跟蹤一下資料連線的建立過程來看一下NetworkAgent的使用。

二、NetworkAgent建立過程

        當資料連線建立成功時,就會在DataConnection中進入DcActiveState的狀態機,然後就會建立資料連線的NetworkAgent物件:
  1. @DataConnection.java  
  2. privateclass DcActiveState extends State {  
  3.     @Overridepublicvoid enter() {  
  4.         mRetryManager.restoreCurMaxRetryCount();  
  5.         mDcController.addActiveDcByCid(DataConnection.this);  
  6.         //更新當前的NetworkInfo狀態
  7.         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, mNetworkInfo.getReason(), null);  
  8.         mNetworkInfo.setExtraInfo(mApnSetting.apn);  
  9.         updateTcpBufferSizes(mRilRat);  
  10.         //資料建立成功,建立並註冊NetworkAgent
  11.         mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),  
  12.                 "DcNetworkAgent", mNetworkInfo, makeNetworkCapabilities(), mLinkProperties,  
  13.                 50);  
  14.     }  
  15. }  
        我們看到,當資料連線建立成功後,就會更新當前的NetworkInfo(點選這裡瞭解NetworkInfo),然後建立當前的NetworkAgent,並把NetworkInfo傳遞給NetworkAgent。
        下面我們來看NetworkAgent的建立過程:
  1. @NetworkAgent.java  
  2. public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {  
  3.     super(looper);  
  4.     LOG_TAG = logTag;  
  5.     mContext = context;  
  6.     if (ni == null || nc == null || lp == null) {  
  7.         thrownew IllegalArgumentException();  
  8.     }  
  9.     ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( Context.CONNECTIVITY_SERVICE);  
  10.     cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni), new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);  
  11. }  
        在NetworkAgent的建立過程中,只做了一件事情,就是將其註冊到ConnectivityService中,而這裡傳遞的引數包含當前NetworkAgent的Messenger(用於與ConnectivityService之間建立AsyncChannel通道)、傳遞進來的NetworkInfo、NetworkCapabilities、以及當前連線的分數score等。
        然後我們繼續來看在ConnectivityService中的註冊過程:
  1. @ConnectivityService.java  
  2. publicvoid registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int currentScore, NetworkMisc networkMisc) {  
  3.     //許可權檢查
  4.     enforceConnectivityInternalPermission();  
  5.     NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),  
  6.             new NetworkInfo(networkInfo), new LinkProperties(linkProperties),  
  7.             new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,  
  8.             new NetworkMisc(networkMisc));  
  9.     synchronized (this) {  
  10.         nai.networkMonitor.systemReady = mSystemReady;  
  11.     }  
  12.     mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));  
  13. }  
        我們看到,當NetworkAgent註冊時,在ConnectivityService的內部建立了一個新的物件NetworkAgentInfo,該物件中保留了傳遞進來的一系列引數,包括NetworkAgent的Messenger物件、NetworkInfo、NetworkCapabilities、score以及建立了一個用於通訊的AsyncChannel通道。
        然後就把當前建立的NetworkAgentInfo物件放入EVENT_REGISTER_NETWORK_AGENT訊息中,傳送給Handler處理:
  1. @ConnectivityService.java  
  2. privateclass InternalHandler extends Handler {  
  3.     publicvoid handleMessage(Message msg) {  
  4.         NetworkInfo info;  
  5.         switch (msg.what) {  
  6.             case EVENT_REGISTER_NETWORK_AGENT: {  
  7.                handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);  
  8.                break;  
  9.            }  
  10.         }  
  11.     }  
  12. }  
        繼續:
  1. privatevoid handleRegisterNetworkAgent(NetworkAgentInfo na) {  
  2.     //將NetworkAgentInfo放入mNetworkAgentInfos中
  3.     mNetworkAgentInfos.put(na.messenger, na);  
  4.     assignNextNetId(na);  
  5.     //發起連線請求
  6.     na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);  
  7.     NetworkInfo networkInfo = na.networkInfo;  
  8.     na.networkInfo = null;  
  9.     //更新最新的NetworkInfo
  10.     updateNetworkInfo(na, networkInfo);  
  11. }  
        在這裡,ConnectivityService做了三個事情:
        1、將新註冊的NetworkAgentInfo儲存到mNetworkAgentInfos中;
        2、利用剛才建立的AsyncChannel向NetworkAgent發起單向連線請求
        3、更新最新的NetworkAgentInfo狀態;

        我們主要關注第二個過程,而第三個過程將會在接下來小節中介紹。
        根據AsyncChannel的原理(不懂的點這裡),此時ConnectivityService發起的是單向的AsyncChannel連線,發起後將會在mTrackerHandler中收到CMD_CHANNEL_HALF_CONNECTED的訊息:
  1. @ConnectivityService.java  
  2. privateclass NetworkStateTrackerHandler extends Handler {  
  3.     publicvoid handleMessage(Message msg) {  
  4.         NetworkInfo info;  
  5.         switch (msg.what) {  
  6.             case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {  
  7.               handleAsyncChannelHalfConnect(msg);  
  8.               break;  
  9.            }  
  10.         }  
  11.     }  
  12. }  
        然後來看詳細處理:
  1. privatevoid handleAsyncChannelHalfConnect(Message msg) {  
  2.     AsyncChannel ac = (AsyncChannel) msg.obj;  
  3.     if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {  
  4.     } elseif (mNetworkAgentInfos.containsKey(msg.replyTo)) {  
  5.         if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {  
  6.             //向NetworkAgent發起雙向連線請求
  7.             mNetworkAgentInfos.get(msg.replyTo).asyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);  
  8.         } else {  
  9.         }  
  10.     }  
  11. }  
        我們看到,當ConnectivityService與NetworkAgent之間單向通道建立完成後,又發起了雙向通道的請求,此時在NetworkAgent端,將會收到CMD_CHANNEL_FULL_CONNECTION的訊息: