先来写个最简单的框架吧。最普通的素质三连。1.加权限 2. 注册广播 3.添加回调
就这么简单,让我们开始吧。
看下最终实现的使用:
public class MainActivity extends AppCompatActivity implements NetChangeObserver {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NetworkManager.getDefault().init(getApplication());
NetworkManager.getDefault().setListener(this);
}
@Override
public void onConnected(NetType type) {
Log.i(Constants.TAG,"网络连上了---type="+type);
}
@Override
public void onDisConnected() {
Log.i(Constants.TAG,"网络断开了");
}
@Override
protected void onDestroy() {
super.onDestroy();
NetworkManager.getDefault().logout();
}
}
日志:
14:22:53.545 1583-1583/com.yirong.netlistener E/NetListener >>>: 网络状态变化了
14:22:53.545 1583-1583/com.yirong.netlistener I/NetListener >>>: 网络连上了
14:22:53.545 1583-1583/com.yirong.netlistener I/NetListener >>>: 网络连上了---type=WIFI
好了,新建一个AndroidLibray开始实现。
权限走一波:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
首先,咱们需要一个监听网络状态的工具类
package com.yirong.library.utils;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.yirong.library.NetworkManager;
import com.yirong.library.type.NetType;
/**
*获取网络状态的工具类
*/
public class NetWorkUtils {
/**
* @return 是否有网络
*/
public static boolean isNetWorkAvailable(){
ConnectivityManager manager = (ConnectivityManager) NetworkManager.getDefault().getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
if(manager == null){
return false;
}
NetworkInfo[] networkInfos = manager.getAllNetworkInfo();
if(networkInfos != null){
for(NetworkInfo info:networkInfos){
if(info.getState() == NetworkInfo.State.CONNECTED){
return true;
}
}
}
return false;
}
/**
* @return 网络类型
*/
public static NetType getNetworkType(){
ConnectivityManager manager = (ConnectivityManager) NetworkManager.getDefault().getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
if(manager == null){
return NetType.NONE;
}
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
if(networkInfo == null){
return NetType.NONE;
}
int type = networkInfo.getType();
if(type == ConnectivityManager.TYPE_MOBILE){
if(networkInfo.getExtraInfo().toLowerCase().equals("cmnet")){
return NetType.CMNET;
}else{
return NetType.CMWAP;
}
}else if(type == ConnectivityManager.TYPE_WIFI){
return NetType.WIFI;
}
return NetType.AUTO;
}
/**
* 打开网络设置界面
* @param context
* @param requestCode 请求跳转
*/
public static void openNetSetting(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);
}
}
在lib中创建一个广播,然后再配置文件里注册一下。
<receiver android:name="com.yirong.library.NetStateReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"></action>
</intent-filter>
</receiver>
完善下咱们的receiver:
package com.yirong.library;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.yirong.library.listener.NetChangeObserver;
import com.yirong.library.type.NetType;
import com.yirong.library.utils.Constants;
import com.yirong.library.utils.NetWorkUtils;
public class NetStateReceiver extends BroadcastReceiver {
NetChangeObserver mNetChangeObserver;
private NetType type;
public NetStateReceiver(){
this.type = NetType.NONE;
}
public void setListener(NetChangeObserver observer){
mNetChangeObserver = observer;
}
@Override
public void onReceive(Context context, Intent intent) {
if(intent == null || intent.getAction() == null){
Log.e(Constants.TAG,"广播异常了");
return;
}
if(intent.getAction().equalsIgnoreCase(Constants.ANDROID_NET_CHANGE_ACTION)){
Log.e(Constants.TAG,"网络状态变化了");
type = NetWorkUtils.getNetworkType();
if(NetWorkUtils.isNetWorkAvailable()){
Log.i(Constants.TAG,"网络连上了");
mNetChangeObserver.onConnected(type);
}else{
Log.i(Constants.TAG,"网络断开了");
mNetChangeObserver.onDisConnected();
}
}
}
}
监听接口:
package com.yirong.library.listener;
import com.yirong.library.type.NetType;
/**
* 网络变化监听
*/
public interface NetChangeObserver {
/**
* 网络连接成功
*/
void onConnected(NetType type);
/**
* 网络断开
*/
void onDisConnected();
}
manager:
package com.yirong.library;
import android.app.Application;
import android.content.IntentFilter;
import com.yirong.library.listener.NetChangeObserver;
import com.yirong.library.utils.Constants;
public class NetworkManager {
private static volatile NetworkManager instance;
private NetStateReceiver mReceiver;
private Application mApplication;
private NetChangeObserver mListener;
public NetworkManager(){
mReceiver = new NetStateReceiver();
}
public static NetworkManager getDefault(){
if(instance == null){
synchronized (NetworkManager.class){
if(instance == null){
instance = new NetworkManager();
}
}
}
return instance;
}
public Application getApplication(){
if(mApplication == null){
throw new RuntimeException("NetworkManager.getDefault().init()没有初始化");
}
return mApplication;
}
public void init(Application application){
this.mApplication = application;
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Constants.ANDROID_NET_CHANGE_ACTION);
mApplication.registerReceiver(mReceiver,intentFilter);
}
public void logout(){
getApplication().unregisterReceiver(mReceiver);
}
public void setListener(NetChangeObserver listener) {
mReceiver.setListener(listener);
}
}
再加上一个枚举类:
/*
*网络类型
* */
public enum NetType {
//有网络,包括Wifi/gprs
AUTO,
//wifi
WIFI,
//PC/笔记本/PDA
CMNET,
//手机端
CMWAP,
//没有网络
NONE
}
OK,一套正常操作,一个最普通网络监听类写完了。接着咱们尝试用注解,让这个小框架看的有档次些。
- 第一个优化点就是这个枚举类了。
我们都知道枚举类是很消耗内存的。他在编译时会生成一些额外的类和数组,造成运行上的负担。
感兴趣可以了解下:https://www.liaohuqiu.net/cn/posts/android-enum-memory-usage/
而在Android的开发文档里,google建议使用@IntDef/@StringDef
注解替代枚举,让我们修改一下枚举类吧。
package com.yirong.library.type;
import android.support.annotation.StringDef;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@StringDef({NetType.AUTO,NetType.WIFI,NetType.CMNET,NetType.CMWAP,NetType.NONE})
public @interface NetType {
//有网络,包括Wifi/gprs
public static final String AUTO = "AUTO";
//wifi
public static final String WIFI = "WIFI";
//PC/笔记本/PDA
public static final String CMNET = "CMNET";
//手机端
public static final String CMWAP = "CMWAP";
//没有网络
public static final String NONE = "NONE";
}
- 第二步,我们试图使用注解取代监听接口。这是我们最终想实现的一个效果.
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册广播
NetworkManager.getDefault().registerObserver(this);
}
//网络监听
@NetworkListener(type = NetType.AUTO)
public void netorkListen(@NetType String type){
switch (type){
case NetType.AUTO:
break;
case NetType.CMNET:
break;
case NetType.CMWAP:
break;
case NetType.WIFI:
break;
case NetType.NONE:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销目标广播
NetworkManager.getDefault().unRegisterObserver(this);
//注销所有广播
NetworkManager.getDefault().unRegisterAllObserver();
}
}
完善一下自定义注解:
@Target(ElementType.METHOD)//注解目标是方法
@Retention(RetentionPolicy.RUNTIME)//jvm运行时执行
public @interface NetworkListener {//注解名
@NetType String type() default NetType.AUTO;//参数默认值
}
然后来捋一下思路:
- 通过activity的对象,我们可以遍历MainActivity中的所有方法。
- 通过注解名判断(NetworkListener ),方法返回类型判断(void),方法参数个数(1)筛选出所有的网络监听方法。
- 这时候,当广播监听到网络变化时,根据NetworkUtils获取网络状态,并根据这个参数,使用反射去触发Activity中的方法。
照样是固定三部曲,按照步骤来:
1.先完善我们的注册方法
在这里,我创建了一个方法封装类。比较简单:MethodManager
/**
* @param register
*/
public void registerObserver(Object register) {
//获取当前Activity or Fragment中所有的网络监听注解方法
mMethodList = mNetworkList.get(register);
if(mMethodList == null){//说明没注册过了
mMethodList = findAnnotationMethod(register)
mNetworkList.put(register, mMethodList);
}
}
这里的mMethodList
是方法封装类(MethodManager)的List,用来存储筛选后的MainActivity中方法,也就是网络监听方法。以下是方法的筛选:
/**
* @param register
* @return MethodList 网络监听注解方法数组
*/
private List<MethodManager> findAnnotationMethod(Object register) {
List<MethodManager> methodList = new ArrayList<>();
Class<?> clazz = register.getClass();
Method[] method = clazz.getMethods();
for (Method m:method){//遍历方法
//找出所有注解方法
NetworkListener annotation = m.getAnnotation(NetworkListener.class);
if (annotation == null){
continue;
}
//判断返回类型
Type genericReturnType = m.getGenericReturnType();
if(!"void".equals(genericReturnType.toString())){
throw new RuntimeException(m.getName()+"返回类型必须是void");
}
//参数校验
Class<?>[] parameterTypes = m.getParameterTypes();
Log.i("m,name",m.getParameterTypes().length+"");
if(parameterTypes.length!= 1){
throw new RuntimeException(m.getName()+"返回参数只有一个");
}
MethodManager methodManager = new MethodManager(parameterTypes[0],annotation.type(),m);
methodList.add(methodManager);
}
return methodList;
}
- 注销广播就比较简单:
/**
*注销
*@param register
*/
public void unRegisterObserver(Object register) {
if(!mNetworkList.isEmpty()){//说明有广播被注册过
mNetworkList.remove(register);
}
Log.i(Constants.TAG,register.getClass().getName()+"注销成功了");
}
public void unRegisterAllObserver() {
if(!mNetworkList.isEmpty()){//说明有广播被注册过
mNetworkList.clear();
}
NetworkManager.getDefault().logout();//注销
}
- 接下来是重头戏了!MainActvity中有几个网络监听方法我们已经Get了,接下来就是要在网络发生变化的时候呢,通过反射,没错说的就是你,那个
invoke
!去触发MainActivty的注解方法。
@Override
public void onReceive(Context context, Intent intent) {
if(intent == null || intent.getAction() == null){
Log.e(Constants.TAG,"广播异常了");
return;
}
if(intent.getAction().equalsIgnoreCase(Constants.ANDROID_NET_CHANGE_ACTION)){
Log.e(Constants.TAG,"网络状态变化了");
type = getNetworkType();
post(type); //分发
}
}
咱们定义一个post方法,去完成这项工作:
/**
* @param netType
*/
private void post(@NetType String netType) {
Set<Object> set = mNetworkList.keySet();
for(Object o: set){
List<MethodManager> methodManagerList = mNetworkList.get(o);
for(MethodManager manager:methodManagerList){
if(manager.getType().isAssignableFrom(netType.getClass())){//如果注解上的参数和网络状态参数类型相同
switch (manager.getNetType()){
case NetType.AUTO:
invoke(manager,o,netType);//反射运行方法
break;
case NetType.CMNET:
if(netType == NetType.CMNET||netType == NetType.NONE ){
invoke(manager,o,netType);
}
break;
case NetType.CMWAP:
if(netType == NetType.CMWAP||netType == NetType.NONE ){
invoke(manager,o,netType);
}
break;
case NetType.WIFI:
if(netType == NetType.WIFI ||netType == NetType.NONE ){
invoke(manager,o,netType);
}
break;
case NetType.NONE:
invoke(manager,o,netType);
break;
}
}
}
}
这里实现了通过网络监听注解上方的参数,我们可以单独监听某一种网络状态的变化。比如
//网络监听
@NetworkListener(type = NetType.WIFI)
public void netorkListen(@NetType String type){
....
}
上方参数表明,只有在WIFI断开和连接的时候,方法才会做出响应。想要所有提示设置为AUTO就行啦。
OK,做完逻辑以后执行一下:
/**
* @param manager 方法管理类
* @param o 方法所有者(activity/Fragment)
* @param netType 网络类型参数
*/
private void invoke(MethodManager manager, Object o, String netType) {
Method executeMethod = manager.getMethod();
try {
executeMethod.invoke(o,netType);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
大功告成!跑一下看看效果!
21:55:36.483 976-976/? E/NetListener >>>: 网络状态变化了
21:55:36.483 976-976/? I/NetListener >>>: WIFI
21:55:36.483 976-976/? I/NetListener >>>: WIFI*
21:55:59.893 976-976/com.yirong.netlistener E/NetListener >>>: 网络状态变化了
21:55:59.893 976-976/com.yirong.netlistener I/NetListener >>>: NONE
21:55:59.893 976-976/com.yirong.netlistener I/NetListener >>>: NONE*
项目地址:https://github.com/CocoYuki/NetListener
- 总结
- 熟练注解使用
- 反射知识
- 枚举优化
- 思想:使用注解实现方法回调,状态筛选
是一个练手的小项目,实际网络请求在7.0时就能通过一个CallBack轻松搞定了。