本文为菜鸟窝作者蒋志碧的连载。“从 0 开始开发一款直播 APP ”系列来聊聊时下最火的直播 APP,如何完整的实现一个类"腾讯直播"的商业化项目
每个程序猿必备的110本经典编程书,免费领取地址:http://mp.weixin.qq.com/s/cx433vAj_CDLzmhOoUS6zA
一、前言
现在大多数 APP 都需要从网络获取数据,访问网络在所难免。但是访问网络之前,我们应该做下网络状态判断,而不是直接使用 HTTP 访问网络。很多人开发经常忽略这块内容跳过网络判断,直接访问网络,当断网时,用户不知,导致用户体验度差,更甚者,当访问某个大流量数据时,用户不希望使用移动网络,而应该使用 Wi-Fi,特别是看高清电影,一部电影看下来流量估计报表,SIM 卡估计都直接负担不起那昂贵的费用了,这时候用户会在心里骂上千万遍。
如果我们的 APP 在加载图片或者大数据下载操作时,提醒用户应该切换到 Wi-Fi 网络进行操作,这样就增加了用户沾粘性,体验效果也会好很多,特别是直播类 APP。判断网络连接是否良好,连接Wi-Fi还是移动网络,断网或者网络改变该如何处理,都需要注意其细节。
二、登录判断网络状态
由于模拟器不支持移动网络,笔者就用真机测试了,但是笔者住的地方没有 Wi-Fi,因此若想要看 Wi-Fi 网络判断,只能通过模拟器了。真机录屏看上去很模糊,但是也只能将就啦。
判断移动网络状态
判断 Wi-Fi 网络状态
三、网络状态详解
3.1、添加权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
3.2、定义常量来标识几种网络类型
//Wi-Fi
public static final int NETTYPE_WIFI = 0;
//无网络
public static final int NETTYPE_NONE = 1;
//2G
public static final int NETTYPE_2G = 2;
//3G
public static final int NETTYPE_3G = 3;
//4G
public static final int NETTYPE_4G = 4;
3.3、判断网络是否连接
我们在做访问的时候都得进行判断是否连网。判断连网也比较简单,就用到了两个类。ConnectivityManager 和 NetworkInfo。
/**
* 检测网络是否连接
* @return
*/
public static boolean checkNetWorkState(Context context){
boolean flag = false;
//得到网络连接信息
manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//判断网络是否连接
if (manager.getActiveNetworkInfo() != null){
flag = manager.getActiveNetworkInfo().isAvailable();
}
if (flag){
//判断网络类型
isNetworkAvailable(context);
}else {
//若网络未连接,则弹出提示进行设置
setNetWork(context);
}
return flag;
}
3.4、判断网络类型
/**
* 网络已经连接,判断是 Wi-Fi 还是移动网络(2G、3G、4G)
* @return
*/
public static void isNetworkAvailable(Context context) {
NetworkInfo.State gprs = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();
NetworkInfo.State wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
if (gprs == NetworkInfo.State.CONNECTED || gprs == NetworkInfo.State.CONNECTING){
Log.e("GPRS","移动网络已打开!");
}
if (wifi == NetworkInfo.State.CONNECTED || wifi == NetworkInfo.State.CONNECTING){
Log.e("WIFI","Wi-Fi已打开!");
}
//获取网络类型
switch (getNetWorkType(context)){
case Constants.NETTYPE_2G:
ToastUtils.showShort(context,"当前网络是2G网络");
break;
case Constants.NETTYPE_3G:
ToastUtils.showShort(context,"当前网络是3G网络");
break;
case Constants.NETTYPE_4G:
ToastUtils.showShort(context,"当前网络是4G网络");
break;
case Constants.NETTYPE_WIFI:
ToastUtils.showShort(context,"当前网络是wifi");
break;
}
}
3.5、获取网络类型
/**
* 获取网络类型
* @param context Context
* @return true 表示网络可用
*/
public static int getNetWorkType(Context context) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
String type = networkInfo.getTypeName();
if (type.equalsIgnoreCase("WIFI")) {
return Constants.NETTYPE_WIFI;//Wi-Fi网络
} else if (type.equalsIgnoreCase("MOBILE")) {
NetworkInfo mobileInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (mobileInfo != null) {
switch (mobileInfo.getType()) {
case ConnectivityManager.TYPE_MOBILE:// 手机网络
switch (mobileInfo.getSubtype()) {
//--------------------Added in API level 1---------------------
//(3G)联通 ~ 400-7000 kbps
case TelephonyManager.NETWORK_TYPE_UMTS:
//(2.5G) 移动和联通 ~ 100 kbps
case TelephonyManager.NETWORK_TYPE_GPRS:
//(2.75G) 2.5G 到 3G 的过渡 移动和联通 ~ 50-100 kbps
case TelephonyManager.NETWORK_TYPE_EDGE:
//-----------------Added in API level 4---------------------
//( 3G )电信 ~ 400-1000 kbps
case TelephonyManager.NETWORK_TYPE_EVDO_0:
//(2G 电信) ~ 14-64 kbps
case TelephonyManager.NETWORK_TYPE_CDMA:
//(3.5G) 属于3G过渡 ~ 600-1400 kbps
case TelephonyManager.NETWORK_TYPE_EVDO_A:
//( 2G ) ~ 50-100 kbps
case TelephonyManager.NETWORK_TYPE_1xRTT:
//---------------------Added in API level 5--------------------
//(3.5G ) ~ 2-14 Mbps
case TelephonyManager.NETWORK_TYPE_HSDPA:
//( 3.5G ) ~ 1-23 Mbps
case TelephonyManager.NETWORK_TYPE_HSUPA:
//( 3G ) 联通 ~ 700-1700 kbps
case TelephonyManager.NETWORK_TYPE_HSPA:
//---------------------Added in API level 8---------------------
//(2G ) ~25 kbps
case TelephonyManager.NETWORK_TYPE_IDEN:
return Constants.NETTYPE_2G;
//---------------------Added in API level 9---------------------
//3G-3.5G ~ 5 Mbps
case TelephonyManager.NETWORK_TYPE_EVDO_B:
//---------------------Added in API level 11--------------------
//(4G) ~ 10+ Mbps
case TelephonyManager.NETWORK_TYPE_LTE:
return Constants.NETTYPE_4G;
//3G(3G到4G的升级产物) ~ 1-2 Mbps
case TelephonyManager.NETWORK_TYPE_EHRPD:
//--------------------Added in API level 13-------------------
//( 3G ) ~ 10-20 Mbps
case TelephonyManager.NETWORK_TYPE_HSPAP:
return Constants.NETTYPE_3G;
//无网络
default:
return Constants.NETTYPE_NONE;
}
}
}
}
}
return Constants.NETTYPE_NONE;
}
3.6、设置网络提示
/**
* 网络为连接时,设置网络
*/
private static void setNetWork(final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("网络连接提示").setMessage("网络不可用,如果继续,请设置网络");
builder.setPositiveButton("设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = null;
/**
* 判断手机系统的版本!如果 API 大于 10 就是 3.0+
* 因为 3.0 以上的版本的设置和 3.0 以下的设置不一样,调用的方法不同
*/
if (Build.VERSION.SDK_INT > 10){
intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
}else {
intent = new Intent();
ComponentName componentName = new ComponentName(
"com.android.settings",
"com.android.settings.WirelessSettings"
);
intent.setComponent(componentName);
intent.setAction("android.intent.action.VIEW");
}
//启动打开 Wi-Fi 设置页面
context.startActivity(intent);
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
return;
}
});
builder.create();
builder.show();
}
在 Presenter 层调用该判断网络请求进行登录操作。具体代码在 MVP 和 MVC 中详细讲过,这里不重复粘贴代码了。
@Override
public boolean checkUserNameLogin(String userName, String password) {
if (OtherUtils.isUsernameVaild(userName)) {
if (OtherUtils.isPasswordValid(password)) {
if (OtherUtils.checkNetWorkState(mLoginView.getContext())) {
return true;
} else {
mLoginView.showMsg("当前无网络连接!");
}
} else {
mLoginView.passwordError("密码过短!");
}
} else {
mLoginView.usernameError("用户名不符合规范!");
}
mLoginView.dismissLoading();
return false;
}
四、ConnectivityManager详解
看到上例中主要使用到的两个类是 ConnectivityManager 和 NetworkInfo,下面先看看 ConnectivityManager 的主要参数和方法。
ConnectivityManager 是网络连接相关的管理器,它主要用于查询网络状态并在网络发生改变时发出状态变化通知。这个类主要负责的下列四个方面:
1、监控网络状态(包括WiFi, GPRS, UMTS等)。
2、当网络连接改变时发送广播 Intent。
3、 当一种网络断开时,试图连接到另一种网络进行故障处理。
4、 提供一系列接口让应用程序查询可获得的网络的粗粒度和细粒度状态。
ConnectivityManager 比较重要的几个类常量
类型 | 常量值 | 说明 |
---|---|---|
int | TYPE_BLUETOOTH | 蓝牙数据连接 |
int | TYPE_ETHERNET | 以太网数据连接 |
int | TYPE_MOBILE | 移动数据连接 |
int | TYPE_WIFI | Wi-Fi连接 |
int | DEFAULT_NETWORK_PREFERENCE | 默认网络连接偏好,建议在config.xml中进行配置.并通过调用 getNetworkPreference() 获取应用的当前设置值。 |
String | EXTRA_EXTRA_INFO | 查询关键字,提供关于网络状态的信息 |
String | EXTRA_NETWORK_INFO | 建议使用getActiveNetworkInfo() or getAllNetworkInfo()获取网络连接信息 |
String | EXTRA_NETWORK_TYPE | 触发 CONNECTIVITY_ACTION广播的网络类型 |
ConnectivityManager 比较重要的几个方法
返回类型 | 方法名 |
---|---|
NetworkInfo | getActiveNetworkInfo() 获取当前连接可用的网络 |
NetworkInfo[] | getAllNetworkInfo() 获取设备支持的所有网络类型的链接状态信息。 |
NetworkInfo | getNetworkInfo(int networkType) 获取特定网络类型的链接状态信息 |
int | getNetworkPreference() 获取当前偏好的网络类型。 |
boolean | isActiveNetworkMetered() 返回当前被计量的活动的数据网络 |
static boolean | isNetworkTypeValid(int networkType) 判断给定的数值是否表示一种网络 |
boolean | requestRouteToHost(int networkType, int hostAddress) 确保存在通过指定网络接口将流量传输到指定主机的网络路由。 |
void | setNetworkPreference(int preference) 指定首选网络类型 |
int | startUsingNetworkFeature(int networkType, String feature) 告诉呼叫者想要开始使用命名功能的底层网络系统。 |
int | stopUsingNetworkFeature(int networkType, String feature) 告诉使用命名功能调用者完成的底层网络系统。 |
五、NetworkInfo 详解
NetworkInfo 是一个描述网络状态的接口,可通过 ConnectivityManager 调用 getActiveNetworkInfo() 获得当前连接的网络类型。
NetworkInfo 有两个枚举类型的成员变量 NetworkInfo.DetailedState 和 NetworkInfo.State,用于查看当前网络的状态。其中 NetworkInfo.State
的值包括:
类型 | 状态值 | 说明 |
---|---|---|
NetworkInfo.State | CONNECTED | 已连接 |
NetworkInfo.State | CONNECTING | 正在连接 |
NetworkInfo.State | DISCONNECTED | 已断开链接 |
NetworkInfo.State | DISCONNECTING | 正在断开链接 |
NetworkInfo.State | SUSPENDED | 暂停/挂起 |
NetworkInfo.State | UNKNOWN | 未知 |
NetworkInfo 还包括一系列可用的方法用于判断当前网络是否可用
,如下:
返回类型 | 方法名 |
---|---|
NetworkInfo.DetailedState | getDetailedState() 获取当前细粒度的网络状态。 |
String | getExtraInfo() 报告关于网络状态的额外信息( 如果有的话由下层网络层提供),如果有一个可用 |
String | getReason() 如果数据网络连接可用,但是连接失败,则通过此方法可获得尝试链接失败的原因 |
boolean | isAvailable() 判断当前网络是否可用 |
boolean | isConnected() 判断当前网络是否存在,并可用于数据传输 |
boolean | isConnectedOrConnecting() 判断网络已连接或正在连接 |
boolean | isFailover() 指示当前试图连接到的网络是否由 ConnectivityManager 尝试断开与其它网络的连接 |
String | toString() 返回一个包含该网络的简单的易懂的字符串描述。 |
一般来说很少用到所有的内容,上面也只是写出一些可能用到的方法和变量。
具体使用在文章前面已经写了,主要是判断网络是否连接,判断网络类型,获取网络类型等。
每个程序猿必备的110本经典编程书,免费领取地址:http://mp.weixin.qq.com/s/cx433vAj_CDLzmhOoUS6zA
参考:
https://my.oschina.net/lzan13/blog/133092
http://blog.csdn.net/ls703/article/details/45823485
http://blog.csdn.net/nanzhiwen666/article/details/8288433#
http://blog.csdn.net/oyangyujun/article/details/41723865