现象:
打开京东/淘宝APP,断开网络连接,发现点击没有任何反应。我们简单猜想一下,他们是怎么做的呢?
一般实现方式:
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.checkNet_tv:
if (!NewWorkUtils.isNetworkConnected(this)) {
return;
}
Intent intent = new Intent(MainActivity.this, OtherActivity.class);
startActivity(intent);
break;
}
}
稍微看下上面的这一段代码,主要逻辑是通过判断网络是否可用,不可用就return,可用才继续往下走。这样显然是可以实现无网络状态时点击无反应这个需求的。但是在项目中,需要判断有网络状态才可以进入下一步的按钮,可能是几十个或者更多呢?每一个都需要写一段相同的逻辑判断一遍吗?如果有几十个都需要相同判断逻辑的按钮,项目中的相同代码会非常多,又或者这里的需求变更,需要改逻辑的位置会非常多,增加代码维护的成本。
那有没有更好的实现方式呢?
答案是有的
接下来插入剧情…
@CheckNet
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.checkNet_tv:
Intent intent = new Intent(MainActivity.this, MainActivity.class);
startActivity(intent);
break;
}
}
相比原来的代码,多了一个注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNet {
}
注解简单理解就是给程序看的一个标签,使用@interface来定义一个注解,注解里面也可以声明变量。声明注解必须声明这个注解的作用域和作用阶段,也就是Target和Retention。常用的Target有ElementType.TYPE,ElementType.FLELD,ElementType.METHOD,分别代表作用在类、成员变量、方法上。常用的Retention有RetentionPolicy.CLASS,Retention.RUNTIME,Retention.SOURCE,分别代表编译阶段,运行阶段,编码阶段。RetentionPolicy.CLASS,Retention.RUNTIME使用的频率会更高一点,Retention.SOURCE常见的注解有@Override。
@Aspect
public class CheckNetAspect {
@Pointcut("execution(@com.demo.checknetdemo.CheckNet * * (..))")
public void onCheckNet() {
}
@Around("onCheckNet()")
public void handleCheckNet(ProceedingJoinPoint joinPoint) throws Throwable {
Object object = joinPoint.getThis();
Context context = getContext(object);
if (context != null) {
if (!isNetworkConnected(context)) {
return;
}
}
joinPoint.proceed();
}
private Context getContext(Object object) {
if (object instanceof Activity) {
return (Activity) object;
} else if (object instanceof Fragment) {
return ((Fragment) object).getContext();
} else if (object instanceof View) {
return ((View) object).getContext();
}
return null;
}
private boolean isNetworkConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null) {
return mNetworkInfo.isAvailable();
}
}
return false;
}
}
@Aspect代表这是一个切面的处理类
@Pointcut("execution(@com.demo.checknetdemo.CheckNet * * (..))")
public void onCheckNet() {
}
@Pointcut代表一个切点,("execution(@com.demo.checknetdemo.CheckNet * * (..))")代表的是切点的完整的路径,后面的* *(..)是一种固定格式,相当于导包时路径后面跟的 *一样。
@Around("onCheckNet()")
public void handleCheckNet(ProceedingJoinPoint joinPoint) throws Throwable {
Object object = joinPoint.getThis();
Context context = getContext(object);
if (context != null) {
if (!isNetworkConnected(context)) {
return;
}
}
joinPoint.proceed();
}
@Around("onCheckNet()")表示这里是针对切面的处理函数,使用@Around注解可以决定函数是否可以往下执行。joinPoint.proceed()表示让函数继续往下执行。这里常用的注解还有@Before,@After,分别代表要把这段代码插入到原方法的前面还是后面。
总结:
AOP面向切面编程是一种编程思想,这种编程方式的存在能使我们的代码更易维护管理。可以使用的场景有很多,如防多次点击、上传埋点、网络判断、权限申请..等等。AspectJ是AOP的一种实现方式。还有APT,Javassist..等等。
网友评论