内容
ADB命令:
打开图片 ./adb shell am start -a android.intent.action.VIEW -t image/* -d /data/user/0/people.projects.moodlighting/cache/x.png
下载图片 ./adb pull /data/user/0/people.projects.moodlighting/cache/x.png D:\temp\
权限:
普通权限直接xml声明即可;危险权限需要代码动态申请
特殊权限则需要编程式Intent请求 - SYSTEM_ALERT_WINDOW 或 WRITE_SETTINGS
授权判断:
Settings.canDrawOverlays(this); // SYSTEM_ALERT_WINDOW
手动开启应用权限:设置》应用》权限》勾上麦克风等
权限框:拒绝(Deny)、本次运行允许(Allow only while the app is use)、仅在使用中允许(Allow all the time/即长期授权)。
权限最佳实践:
MediaProjection 调用期间会自动授予 SYSTEM_ALERT_WINDOW 权限,故可互调保活。
依赖库权限合并情况 - build\intermediates\manifest_merge_blame_file\
删除第三方权限 - <uses-permission android:name="android.permission.X" tools:node="remove" />;谷歌gms和firebase库依赖的datatransport均引入了网络访问权限ACCESS_NETWORK_STATE。
获取xml声明的权限列表(即<uses-permission />) - pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS).requestedPermissions;
授权框 - com.google.android.permissioncontroller/com.android.permissioncontroller.permission.ui.GrantPermissionsActivity
重置app权限状态(首次和二次拒绝) - ./adb shell pm clear-permission-flags com.example.myapplication android.permission.CAMERA user-set user-fixed
说明 - AS IDE权限检查的抑制注解:@SuppressLint("MissingPermission")
改RequestPermission为RequestMultiplePermissions可同时请求多个权限
运行时权限 - Protection level: dangerous 标识了normal的安装时即获取
https://developer.android.com/reference/android/Manifest.permission
注意 - 需要动态授权的项目,比如TakePicturePreview不能直接launch(null),必须外层用RequestPermission包裹下。
实例:
说明 - 只能用于 Activity 和 Fragment 类中,而非 Service 或广播;Android 12、15 授权框必须后退才会dismiss(),Android 14 失去焦点就触发dismiss()。
ActivityResultCallback<Boolean> arc = isGranted -> {
System.out.println("isGranted - " + isGranted);
if (isGranted) { /* 做业务 */ } else {
System.out.println("拒绝"); // dismiss()也会进入。
}
};
ActivityResultLauncher<String> arl = registerForActivityResult(
new ActivityResultContracts.RequestPermission(), arc);
// 上方放类成员,下方则放调用处。
var p = android.Manifest.permission.CAMERA;
// [弹框检测方案] 在 onCreate 预热下授权框,避免设备重启后首次载入较慢;将未定义权限名请求加入:
ActivityCompat.requestPermissions(this, new String[]{ p }, Integer.MAX_VALUE);
if (ContextCompat.checkSelfPermission(this, p) == PackageManager.PERMISSION_GRANTED) {
arc.onActivityResult(true); // 授权后代码归并至一处
} else if (this.shouldShowRequestPermissionRationale(p)) {
arl.launch(p); // First Time Deny
} else {
System.out.println("First Time Launch and Second Time Deny - else");
arl.launch(p);
new Handler().postDelayed(() -> { // 为true则指已拒绝2次不再弹框,为false则由授权框dismiss()触发。
// or 回调快慢方案 time = System.currentTimeMillis(); (System.currentTimeMillis() - time)<400;
if (notShownGrantPermissionsActivity(MainActivity.this)) {
new AlertDialog.Builder(this).setMessage("[扫码]功能需要摄像头拍照"+"\n\uD83D\uDE4F[" + p + "]")
.setPositiveButton("✅ 开始授权", (di, i) -> PermissionCommon.toAppSettings(MainActivity.this))
.setNegativeButton("\uD83D\uDEAB 拒绝授权(功能受限)", (di, i) ->
Toast.makeText(this, "仅基本功能", Toast.LENGTH_SHORT).show()).show();
}
}, 250);
}
public static boolean notShownGrantPermissionsActivity(Activity activity) {
boolean dialogIsHide = false;
var activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
var infos = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (var info : infos) {
if (info.id == activity.getTaskId()) {
Log.d("TaskInfo", "Found task: " + info.topActivity);
dialogIsHide = activity.getComponentName().equals(info.topActivity);
}
}
return dialogIsHide;
}
try {
var packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_PERMISSIONS);
var ps = packageInfo.requestedPermissions; // 静态权限列表,非runtime权限。
System.out.println("permissions - " + ps);
if (ps != null) {
for (var permission : ps) {
var permissionInfo = pm.getPermissionInfo(permission, PackageManager.GET_META_DATA);
System.out.println("permissionInfo - " + permissionInfo);
// 获取 flag 值,可能需要反射
int flags = permissionInfo.flags;
// 根据 flags 值判断权限状态
// PackageManager.java
final int FLAG_PERMISSION_USER_SET = 1 << 0;
final int FLAG_PERMISSION_USER_FIXED = 1 << 1;
System.out.println("flags - " + flags);
if ((flags & FLAG_PERMISSION_USER_FIXED) != 0) {
// 用户已明确拒绝权限
System.out.println("用户已明确拒绝权限");
} else if ((flags & FLAG_PERMISSION_USER_SET) != 0) {
// 用户曾手动设置过该权限
System.out.println("用户曾手动设置过该权限");
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace(System.err);
}