在上一节中介绍了如何利用AspectJ进行AOP编程,那么既然知道了AOP,那么AOP在实际的项目中会在哪里用到呢?
说一下我自己的项目,我现在负责的项目中App很多,几乎每个App都需要权限申请,之前是每个App都自己做一套权限申请,UI的样式也不统一,交互设计经常吐槽,其实利用AOP完全就可以把权限申请拉齐,下沉到base,每个项目依赖base_permission。
1 传统权限申请
在Android 6.0之后,部分权限申请动态配置,用户如果明确禁止该权限,那么只能去设置页开启,以下就是传统的权限申请流程
t1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getSD();
}
});
public void getSD(){
if(ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}else {
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == 1){
for (int result : grantResults) {
if(result == PackageManager.PERMISSION_GRANTED){
Log.e("TAG","获取到权限");
}else {
Log.e("TAG","禁止获取权限");
}
}
}
}
这是OOP思想进行权限申请,即便是将其封装为一套权限申请工具,那么每次调用时都需要调用其中的方法做处理,而且不易于扩展。
2 AOP思想打造权限申请框架
如果使用AOP的思想,那么getSD()方法就是一个切入点,从而针对切入点织入代码
2.1 注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {
String[] value();
int requestCode();
}
Permission注解用于修饰 切入点,其中传入的参数是按照传统的请求方式设置的,在申请权限时,我需要对哪个权限申请,申请权限时需要设置一个请求码判断是否获取到了权限
@Permission(value = Manifest.permission.WRITE_EXTERNAL_STORAGE,requestCode = 1)
public void getSD(){
}
2.2 权限织入类
@Aspect
public class PermissionWearAspectJ {
@Pointcut("execution(@com.take.aop.annatation.Permission * * (..))")
public void permission(){
}
@Around("permission()")
public void checkPermission(ProceedingJoinPoint joinPoint){
}
}
这样找到所有标注Permission注解的方法作为切入点—那么这里有一个问题,我在织入类中进行权限申请,能可以吗?
显然是不行的,因此在传统的权限申请中,需要onRequestPermissionsResult回调判断用户是否拒绝或者同意这个权限,在织入类中是没有这个回调的,那么该怎么办呢? ------ 办法就是启动一个透明的Activity,在透明的Activity中只进行权限获取结果的回调
<style name="Transparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
2.1 获取上下文
如何在织入类中获取上下文对象,这得借助到ProceedingJoinPoint;
Object aThis = joinPoint.getThis();
Context context = null;
if(aThis instanceof Context){
context = (Context) aThis;
}else if(aThis instanceof Fragment){
context = ((Fragment) aThis).getContext();
}
ProceedingJoinPoint的getThis方法,就是拿到的切入点方法所在的上下文,getSD所在的上下文就是MainActivity。
2.2 获取注解参数
在切入点方法注解中,传入了2个参数,分别是申请权限名称和请求码,那么如何获取到这个两个参数,那么还是需要从切入点赋值
@Pointcut("execution(@com.take.aop.annatation.Permission * * (..)) && @annotation(permission)")
public void permission(Permission permission){
}
@annotation(permission) 代表注解的赋值在切入点传入接收,那么在permission方法中就可以接收,同样,在Advice中也可以接收这个permission参数
2.3 透明Activity配置
public class TransParentActivity extends AppCompatActivity {
private static PermissionResultCallback mCallback;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if(intent == null){
this.finish();
return;
}
String[] permissions = getIntent().getStringArrayExtra("permission");
int code = getIntent().getIntExtra("code", -99);
if(permissions == null || code == -99 || mCallback == null){
this.finish();
return;
}
if(PermissionUtils.hasAllPermission(this,permissions)){
mCallback.onPermissionGranted();
this.finish();
return;
}
ActivityCompat.requestPermissions(this,permissions,code);
}
public static void openActivity(Context context,String[] permission,int requestCode,PermissionResultCallback callback){
mCallback = callback;
Intent intent = new Intent();
intent.putExtra("permission",permission);
intent.putExtra("code",requestCode);
intent.setClass(context,TransParentActivity.class);
context.startActivity(intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(PermissionUtils.hasAllPermission(this,permissions)){
mCallback.onPermissionGranted();
this.finish();
return;
}
if(PermissionUtils.isUserReject(this,permissions)){
mCallback.onPermissionDenied();
this.finish();
return;
}
mCallback.onPermissionReject();
this.finish();
}
}
其实,透明Activity的主要工作就是判断当前app是否拥有了某个权限,以及做用户权限申请操作的回调
public interface PermissionResultCallback {
void onPermissionGranted();
void onPermissionReject();
void onPermissionDenied();
}
在结果的回调里,可以做相应的处理
最终的织入类
@Aspect
public class PermissionWearAspectJ {
@Pointcut("execution(@com.take.aop.annatation.Permission * * (..)) && @annotation(permission)")
public void permission(Permission permission){
}
@Around("permission(permission)")
public void checkPermission(ProceedingJoinPoint joinPoint,Permission permission){
Object aThis = joinPoint.getThis();
Context context = null;
if(aThis instanceof Context){
context = (Context) aThis;
}else if(aThis instanceof Fragment){
context = ((Fragment) aThis).getContext();
}
if(context == null || permission == null || permission.value().length == 0){
return;
}
String[] value = permission.value();
int requestCode = permission.requestCode();
Log.e("TAG","value -"+value+"requestCode = "+requestCode);
TransParentActivity.openActivity(context, value, requestCode, new PermissionResultCallback() {
@Override
public void onPermissionGranted() {
Log.e("TAG","获取了权限");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@Override
public void onPermissionReject() {
Log.e("TAG","拒绝了权限");
}
@Override
public void onPermissionDenied() {
}
});
}
}
总结:其实这只是一个简单的方式来利用AOP思想来做权限申请框架,里面可能涉及到很多自己的业务逻辑,之后会封装一个完整的框架放在git上提供给各位参考。
|