Android---管理网络的使用

来源:互联网 发布:6级听力 知乎 编辑:程序博客网 时间:2024/04/28 10:49

本文译自:http://developer.android.com/training/basics/network-ops/managing.html

本文介绍如何编写细粒度的应用程序,以便控制网络资源的使用。如果你的应用程序执行很多网络操作,那么你应该给用户提供设置功能,允许用户来控制应用程序的数据处理方式,如应用程序是否经常同步数据;是否是在只有Wi-Fi时才上传或下载数据;手机漫游时,是否使用数据等等。把这些控制提供给用户,用户很少会在流量达到极限时禁止你的应用程序访问后台的数据,因为他们不能够精确的计算你的应用程序对数据流量的使用情况。

对于如何编写最小化的消耗电池电量的下载和网络连接的应用程序,请看“优化电池消耗”和“不消耗电池电量的数据传输

检查设备的网络连接

一个设备可以有各种类型的网络连接。本文关注Wi-Fi和移动网络的连接。完整的可能的网络类型,请看ConnectivityManager

通常,Wi-Fi比较快。而且移动数据经常是计费的,这可能很昂贵。应用程序通常的策略是只有在Wi-Fi网络有效时,才获取大数据。

在执行网络操作之前,检查网络连接的状态是一种好的实践。这样可以防止你的应用程序无意的使用了错误的网络收音机。如果网络连接时无效的,你的应用程序应该给用户提供良好的响应。通常使用以下类来检查网络连接:

1.  ConnectivityManager:这个类可以提供网络连接的状态。在网络连接发生变化时,它也会通知应用程序。

2. NetworkInfo:说明给定类型的网络接口的状态(当前可以是移动网络,也可以是Wi-Fi网络)

以下代码片段用于检查Wi-Fi和移动网络的连接。它判断这些网络接口是否有效(也就是说,判断是否可以建立网络连接)或已连接(也就是说,判断网络连接是否存在,并是否可以建立套接字和传递数据):

privatestaticfinalString DEBUG_TAG="NetworkStatusExample";
...
     
ConnectivityManager connMgr = (ConnectivityManager)
        getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
boolean isWifiConn = networkInfo.isConnected();
networkInfo =connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileConn = networkInfo.isConnected();
Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);

注意,网络是否有效,不应该是你决策的基础。在网络操作之前,应该始终调用isConnected()方法来检查网络连接,因为isConnected()方法会处理移动网络、飞行模式,以及受限制的后台数据等情况。

以下是检查网络接口是否有效的更简洁的方法。GetActiveNetworkInfo()方法返回的NetworkInfo实例代表了它能够找到的第一个被连接的网络接口,如果返回null,则没有网络接口被连接(这意味着互联网连接无效):

publicboolean isOnline(){
   
ConnectivityManager connMgr = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
}  

使用NetworkInfo.DetailedState可以查询更详细的状态信息,但这几乎不需要。

管理网络的使用

你可以实现一个用于设置的Activity,以便用户明确的控制你的应用程序的网络资源的使用。例如:

1. 你可以让用户只有在设备连接到Wi-Fi网络时才上传视频;

2. 你可以根据指定的规范,如可用的网络、时间间隔等来同步(或不同步)数据。

要编写一个支持网络访问和管理网络使用的应用程序,你的清单文件必须要申请正确的权限和Intent过滤器。

以下摘录了清单中包含的权限:

android.permission.INTERNET---允许应用程序打开网络套接字。

android.permission.ACCESS_NETWORK_STATE---允许应用程序访问有关网络的信息。

你可以声明用于过滤ACTION_MANAGE_NETWORK_USAGE类型动作(在Android4.0中被引入)的Intent过滤器,指明你的应用程序定义了一个提供控制数据使用选项的ActivityACTION_MANAGE_NETWORK_USAGE动作会显示特定应用程序中,用于管理网络数据使用的设置选项。当你的应用程序有了这个允许用户控制网络使用的设置Activity时,就应该给这个Activity设置这种类型的Intent过滤器。在示例应用程序中,这个操作是由SettingsActivity类来处理的,它会显示一个让用户决定的设置用UI界面。

<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.networkusage"
    ...>

    <uses-sdk android:minSdkVersion="4"
           android:targetSdkVersion="14" />
       
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        ...>
        ...
        <activity android:label="SettingsActivity" android:name=".SettingsActivity">
             <intent-filter>
                <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
                <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
        </activity>
    </application>
</manifest>

实现一个设置用的Activity

在上面的清单摘要中可以以看到,该示例应用程序的Activity---SettingsActivity有一个适用ACTION_MANAGE_NETWORK_USAGE动作的Intent过滤器。SettingsActivity是PreferenceActivity的子类。它会显示一个设置界面(如下图1所示),让用户指定以下设置:

1. 是否显示每个XML条目的概要,或只是显示每个条目的连接;

2. 如果网络连接有效时,是否下载XML数据,或是只有在Wi-Fi时才有效。

图1.设置Activity

注意,SettingsActivity实现了OnSharePreferenceChangeListener接口。当用户改变设置时,它会触发onSharedPreferenceChanged()方法,它把refreshDisplay设置为true。这会导致用户返回主Activity时显示的刷新:

