Android网络切换分析

转载自:http://blog.csdn.net/jsonz/article/details/20548629

        在Android系统中,不同类型的网络同时开启时,系统总会自动选择一种网络。这是怎么实现的呢?我们不难想到,如果各种网络已经事先定义好了优先级,那么系统就能根据网络类型的优先级自动进行选择。下面将详细讲述:

1. 网络优先级配置文件

        各种不同类型的网络是在文件config.xml中进行配置,config.xml的路径如下:\android\frameworks\base\core\res\res\values 其中网络配置的内容如下:

  1. <string-array translatable="false" name="networkAttributes">  
  2.     <item>"wifi,1,1,1,-1,true"</item>  
  3.     <item>"ethernet,9,9,0,-1,true"</item>  
  4.     <item>"mobile,0,0,0,-1,true"</item>  
  5.     <item>"mobile_mms,2,0,2,60000,true"</item>  
  6.     <item>"mobile_supl,3,0,2,60000,true"</item>  
  7.     <item>"mobile_hipri,5,0,3,60000,true"</item>  
  8.     <item>"mobile_fota,10,0,2,60000,true"</item>  
  9.     <item>"mobile_ims,11,0,2,60000,true"</item>  
  10.     <item>"mobile_cbs,12,0,2,60000,true"</item>  
  11.     <item>"wifi_p2p,13,1,0,-1,true"</item>  
  12. </string-array>  
    <string-array translatable="false" name="networkAttributes">
        <item>"wifi,1,1,1,-1,true"</item>
        <item>"ethernet,9,9,0,-1,true"</item>
        <item>"mobile,0,0,0,-1,true"</item>
        <item>"mobile_mms,2,0,2,60000,true"</item>
        <item>"mobile_supl,3,0,2,60000,true"</item>
        <item>"mobile_hipri,5,0,3,60000,true"</item>
        <item>"mobile_fota,10,0,2,60000,true"</item>
        <item>"mobile_ims,11,0,2,60000,true"</item>
        <item>"mobile_cbs,12,0,2,60000,true"</item>
        <item>"wifi_p2p,13,1,0,-1,true"</item>
    </string-array>

上面各项字符串的排列顺序与类NetworkConfig的成员变量一一对应,按顺序如下:

    name,type,radio,priority,restoreTime,dependencyMet

第4个元素就是我们需要的优先级设置值

2. ConnectivityService服务

        在上面的内容中,我们在配置文件中设置了不同网络类型的优先级。那么配置文件里面的设置是在什么时候被系统读取使用的呢?通过查看源代码会发现是在类ConnectivityService的构造函数中被调用的。具体代码如下:

  1. String[] naStrings = context.getResources().getStringArray(  
  2.                 com.android.internal.R.array.networkAttributes);  
  3.         for (String naString : naStrings) {  
  4.             try {  
  5.                 NetworkConfig n = new NetworkConfig(naString);  
  6.                 if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) {  
  7.                     loge("Error in networkAttributes - ignoring attempt to define type " +  
  8.                             n.type);  
  9.                     continue;  
  10.                 }  
  11.                 if (mNetConfigs[n.type] != null) {  
  12.                     loge("Error in networkAttributes - ignoring attempt to redefine type " +  
  13.                             n.type);  
  14.                     continue;  
  15.                 }  
  16.                 if ((mRadioAttributes[n.radio] == null) && (n.type != ConnectivityManager.TYPE_ETHERNET)) {  
  17.                     loge("Error in networkAttributes - ignoring attempt to use undefined " +  
  18.                             "radio " + n.radio + " in network type " + n.type);  
  19.                     continue;  
  20.                 }  
  21.                 mNetConfigs[n.type] = n;  
  22.                 mNetworksDefined++;  
  23.             } catch(Exception e) {  
  24.                 // ignore it - leave the entry null  
  25.             }  
  26.         }  
