- 如果程序频繁访问网络,应当提供给用户相关的设置,允许用户控制程序的数据使用习惯:
> 程序同步数据的周期
> 是否仅在Wifi下执行上传下载操作
> 是否允许在漫游时使用数据
> 这样,用户才不会断然禁止你的程序访问网络
- 参考:
检查设备的网络连接
- Wifi:快,便宜
- 移动网络:贵
- 通常的策略:仅在Wifi下进行大数据传输
- 最佳实践:在执行网络操作前检查网络连接状态:
- ConnectivityManager:应答对网络连接状态的查询,也在网络连接发生变化时发出通知
- NetworkInfo:针对给出的类型描述其网络接口状态
- 例子:测试Wifi和移动网络的状态 - 是否可用,是否连接等:
> 关键组件:ConnectivityManager, NetworkInfo
> 关键方法:ConnectivityManager.getNetworkInfo(), NetworkInfo.isConnected()
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);
- 注意:你的决策不应当基于连接是否“可用”,而总是要检查isConnected(),因为移动网络可能不正常,设备可能处于飞行模式,诸如此类。
- 以下是一种更精确的网络接口检测方法:
> getActiveNetworkInfo()返回一个NetworkInfo对象,标识能找到的第一个已经连接的网络接口
> 返回null,则表示无连接,即网络不可用
> 关键方法:
ConnectivityManager.getActiveNetworkInfo()
public boolean isOnline() {
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
管理网络使用
- 可以提供给用户一个设置屏幕,让他们显式的控制网络资源的使用,如:
> 设置是否只在Wifi下上传视频
> 设置根据网络可用性或者时间周期之类的标准来执行同步
- 开发支持网络访问和网络使用管理的应用,需要使用相关的权限和intent filters
>
android.permission.INTERNET:允许应用打开网络套接字
>
android.permission.ACCESS_NETWORK_STAT:允许应用访问网络相关信息
- 为
ACTION_MANAGE_NETWORK_USAGE
行为声明intent filter:
> 以示你的应用定义了一个提供数据使用控制选项的活动
<?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>
实现一个设置活动
![执行网络操作(二):管理网络的使用 Preferences panel](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
![执行网络操作(二):管理网络的使用 Setting a network preference](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
- SettingsActivity实现了
OnSharedPreferenceChange Listener接口。用户改变了选项后即触发
onSharedPreferenceChange d(),将
refreshDisplay设为true。主活动因此会刷新显示:
public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChange Listener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Loads the XML preferences file
addPreferencesFromResour ce(R.xml.preferences);
}
@Override
protected void onResume() {
super.onResume();
// Registers a listener whenever a key changes
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferen ceChangeListener(this);
}
@Override
protected 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().unregisterOnSharedPrefer enceChangeListener(this);
}
// When the user changes the preferences selection,
// onSharedPreferenceChange d() restarts the main activity as a new
// task. Sets the 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.
@Override
public void onSharedPreferenceChange d(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;
}
}
响应设置变化
- 当用户改变设置,通常会对应用行为产生重要影响。
- 本例中,程序在onStart()中检查preferences设置。如果设置与设备网络连接能够匹配,应用将下载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();
@Override
public 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.
@Override
public void onStart () {
super.onStart();
// Gets the user's network preference settings
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferen ces(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 subclass
new DownloadXmlTask().execute(URL);
} else {
showErrorPage();
}
}
...
}
侦测连接变化
- 最后用一个BroadcastReceiver的子类来处理连接变化
- 拦截
CONNECTIVITY_ACTION
行为,确定网络状态如何,从而设置两个标记
- NetworkReceiver在活动创建和销毁时注册、注销,这样只有在使用程序时才会工作,避免费电
- 如果在manifest中注册,则全程激活,只能通过
setComponentEnabledSetti ng()
来开关
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();
}
}