本文為菜鳥窩作者蔣志碧的連載酌儒〖稍酰“從 0 開始開發(fā)一款直播 APP ”系列來聊聊時(shí)下最火的直播 APP酪夷,如何完整的實(shí)現(xiàn)一個(gè)類"騰訊直播"的商業(yè)化項(xiàng)目
每個(gè)程序猿必備的110本經(jīng)典編程書捶索,免費(fèi)領(lǐng)取地址:http://mp.weixin.qq.com/s/cx433vAj_CDLzmhOoUS6zA
一、前言
現(xiàn)在大多數(shù) APP 都需要從網(wǎng)絡(luò)獲取數(shù)據(jù)辅甥,訪問網(wǎng)絡(luò)在所難免燎竖。但是訪問網(wǎng)絡(luò)之前构回,我們應(yīng)該做下網(wǎng)絡(luò)狀態(tài)判斷,而不是直接使用 HTTP 訪問網(wǎng)絡(luò)脐供。很多人開發(fā)經(jīng)常忽略這塊內(nèi)容跳過網(wǎng)絡(luò)判斷借跪,直接訪問網(wǎng)絡(luò)掏愁,當(dāng)斷網(wǎng)時(shí),用戶不知沦泌,導(dǎo)致用戶體驗(yàn)度差辛掠,更甚者,當(dāng)訪問某個(gè)大流量數(shù)據(jù)時(shí)他宛,用戶不希望使用移動(dòng)網(wǎng)絡(luò)厅各,而應(yīng)該使用 Wi-Fi,特別是看高清電影袁梗,一部電影看下來流量估計(jì)報(bào)表遮怜,SIM 卡估計(jì)都直接負(fù)擔(dān)不起那昂貴的費(fèi)用了鸿市,這時(shí)候用戶會(huì)在心里罵上千萬遍。
如果我們的 APP 在加載圖片或者大數(shù)據(jù)下載操作時(shí)陌凳,提醒用戶應(yīng)該切換到 Wi-Fi 網(wǎng)絡(luò)進(jìn)行操作合敦,這樣就增加了用戶沾粘性验游,體驗(yàn)效果也會(huì)好很多耕蝉,特別是直播類 APP。判斷網(wǎng)絡(luò)連接是否良好炒俱,連接Wi-Fi還是移動(dòng)網(wǎng)絡(luò)爪膊,斷網(wǎng)或者網(wǎng)絡(luò)改變?cè)撊绾翁幚硗剖ⅲ夹枰⒁馄浼?xì)節(jié)谦铃。
二、登錄判斷網(wǎng)絡(luò)狀態(tài)
由于模擬器不支持移動(dòng)網(wǎng)絡(luò)瘪菌,筆者就用真機(jī)測(cè)試了师妙,但是筆者住的地方?jīng)]有 Wi-Fi,因此若想要看 Wi-Fi 網(wǎng)絡(luò)判斷怔檩,只能通過模擬器了蓄诽。真機(jī)錄屏看上去很模糊仑氛,但是也只能將就啦。
判斷移動(dòng)網(wǎng)絡(luò)狀態(tài)
判斷 Wi-Fi 網(wǎng)絡(luò)狀態(tài)
三、網(wǎng)絡(luò)狀態(tài)詳解
3.1米酬、添加權(quán)限
<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趋箩、定義常量來標(biāo)識(shí)幾種網(wǎng)絡(luò)類型
//Wi-Fi
public static final int NETTYPE_WIFI = 0;
//無網(wǎng)絡(luò)
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叫确、判斷網(wǎng)絡(luò)是否連接
我們?cè)谧鲈L問的時(shí)候都得進(jìn)行判斷是否連網(wǎng)。判斷連網(wǎng)也比較簡(jiǎn)單飞盆,就用到了兩個(gè)類次乓。ConnectivityManager 和 NetworkInfo票腰。
/**
* 檢測(cè)網(wǎng)絡(luò)是否連接
* @return
*/
public static boolean checkNetWorkState(Context context){
boolean flag = false;
//得到網(wǎng)絡(luò)連接信息
manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//判斷網(wǎng)絡(luò)是否連接
if (manager.getActiveNetworkInfo() != null){
flag = manager.getActiveNetworkInfo().isAvailable();
}
if (flag){
//判斷網(wǎng)絡(luò)類型
isNetworkAvailable(context);
}else {
//若網(wǎng)絡(luò)未連接杏慰,則彈出提示進(jìn)行設(shè)置
setNetWork(context);
}
return flag;
}
3.4炼鞠、判斷網(wǎng)絡(luò)類型
/**
* 網(wǎng)絡(luò)已經(jīng)連接谒主,判斷是 Wi-Fi 還是移動(dòng)網(wǎng)絡(luò)(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","移動(dòng)網(wǎng)絡(luò)已打開!");
}
if (wifi == NetworkInfo.State.CONNECTED || wifi == NetworkInfo.State.CONNECTING){
Log.e("WIFI","Wi-Fi已打開备典!");
}
//獲取網(wǎng)絡(luò)類型
switch (getNetWorkType(context)){
case Constants.NETTYPE_2G:
ToastUtils.showShort(context,"當(dāng)前網(wǎng)絡(luò)是2G網(wǎng)絡(luò)");
break;
case Constants.NETTYPE_3G:
ToastUtils.showShort(context,"當(dāng)前網(wǎng)絡(luò)是3G網(wǎng)絡(luò)");
break;
case Constants.NETTYPE_4G:
ToastUtils.showShort(context,"當(dāng)前網(wǎng)絡(luò)是4G網(wǎng)絡(luò)");
break;
case Constants.NETTYPE_WIFI:
ToastUtils.showShort(context,"當(dāng)前網(wǎng)絡(luò)是wifi");
break;
}
}
3.5提佣、獲取網(wǎng)絡(luò)類型
/**
* 獲取網(wǎng)絡(luò)類型
* @param context Context
* @return true 表示網(wǎng)絡(luò)可用
*/
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網(wǎng)絡(luò)
} else if (type.equalsIgnoreCase("MOBILE")) {
NetworkInfo mobileInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (mobileInfo != null) {
switch (mobileInfo.getType()) {
case ConnectivityManager.TYPE_MOBILE:// 手機(jī)網(wǎng)絡(luò)
switch (mobileInfo.getSubtype()) {
//--------------------Added in API level 1---------------------
//(3G)聯(lián)通 ~ 400-7000 kbps
case TelephonyManager.NETWORK_TYPE_UMTS:
//(2.5G) 移動(dòng)和聯(lián)通 ~ 100 kbps
case TelephonyManager.NETWORK_TYPE_GPRS:
//(2.75G) 2.5G 到 3G 的過渡 移動(dòng)和聯(lián)通 ~ 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 ) 聯(lián)通 ~ 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的升級(jí)產(chǎn)物) ~ 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;
//無網(wǎng)絡(luò)
default:
return Constants.NETTYPE_NONE;
}
}
}
}
}
return Constants.NETTYPE_NONE;
}
3.6术荤、設(shè)置網(wǎng)絡(luò)提示
/**
* 網(wǎng)絡(luò)為連接時(shí),設(shè)置網(wǎng)絡(luò)
*/
private static void setNetWork(final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("網(wǎng)絡(luò)連接提示").setMessage("網(wǎng)絡(luò)不可用端圈,如果繼續(xù)舱权,請(qǐng)?jiān)O(shè)置網(wǎng)絡(luò)");
builder.setPositiveButton("設(shè)置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = null;
/**
* 判斷手機(jī)系統(tǒng)的版本仑嗅!如果 API 大于 10 就是 3.0+
* 因?yàn)?3.0 以上的版本的設(shè)置和 3.0 以下的設(shè)置不一樣泣特,調(diào)用的方法不同
*/
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");
}
//啟動(dòng)打開 Wi-Fi 設(shè)置頁面
context.startActivity(intent);
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
return;
}
});
builder.create();
builder.show();
}
在 Presenter 層調(diào)用該判斷網(wǎng)絡(luò)請(qǐng)求進(jìn)行登錄操作屎媳。具體代碼在 MVP 和 MVC 中詳細(xì)講過,這里不重復(fù)粘貼代碼了颜价。
@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("當(dāng)前無網(wǎng)絡(luò)連接诉濒!");
}
} else {
mLoginView.passwordError("密碼過短未荒!");
}
} else {
mLoginView.usernameError("用戶名不符合規(guī)范!");
}
mLoginView.dismissLoading();
return false;
}
四寨腔、ConnectivityManager詳解
看到上例中主要使用到的兩個(gè)類是 ConnectivityManager 和 NetworkInfo率寡,下面先看看 ConnectivityManager 的主要參數(shù)和方法冶共。
ConnectivityManager 是網(wǎng)絡(luò)連接相關(guān)的管理器,它主要用于查詢網(wǎng)絡(luò)狀態(tài)并在網(wǎng)絡(luò)發(fā)生改變時(shí)發(fā)出狀態(tài)變化通知家卖。這個(gè)類主要負(fù)責(zé)的下列四個(gè)方面:
1上荡、監(jiān)控網(wǎng)絡(luò)狀態(tài)(包括WiFi, GPRS, UMTS等)馒闷。
2、當(dāng)網(wǎng)絡(luò)連接改變時(shí)發(fā)送廣播 Intent沛善。
3塞祈、 當(dāng)一種網(wǎng)絡(luò)斷開時(shí)议薪,試圖連接到另一種網(wǎng)絡(luò)進(jìn)行故障處理。
4产捞、 提供一系列接口讓應(yīng)用程序查詢可獲得的網(wǎng)絡(luò)的粗粒度和細(xì)粒度狀態(tài)哼御。
ConnectivityManager 比較重要的幾個(gè)類常量
類型 | 常量值 | 說明 |
---|---|---|
int | TYPE_BLUETOOTH | 藍(lán)牙數(shù)據(jù)連接 |
int | TYPE_ETHERNET | 以太網(wǎng)數(shù)據(jù)連接 |
int | TYPE_MOBILE | 移動(dòng)數(shù)據(jù)連接 |
int | TYPE_WIFI | Wi-Fi連接 |
int | DEFAULT_NETWORK_PREFERENCE | 默認(rèn)網(wǎng)絡(luò)連接偏好,建議在config.xml中進(jìn)行配置.并通過調(diào)用 getNetworkPreference() 獲取應(yīng)用的當(dāng)前設(shè)置值赶促。 |
String | EXTRA_EXTRA_INFO | 查詢關(guān)鍵字挟炬,提供關(guān)于網(wǎng)絡(luò)狀態(tài)的信息 |
String | EXTRA_NETWORK_INFO | 建議使用getActiveNetworkInfo() or getAllNetworkInfo()獲取網(wǎng)絡(luò)連接信息 |
String | EXTRA_NETWORK_TYPE | 觸發(fā) CONNECTIVITY_ACTION廣播的網(wǎng)絡(luò)類型 |
ConnectivityManager 比較重要的幾個(gè)方法
返回類型 | 方法名 |
---|---|
NetworkInfo | getActiveNetworkInfo() 獲取當(dāng)前連接可用的網(wǎng)絡(luò) |
NetworkInfo[] | getAllNetworkInfo() 獲取設(shè)備支持的所有網(wǎng)絡(luò)類型的鏈接狀態(tài)信息谤祖。 |
NetworkInfo | getNetworkInfo(int networkType) 獲取特定網(wǎng)絡(luò)類型的鏈接狀態(tài)信息 |
int | getNetworkPreference() 獲取當(dāng)前偏好的網(wǎng)絡(luò)類型粥喜。 |
boolean | isActiveNetworkMetered() 返回當(dāng)前被計(jì)量的活動(dòng)的數(shù)據(jù)網(wǎng)絡(luò) |
static boolean | isNetworkTypeValid(int networkType) 判斷給定的數(shù)值是否表示一種網(wǎng)絡(luò) |
boolean | requestRouteToHost(int networkType, int hostAddress) 確保存在通過指定網(wǎng)絡(luò)接口將流量傳輸?shù)街付ㄖ鳈C(jī)的網(wǎng)絡(luò)路由容客。 |
void | setNetworkPreference(int preference) 指定首選網(wǎng)絡(luò)類型 |
int | startUsingNetworkFeature(int networkType, String feature) 告訴呼叫者想要開始使用命名功能的底層網(wǎng)絡(luò)系統(tǒng)。 |
int | stopUsingNetworkFeature(int networkType, String feature) 告訴使用命名功能調(diào)用者完成的底層網(wǎng)絡(luò)系統(tǒng)但两。 |
五供置、NetworkInfo 詳解
NetworkInfo 是一個(gè)描述網(wǎng)絡(luò)狀態(tài)的接口芥丧,可通過 ConnectivityManager 調(diào)用 getActiveNetworkInfo() 獲得當(dāng)前連接的網(wǎng)絡(luò)類型。
NetworkInfo 有兩個(gè)枚舉類型的成員變量 NetworkInfo.DetailedState 和 NetworkInfo.State擅耽,用于查看當(dāng)前網(wǎng)絡(luò)的狀態(tài)乖仇。其中 NetworkInfo.State
的值包括:
類型 | 狀態(tài)值 | 說明 |
---|---|---|
NetworkInfo.State | CONNECTED | 已連接 |
NetworkInfo.State | CONNECTING | 正在連接 |
NetworkInfo.State | DISCONNECTED | 已斷開鏈接 |
NetworkInfo.State | DISCONNECTING | 正在斷開鏈接 |
NetworkInfo.State | SUSPENDED | 暫停/掛起 |
NetworkInfo.State | UNKNOWN | 未知 |
NetworkInfo 還包括一系列可用的方法用于判斷當(dāng)前網(wǎng)絡(luò)是否可用
乃沙,如下:
返回類型 | 方法名 |
---|---|
NetworkInfo.DetailedState | getDetailedState() 獲取當(dāng)前細(xì)粒度的網(wǎng)絡(luò)狀態(tài)诗舰。 |
String | getExtraInfo() 報(bào)告關(guān)于網(wǎng)絡(luò)狀態(tài)的額外信息( 如果有的話由下層網(wǎng)絡(luò)層提供)眶根,如果有一個(gè)可用 |
String | getReason() 如果數(shù)據(jù)網(wǎng)絡(luò)連接可用边琉,但是連接失敗艺骂,則通過此方法可獲得嘗試鏈接失敗的原因 |
boolean | isAvailable() 判斷當(dāng)前網(wǎng)絡(luò)是否可用 |
boolean | isConnected() 判斷當(dāng)前網(wǎng)絡(luò)是否存在隆夯,并可用于數(shù)據(jù)傳輸 |
boolean | isConnectedOrConnecting() 判斷網(wǎng)絡(luò)已連接或正在連接 |
boolean | isFailover() 指示當(dāng)前試圖連接到的網(wǎng)絡(luò)是否由 ConnectivityManager 嘗試斷開與其它網(wǎng)絡(luò)的連接 |
String | toString() 返回一個(gè)包含該網(wǎng)絡(luò)的簡(jiǎn)單的易懂的字符串描述蹄衷。 |
一般來說很少用到所有的內(nèi)容厘肮,上面也只是寫出一些可能用到的方法和變量类茂。
具體使用在文章前面已經(jīng)寫了,主要是判斷網(wǎng)絡(luò)是否連接厚骗,判斷網(wǎng)絡(luò)類型兢哭,獲取網(wǎng)絡(luò)類型等迟螺。
每個(gè)程序猿必備的110本經(jīng)典編程書,免費(fèi)領(lǐng)取地址: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