publicclassSettingsActivityextendsPreferenceActivityimplementsOnSharedPreferenceChangeListener{
   

    @Override
    protected void onCreate(BundlesavedInstanceState) {
        super.onCreate(savedInstanceState);
       
        // Loads theXML preferences file
        addPreferencesFromResource(R.xml.preferences);
    }
 
    @Override
    protected void onResume() {
        super.onResume();

        // Registersa listener whenever a key changes            
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }
 
    @Override
    protected void onPause() {
        super.onPause();

       //Unregisters the listener set in onResume().
       // It's bestpractice to unregister listeners when your app isn't using them to cut down on
       //unnecessary system overhead. You do this in onPause().           
       getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);    
    }
 
    // When the user changesthe preferences selection,
    //onSharedPreferenceChanged() restarts the main activity as a new
    // task. Sets the refreshDisplayflag to "true" to indicate that
    // the main activityshould update its display.
    // The main activityqueries the PreferenceManager to get the latest settings.
   
    @Override
    public voidonSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {    
        // SetsrefreshDisplay to true so that when the user returns to the main
        // activity,the display refreshes to reflect the new settings.
        NetworkActivity.refreshDisplay = true;
    }
}

响应设置的改变

当用户改变这个设置界面中的设置选项时,它会影响应用程序的行为。在本段中,应用程序会onStart()方法中检查选项设置。如果有跟设置匹配的设备网络连接(例如,如果设置是Wi-Fi,并且设备的Wi-Fi连接有效),应程序就可以下载数据,并刷新显示。

publicclassNetworkActivityextendsActivity{
   
public static final String WIFI = "Wi-Fi";
    public static final String ANY = "Any";
    private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";
   
    // Whether there is aWi-Fi connection.
    private static boolean wifiConnected = false;
    // Whether there is amobile connection.
    private static boolean mobileConnected = false;
    // Whether the displayshould be refreshed.
    public static boolean refreshDisplay = true;
   
    // The user's currentnetwork preference setting.
    public static String sPref = null;
   
    // The BroadcastReceiverthat tracks network connectivity changes.
    private NetworkReceiverreceiver = new NetworkReceiver();
   
    @Override
    public void onCreate(BundlesavedInstanceState) {
        super.onCreate(savedInstanceState);
       
        // RegistersBroadcastReceiver to track network connection changes.
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        receiver = new NetworkReceiver();
        this.registerReceiver(receiver, filter);
    }
   
    @Override
    public void onDestroy() {
        super.onDestroy();
        //Unregisters BroadcastReceiver when app is destroyed.
        if (receiver != null) {
            this.unregisterReceiver(receiver);
        }
    }
   
    // Refreshes the displayif the network connection and the
    // pref settings allow it.
   
    @Override
    public void onStart () {
        super.onStart();  
       
        // Gets theuser's network preference settings
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
       
        // Retrievesa string value for the preferences. The second parameter
        // is thedefault value to use if a preference value is not found.
        sPref = sharedPrefs.getString("listPref", "Wi-Fi");

        updateConnectedFlags();
       
        if(refreshDisplay){
            loadPage();    
        }
    }
   
    // Checks the networkconnection and sets the wifiConnected and mobileConnected
    // variables accordingly.
    public voidupdateConnectedFlags() {
        ConnectivityManager connMgr = (ConnectivityManager)
               getSystemService(Context.CONNECTIVITY_SERVICE);
       
        NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
        if (activeInfo != null && activeInfo.isConnected()) {
            wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
            mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
        } else {
            wifiConnected = false;
            mobileConnected = false;
        }  
    }
     
    // Uses AsyncTask subclassto download the XML feed from stackoverflow.com.
    public void loadPage() {
        if (((sPref.equals(ANY)) && (wifiConnected ||mobileConnected))
                || ((sPref.equals(WIFI)) && (wifiConnected))) {
            // AsyncTask subclass
            new DownloadXmlTask().execute(URL);
        } else {
            showErrorPage();
        }
    }
...
   
}

检测连接的变化

最后的问题是BroadcastReceiver之类---NetworkReceiver。当设备的网络连接变化时,NetworkReceiver会截获CONNECTIVITY_ACTION动作,来判断网络连接的状态,并相应把wifiConnectedmobileConnected标记设置为truefalse。这样做的结果是,下次用户返回该App时,该应用程序只会下载最新的数据,并且如果NetworkActivity.refreshDisplay被设置为true,就会显示更新。

设置不必要的广播接收器被认为是系统资源的浪费。该示例应用程序在onCreate()方法中注册了广播接收器---NetworkReceiver,在onDestroy()方法中解除注册。这比在清单中声明<receiver>要轻量。当在清单中声明一个<receiver>时,会在任何时候唤醒你的应用程序,即使它很久没有被运行。通过在主Activity中注册和解除注册NetworkReceiver广播接收器,可以确保在用户退出应用程序后不被唤醒。如果在清单中声明了<receiver>,并且你清楚的知道需要它的地方,就可以使用setComponentEnabledSetting()方法来启用和禁用它。

以下是NetworkReceiver接收器:

publicclassNetworkReceiverextendsBroadcastReceiver{  
     

@Override
public void onReceive(Context context, Intent intent) {
    ConnectivityManager conn =  (ConnectivityManager)
        context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = conn.getActiveNetworkInfo();
       
    // Checks the user prefsand the network connection. Based on the result, decides whether
    // to refresh the displayor keep the current display.
    // If the userpref isWi-Fi only, checks to see if the device has a Wi-Fi connection.
    if (WIFI.equals(sPref) && networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
        // If devicehas its Wi-Fi connection, sets refreshDisplay
        // to true.This causes the display to be refreshed when the user
        // returnsto the app.
        refreshDisplay = true;
        Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();

    // If the setting is ANYnetwork and there is a network connection
    // (which by process ofelimination would be mobile), sets refreshDisplay to true.
    } else if (ANY.equals(sPref) && networkInfo != null) {
        refreshDisplay = true;
                 
    // Otherwise, the appcan't download content--either because there is no network
    // connection (mobile orWi-Fi), or because the pref setting is WIFI, and there
    // is no Wi-Fi connection.
    // Sets refreshDisplay tofalse.
    } else {
        refreshDisplay = false;
        Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
    }
}

 

原创粉丝点击