1、核心逻辑
在Activity或许fragment中,写在几个办法写一些注释,用来表示权限恳求成功
,恳求失利
,多次回绝
。以上便是运用者需求做的。
简略吧,简略就对了,不用传任何上下文。只需求写注解。给大家看下。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void permissionRequestTest(View view) {
testRequest();
}
// 恳求权限 函数名可以随意些
@Permission(value = Manifest.permission.READ_EXTERNAL_STORAGE, requestCode = 200)
public void testRequest() {
Toast.makeText(this, "权限恳求成功...", Toast.LENGTH_SHORT).show();
}
// 权限被撤销 函数名可以随意些
@PermissionCancel
public void testCancel() {
Toast.makeText(this, "权限被回绝", Toast.LENGTH_SHORT).show();
}
// 多次回绝,还勾选了“不再提示”
@PermissionDenied
public void testDenied() {
Toast.makeText(this, "权限被回绝(用户勾选了 不再提示),留意:你有必要要去设置中翻开此权限,不然功能无法运用", Toast.LENGTH_SHORT).show();
}
2、完成
需求用到的技术有Aspect、注解、反射
2.1、Aspect
它的作用便是绑架被注释的办法的履行。比如上方testRequest()是用来恳求权限的,可是我在ASPECT中装备阻拦@permission注释的办法。先做判别。
假如没有听过Aspect的话,AOP面向切面编程,大家应该听说过,它可以用来装备事务、做日志、权限验证、在用户恳求时做一些处理等等。而用@Aspect做一个切面,就可以直接完成。
2.2、PermissionAspect
咱们会创建一个PermissionAspect类,整一个切点,让@Permission被绑架。
// AOP 思想 切面的思想
// 切点 --- 是注解 @
// * * 任何函数 都可以运用 此注解
//(..) 我要带参数 带的参数便是后面那个 @annotation(permission)意思便是 参数里是permission。这样我就拿到了Permission注解它里边的参数
//这样经过切点就拿到了下面这个注解
//@Permission(value = Manifest.permission.READ_EXTERNAL_STORAGE, requestCode = 200) 这便是 Permission permission
@Pointcut
("execution(@com.derry.premissionstudy.permission.annotation.Permission * *(..)) && @annotation(permission)")
//那么这个
// @Permission == permission
public void pointActionMethod(Permission permission) {
} // 切点函数
//切面
@Around("pointActionMethod(permission)")
public void aProceedingJoinPoint(final ProceedingJoinPoint point,Permission permission) throws Throwable{
//我需求拿到 MainActivity this
这样@Permission就被切点绑架了,然后办法就会跑到切面aProceedingJoinPoint。然后获取上下文Context,把权限恳求交给一个通明的Activity来做。做完之后判别成果,用户是赞同了还是回绝了还是曲线了。赞同了直接履行point.proceed(),其他方法则经过Activity或许fragment获取带注解的办法,反射履行即可。
//切面
@Around("pointActionMethod(permission)")
public void aProceedingJoinPoint(final ProceedingJoinPoint point,Permission permission) throws Throwable{
//我需求拿到 MainActivity this
Context context = null;
// MainActivity this == thisObject
final Object thisobject = point.getThis();
// context初始化
if(thisobject instanceof Context){
context = (Context) thisobject;
} else if(thisobject instanceof Fragment){
context = ((Fragment) thisobject).getActivity();
}
// 判别是否为null
if (null == context || permission == null) {
throw new IllegalAccessException("null == context || permission == null is null");
}
//trestRequest 次函数被控制了 不会执
//
//动态恳求 风险权限 通明的空白的Ativity
//这儿一定要得知接口三个状况 现已授权 撤销授权 回绝授权
//调用 空白的Actiivty 开端授权
MyPermissionActivity.requestPermissionAction(context, permission.value(), permission.requestCode(), new IPermission() {
@Override
public void ganted() {
try {
point.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@Override
public void cancel() {
PermissionUtils.invokeAnnotion(thisobject, PermissionCancel.class);
}
@Override
public void denied() {
PermissionUtils.invokeAnnotion(thisobject, PermissionDenied.class);
}
});
}
2.3、空白履行权限的Activity
履行恳求权限的Activity的的相应办法会流到PermissionAspect,然后到空白Activity恳求。恳求完之后的成果,再经过回调传回去就好了。
public class MyPermissionActivity extends AppCompatActivity {
// 定义权限处理的标记, ---- 接收用户传递进来的
private final static String PARAM_PERMSSION = "param_permission";
private final static String PARAM_PERMSSION_CODE = "param_permission_code";
public final static int PARAM_PERMSSION_CODE_DEFAULT = -1;
private String[] permissions;
private int requestCode;
// 方便回调的监听 告知外交 已授权,被回绝,被撤销
private static IPermission iPermissionListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_permission);
permissions = getIntent().getStringArrayExtra(PARAM_PERMSSION);
requestCode = getIntent().getIntExtra(PARAM_PERMSSION_CODE, PARAM_PERMSSION_CODE_DEFAULT);
if(permissions == null){
this.finish();
return;
}
// 可以走到这儿,就开端去查看,是否现已授权了
boolean permissionRequest = PermissionUtils.hasPermissionRequest(this,permissions);
if (permissionRequest) {
// 经过监听接口,告知外交,现已授权了
iPermissionListener.ganted();
this.finish();
return;
}
ActivityCompat.requestPermissions(this,permissions,requestCode);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(PermissionUtils.requestPermissionSuccess(grantResults)){
iPermissionListener.ganted();//现已授权成功了 告知AspectJ
this.finish();
}
// 没有成功,可能是用户 不听话
// 假如用户点击了,回绝(勾选了”不再提示“) 等操作
if(!PermissionUtils.shouldShowRequestPermissionRationable(this,permissions)){
iPermissionListener.denied();
this.finish();
return;
}
// 撤销
iPermissionListener.cancel(); // 接口告知 AspectJ
this.finish();
return;
}
// 让此Activity不要有任何动画
@Override
public void finish() {
super.finish();
overridePendingTransition(0, 0);
}
public static void requestPermissionAction(Context context, String[] permissions, int requestCode, IPermission iPermissionLIstener){
MyPermissionActivity.iPermissionListener = iPermissionLIstener;
Intent intent = new Intent(context,MyPermissionActivity.class);
//作用
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Bundle bundle = new Bundle();
Log.d("TAG", "requestPermissionAction: "+requestCode);
bundle.putInt(PARAM_PERMSSION_CODE,requestCode);
bundle.putStringArray(PARAM_PERMSSION,permissions);
intent.putExtras(bundle);
context.startActivity(intent);
}
}
2.4、其它
app gradle中
apply plugin: 'com.android.application'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.netease.premissionstudy"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'org.aspectj:aspectjrt:1.8.13'
}
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.8",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
log.debug "ajc args: " + Arrays.toString(args)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
项目 gradle中
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
3、总结
其实核心便是,用aspect去绑架注解,然后让一个公共的Activity来处理这个工作,然后回调,再反射其它办法履行。
等有时间了给他打成一个包。让未被注册的空白Activity也能运用。就简略多了。至于未被注册的空白Activity请看我的文章假如启动一个未注册的Activity – ()