本系列是在某平台公开课学习后的实践总结。
需求场景
- 根据用户当前网络状态来决定下一步逻辑,如流量条件下打开MV需要弹出
即将消耗流量
的提示,WiFi则可以直接播放。 - 需要即时监听网络变化,如播放MV时,WiFi不稳定断开切换到流量,需要提示用户,避免消耗大量流量。
- 每次执行逻辑前都用if语句判断网络,不够优雅。
实现思路
在公开课方案的基础上,兼容Android 5.0前后机型。
- 暴露
NetworkManager
调度中心给用户进行app初始化、页面注册。内部会处理版本判断,5.0及以上使用ConnectivityManager.NetworkCallback
方案,以下使用BroadcastReceiver
方案。(为何两个方案并行?使用NetworkCallback
会比广播稳定,但他不支持5.0以下机型) - 页面注册后,写一个含有指定注解和符合约定的方法来监听网络变化,用法跟EventBus类似。
@Network(netType = NetType.AUTO)
public void netword(NetType netType) {
switch (netType) {
case AUTO:
Log.d(Constants.LOG_TAG, "页面回调 -- AUTO ");
break;
case WIFI:
Log.d(Constants.LOG_TAG, "页面回调 -- WIFI ");
break;
case CMNET:
Log.d(Constants.LOG_TAG, "页面回调 -- CMNET ");
break;
case CMWAP:
Log.d(Constants.LOG_TAG, "页面回调 -- CMWAP ");
break;
case NONE:
Log.d(Constants.LOG_TAG, "页面回调 -- NONE ");
break;
}
}
- 用数据中心
Map<Object, List<MethodManager>> mNetworkList
存储每个页面的网络监听方法,其中Object
表示页面如activity或fragment,List
表示该页面的网络监听方法集合,MethodManager
是进一步包装的方法bean。核心思路跟EventBus类似。 - 网络变换后,在两种方案的监听处对上述map进行遍历推送。如
BroadcastReceiver
的onReceive
、ConnectivityManager.NetworkCallback
的onCapabilitiesChanged
。所谓推送,就是从map中取出方法进行invoke。至此,页面中的监听方法得以执行。
调用层使用姿势
在APP里初始化。
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
//网络监听初始化
NetworkManager.getDefault().init(this);
}
}
在页面中注册监听。
public class MainActivity extends AppCompatActivity {
private TextView tv;
private StringBuilder sb = new StringBuilder();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.tv);
//在需要进行网络监听的页面进行注册
NetworkManager.getDefault().registerObserver(this);
}
//监听所有网络情况,根据业务需求来定
@Network(netType = NetType.AUTO)
public void netword(NetType netType) {
switch (netType) {
case AUTO:
Log.d(Constants.LOG_TAG, "页面回调 -- AUTO ");
sb.append("页面回调 -- AUTO ");
break;
case WIFI:
Log.d(Constants.LOG_TAG, "页面回调 -- WIFI ");
sb.append("页面回调 -- WIFI ");
break;
case CMNET:
Log.d(Constants.LOG_TAG, "页面回调 -- CMNET ");
sb.append("页面回调 -- CMNET ");
break;
case CMWAP:
Log.d(Constants.LOG_TAG, "页面回调 -- CMWAP ");
sb.append("页面回调 -- CMWAP ");
break;
case NONE:
Log.d(Constants.LOG_TAG, "页面回调 -- NONE ");
sb.append("页面回调 -- NONE ");
break;
}
sb.append("\n");
tv.setText(sb.toString());
}
@Override
protected void onDestroy() {
super.onDestroy();
//退出时注销网络监听
NetworkManager.getDefault().unRegisterObserver(this);
//如果是首页退出,还可以再次注销所有
NetworkManager.getDefault().unRegisterAllObserver();
}
}
底层实现
共9个类,重点在最后3个。
- Network 注解
- MethodManager 将方法和其他参数封装成对象
- NetType 网络类型枚举
- BaseUtils 基层代码抽取
- Constants 常量类
- NetworkUtils 网络工具类
-
NetStateReceiver
广播接收器 -
NetworkCallbackImpl
继承系统提供的网络变化回调NetworkCallback -
NetworkManager
调度中心,提供给用户使用
按依赖顺序列出各类的详细实现。
- NetType
/**
* 网络类型枚举
*/
public enum NetType {
//有网络,包括WiFi、GPRS
AUTO,
//WiFi
WIFI,
//主要是为PC、笔记本电脑、PDA等利用GPRS上网服务
CMNET,
//主要是手机
CMWAP,
//无网络
NONE
}
- Network
/**
* 监听的网络类型注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Network {
NetType netType() default NetType.AUTO;
}
- MethodManager
/**
* 将方法和其他参数封装成对象
*/
public class MethodManager {
//参数类型
private Class<?> mType;
//网络类型
private NetType mNetType;
//方法
private Method mMethod;
public MethodManager(Class<?> type, NetType netType, Method method) {
mType = type;
mNetType = netType;
mMethod = method;
}
public Class<?> getType() {
return mType;
}
public void setType(Class<?> type) {
mType = type;
}
public NetType getNetType() {
return mNetType;
}
public void setNetType(NetType netType) {
mNetType = netType;
}
public Method getMethod() {
return mMethod;
}
public void setMethod(Method method) {
mMethod = method;
}
}
- Constants
/**
* 常量类
*/
public class Constants {
public static final String LOG_TAG = "network_monitor_tag";
public static final String ANDROID_NET_CHANGE_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
//跳转设置回调请求码
public static final int SETTING_REQUEST_CODE = 123;
}
- NetworkUtils
/**
* 网络工具类
*/
public class NetworkUtils {
/**
* 网络是否可用
*
* @return
*/
@SuppressLint("MissingPermission")
public static boolean isNetworkAvailable() {
ConnectivityManager manager = (ConnectivityManager) NetworkManager.getDefault().getApplication()
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null == manager) {
return false;
}
NetworkInfo[] infos = manager.getAllNetworkInfo();
if (null != infos) {
for (NetworkInfo info : infos) {
if (info.getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
}
return false;
}
/**
* 获取网络类型
*
* @return
*/
@SuppressLint("MissingPermission")
public static NetType getNetType() {
ConnectivityManager manager = (ConnectivityManager) NetworkManager.getDefault().getApplication()
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null == manager) {
return NetType.NONE;
}
NetworkInfo info = manager.getActiveNetworkInfo();
if (null == info) {
return NetType.NONE;
}
int nType = info.getType();
if (nType == ConnectivityManager.TYPE_MOBILE) {
if (info.getExtraInfo().toLowerCase().equals("cmnet")) {
return NetType.CMNET;
} else {
return NetType.CMWAP;
}
} else if (nType == ConnectivityManager.TYPE_WIFI) {
return NetType.WIFI;
}
return NetType.NONE;
}
/**
* 打开网络设置界面
*/
public static void openSettings(Context context, int requestCode) {
Intent intent = new Intent("/");
ComponentName cn = new ComponentName("com.android.settings",
"com.android.settings.WirelessSettings");
intent.setComponent(cn);
intent.setAction("android.intent.action.VIEW");
((Activity) context).startActivityForResult(intent, requestCode);
}
}
- BaseUtils
/**
* 基层代码,无需细看
*/
public class BaseUtils {
/**
* 推送(遍历并执行方法集)
*/
public static void post(NetType netType, Map<Object, List<MethodManager>> networkList) {
Set<Object> keySet = networkList.keySet();
for (Object object : keySet) {
List<MethodManager> methodList = networkList.get(object);
if (null != methodList) {
for (MethodManager methodManager : methodList) {
if (methodManager.getType().isAssignableFrom(netType.getClass())) {
switch (methodManager.getNetType()) {
case AUTO:
invoke(methodManager, object, netType);
break;
case WIFI:
if (netType == NetType.WIFI || netType == NetType.NONE) {
invoke(methodManager, object, netType);
}
break;
case CMNET:
if (netType == NetType.CMNET || netType == NetType.NONE) {
invoke(methodManager, object, netType);
}
break;
case CMWAP:
if (netType == NetType.CMWAP || netType == NetType.NONE) {
invoke(methodManager, object, netType);
}
break;
case NONE:
if (netType == NetType.NONE) {
invoke(methodManager, object, netType);
}
break;
default:
break;
}
}
}
}
}
}
/**
* 方法执行,抛异常忽略即可
*/
private static void invoke(MethodManager methodManager, Object object, NetType netType) {
Method method = methodManager.getMethod();
try {
method.invoke(object, netType);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 找出页面中符合要求的方法并存储
*/
public static List<MethodManager> findAnnotationMethod(Object object) {
List<MethodManager> methodList = new ArrayList<>();
Class<?> clazz = object.getClass();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
Network network = method.getAnnotation(Network.class);
if (null == network) {
continue;
}
Type returnType = method.getGenericReturnType();
if (!"void".equals(returnType.toString())) {
throw new RuntimeException("method return type must be void");
}
Class<?>[] parameterTypes = method.getParameterTypes();
if (1 != parameterTypes.length) {
throw new RuntimeException("method parameter count must be one");
}
MethodManager methodManager = new MethodManager(parameterTypes[0], network.netType(), method);
methodList.add(methodManager);
}
return methodList;
}
}
- NetStateReceiver
/**
* 广播接收器
*/
public class NetStateReceiver extends BroadcastReceiver {
private NetType mNetType;
private Map<Object, List<MethodManager>> mNetworkList;
public NetStateReceiver() {
//初始化为无网络
mNetType = NetType.NONE;
mNetworkList = new HashMap<>();
}
@Override
public void onReceive(Context context, Intent intent) {
if (null != intent && null != intent.getAction()) {
if (intent.getAction().equalsIgnoreCase(Constants.ANDROID_NET_CHANGE_ACTION)) {
Log.d(Constants.LOG_TAG, "network change");
mNetType = NetworkUtils.getNetType();
if (NetworkUtils.isNetworkAvailable()) {
Log.d(Constants.LOG_TAG, "network connect");
} else {
Log.d(Constants.LOG_TAG, "network disconnect");
}
BaseUtils.post(mNetType, mNetworkList);
}
}
}
/**
* 页面注册
*
* @param object 可以是activity、fragment
*/
public void registerObserver(Object object) {
List<MethodManager> methodList = mNetworkList.get(object);
if (methodList == null) {
methodList = BaseUtils.findAnnotationMethod(object);
mNetworkList.put(object, methodList);
}
}
/**
* 页面注销网络监听
*
* @param object
*/
public void unRegisterObserver(Object object) {
if (!mNetworkList.isEmpty()) {
mNetworkList.remove(object);
}
}
/**
* 注销所有页面的网络监听,一般用于首页
*/
public void unRegisterAllObserver() {
if (!mNetworkList.isEmpty()) {
mNetworkList.clear();
}
NetworkManager.getDefault().getApplication().unregisterReceiver(this);
}
}
- NetworkCallbackImpl
/**
* 系统的网络监听回调
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class NetworkCallbackImpl extends ConnectivityManager.NetworkCallback {
private NetType mNetType;
private Map<Object, List<MethodManager>> mNetworkList;
public NetworkCallbackImpl() {
//初始化为无网络
mNetType = NetType.NONE;
mNetworkList = new HashMap<>();
}
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
Log.d(Constants.LOG_TAG, "NetworkCallbackImpl - onAvailable");
}
@Override
public void onLost(Network network) {
super.onLost(network);
Log.d(Constants.LOG_TAG, "NetworkCallbackImpl - onLost");
//onLost时不会触发onCapabilitiesChanged,所以这里单独推送
BaseUtils.post(NetType.NONE, mNetworkList);
}
/**
* onCapabilitiesChanged方法触发次数不定,但是有if语句保护,不会导致重复推送
*
* @param network
* @param networkCapabilities
*/
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities);
Log.d(Constants.LOG_TAG, "NetworkCallbackImpl - onCapabilitiesChanged");
if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
Log.d(Constants.LOG_TAG, "NetworkCallbackImpl - wifi");
//推送
BaseUtils.post(NetType.WIFI, mNetworkList);
} else {
Log.d(Constants.LOG_TAG, "NetworkCallbackImpl - not wifi");
//推送,这里先用cmwap表示非WiFi
BaseUtils.post(NetType.CMWAP, mNetworkList);
}
}
}
/**
* 页面注册
*
* @param object 可以是activity、fragment
*/
public void registerObserver(Object object) {
List<MethodManager> methodList = mNetworkList.get(object);
if (methodList == null) {
methodList = BaseUtils.findAnnotationMethod(object);
mNetworkList.put(object, methodList);
}
}
/**
* 页面注销网络监听
*
* @param object
*/
public void unRegisterObserver(Object object) {
if (!mNetworkList.isEmpty()) {
mNetworkList.remove(object);
}
}
/**
* 注销所有页面的网络监听,一般用于首页
*/
public void unRegisterAllObserver() {
if (!mNetworkList.isEmpty()) {
mNetworkList.clear();
}
}
}
- NetworkManager
/**
* 中间层,用于调度 NetworkCallbackImpl or NetStateReceiver
* 5.0以下:NetStateReceiver、5.0及以上:NetworkCallbackImpl
* 原因:NetworkCallbackImpl并不支持Android5.0之前的设备
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class NetworkManager {
private static NetworkManager instance = new NetworkManager();
private Application mApplication;
private NetStateReceiver mReceiver;
private NetworkCallbackImpl mNetworkCallback;
private NetworkManager() {
}
public static NetworkManager getDefault() {
return instance;
}
public Application getApplication() {
if (null == mApplication) {
throw new RuntimeException("application can't be null");
}
return mApplication;
}
@SuppressLint("MissingPermission")
public void init(Application app) {
mApplication = app;
if (onLollipop()) {
mNetworkCallback = new NetworkCallbackImpl();
NetworkRequest.Builder builder = new NetworkRequest.Builder();
NetworkRequest request = builder.build();
ConnectivityManager cm = (ConnectivityManager) NetworkManager.getDefault().getApplication()
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null != cm) {
cm.registerNetworkCallback(request, mNetworkCallback);
}
} else {
mReceiver = new NetStateReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Constants.ANDROID_NET_CHANGE_ACTION);
mApplication.registerReceiver(mReceiver, filter);
}
}
public void registerObserver(Object object) {
if (onLollipop()) {
mNetworkCallback.registerObserver(object);
} else {
mReceiver.registerObserver(object);
}
}
public void unRegisterObserver(Object object) {
if (onLollipop()) {
mNetworkCallback.unRegisterObserver(object);
} else {
mReceiver.unRegisterObserver(object);
}
}
public void unRegisterAllObserver() {
if (onLollipop()) {
mNetworkCallback.unRegisterAllObserver();
} else {
mReceiver.unRegisterAllObserver();
}
}
/**
* 系统是否在5.0及以上
*
* @return
*/
public static boolean onLollipop() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return true;
}
return false;
}
}
运行效果
4.x机型:
8.x机型:
心得
采用注解标记方法,然后存储到数据中心,后续进行遍历推送。这个思路可用于页面间通信、网络监听等。相比于接口回调更加解耦,在同时开启的页面数不会太多(数据中心不会太庞大)的场景下是个不错的选择。另外推送方式也支持多种场景,如EventBus按需手动post,网络监听由系统post。
后记
仅做了初步实现,还需持续优化,排查bug。
完整项目见github
网友评论