【移动应用开发技术】通过AOP的思想 打造万能动态权限申请框架Demo完全解析_第1页
【移动应用开发技术】通过AOP的思想 打造万能动态权限申请框架Demo完全解析_第2页
【移动应用开发技术】通过AOP的思想 打造万能动态权限申请框架Demo完全解析_第3页
【移动应用开发技术】通过AOP的思想 打造万能动态权限申请框架Demo完全解析_第4页
【移动应用开发技术】通过AOP的思想 打造万能动态权限申请框架Demo完全解析_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】通过AOP的思想打造万能动态权限申请框架Demo完全解析

/xiangjiana/Android-MS在project的build.gradle添加aspectJgradle插件appmodule是使用框架的地方

上面我说到了,使用框架思想,消除了Activity,Fragment,Service,普通类在申请权限时的差异性,可以全部以普通类的方式来申请权限并且处理回调。所以这里展示Activity,Fragment,Service的动态权限申请写法。

普通类

publicclassLocationUtil{

privateStringTAG="LocationUtil";

@PermissionNeed(

permissions={Manifest.permission.ACCESS_FINE_LOCATION,

requestCode=PermissionRequestCodeConst.REQUEST_CODE_LOCATION)

publicvoidgetLocation(){

Log.e(TAG,"申请位置权限之后,我要获取经纬度");

}

/**

*这里写的要特别注意,denied方法,必须是带有一个int参数的方法,下面的也一样

*@paramrequestCode

*/

@permissionDenied

publicvoiddenied(intrequestcode){

Log.e(TAG,"用户不给阿'');

}

@permisssionDeniedForever

publicvoiddenidFoever(intrequestcode){

Log.e(TAG,''用户永久拒绝'');

}

}

Activity

pubilcclassMainActivityextendsAppcompatActivity{

privatestaticfinalstringTAG=''permissionAspectTag'';

@override

prtectedvoidonCreate(BundlesaveInstanceState){

super.onCreate(saveInstanceState);

setcontenceView(R.layout.activity_main);

上面我说到了,使用框架思想,消除了Activity,Fragment,Service,普通类在申请权限时的差异性,可以全部以普通类的方式来申请权限并且处理回调。所以这里展示Activity,Fragment,Service的动态权限申请写法。findViewById(R.id.btn_location).setonclicklistener(v->getlocationpermission()findviewById(R.id.btn_contact).setonclicklistener(v->getcontactpermission());}@permissionNeed(br/>}@permissionNeed(CONTACTS,Mainfest.permission.WRITE,Manifest.permission.GET_ACCOUNTS},requestcode=permissionsRequestcodeconst.REQUEST_CODE_CONTACTprivatevoidgetcontactpermission(){log.d(TAG,''getcontactpermission'');}@PermissionNeed(br/>log.d(TAG,''getcontactpermission'');}@PermissionNeed(requestCode=PermissionRequestCodeConst.REQUEST_CODE_LOCATION)privatevoidgetLocationPermission(){Log.d(TAG,"getLocationPermission");}@PermissionDeniedbr/>@PermissionDeniedswitch(requestCode){casePermissionRequestCodeConst.REQUEST_CODE_CONTACTLog.d(TAG,"联系人权限被拒绝");break;casePermissionRequestCodeConst.REQUEST_CODE_LOCATION:Log.d(TAG,"位置权限被拒绝");break;default:break;}}publicclassMyFragmentextendsFragment{@Nullablebr/>@NullablepublicViewonCreateView(LayoutInflaterinflater,@NullableViewGroupcontainer,BundlesavedInstanceState){getLocation();returnsuper.onCreateView(inflater,container,savedInstanceState);}privateStringTAG="LocationUtil";@PermissionNeed(permissions={Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},requestCode=PermissionRequestCodeConst.REQUEST_CODE_LOCATION)publicvoidgetLocation(){Log.e(TAG,"申请位置权限之后,我要获取经纬度");}/**}publicclassMyServiceextendsService{@Nullablebr/>@NullablepublicIBinderonBind(Intentintent){returnnull;}@Overridebr/>}@OverridegetLocation();returnsuper.onStartCommand(intent,flags,startId);}privateStringTAG="LocationUtil";@PermissionNeed(permissions={Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},requestCode=PermissionRequestCodeConst.REQUEST_CODE_LOCATION)publicvoidgetLocation(){Log.e(TAG,"申请位置权限之后,我要获取经纬度");}/*这里写的要特别注意,denied方法,必须是带有一个int参数的方法,下面的也一样}这里有个坑:被@PermissionDenied和@PermissionDeniedForever修饰的方法,必须有且仅有一个int类型参数,返回值随意.类结构图3个注解@PermissionDenied@PermissionDeniedForever@PermissionNeed/**

*被此注解修饰的方法,会在方法执行之前去申请相应的权限,只有用户授予权限,被修饰的方法体才会执行

*/

@Target(ElementType.METHOD)//此注解用于修饰方法

@Retention(RetentionPolicy.RUNTIME)//注解保留到运行时,因为可能需要反射执行方法(上面说了修饰的是方法!)

public@interfacePermissionNeed{

String[]permissions();//需要申请的权限,支持多个,需要传入String数组

intrequestCode()default0;//此次申请权限之后的返回码

}/**

*被此注解修饰的方法,会在权限申请失败时被调用

*/

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public@interfacePermissionDenied{

}/**

*被此注解修饰的方法,会在用户永久禁止权限之后被调用

*/

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public@interfacePermissionDeniedForever{

}处理权限回调结果的接口IPermissionCallback/**

*权限申请结果接口

*/

publicinterfaceIPermissionCallback{

/**

*授予权限

*/

voidgranted(intrequestCode);

/**

*这次拒绝,但是并没有勾选"以后不再提示"

*/

voiddenied(intrequestCode);

/**

*勾选"以后不再提示",并且拒绝

*/

voiddeniedForever(intrequestCode);

}

以上都是事先要预备好的东西,接下来进入核心

以上都是事先要预备好的东西,接下来进入核心PermissionAspect类@Aspect

publicclasspermissinbAspect{

privatestaticfinalStringTAG="PermissionAspectTag";

privatefinalStringpointcutExpression="execution(@com.zhou.zpermission.annotation.PermissionNeed**(..))&&@annotation(permissionNeed)";

@Pointcut(value=pointcutExpression)

publicvoidrequestPermission(PermissionNeedpermissionNeed){

Log.d(TAG,"pointCut定义切入点");

}

@Around("requestPermission(permissionNeed)")

Log.d(TAG,"pointCut定义切入点");

}此段代码解读如下:使用@Aspect注解来修饰类,@Aspect是来自AspectJ框架的注解,被它修饰的类,在编译时会被认为是一个切面类切入点可以是以下类型,不同的类型有不同的语法,我目前使用的是methodexecution,也就是函数执行时。这意味着,当切入点的方法即将开始执行的时候,我们插入的逻辑将会被执行。与之类似的有一个methodcall,这个不一样,这个是在切入点的方法被调用时,也就是说,当侦测到该方法被外界调用的时候,而非方法自己执行。这两者有细微差别。至于其他的类型,暂且按下不详述。除了类型之外,这里还有一个重点,那就是MethodSignature的概念,这个类似于jni里的方法签名,是为了标记一个或者一类方法,AspectJ框架通过这个方法签名,来确定JVM的所有class对象中,有哪些方法需要被插入新的逻辑。具体的签名的语法规则为:看不懂?看不懂就对了,举个例子:execution(@com.zhou.zpermission.annotation.PermissionNeed**(..))&&@annotation(permissionNeed)这是Demo中我这么写的,现在逐步解析:

execution表示方法执行时作为切入点

@com.zhou.zpermission.annotation.PermissionNeed表示切入点的方法必须有这个注解修饰

**(..))这个比较诡异,我们知道,一个方法写完整一点可能是这个样子privatevoidtest(inta)

但是如果我们不计较访问权限,不计较返回值类型,也不计较函数名,甚至不计较参数列表的话,就可以写成这个样子**(..)).表示任意方法除此之外,还有后半截&&@annotation(permission),它的含义为:

切入点方法需要接收来自注解的参数。即切入点@Pointcut规定切入点的时候,只识别被@com.zhou.zpermission.annotation.PermissionNeed标记的方法,但是这个@com.zhou.zpermission.annotation.PermissionNeed注解,是有自己的参数值的,所以,必须传入这个值给到切入方法requestPermission(PermissionNeedpermissionNeed)去使用。有点绕!一张图说清楚:图中3个字符串必须一摸一样,不然编译就会报错,而且报错原因还不明确。

切入点方法需要接收来自注解的参数。即切入点@Pointcut规定切入点的时候,只识别被@com.zhou.zpermission.annotation.PermissionNeed标记的方法,但是这个@com.zhou.zpermission.annotation.PermissionNeed注解,是有自己的参数值的,所以,必须传入这个值给到切入方法requestPermission(PermissionNeedpermissionNeed)去使用。有点绕!一张图说清楚:图中3个字符串必须一摸一样,不然编译就会报错,而且报错原因还不明确。使用@Around注解来修饰方法doPermission(),被它修饰的方法会被认为是一个切入策略。Around注解的参数为:"requestPermission(permissionNeed)",也就是pointcut修饰的方法名(形参名)在我们已经定义好切入点requestPermission(PermissionNeedpermissionNeed)的前提下,如果程序已经执行到了切入点,那么我是选择怎么样的策略,目前所选择的策略是Around,也就是,完全替代切入点的方法,但是依然保留了执行原方法逻辑的可能性joinPceed();除了@Around策略之外,还有以下:PermissionAspect类的作用是:定义切入点和切入策略,那么现在我们确定切入点是被注解@PermissionNeed修饰的方法,切入策略是@Around,那么,切入之后我们做了哪些事呢?接下往下看...PermissionAspectActivity类publicclasspermissionAspectActivityextendsAppcompatActivity{

privatefinalstaticStringpermissionsTag="permissions";

privatefinalstaticStringrequestCodeTag="requestCode";

privatestaticIPermissionCallbackmCallback;

/**

*启动当前这个Activity

*/

publicstaticvoidstartActivity(Contextcontext,String[]permissions,intrequestCode,IPermissionCallbackcallback){

Log.d("PermissionAspectTag","contextis:"+context.getClass().getSimpleName());

if(context==null)return;

mCallback=callback;

//启动当前这个Activiyt并且取消切换动画

Intentintent=newIntent(context,PermissionAspectActivity.class);

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP);

//开启新的任务栈并且清除栈顶...为何要清除栈顶

intent.putExtra(permissionsTag,permissions);

intent.putExtra(requestCodeTag,requestCode);

context.startActivity(intent);

//利用context启动activity

if(contextinstanceofActivity){

//并且,如果是activity启动的,那么还要屏蔽掉activity切换动画

((Activity)context).overridePendingTransition(0,0);

}

}

@Override

protectedvoidonCreate(@NullableBundlesavedInstanceState){

super.onCreate(savedInstanceState);

Intentintent=getIntent();

String[]permissions=intent.getStringArrayExtra(permissionsTag);

intrequestCode=intent.getIntExtra(requestCodeTag,0);

if(PermissionUtil.hasSelfPermissions(this,permissions)){

mCallback.granted(requestCode);

finish();

overridePendingTransition(0,0);

}elseif(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){

requestPermissions(permissions,requestCode);

}

}

@Override

publicvoidonRequestPermissionsResult(intrequestCode,@NonNullString[]permissions,@NonNullint[]grantResults){

//现在拿到了权限的申请结果,那么如何处理,我这个Activity只是为了申请,然后把结果告诉外界,所以结果的处理只能是外界传进来

booleangranted=PermissionUtil.verifyPermissions(grantResults);

if(granted){

//如果用户给了权限

mCallback.granted(requestCode);

}else{

if(PermissionUtil.shouldShowRequestPermissionRationale(this,permissions)){

mCallback.denied(requestCode);

}else{

mCallback.deniedForever(requestCode);

}

}

finish();

overridePendingTransition(0,0);

}

}解读:

1.提供一个静态方法publicstaticvoidstartActivity(Contextcontext,String[]permissions,intrequestCode,IPermissionCallbackcallback),用于启动自己PermissionAspectActivity,接收的参数分别为:context,需要的权限数组,权限返回码,权限结果回调接口

onCreate方法中,检查是否已经有想要申请的权限,如果有,直接调用mCallback.granted(requestCode);并且结束自身,并且要注意隐藏Activity的切换动画。如果没有,那么,就去requestPermissions(permissions,requestCode);申请权限。

处理权限申请的回调,并且分情况调用mCallback的回调方法,然后结束自身

1.提供一个静态方法publicstaticvoidstartActivity(Contextcontext,String[]permissions,intrequestCode,IPermissionCallbackcallback),用于启动自己PermissionAspectActivity,接收的参数分别为:context,需要的权限数组,权限返回码,权限结果回调接口需要注意:PermissionAspectActivity必须在module的清单文件中注册并且要定义它的theme使得Activity完全透明Gif图效果演示:

所谓AOP(ApsectOrientedProgramming)面向切面编程。

此概念是基于OOP(ObjectOrientiedProgramming)面向对象编程。在OOP中,我们可以把不同的业务功能都分成一个一个的模块,然后每一个模块有自己的专一职责,从而优化编程过程,降低编程犯错几率。但是随着OOP类的数量的增加,我们会发现,在某一些业务类中,经常有一些相同的代码在重复编写,但是无可奈何,比如日志打印/动态权限申请/埋点数据上报/用户登录状态检查/服务器端口连通性检查等等。这些代码,我们虽然可以他们抽离出来整理到一个个专一的模块中,但是调用的时候,还是到处分散的,并且这些调用还***了本来不直接相关的业务代码,让我们阅读业务代码十分费劲。

而AOP的出现,就是基于OOP的这种缺陷而出现的优化方案。利用AOP,我们可以对业务逻辑的各个部分进行隔离,使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高开发效率,减少犯错概率。

所谓AOP(ApsectOrientedProgramming)面向切面编程。此概念是基于OOP(ObjectOrientiedProgramming)面向对象编程。在OOP中,我们可以把不同的业务功能都分成一个一个的模块,然后每一个模块有自己的专一职责,从而优化编程过程,降低编程犯错几率。但是随着OOP类的数量的增加

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论