AspectJ是Android AOP三剑客之一,对于一些面向切面的需求,能提供很好的解决方案。
但最近,在使用过程中,发现一个问题,对于切点的织入,不仅仅是当前类,当前类的子类也会被织入,从而导致重复织入的问题。

比如我们有一个基类BaseActivity

public class BaseActivity extends AppCompatActivity { 

    @Override
    public boolean onCreateOptionsMenu(Menu menu) { 
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) { 
        return super.onOptionsItemSelected(item);
    }
}

有一个主页Activity,继承自BaseActivity

public class MainActivity extends BaseActivity { 
	@Override
    protected void onCreate(@androidx.annotation.Nullable Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) { 
        getMenuInflater().inflate(R.menu.menu_home, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) { 
        if (item.getItemId() == R.id.option_normal_1) { 
            Toast.makeText(MainActivity.this, "Normal1", Toast.LENGTH_SHORT).show();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

接着,我们新建一个BaseActivity的切面

@Aspect
public class CheckBackAspect { 
    @Pointcut("execution(boolean com.heiko.myaspecttest2.BaseActivity.onOptionsItemSelected(..))")
    public void methodMyOnOptionsItemSelected() { 

    }

    @Around("methodMyOnOptionsItemSelected()")
    public boolean aroundMyOnOptionsItemSelected(ProceedingJoinPoint joinPoint) throws Throwable { 
        Log.i("CheckBackAspect", "----- methodMyOnOptionsItemSelected -----");
        return (boolean) joinPoint.proceed();
    }
}

运行程序,点击菜单的时候,会发现日志会被调用两遍。

----- methodMyOnOptionsItemSelected -----
----- methodMyOnOptionsItemSelected -----

这和我们想要的效果是有偏差的,为此,我们打印下joinPoint有哪些参数

@Aspect
public class CheckBackAspect { 
    @Pointcut("execution(boolean com.heiko.myaspecttest2.BaseActivity.onOptionsItemSelected(..))")
    public void methodMyOnOptionsItemSelected() { 

    }

    @Around("methodMyOnOptionsItemSelected()")
    public boolean aroundMyOnOptionsItemSelected(ProceedingJoinPoint joinPoint) throws Throwable { 
        String kind = joinPoint.getKind();
        String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
        String signatureName = joinPoint.getSignature().getName();
        String srouceFileName = joinPoint.getSourceLocation().getFileName();
        String sourceWithinType = joinPoint.getSourceLocation().getWithinType().getName();
        Object target = joinPoint.getTarget();
        Log.i("CheckBackAspect", "----- methodMyOnOptionsItemSelected -----");
        Log.i("CheckBackAspect", "kind:" + kind);
        Log.i("CheckBackAspect", "declaringTypeName:" + declaringTypeName);
        Log.i("CheckBackAspect", "signatureName:" + signatureName);
        Log.i("CheckBackAspect", "srouceFileName:" + srouceFileName);
        Log.i("CheckBackAspect", "sourceWithinType:" + sourceWithinType);
        Log.i("CheckBackAspect", "target:" + target);
        return (boolean) joinPoint.proceed();
    }
}

点击菜单的时候,可以看到

----- methodMyOnOptionsItemSelected -----
kind:method-execution
declaringTypeName:com.heiko.myaspecttest2.MainActivity
signatureName:onOptionsItemSelected
srouceFileName:MainActivity.java
sourceWithinType:com.heiko.myaspecttest2.MainActivity
target:com.heiko.myaspecttest2.MainActivity@8f6cec4
----- methodMyOnOptionsItemSelected -----
kind:method-execution
declaringTypeName:com.heiko.myaspecttest2.BaseActivity
signatureName:onOptionsItemSelected
srouceFileName:BaseActivity.java
sourceWithinType:com.heiko.myaspecttest2.BaseActivity
target:com.heiko.myaspecttest2.MainActivity@8f6cec4

可以看到,我们可以通过declaringTypeNamesourceWithinType来判断一下,拦截掉不需要的切面执行。

@Aspect
public class CheckBackAspect { 
    @Pointcut("execution(boolean com.heiko.myaspecttest2.BaseActivity.onOptionsItemSelected(..))")
    public void methodMyOnOptionsItemSelected() { 

    }

    @Around("methodMyOnOptionsItemSelected()")
    public boolean aroundMyOnOptionsItemSelected(ProceedingJoinPoint joinPoint) throws Throwable { 
        String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
        if ("com.heiko.myaspecttest2.BaseActivity".equals(declaringTypeName)) { 
            Log.i("CheckBackAspect", "执行onOptionsItemSelected切面");
            //执行了BaseActivity的onOptionsItemSelected切面
            //这里可以进行相关业务逻辑...
            return (boolean) joinPoint.proceed();
        }else{ 
            //对其他子类的切面,不进行操作
            return (boolean) joinPoint.proceed();
        }
    }
}

我们再来运行下程序,点击菜单,可以看到,只打印了如下日志

执行onOptionsItemSelected切面

至此,我们就解决了切面重复织入的问题了。

本文地址:https://blog.csdn.net/EthanCo/article/details/109583110