管理对网络的使用
這一節(jié)描述了如何寫(xiě)出可以對(duì)網(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í)是否使用數(shù)據(jù),等等。通過(guò)這些對(duì)app的控制,則當(dāng)達(dá)到用戶的數(shù)據(jù)流量限制時(shí),他們禁用你app的后臺(tái)數(shù)據(jù)訪問(wèn)的可能性就會(huì)大大降低,因?yàn)樗麄兛梢源?/span>精確地控制你的app對(duì)數(shù)據(jù)的使用。
關(guān)于如何寫(xiě)出最小化下載和網(wǎng)絡(luò)連接對(duì)于電池壽命的影響的app的通用指南,請(qǐng)參考Optimizing Battery Life和Transferring Data Without Draining the Battery。
檢查設(shè)備的網(wǎng)絡(luò)連接
一個(gè)設(shè)備可以具有多種類型的網(wǎng)絡(luò)連接。這一節(jié)將集中于使用一個(gè)Wi-Fi或一個(gè)移動(dòng)網(wǎng)絡(luò)連接。關(guān)于可能的網(wǎng)絡(luò)類型的完整列表,請(qǐng)參考ConnectivityManager。
Wi-Fi通常更快。而移動(dòng)數(shù)據(jù)通常是計(jì)量的,并要付費(fèi)。apps一個(gè)常用的策略是,只在Wi-Fi網(wǎng)絡(luò)可用時(shí)才獲取量大的數(shù)據(jù)。
在你執(zhí)行網(wǎng)絡(luò)操作前,檢查網(wǎng)絡(luò)連接的狀態(tài)是一個(gè)很好的實(shí)踐。此外,這可以防止你的app不小心使用了錯(cuò)誤的radio。如果一個(gè)網(wǎng)絡(luò)無(wú)法訪問(wèn),你的應(yīng)用程序應(yīng)該友好地作出反應(yīng)。要檢查網(wǎng)絡(luò)連接,通常你要使用下面的類:
- ConnectivityManager:可以了解網(wǎng)絡(luò)連接的狀態(tài)。當(dāng)網(wǎng)絡(luò)連接改變時(shí),它也會(huì)通知應(yīng)用。
- NetworkInfo:描述了一個(gè)給定類型(當(dāng)前是移動(dòng)或Wi-Fi)的網(wǎng)絡(luò)接口的狀態(tài)。
這個(gè)代碼片段測(cè)試了Wi-Fi和移動(dòng)網(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)該把決定建立在一個(gè)網(wǎng)絡(luò)是否“可用”的基礎(chǔ)上。你應(yīng)該總是在執(zhí)行網(wǎng)絡(luò)操作之前檢查 isConnected(),因?yàn)?/span>isConnected()會(huì)處理諸如flaky移動(dòng)網(wǎng)絡(luò),飛行模式,和受限的后臺(tái)數(shù)據(jù)等情況。
檢查一個(gè)網(wǎng)絡(luò)接口是否可用的更簡(jiǎn)明的方式如下。getActiveNetworkInfo()方法返回一個(gè)NetworkInfo實(shí)例,其代表它能找到的第一個(gè)已連接的網(wǎng)絡(luò)接口,或者如果沒(méi)有已連接的接口(意味著一個(gè)因特網(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)該很少用到。
管理對(duì)網(wǎng)絡(luò)的使用
你可以實(shí)現(xiàn)實(shí)現(xiàn)一個(gè)preferences activity,以使用戶能夠顯式地控制你的app對(duì)于網(wǎng)絡(luò)資源的使用。比如:
- 你可以允許用戶只有在設(shè)備連接了Wi-Fi網(wǎng)絡(luò)的情況下才上傳視頻。
- 你可以依據(jù)于特定的條件,比如網(wǎng)絡(luò)的可用性,時(shí)間間隔等等來(lái)確定是否同步(或不同步)。
要編寫(xiě)一個(gè)支持訪問(wèn)網(wǎng)絡(luò)并管理對(duì)網(wǎng)絡(luò)的使用的app,你的manifest必須具有正確的permissions及intent filters。
- manifest需要包含下面的permissions:
- android.permission.INTERNET—允許應(yīng)用程序打開(kāi)網(wǎng)絡(luò)sockets。
- android.permission.ACCESS_NETWORK_STATE—允許應(yīng)用程序訪問(wèn)關(guān)于網(wǎng)絡(luò)的信息。
- 你可以為ACTION_MANAGE_NETWORK_USAGE?action(在Android 4.0中引入)聲明intent filter以表明你的應(yīng)用程序定義了一個(gè)activity,其提供了控制數(shù)據(jù)使用的選項(xiàng)。ACTION_MANAGE_NETWORK_USAGE顯示設(shè)置項(xiàng)來(lái)管理一個(gè)特定的應(yīng)用的網(wǎng)絡(luò)數(shù)據(jù)使用。當(dāng)你的app具有一個(gè)允許用戶控制網(wǎng)絡(luò)使用的設(shè)置activity時(shí),你應(yīng)該為那個(gè)activity聲明這個(gè)intent filter。在這個(gè)示例應(yīng)用中,這個(gè)action有類SettingsActivity來(lái)處理,它顯示一個(gè)preferences UI以使用戶決定何時(shí)下載一個(gè)feed。
實(shí)現(xiàn)一個(gè)Preferences Activity
如你在上面引用的manifest中所看到的那樣,示例app的activity SettingsActivity有一個(gè)ACTION_MANAGE_NETWORK_USAGE?action的intent filter。SettingsActivity是PreferenceActivity的一個(gè)子類。它顯示一個(gè)preferences界面(如figure 1中所示的那樣)以使用戶能夠指定如下的一些內(nèi)容:
- 是否為每個(gè)XML feed項(xiàng)顯示summaries,或者只是每一項(xiàng)的一個(gè)鏈接。
- 當(dāng)任何網(wǎng)絡(luò)連接可用時(shí)是否下載XML feed,或者只有Wi-Fi可用時(shí)才下載。
?? ??
Figure 1.?Preferences activity.
這里是SettingsActivity。注意,它實(shí)現(xiàn)了OnSharedPreferenceChangeListener。當(dāng)一個(gè)用戶改變了一個(gè)preference時(shí),它能使onSharedPreferenceChanged()被調(diào)用到,其會(huì)把refreshDisplay設(shè)置為true。這會(huì)導(dǎo)致用戶返回到主activity時(shí)刷新顯示:
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時(shí),它通常會(huì)對(duì)app的行為產(chǎn)生影響。在這個(gè)代碼片段中,app在onStart()中檢查preferences設(shè)置。如果設(shè)置與設(shè)備的網(wǎng)絡(luò)連接匹配(比如,如果設(shè)置中是“Wi-Fi”,并且設(shè)備有一個(gè)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();}} ...}
探測(cè)連接的改變
最后一個(gè)令人疑惑的地方是BroadcastReceiver的子類,NetworkReceiver。當(dāng)設(shè)備的網(wǎng)絡(luò)連接改變時(shí),NetworkReceiver攔截了action?CONNECTIVITY_ACTION,其確定網(wǎng)絡(luò)連接狀態(tài)是什麼,并據(jù)此設(shè)置標(biāo)記wifiConnected和mobileConnected為true/false。結(jié)果就是下一次用戶返回到app,app只有在NetworkActivity.refereshDisplay被設(shè)置為true時(shí),才下載最新的feed,并更新顯示。
在不必要的時(shí)候設(shè)置一個(gè)BroadcastReceiver可能是一個(gè)系統(tǒng)資源的泄漏。示例應(yīng)用在onCreate()中注冊(cè)BroadcastReceiver?NetworkReceiver,并在onDestroy()中注銷它。相對(duì)于在manifest中聲明一個(gè)<receiver>,這是一種更輕量級(jí)的方法。當(dāng)你在manifest中聲明一個(gè)<receiver>時(shí),它可以在任何時(shí)刻喚醒你的app,即使你已經(jīng)幾周沒(méi)有運(yùn)行過(guò)它了。通過(guò)在主activity中注冊(cè)和注銷NetworkReceiver,你可以確保在用戶離開(kāi)app之后app不會(huì)被喚醒。如果你確實(shí)在manifest中聲明了一個(gè)<receiver>,并且你明確知道什麼地方會(huì)用到它,你可以適當(dāng)?shù)厥褂?span style="font-size:16px;">setComponentEnabledSetting()來(lái)啟用或禁用它。
這里是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内置模块