Android系统中可以提供Internet能力的网络类型有移动网络,WIFI网络,及有线Ethernet。
当Android上层APP访问网络时,如果APP没有明确请求网络的具体类型,通常会走手机默认网络类型。如果APP对某些特定网络比较依赖,比如MMS期望总是使用移动网络。
如下,当手机默认网络是WIFI时,三方APP也可以使用ConnectivityManager.requestNetwork API传入CELLULAR网络参数,这时移动网络如果已经断开,接着会重连并回调结果给ConnectivityManager。
private NetworkCallbackImpl mNetworkCallback;
private ConnectivityManager mCm = null;
private void init() {
mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
mNetworkCallback = new NetworkCallbackImpl();
}
private void requestCellNetwork() {
try {
mCm.requestNetwork(
new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build(),
mNetworkCallback);
} catch (Exception ex) {
Log.e(TAG, "requestNetwork failed, " + ex);
}
}
三方APP可以通过监听网络变化,或主动调用ConnectivityManager.getAllNetworks获取到移动网络的Network对象。
class NetworkCallbackImpl extends ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
Log.d(TAG, "onAvailable network:" + network);
trySetNetwork(network);
}
@Override
public void onLost(Network network) {
super.onLost(network);
Log.d(TAG, "onLost network:" + network);
tryResetNetwork(network);
}
@Override
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities);
Log.d(TAG, "onCapabilitiesChanged network:" + network);
trySetNetwork(network);
}
}
private void updateNetFromAllNetworks() {
Network[] networks = mCm.getAllNetworks();
if (networks == null) {
Log.d(TAG, "updateNetFromAllNetworks, but getAllNetworks is null");
return;
}
for (Network network : networks) {
Log.d(TAG, "updateNetFromAllNetworks network:" + network);
trySetNetwork(network);
}
}
private void trySetNetwork(Network network) {
NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(network);
if (networkCapabilities == null) {
Log.d(TAG, "trySetNetwork, but networkCapabilities is null, network:" + network);
return;
}
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
&& networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
Log.d(TAG, "trySetNetwork cell network:" + network);
mCellNetwork = network;
} else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
&& networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
Log.d(TAG, "trySetNetwork wifi network:" + network);
mWifiNetwork = network;
}
}
private void tryResetNetwork(Network network) {
NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(network);
if (networkCapabilities == null) {
Log.d(TAG, "tryResetNetwork, but networkCapabilities is null, network:" + network);
return;
}
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
&& network == mCellNetwork) {
Log.d(TAG, "tryResetNetwork cell network:" + network);
mCellNetwork = null;
} else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
&& network == mCellNetwork) {
Log.d(TAG, "tryResetNetwork wifi network:" + network);
mWifiNetwork = null;
}
}
当获取到特定网络的Network对象,接着使用该Network去请求网络。
private static final int NET_PROBE_TIMEOUT_IN_MILLIS = 5 * 1000;
private boolean success = false;
/* Do a URL fetch on a known web server to see if we get the data we expect */
private void sendHttpProbe(Network network, String netUrl) {
HttpURLConnection urlConnection = null;
int result = 0;
content = "Empty data";
try {
URL url = new URL(netUrl);
urlConnection = (HttpURLConnection) network.openConnection(url);
urlConnection.setInstanceFollowRedirects(true);
urlConnection.setConnectTimeout(NET_PROBE_TIMEOUT_IN_MILLIS);
urlConnection.setReadTimeout(NET_PROBE_TIMEOUT_IN_MILLIS);
urlConnection.setRequestProperty("Connection", "close");
urlConnection.setUseCaches(false);
urlConnection.connect();
result = urlConnection.getResponseCode();
content = readAsString(urlConnection.getInputStream());
success = true;
} catch (Exception e) {
success = false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
private String readAsString(InputStream is) throws IOException {
final StringBuilder builder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
String inputLine;
while ((inputLine = bufferedReader.readLine()) != null) {
Log.d("inputLine:", inputLine);
builder.append(inputLine);
}
return builder.toString();
}
在Android framework网络模块会对每种提供网络能力的网络类型进行打分,Ethernet默认70分,WIFI 60分,移动网络 50分。分数越高作为默认网络的优先级越高。当特点网络类型网络质量发生变化,会动态给它们加减分,从而影响到系统默认网络的切换。
45/**
46 * {@link NetworkFactory} that represents Ethernet networks.
47 *
48 * This class reports a static network score of 70 when it is tracking an interface and that
49 * interface's link is up, and a score of 0 otherwise.
50 */
51public class EthernetNetworkFactory extends NetworkFactory {
52 private final static String TAG = EthernetNetworkFactory.class.getSimpleName();
53 final static boolean DBG = true;
54
55 private final static int NETWORK_SCORE = 70;
56 private static final String NETWORK_TYPE = "Ethernet";