Android权限申请优化

文章介绍了两种Android权限申请的优化方案,一是通过重写Activity并利用订阅-通知机制,二是创建无界面Fragment并通过registerForActivityResult简化流程。这两种方案都旨在解决传统权限申请方式中的代码中断和逻辑不直观问题。
摘要由CSDN通过智能技术生成

背景

早期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)了,新方法与权限申请的流程一致,上面的代码中有涉及了。当然此方法还可以用于拍照等其他调用,官方提供了多个响应体,项目中可以按照这个流程对拍照、文件获取这些常见操作进行封装。
三连!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百忧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值