【從 0 開始開發(fā)一款直播 APP】9 網(wǎng)絡(luò)連接狀態(tài)源碼詳解

本文為菜鳥窩作者蔣志碧的連載。“從 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)也比較簡單,就用到了兩個(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ò)的簡單的易懂的字符串描述。

一般來說很少用到所有的內(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容