背景
早期Android在权限申请时需要在一个地方调用权限申请,然后重写onRequestPermissionsResult
,处理对应的逻辑,这导致代码逻辑中断,代码不够直观,另外设置大量的requestCode
容易混乱,所以有了一些动态申请权限的工具,本文介绍个人实现的权限申请工具。
方案一
重写Activity,通过订阅-通知的方式来整合申请流程。申请权限是向requestPermissionsResultMap
注入响应,实现订阅,在onRequestPermissionsResult
通知对应的订阅
public class MainActivity extends AppCompatActivity {
private final Map<String, OnRequestPermissionsResult> requestPermissionsResultMap = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE) != PackageManager.PERMISSION_GRANTED){
requestPermissions(new String[]{
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_NETWORK_STATE
}, NET_PERMISSION, (requestCode, permissions, grantResults) -> doSomething());
return;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestPermissionsResultMap.get(requestCode+"") != null) {
requestPermissionsResultMap.get(requestCode+"").result(requestCode, permissions, grantResults);
}
}
public void requestPermissions(String[] permissions, int requestCode, OnRequestPermissionsResult onRequestPermissionsResult){
requestPermissionsResultMap.put(requestCode+"", onRequestPermissionsResult);
ActivityCompat.requestPermissions(this, permissions, requestCode);
}
interface OnRequestPermissionsResult{
void result(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults);
}
}
这种方案与EsayPermission有些类似,需要重写onRequestPermissionsResult
,在项目中一般会有自定义的Activity基类,所以可以实现统一的改造,但是如果没有基类,或者需要在Fragment/View中调用就需要额外操作,其实并不是那么方便
方案二
启动一个无界面的Fragment用于申请权限,解决方案一的遗留问题
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
public class LauncherFragment extends Fragment {
private final static String TAG = "com.wangyou.Launcher";
// 申请权限
private String[] requestPermission = new String[]{};
private OnRequestPermissionsResult onRequestPermissionsResult;
private ActivityResultLauncher<String[]> permissionActivityLauncher;
// 启动Activity,拍照等流程也可以参考此操作
private ActivityResultLauncher<Intent> startActivityLauncher;
private StartActivityResult startActivityResult;
@VisibleForTesting
private static Lazy<LauncherFragment> launcherFragment;
public LauncherFragment(){
}
public static LauncherFragment getInstance(@NonNull final FragmentActivity activity) {
launcherFragment = getLazySingleton(activity.getSupportFragmentManager());
return launcherFragment.get();
}
public static LauncherFragment getInstance(@NonNull final Fragment fragment) {
launcherFragment = getLazySingleton(fragment.getChildFragmentManager());
return launcherFragment.get();
}
@NonNull
private static Lazy<LauncherFragment> getLazySingleton(@NonNull final FragmentManager fragmentManager) {
return new Lazy<LauncherFragment>() {
private LauncherFragment launcherFragment;
@Override
public synchronized LauncherFragment get() {
if (launcherFragment == null) {
launcherFragment = getLauncherFragment(fragmentManager);
}
return launcherFragment;
}
};
}
private static LauncherFragment getLauncherFragment(@NonNull final FragmentManager fragmentManager) {
LauncherFragment permissionsFragment = findLauncherFragment(fragmentManager);
boolean isNewInstance = permissionsFragment == null;
if (isNewInstance) {
permissionsFragment = new LauncherFragment();
fragmentManager
.beginTransaction()
.add(permissionsFragment, TAG)
.commitNow();
}
return permissionsFragment;
}
private static LauncherFragment findLauncherFragment(@NonNull final FragmentManager fragmentManager) {
return (LauncherFragment) fragmentManager.findFragmentByTag(TAG);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
permissionActivityLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {
int[] grantResults = new int[requestPermission.length];
for (int i = 0; i < requestPermission.length; i++) {
if (Boolean.TRUE.equals(result.getOrDefault(requestPermission[i], false))){
grantResults[i] = PackageManager.PERMISSION_DENIED;
}else{
grantResults[i] = PackageManager.PERMISSION_GRANTED;
}
}
if (onRequestPermissionsResult != null){
onRequestPermissionsResult.result(requestPermission, grantResults);
}
});
startActivityLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (startActivityResult != null){
startActivityResult.result(result.getResultCode(), result.getData());
}
});
}
public void requestPermission(@NonNull String[] permissions, OnRequestPermissionsResult onRequestPermissionsResult){
this.requestPermission = permissions;
this.onRequestPermissionsResult = onRequestPermissionsResult;
permissionActivityLauncher.launch(this.requestPermission);
}
public void startActivityForResult(Intent intent, StartActivityResult startActivityResult){
this.startActivityResult = startActivityResult;
startActivityLauncher.launch(intent);
}
interface OnRequestPermissionsResult{
void result(@NonNull String[] permissions, @NonNull int[] grantResults);
}
interface StartActivityResult{
void result(int resultCode, Intent result);
}
@FunctionalInterface
public interface Lazy<V> {
V get();
}
}
// 以申请GPS权限为例
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
LauncherFragment.getInstance(MainActivity.this).requestPermission(new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
}, (permissions, grantResults) -> {
doSomething();
});
return;
}
如果看过RxPermission的源码会发现有部分代码与其一样,区别是权限申请的方式,此处采用了registerForActivityResult
,由于目前此方法需要在onCreate中提前调用,所以需要在main线程中执行,如此就可以在任何地方方便地调用了。虽然此处没有使用RxJava,但其实没什么差别,本质上都可以看做是一种观察者模式的应用,更多的细节可以自行优化。
补充
权限申请本质上是启动一个带返回响应的Activity,过去startActivityForResult
其实与权限申请一样,会导致逻辑分离,而且此方法也被废弃(Deprecated)了,新方法与权限申请的流程一致,上面的代码中有涉及了。当然此方法还可以用于拍照等其他调用,官方提供了多个响应体,项目中可以按照这个流程对拍照、文件获取这些常见操作进行封装。
三连!!!