现象描述
当我们打开京东 app 进入首页,如果当前是没有网络的状态,里面的按钮点击是没有反应的。只有当我们打开网络的情况下,点击按钮才能跳转页面,按照我们一般人写代码的逻辑应该是这个样子:
/** * 跳转到待收货页面 */ public void jumpWaitReceiving() { // 判断当前有没有网络 if(CheckNetUtil.isNetworkAvailable(this)) { // 当前有网络我才跳转,进入待收货页面 Intent intent = new Intent(this, WaitReceivingActivity.class); startActivity(intent); } } /** * 跳转到我的钱包页面 */ public void jumpMineWallet() { if(CheckNetUtil.isNetworkAvailable(this)) { Intent intent = new Intent(this, MineWalletActivity.class); startActivity(intent); } }
上面这段代码看似没有任何问题,完全满足京东的网络处理需求,就写一个 if(有网) 跳转到下一个页面,没网就不做任何处理。但是真的没有问题吗? 按照京东的页面,这些 if() 代码估计要写上几十次,而且有些在 Activity,有些甚至在 Fragment 中,很难管理。如果有一天需求变动,我们估计要改动多处。我们到底有没有更好的方式,且接着往下看。
面向切面
我们现在想做的其实就是,我根本不想写那么多的 if() 代码,而且写得越多越不好管理,比如有一天没网络要弹 Toast ,那么岂不是很多地方要去改动。所以接下来,我们打算采用面向切面的编程思想,把网络检测切出来统一管理。第一,保证代码的简洁性,第二,需求有变动时我们只需要统一改动那一部分代码,第三,有很多这里不一一列出来了。
那么什么是 AOP ? 好处又有什么?
面向切面(AOP)其实就是把众多方法中的所有共有代码全部抽取出来,放置到某个地方集中管理,然后在具体运行时,再由容器动态织入这些共有代码的话,最起码可以解决两个问题:
1.1 android程序员在编写具体的业务逻辑处理方法时,只需关心核心的业务逻辑处理,既提高了工作效率,又使代码变更简洁优雅。
1.2 在日后的维护中由于业务逻辑代码与共有代码分开存放,而且共有代码是集中存放的,因此使维护工作变得简单轻松。
那到底应该怎么写呢? 请看我最终的代码,代码如下:
/** * 跳转到待收货页面 */ @CheckNet public void jumpWaitReceiving() { Intent intent = new Intent(this, WaitReceivingActivity.class); startActivity(intent); } /** * 跳转到我的钱包页面 */ @CheckNet public void jumpMineWallet() { Intent intent = new Intent(this, MineWalletActivity.class); startActivity(intent); }
1 /** 2 * 处理网络检测切面 3 */ 4 @Aspect 5 public class SectionAspect { 6 7 /** 8 * 找到处理的切点 9 * * *(..) 可以处理所有的方法 10 */ 11 @Pointcut("execution(@com.darren.architect_day02.CheckNet * *(..))") 12 public void checkNetBehavior() { 13 14 } 15 16 /** 17 * 处理切面 18 */ 19 @Around("checkNetBehavior()") 20 public Object checkNet(ProceedingJoinPoint joinPoint) throws Throwable { 21 Log.e("TAG", "checkNet"); 22 // 做埋点 日志上传 权限检测(我写的,RxPermission , easyPermission) 网络检测 23 // 网络检测 24 // 1.获取 CheckNet 注解 NDK 图片压缩 C++ 调用Java 方法 25 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); 26 CheckNet checkNet = signature.getMethod().getAnnotation(CheckNet.class); 27 if (checkNet != null) { 28 // 2.判断有没有网络 怎么样获取 context? 29 Object object = joinPoint.getThis();// View Activity Fragment ; getThis() 当前切点方法所在的类 30 Context context = getContext(object); 31 if (context != null) { 32 if (!isNetworkAvailable(context)) { 33 // 3.没有网络不要往下执行 34 Toast.makeText(context,"请检查您的网络",Toast.LENGTH_LONG).show(); 35 return null; 36 } 37 } 38 } 39 return joinPoint.proceed(); 40 } 41 42 /** 43 * 通过对象获取上下文 44 * 45 * @param object 46 * @return 47 */ 48 private Context getContext(Object object) { 49 if (object instanceof Activity) { 50 return (Activity) object; 51 } else if (object instanceof Fragment) { 52 Fragment fragment = (Fragment) object; 53 return fragment.getActivity(); 54 } else if (object instanceof View) { 55 View view = (View) object; 56 return view.getContext(); 57 } 58 return null; 59 } 60 61 /** 62 * 检查当前网络是否可用 63 * 64 * @return 65 */ 66 private static boolean isNetworkAvailable(Context context) { 67 // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) 68 ConnectivityManager connectivityManager = (ConnectivityManager) 69 context.getSystemService(Context.CONNECTIVITY_SERVICE); 70 if (connectivityManager != null) { 71 // 获取NetworkInfo对象 72 NetworkInfo[] networkInfo = connectivityManager.getAllNetworkInfo(); 73 74 if (networkInfo != null && networkInfo.length > 0) { 75 for (int i = 0; i < networkInfo.length; i++) { 76 // 判断当前网络状态是否为连接状态 77 if (networkInfo[i].getState() == NetworkInfo.State.CONNECTED) { 78 return true; 79 } 80 } 81 } 82 } 83 return false; 84 } 85 }