管理对网络的使用
這一節(jié)描述了如何寫出可以對網(wǎng)絡(luò)資源的使用進(jìn)行細(xì)粒度控制的應(yīng)用程序。如果你的應(yīng)用程序執(zhí)行了大量的網(wǎng)絡(luò)操作,則你應(yīng)該提供用戶設(shè)置,以允許用戶控制你的app的數(shù)據(jù)習(xí)性,比如你的app同步數(shù)據(jù)的頻率,是否只在Wi-Fi的情況下執(zhí)行上傳/下載,漫游時是否使用數(shù)據(jù),等等。通過這些對app的控制,則當(dāng)達(dá)到用戶的數(shù)據(jù)流量限制時,他們禁用你app的后臺數(shù)據(jù)訪問的可能性就會大大降低,因?yàn)樗麄兛梢源?/span>精確地控制你的app對數(shù)據(jù)的使用。
關(guān)于如何寫出最小化下載和網(wǎng)絡(luò)連接對于電池壽命的影響的app的通用指南,請參考Optimizing Battery Life和Transferring Data Without Draining the Battery。
檢查設(shè)備的網(wǎng)絡(luò)連接
一個設(shè)備可以具有多種類型的網(wǎng)絡(luò)連接。這一節(jié)將集中于使用一個Wi-Fi或一個移動網(wǎng)絡(luò)連接。關(guān)于可能的網(wǎng)絡(luò)類型的完整列表,請參考ConnectivityManager。
Wi-Fi通常更快。而移動數(shù)據(jù)通常是計量的,并要付費(fèi)。apps一個常用的策略是,只在Wi-Fi網(wǎng)絡(luò)可用時才獲取量大的數(shù)據(jù)。
在你執(zhí)行網(wǎng)絡(luò)操作前,檢查網(wǎng)絡(luò)連接的狀態(tài)是一個很好的實(shí)踐。此外,這可以防止你的app不小心使用了錯誤的radio。如果一個網(wǎng)絡(luò)無法訪問,你的應(yīng)用程序應(yīng)該友好地作出反應(yīng)。要檢查網(wǎng)絡(luò)連接,通常你要使用下面的類:
- ConnectivityManager:可以了解網(wǎng)絡(luò)連接的狀態(tài)。當(dāng)網(wǎng)絡(luò)連接改變時,它也會通知應(yīng)用。
- NetworkInfo:描述了一個給定類型(當(dāng)前是移動或Wi-Fi)的網(wǎng)絡(luò)接口的狀態(tài)。
這個代碼片段測試了Wi-Fi和移動網(wǎng)絡(luò)連接。它確定了這些網(wǎng)絡(luò)接口是否可用(即網(wǎng)絡(luò)連接是否possible) 和/或已經(jīng)連接(即,網(wǎng)絡(luò)連接是否存在及是否能夠建立sockets和傳遞數(shù)據(jù))在:
private static final String 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);
注意你不應(yīng)該把決定建立在一個網(wǎng)絡(luò)是否“可用”的基礎(chǔ)上。你應(yīng)該總是在執(zhí)行網(wǎng)絡(luò)操作之前檢查 isConnected(),因?yàn)?/span>isConnected()會處理諸如flaky移動網(wǎng)絡(luò),飛行模式,和受限的后臺數(shù)據(jù)等情況。
檢查一個網(wǎng)絡(luò)接口是否可用的更簡明的方式如下。getActiveNetworkInfo()方法返回一個NetworkInfo實(shí)例,其代表它能找到的第一個已連接的網(wǎng)絡(luò)接口,或者如果沒有已連接的接口(意味著一個因特網(wǎng)連接不可用)則為null:
public boolean isOnline() {ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();return (networkInfo != null && networkInfo.isConnected()); }
要獲取更多細(xì)粒度的狀態(tài),你可以使用etworkInfo.DetailedState,但這應(yīng)該很少用到。
管理對網(wǎng)絡(luò)的使用
你可以實(shí)現(xiàn)實(shí)現(xiàn)一個preferences activity,以使用戶能夠顯式地控制你的app對于網(wǎng)絡(luò)資源的使用。比如:
- 你可以允許用戶只有在設(shè)備連接了Wi-Fi網(wǎng)絡(luò)的情況下才上傳視頻。
- 你可以依據(jù)于特定的條件,比如網(wǎng)絡(luò)的可用性,時間間隔等等來確定是否同步(或不同步)。
要編寫一個支持訪問網(wǎng)絡(luò)并管理對網(wǎng)絡(luò)的使用的app,你的manifest必須具有正確的permissions及intent filters。
- manifest需要包含下面的permissions:
- android.permission.INTERNET—允許應(yīng)用程序打開網(wǎng)絡(luò)sockets。
- android.permission.ACCESS_NETWORK_STATE—允許應(yīng)用程序訪問關(guān)于網(wǎng)絡(luò)的信息。
- 你可以為ACTION_MANAGE_NETWORK_USAGE?action(在Android 4.0中引入)聲明intent filter以表明你的應(yīng)用程序定義了一個activity,其提供了控制數(shù)據(jù)使用的選項。ACTION_MANAGE_NETWORK_USAGE顯示設(shè)置項來管理一個特定的應(yīng)用的網(wǎng)絡(luò)數(shù)據(jù)使用。當(dāng)你的app具有一個允許用戶控制網(wǎng)絡(luò)使用的設(shè)置activity時,你應(yīng)該為那個activity聲明這個intent filter。在這個示例應(yīng)用中,這個action有類SettingsActivity來處理,它顯示一個preferences UI以使用戶決定何時下載一個feed。
實(shí)現(xiàn)一個Preferences Activity
如你在上面引用的manifest中所看到的那樣,示例app的activity SettingsActivity有一個ACTION_MANAGE_NETWORK_USAGE?action的intent filter。SettingsActivity是PreferenceActivity的一個子類。它顯示一個preferences界面(如figure 1中所示的那樣)以使用戶能夠指定如下的一些內(nèi)容:
- 是否為每個XML feed項顯示summaries,或者只是每一項的一個鏈接。
- 當(dāng)任何網(wǎng)絡(luò)連接可用時是否下載XML feed,或者只有Wi-Fi可用時才下載。
?? ??
Figure 1.?Preferences activity.
這里是SettingsActivity。注意,它實(shí)現(xiàn)了OnSharedPreferenceChangeListener。當(dāng)一個用戶改變了一個preference時,它能使onSharedPreferenceChanged()被調(diào)用到,其會把refreshDisplay設(shè)置為true。這會導(dǎo)致用戶返回到主activity時刷新顯示:
public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// Loads the XML preferences fileaddPreferencesFromResource(R.xml.preferences);}@Overrideprotected void onResume() {super.onResume();// Registers a listener whenever a key changes getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);}@Overrideprotected void onPause() {super.onPause();// Unregisters the listener set in onResume().// It's best practice 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 changes the preferences selection, // onSharedPreferenceChanged() restarts the main activity as a new// task. Sets the refreshDisplay flag to "true" to indicate that// the main activity should update its display.// The main activity queries the PreferenceManager to get the latest settings.@Overridepublic void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { // Sets refreshDisplay to true so that when the user returns to the main// activity, the display refreshes to reflect the new settings.NetworkActivity.refreshDisplay = true;} }
響應(yīng)Preference的變化
當(dāng)用戶在設(shè)置界面中改變了preferences時,它通常會對app的行為產(chǎn)生影響。在這個代碼片段中,app在onStart()中檢查preferences設(shè)置。如果設(shè)置與設(shè)備的網(wǎng)絡(luò)連接匹配(比如,如果設(shè)置中是“Wi-Fi”,并且設(shè)備有一個Wi-Fi連接),則app下載feed并刷新顯示。
public class NetworkActivity extends Activity {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 a Wi-Fi connection.private static boolean wifiConnected = false; // Whether there is a mobile connection.private static boolean mobileConnected = false;// Whether the display should be refreshed.public static boolean refreshDisplay = true;// The user's current network preference setting.public static String sPref = null;// The BroadcastReceiver that tracks network connectivity changes.private NetworkReceiver receiver = new NetworkReceiver();@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// Registers BroadcastReceiver 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 display if the network connection and the// pref settings allow it.@Overridepublic void onStart () {super.onStart(); // Gets the user's network preference settingsSharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);// Retrieves a string value for the preferences. The second parameter// is the default value to use if a preference value is not found.sPref = sharedPrefs.getString("listPref", "Wi-Fi");updateConnectedFlags(); if(refreshDisplay){loadPage(); }}// Checks the network connection and sets the wifiConnected and mobileConnected// variables accordingly. public void updateConnectedFlags() {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 subclass to download the XML feed from stackoverflow.com.public void loadPage() {if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))|| ((sPref.equals(WIFI)) && (wifiConnected))) {// AsyncTask subclassnew DownloadXmlTask().execute(URL);} else {showErrorPage();}} ...}
探測連接的改變
最后一個令人疑惑的地方是BroadcastReceiver的子類,NetworkReceiver。當(dāng)設(shè)備的網(wǎng)絡(luò)連接改變時,NetworkReceiver攔截了action?CONNECTIVITY_ACTION,其確定網(wǎng)絡(luò)連接狀態(tài)是什麼,并據(jù)此設(shè)置標(biāo)記wifiConnected和mobileConnected為true/false。結(jié)果就是下一次用戶返回到app,app只有在NetworkActivity.refereshDisplay被設(shè)置為true時,才下載最新的feed,并更新顯示。
在不必要的時候設(shè)置一個BroadcastReceiver可能是一個系統(tǒng)資源的泄漏。示例應(yīng)用在onCreate()中注冊BroadcastReceiver?NetworkReceiver,并在onDestroy()中注銷它。相對于在manifest中聲明一個<receiver>,這是一種更輕量級的方法。當(dāng)你在manifest中聲明一個<receiver>時,它可以在任何時刻喚醒你的app,即使你已經(jīng)幾周沒有運(yùn)行過它了。通過在主activity中注冊和注銷NetworkReceiver,你可以確保在用戶離開app之后app不會被喚醒。如果你確實(shí)在manifest中聲明了一個<receiver>,并且你明確知道什麼地方會用到它,你可以適當(dāng)?shù)厥褂?span style="font-size:16px;">setComponentEnabledSetting()來啟用或禁用它。
這里是NetworkReceiver:
public class NetworkReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) {ConnectivityManager conn = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo networkInfo = conn.getActiveNetworkInfo();// Checks the user prefs and the network connection. Based on the result, decides whether// to refresh the display or keep the current display.// If the userpref is Wi-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 device has its Wi-Fi connection, sets refreshDisplay// to true. This causes the display to be refreshed when the user// returns to the app.refreshDisplay = true;Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();// If the setting is ANY network and there is a network connection// (which by process of elimination would be mobile), sets refreshDisplay to true.} else if (ANY.equals(sPref) && networkInfo != null) {refreshDisplay = true;// Otherwise, the app can't download content--either because there is no network// connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there // is no Wi-Fi connection.// Sets refreshDisplay to false.} else {refreshDisplay = false;Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();} }譯自:http://developer.android.com/training/basics/network-ops/managing.html。
Done.
轉(zhuǎn)載于:https://my.oschina.net/wolfcs/blog/297764
總結(jié)
- 上一篇: 中医药专家开年会 推荐11种最靠谱的抗癌
- 下一篇: ansible内置模块