String[] naStrings = context.getResources().getStringArray(
                com.android.internal.R.array.networkAttributes);
        for (String naString : naStrings) {
            try {
                NetworkConfig n = new NetworkConfig(naString);
                if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) {
                    loge("Error in networkAttributes - ignoring attempt to define type " +
                            n.type);
                    continue;
                }
                if (mNetConfigs[n.type] != null) {
                    loge("Error in networkAttributes - ignoring attempt to redefine type " +
                            n.type);
                    continue;
                }
                if ((mRadioAttributes[n.radio] == null) && (n.type != ConnectivityManager.TYPE_ETHERNET)) {
                    loge("Error in networkAttributes - ignoring attempt to use undefined " +
                            "radio " + n.radio + " in network type " + n.type);
                    continue;
                }
                mNetConfigs[n.type] = n;
                mNetworksDefined++;
            } catch(Exception e) {
                // ignore it - leave the entry null
            }
        }

        首先获取config.xml中的字符串保存在String数组naStrings 中,然后循环处理数组中的每个元素(如"wifi,1,1,1,-1,true")。之前我们说过,每个元素中的各项跟类NetworkConfig的成员变量对应。这里的处理主要是在声明一个NetworkConfig对象时把每个String元素作为参数,最后把每个NetworkConfig对象加到数组mNetConfigs中。至此,有关各种网络的配置都保存在数组mNetConfigs里。接下来,看看mNetConfigs数组是在什么时候派上用场的?

        通过阅读源代码,可以知道各种网络在启动的时候,会发出一个message(EVENT_STATE_CHANGED),这个消息会在ConnectivityService的内部类NetworkStateTrackerHandler中被处理。代码如下:

  1. private class NetworkStateTrackerHandler extends Handler {  
  2.                 ......  
  3.         @Override  
  4.         public void handleMessage(Message msg) {  
  5.             NetworkInfo info;  
  6.             switch (msg.what) {  
  7.                 case NetworkStateTracker.EVENT_STATE_CHANGED:  
  8.                                         ......  
  9.                     if (info.getDetailedState() ==  
  10.                             NetworkInfo.DetailedState.FAILED) {  
  11.                         handleConnectionFailure(info);  
  12.                     } else if (info.getDetailedState() ==  
  13.                             DetailedState.CAPTIVE_PORTAL_CHECK) {  
  14.                         handleCaptivePortalTrackerCheck(info);  
  15.                     } else if (state == NetworkInfo.State.DISCONNECTED) {  
  16.                         handleDisconnect(info);  
  17.                     } else if (state == NetworkInfo.State.SUSPENDED) {  
  18.                         handleDisconnect(info);  
  19.                     } else if (state == NetworkInfo.State.CONNECTED) {  
  20.                         handleConnect(info);  
  21.                     }  
  22.                 ......  
private class NetworkStateTrackerHandler extends Handler {
				......
        @Override
        public void handleMessage(Message msg) {
            NetworkInfo info;
            switch (msg.what) {
                case NetworkStateTracker.EVENT_STATE_CHANGED:
										......
                    if (info.getDetailedState() ==
                            NetworkInfo.DetailedState.FAILED) {
                        handleConnectionFailure(info);
                    } else if (info.getDetailedState() ==
                            DetailedState.CAPTIVE_PORTAL_CHECK) {
                        handleCaptivePortalTrackerCheck(info);
                    } else if (state == NetworkInfo.State.DISCONNECTED) {
                        handleDisconnect(info);
                    } else if (state == NetworkInfo.State.SUSPENDED) {
                        handleDisconnect(info);
                    } else if (state == NetworkInfo.State.CONNECTED) {
                        handleConnect(info);
                    }
				......

        当网络启动时,最终会调用到函数handleConnect,下面看看handleConnect具体实现:

  1. if (mNetConfigs[newNetType].isDefault()) {  
  2.            if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {  
  3.                if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {  
  4.                    // tear down the other  
  5.                    NetworkStateTracker otherNet =  
  6.                            mNetTrackers[mActiveDefaultNetwork];  
  7.                    if (DBG) {  
  8.                        log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +  
  9.                            " teardown");  
  10.                    }  
  11.                    if (!teardown(otherNet)) {  
  12.                        loge("Network declined teardown request");  
  13.                        teardown(thisNet);  
  14.                        return;  
  15.                    }  
  16.                } else {  
  17.                       // don't accept this one  
  18.                        if (VDBG) {  
  19.                            log("Not broadcasting CONNECT_ACTION " +  
  20.                                "to torn down network " + info.getTypeName());  
  21.                        }  
  22.                        teardown(thisNet);  
  23.                        return;  
  24.                }  
  25.            }  
  26.                     ......  
  27.            mActiveDefaultNetwork = newNetType;  
 if (mNetConfigs[newNetType].isDefault()) {
            if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {
                if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
                    // tear down the other
                    NetworkStateTracker otherNet =
                            mNetTrackers[mActiveDefaultNetwork];
                    if (DBG) {
                        log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
                            " teardown");
                    }
                    if (!teardown(otherNet)) {
                        loge("Network declined teardown request");
                        teardown(thisNet);
                        return;
                    }
                } else {
                       // don't accept this one
                        if (VDBG) {
                            log("Not broadcasting CONNECT_ACTION " +
                                "to torn down network " + info.getTypeName());
                        }
                        teardown(thisNet);
                        return;
                }
            }
						......
            mActiveDefaultNetwork = newNetType;

        第一个if是判断网络是不是默认路由的,上文提到的config.xml中wifi、Ethernet、mobile都是默认路由的。第二个if是根据变量mActiveDefaultNetwork 来判断是否有其他网络正在运行。如果有的话,就对两种网络的优先级进行比较。优先比较是通过函数isNewNetTypePreferredOverCurrentNetType来实现的。如果返回false则不连接网络并返回。如果返回true则断开当前网络,并把newNetType赋给mActiveDefaultNetwork,确保之后的网络操作使用的是新的网络连接。

        到这里已经完成了网络切换的流程。下面分析函数isNewNetTypePreferredOverCurrentNetType,代码如下:

  1. private boolean isNewNetTypePreferredOverCurrentNetType(int type) {  
  2.         if ((type != mNetworkPreference &&  
  3.                     mNetConfigs[mActiveDefaultNetwork].priority >  
  4.                     mNetConfigs[type].priority) ||  
  5.                 mNetworkPreference == mActiveDefaultNetwork) return false;  
  6.         return true;  
  7.     }  
private boolean isNewNetTypePreferredOverCurrentNetType(int type) {
        if ((type != mNetworkPreference &&
                    mNetConfigs[mActiveDefaultNetwork].priority >
                    mNetConfigs[type].priority) ||
                mNetworkPreference == mActiveDefaultNetwork) return false;
        return true;
    }

        函数isNewNetTypePreferredOverCurrentNetType是判断是否切换为新启动的网络。从函数代码可以发现,变量mNetworkPreference 起到了很关键的作用,因为当mActiveDefaultNetwork等于mNetworkPreference时,不管优先级怎样,函数直接返回false。所以必须搞清楚mNetworkPreference的值到底是什么?

        通过查找,知道mNetworkPreference是在ConnectivityService的构造函数中初始化的。

    mNetworkPreference = getPersistedNetworkPreference();

        下面看一下函数getPersistedNetworkPreference返回的是什么值?

  1. private int getPersistedNetworkPreference() {  
  2.         final ContentResolver cr = mContext.getContentResolver();  
  3.   
  4.         final int networkPrefSetting = Settings.Global  
  5.                 .getInt(cr, Settings.Global.NETWORK_PREFERENCE, -1);  
  6.         if (networkPrefSetting != -1) {  
  7.             return networkPrefSetting;  
  8.         }  
  9.   
  10.         return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;  
  11.     }  
private int getPersistedNetworkPreference() {
        final ContentResolver cr = mContext.getContentResolver();

        final int networkPrefSetting = Settings.Global
                .getInt(cr, Settings.Global.NETWORK_PREFERENCE, -1);
        if (networkPrefSetting != -1) {
            return networkPrefSetting;
        }

        return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
    }

        第1、2句是获取ContentProvider与Settings.Global.NETWORK_PREFERENCE对应的值。该ContentProvider是在类DataBaseHelper中初始化,其中有一句关键的代码

  1. db.execSQL("INSERT INTO system ('name', 'value') values ('network_preference', '" +  
  2.                         ConnectivityManager.DEFAULT_NETWORK_PREFERENCE + "')");  
db.execSQL("INSERT INTO system ('name', 'value') values ('network_preference', '" +
                        ConnectivityManager.DEFAULT_NETWORK_PREFERENCE + "')");

        可见其实保存的也是ConnectivityManager.DEFAULT_NETWORK_PREFERENCE的值。至此,可以明确一点,那就是

mNetworkPreference = ConnectivityManager.DEFAULT_NETWORK_PREFERENCE

3 网络相关的svc命令

        上面我们提到的变量mNetworkPreference除了在初始化的时候会改变它的值,还有一个函数也能改变它的值。这个函数是ConnectivityService中的setNetworkPreference。查看源代码可以发现,DataCommand和WifiCommand都调用了ConnectivityService中的这个函数。而DataCommand和WifiCommand是在Svc中初始化的。看到这里,大概可以猜到通过Android的svc命令能够实现上述两个类定义的功能。其中,DataCommand实现对移动网络的开启、关闭和优先选择设置,WifiCommand实现对wifi网络的开启、关闭和优先选择设置。下面看一下命令svc的使用方法:

  1. # svc data  
  2. svc data  
  3. Control mobile data connectivity  
  4. usage: svc data [enable|disable]  
  5. Turn mobile data on or off.  
  6. //设置移动网络的数据是否启用  
  7. svc data prefer  
  8. Set mobile as the preferred data network  
  9. //设置移动网络的数据优先于WIFI  
  10.    
  11. # svc wifi  
  12. svc wifi  
  13. Control the Wi-Fi manager  
  14. usage: svc wifi [enable|disable]  
  15. Turn Wi-Fi on or off.//设置WIFI是否启用  
  16. svc wifi prefer  
  17. Set Wi-Fi as the preferred data network //设置WIFI优先于移动网络的数据,一般应设置成这样,除非你刻意使用移动网络数据传输  
# svc data
svc data
Control mobile data connectivity
usage: svc data [enable|disable]
Turn mobile data on or off.
//设置移动网络的数据是否启用
svc data prefer
Set mobile as the preferred data network
//设置移动网络的数据优先于WIFI
 
# svc wifi
svc wifi
Control the Wi-Fi manager
usage: svc wifi [enable|disable]
Turn Wi-Fi on or off.//设置WIFI是否启用
svc wifi prefer
Set Wi-Fi as the preferred data network //设置WIFI优先于移动网络的数据,一般应设置成这样,除非你刻意使用移动网络数据传输

svc data prefer最终会改变mNetworkPreference的值。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值