在Android开发中,许多功能需要网络连接,所以在开发过程中需要进行手机网络的检测。而每做一个App,就要进行检测,那么我直接将检测网络状态的功能封装成一个类。
检测网络状态所需要的Api:
实现网络状态检测功能,首先获得ConnectivityManager对象,再获取ConnectivityManager对象对应的NetworkInfo对象,并根据需要从NetworkInfo对象取出关于网络连接的信息,进行后续处理。
注意:Api23及以上时,getNetworkInfo(int networkType)方法已被弃用,取而代之的是:
getAllNetworks();
getNetworkInfo(android.net.Network);
getNetworkInfo(Network network);
所以我们检测网络状态时需要分版本进行检测。
我们使用Android的四大组件之一的BroadcastReceiver(广播接收者)进行接收网络变化的后续处理。
上代码。
实现监听网络状态变更的广播接收器——NetBroadcastReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.maigu.yang.networkinit.bean.NetworkChangeEvent;
import com.maigu.yang.networkinit.utils.NetUtils;
import org.greenrobot.eventbus.EventBus;
public class NetBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//**判断当前的网络连接状态是否可用*/
boolean isConnected = NetUtils.isConnected(context);
EventBus.getDefault().post(new NetworkChangeEvent(isConnected));
}
}
我们使用广播接收器接收网络变化的 Intent,这里直接使用静态注册的方法,因为我们不需要在每个页面单独注册这个 Receiver,那太重量级了。
在AndroidManifest.xml进行广播的静态注册,以及网络权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.maigu.yang.networkinit">
<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" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
<receiver
android:name=".broadcast.NetBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action
android:name="android.net.conn.CONNECTIVITY_CHANGE"
tools:ignore="BatteryLife" />
<category android:name="android.intent.category.DEFAULT" />
intent-filter>
receiver>
application>
manifest>
然后是监听网络状态的NetUtils.java工具类
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
import android.util.Log;
import android.widget.Toast;
/**
* 作者 yunyang
* 时间 2018/11/7 13:14
* 文件 NetWorkInit
* 描述 网络工具类
*/
public class NetUtils {
/**
* 判断网络是否连接
*
* @param context
* @return 网络是否连接Boolean(true|false)
*/
@SuppressWarnings({"ConstantConditions", "ForLoopReplaceableByForEach", "IfCanBeSwitch"})
public static boolean isConnected(Context context) {
StringBuilder sb = null;
//检测API是不是小于21,因为到了API21之后getNetworkInfo(int networkType)方法被弃用
/**
* 在系统版本小于21之前,使用以下的方式获取当前网络状态:
* 先利用Context对象获取ConnectivityManager对象,
* 然后利用ConnectivityManager对象获取NetworkInfo对象,
* 然后根据NetworkInfo对象的类型来返回不同的网络状态。
* 有三种,移动,Wi-Fi,无网络连接。
*/
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
//获得ConnectivityManager对象
ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//获取ConnectivityManager对象对应的NetworkInfo对象
//获取WIFI连接的信息
NetworkInfo wifiNetworkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
//获取移动数据连接的信息
NetworkInfo dataNetworkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (wifiNetworkInfo.isConnected() && dataNetworkInfo.isConnected()) {
Toast.makeText(context, "WIFI已连接,移动数据已连接", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "WIFI已连接,移动数据已连接");
return true;
} else if (wifiNetworkInfo.isConnected() && !dataNetworkInfo.isConnected()) {
Toast.makeText(context, "WIFI已连接,移动数据已断开", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "WIFI已连接,移动数据已断开");
return true;
} else if (!wifiNetworkInfo.isConnected() && dataNetworkInfo.isConnected()) {
Toast.makeText(context, "WIFI已断开,移动数据已连接", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "WIFI已断开,移动数据已连接");
return true;
} else {
Toast.makeText(context, "WIFI已断开,移动数据已断开", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "WIFI已断开,移动数据已断开");
return false;
}
} else {
/**
* 在系统21及之后,获取网络连接状态的方式:利用ConnectivityManager对象获取
* 所有的网络连接信息,然后遍历每个网络连接,获取相应的NetworkInfo,
* 然后根据NetworkInfo对象的类型来返回不同的网络状态。
*/
if (sb != null) {
sb.setLength(0);
}
//获得ConnectivityManager对象
ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//获取所有网络连接的信息
Network[] networks = connMgr.getAllNetworks();
//用于存放网络连接信息
sb = new StringBuilder();
//通过循环将网络信息逐个取出来
for (int i = 0; i < networks.length; i++) {
//获取ConnectivityManager对象对应的NetworkInfo对象
NetworkInfo networkInfo = connMgr.getNetworkInfo(networks[i]);
sb.append(networkInfo.getTypeName() + " connect is " + networkInfo.isConnected());
}
Log.e("yunyang", "sb.toString() : " + sb.toString());
if (sb.toString().equals("WIFI connect is true")) {
Toast.makeText(context, "WIFI已连接", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "WIFI已连接");
return true;
} else if (sb.toString().equals("MOBILE connect is true")) {
Toast.makeText(context, "移动数据已连接", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "移动数据已连接");
return true;
} else if (sb.toString().equals("MOBILE connect is trueWIFI connect is true")
|| sb.toString().equals("WIFI connect is trueMOBILE connect is true")) {
Toast.makeText(context, "WIFI已连接,移动数据已连接", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "WIFI已连接,移动数据已连接");
return true;
} else if (sb.toString().equals("MOBILE connect is falseWIFI connect is true")
|| sb.toString().equals("WIFI connect is trueMOBILE connect is false")) {
Toast.makeText(context, "WIFI已连接,移动数据已断开", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "WIFI已连接,移动数据已断开");
return true;
} else if (sb.toString().equals("MOBILE connect is trueWIFI connect is false")
|| sb.toString().equals("WIFI connect is falseMOBILE connect is true")) {
Toast.makeText(context, "WIFI已断开,移动数据已连接", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "WIFI已断开,移动数据已连接");
return true;
} else {
Toast.makeText(context, "WIFI已断开,移动数据已断开", Toast.LENGTH_SHORT).show();
Log.e("yunyang", "WIFI已断开,移动数据已断开");
return false;
}
}
}
}
我们希望当用户网络连接不可用时,及时提醒用户当前的网络状态。当连接恢复时,将提示用的视图隐藏,并且我们希望这个提示视图可以工作在所有需要网络的页面中。
所以我创建一个基类 BaseActivity ,所有页面继承该文件,在该文件中实现根据网络状态显示提示、隐藏提示。
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.maigu.yang.networkinit.R;
import com.maigu.yang.networkinit.bean.NetworkChangeEvent;
import com.maigu.yang.networkinit.utils.ActivityCollector;
import com.maigu.yang.networkinit.utils.NetUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
/**
* 作者 yunyang
* 时间 2018/11/7 13:18
* 文件 NetWorkInit
* 描述 BaseActivity
*/
public class BaseActivity extends AppCompatActivity {
protected Context mContext;
protected boolean mCheckNetWork = true; //默认检查网络状态
View mTipView;
WindowManager mWindowManager;
WindowManager.LayoutParams mLayoutParams;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
ActivityCollector.addActivity(this);// 将正在创建的活动添加到活动管理器里
initTipView();//初始化提示View
EventBus.getDefault().register(this);
}
@Override
protected void onResume() {
super.onResume();
//在无网络情况下打开APP时,系统不会发送网络状况变更的Intent,需要自己手动检查
hasNetWork(NetUtils.isConnected(mContext));
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);// 将活动管理器里活动移除
EventBus.getDefault().unregister(this);
}
@Override
public void finish() {
super.finish();
//当提示View被动态添加后直接关闭页面会导致该View内存溢出,所以需要在finish时移除
if (mTipView != null && mTipView.getParent() != null) {
mWindowManager.removeView(mTipView);
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onNetworkChangeEvent(NetworkChangeEvent event) {
hasNetWork(event.isConnected);
}
private void hasNetWork(boolean has) {
if (isCheckNetWork()) {
if (has) {
if (mTipView != null && mTipView.getParent() != null) {
mWindowManager.removeView(mTipView);
Log.e("yunyang", "有网络");
}
} else {
if (mTipView.getParent() == null) {
mWindowManager.addView(mTipView, mLayoutParams);
Log.e("yunyang", "无网络");
}
}
}
}
/**
* 默认所有继承 BaseActivity 的页面当网络状况变化活无网络时都会显示提示,
* 如果某个页面不需要网络状态提示,可以在该页面 onCreate
* 方法中调用 setCheckNetWork(false) 即可。
*
* @param checkNetWork
*/
public void setCheckNetWork(boolean checkNetWork) {
mCheckNetWork = checkNetWork;
}
public boolean isCheckNetWork() {
return mCheckNetWork;
}
private void initTipView() {
LayoutInflater inflater = getLayoutInflater();
mTipView = inflater.inflate(R.layout.layout_network_tip, null); //提示View布局
mWindowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
mLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT);
//使用非CENTER时,可以通过设置XY的值来改变View的位置
mLayoutParams.gravity = Gravity.TOP;
mLayoutParams.x = 0;
mLayoutParams.y = 180;
// mLayoutParams.gravity = Gravity.CENTER;
}
}
BaseActivity中使用网络状态提示视图的布局layout_network_tip.java
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="网络连接不可用,请检查网络设置"
android:textColor="@color/colorAccent"
android:textSize="18sp"
tools:ignore="HardcodedText" />
RelativeLayout>
而且我们的广播是静态注册,并在广播接收者里面使用EventBus三方库。传递信息。那么EventBus是什么呢?
EventBus是Android和Java的发布/订阅事件总线。
简化了组件之间的通信
将事件发送者和接收者分离
在活动,片段和后台线程中表现良好
避免复杂且容易出错的依赖关系和生命周期问题
使您的代码更简单
很快
很小(约50k罐)
已经通过100,000,000+安装的应用程序在实践中得到证实
具有交付线程,用户优先级等高级功能。
关于EventBus的使用,如上EventBus的超链接跳转到Github进行学习。
// EventBus
implementation 'org.greenrobot:eventbus:3.1.1'
这里我简单说一下EventBus的简单使用。
/**
* 作者 yunyang
* 时间 2018/11/7 13:16
* 文件 NetWorkInit
* 描述 EventBus——事件Event
*/
public class NetworkChangeEvent {
public boolean isConnected; //是否存在网络
public NetworkChangeEvent(boolean isConnected) {
this.isConnected = isConnected;
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onNetworkChangeEvent(NetworkChangeEvent event) {
hasNetWork(event.isConnected);
}
BaseActivity中还有一个ActivityCollector,活动管理器类。
ActivityCollector.java
import android.app.Activity;
import java.util.ArrayList;
import java.util.List;
/**
* 作者 yunyang
* 时间 2018/11/7 13:31
* 文件 NetWorkInit
* 描述 在活动管理器中,通过一个List来暂存活动。
*/
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<Activity>();
/**
* 用于向List中添加一个活动。
*
* @param activity
*/
public static void addActivity(Activity activity) {
activities.add(activity);
}
/**
* 用于从List中移除活动。
*
* @param activity
*/
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
/**
* 用于将List中存储的活动全部都销毁掉。
*/
public static void finishAll() {
for (Activity activity : activities) {
if (!activity.isFinishing())
activity.finish();
}
}
}
使用动态广播注册方式
修改MainActivity中的代码
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.os.Bundle;
import com.maigu.yang.networkinit.base.BaseActivity;
import com.maigu.yang.networkinit.broadcast.NetBroadcastReceiver;
public class MainActivity extends BaseActivity {
NetBroadcastReceiver mNetBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//在onResume()方法注册
@Override
protected void onResume() {
if (mNetBroadcastReceiver == null) {
mNetBroadcastReceiver = new NetBroadcastReceiver();
}
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mNetBroadcastReceiver, filter);
System.out.println("注册");
super.onResume();
}
//onPause()方法注销
@Override
protected void onPause() {
unregisterReceiver(mNetBroadcastReceiver);
System.out.println("注销");
super.onPause();
}
}
还有AndroidManifest.xml文件中的代码
<receiver
android:name=".broadcast.NetBroadcastReceiver"
android:enabled="true"
android:exported="true">
receiver>
那么是使用静态广播还是动态注册广播的方式好呢?
我们的应用要监听网络状态的变化,主要是为了在网络错误的情况下方便进行处理,退出我们当前的应用之后当然不需要监听了,所以这里选择动态注册广播更好一点。
动态注册:随着所在的Activity或者应用销毁 以后,不会受到该广播
静态注册:退出应用后,仍然能够收到相应的广播
共同点:都需要在AndroidManifest.xml清单文件里面注册。
最后测试结果。
因为我们的监听网络状态的Api发生了改变,所以我们先使用4.4的模拟器进行测试,然后在使用6.0的模拟器进行测试。
4.4模拟器演示效果