RxPermissions 源码解析之举一反三

作者 : 开心源码 本文共18169个字,预计阅读时间需要46分钟 发布时间: 2022-05-12 共193人阅读

[toc]

RxPermissions 源码解析

简介

RxPermissions 是基于 RxJava 开发的用于帮助 在Android 6.0 中解决运行时权限检测的框架。在 Android 6.0 中添加了对危险权限的动态申请,而不是像 Android 6.0 之前的默认一律获取的方式。

原始动态权限的获取

假如按照以往的获取权限方式的话,那么我们获取权限一般需要有 3 个步骤,第一步是先判断当前能否已经获取到该权限了;第 2 步申请对应的权限;第 3 步在 Activity 或者者 Fragment 中解决获取权限的结果。具体的实现步骤如下:

  • step 1:判断权限能否已经获取。
if (ActivityCompat.shouldShowRequestPermissionRationale(this,                Manifest.permission.CAMERA)) {            //用于开发者提醒客户权限的用途        } else {            //申请权限        }
  • step 2:申请权限
ActivityCompat.requestPermissions(MainActivity.this,                                    new String[]{Manifest.permission.CAMERA},                                    REQUEST_CAMERA);
  • step 3:结果解决
@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,        @NonNull int[] grantResults) {    // 判断请求码,确定当前申请的权限    if (requestCode == REQUEST_CAMERA) {        //判断权限能否申请通过        if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {            //受权成功        } else {               //受权失败        }    } else {        super.onRequestPermissionsResult(requestCode, permissions, grantResults);    }}

RxPermissions 的简单使用

其实 RxPermissions 的使用方式有两种

  • 方式 1:
RxPermissions rxPermissions = new RxPermissions(MainActivity.this);        rxPermissions                .request(Manifest.permission.READ_EXTERNAL_STORAGE,                        Manifest.permission.WRITE_EXTERNAL_STORAGE)//这里填写所需要的权限                .subscribe(new Consumer<Boolean>() {                    @Override                    public void accept(Boolean aBoolean) throws Exception {                        if (aBoolean) {                            // 通过                        }else{                            // 拒绝                        }                    }                });
  • 方式 2:结合 RxBinding 来使用
RxPermissions rxPermissions = new RxPermissions(MainActivity.this);// Must be done during an initialization phase like onCreateRxView.clicks(findViewById(R.id.enableCamera))    .compose(rxPermissions.ensure(Manifest.permission.CAMERA))    .subscribe(granted -> {        // R.id.enableCamera has been clicked    });

源码分析

整体详情

接着我们来对这个 RxPermissions 进行一个源码的解析,但是打开源码的时候,我们可以发现,这个库里面,其实就只有 3 个类:RxPermissions、RxPermissionsFragment、Permission

  • RxPermissions
    • 最主要的实现类,利用 rxjava,为我们提供了方便权限申请的类
  • RxPermissionsFragment
    • 是一个 fragment,主要的动态权限获取类
  • Permission
    • 定义的权限的 model 类

源码分析

RxPermissions 实例创立

对于源码的分析,我们应该先从简单的使用入手。下面我们可以先看看实例化 RxPermissionsFragment 的时候是做了什么?

    RxPermissionsFragment mRxPermissionsFragment;    public RxPermissions(@NonNull Activity activity) {        mRxPermissionsFragment = getRxPermissionsFragment(activity);    }

我们可以看到,上面的代码中,实例化 RxPermissionsFragment 的时候,里面先创立了一个 RxPermissionsFragment 的实例。我们再接着看 getRxPermissionsFragment 这个方法的实现。

    private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {        //  查找 RxPermissionsFragment 能否已经被增加了        RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);        boolean isNewInstance = rxPermissionsFragment == null;        if (isNewInstance) {            rxPermissionsFragment = new RxPermissionsFragment();            FragmentManager fragmentManager = activity.getFragmentManager();            fragmentManager                    .beginTransaction()                    .add(rxPermissionsFragment, TAG)                    .commitAllowingStateLoss();            fragmentManager.executePendingTransactions();        }        return rxPermissionsFragment;    }

在 getRxPermissionsFragment() 这个方法中,首先是先查找当前能否已经增加了这个 rxPermissionsFragment 的实例,假如已经增加,那么直接返回已经增加的实例,假如没有增加过的话,那么就重新再创立一个 RxPermissionsFragment 实例并提交;

    private RxPermissionsFragment findRxPermissionsFragment(Activity activity) {        return (RxPermissionsFragment) activity.getFragmentManager().findFragmentByTag(TAG);    }

到此,rxPermissionsFragment 的实例化已经完成,接着我们需要看看 request 这个方法中实现了什么。

request 方法

    public Observable<Boolean> request(final String... permissions) {        return Observable.just(TRIGGER).compose(ensure(permissions));    }
    static final Object TRIGGER = new Object();

从上面的代码中,我们可以看到,request 方法中需要传入的参数是一个 权限的数组,返回值是 Observable<Boolean> 对象。Observable.just(TRIGGER) 是快捷创立一个 Observable 的方式,因为 TRIGGER 是一个空的 Object 对象,所以 TRIGGER 就是一个占位符而已,Observable.just(TRIGGER) 创立的是一个 Observable<Object>,之后通过 compose 将 Observable<Object> 转化为 Observable<Boolean> 并返回。在 compose 中需要的参数是一个 ObservableTransformer,那么我们接着看 ensure() 这个方法。

ensure(permissions);

public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {        // 创立一个Transformer对象返回        return new ObservableTransformer<T, Boolean>() {            @Override            public ObservableSource<Boolean> apply(Observable<T> o) {                //request(o, permissions) 方法返回 Observable<Permission> 对象                return request(o, permissions)                        // 将 Observable<Permission> 转换为 Observable<Boolean>,在这里会等待所有的权限都返回了一次性发射数据。                        .buffer(permissions.length)                        .flatMap(new Function<List<Permission>, ObservableSource<Boolean>>() {                            @Override                            public ObservableSource<Boolean> apply(List<Permission> permissions) throws Exception {                                // 假如permissions为空那么直接返回Observable.empty();                                if (permissions.isEmpty()) {                                    // Occurs during orientation change, when the subject receives onComplete.                                    // In that case we don't want to propagate that empty list to the                                    // subscriber, only the onComplete.                                    return Observable.empty();                                }                                // Return true if all permissions are granted.                                for (Permission p : permissions) {                                    if (!p.granted) {                                        return Observable.just(false);                                    }                                }                                return Observable.just(true);                            }                        });            }        };    }

在 ensure 的这个方法中,最终会返回的是 ObservableTransformer<T, Boolean> 对象。接着我们看看 ObservableTransformer 的匿名实现类里面的 apply 方法,这里实现的就是将 Observable<Permission> 转换为 Observable<Boolean> 的操作。我们对 apply 这个方法里面的代码进行简化一下。

return request(o,permissions)    .buffer(permissions.length)    .flatMap(new Function<List<Permission>, ObservableSource<Boolean>>{});
  • request() 方法返回 Observable<Permission> 对象
  • buffer(len) 操作符将一个 Observable<Permission> 变换为 Observable<List<Permission>>,原来的 Observable 正常发射数据,变换产生的 Observable 发射这些数据的缓存集合。buffer 将数据缓存到一个集合当中,而后在适当(比方:所有请求的权限结果都返回了)的时机一起发送。
  • flatMap() 方法将 Observable<List<Permission>> 转化为 Observable<Boolean>

request(o, permissions);

    private Observable<Permission> request(final Observable<?> trigger, final String... permissions) {        if (permissions == null || permissions.length == 0) {            throw new IllegalArgumentException("RxPermissions.request/requestEach requires at least one input permission");        }        return oneOf(trigger, pending(permissions))                .flatMap(new Function<Object, Observable<Permission>>() {                    @Override                    public Observable<Permission> apply(Object o) throws Exception {                        return requestImplementation(permissions);                    }                });    }

在 request 这个方法里面,其实 oneOf() 和 pending() 方法我们可以忽略的,主要的话,我们应该关注 requestImplementation(final String… permissions) 这个方法,在这个方法里面,主要实现了权限的请求。

requestImplementation

    @TargetApi(Build.VERSION_CODES.M)    private Observable<Permission> requestImplementation(final String... permissions) {        List<Observable<Permission>> list = new ArrayList<>(permissions.length);        List<String> unrequestedPermissions = new ArrayList<>();        // In case of multiple permissions, we create an Observable for each of them.        // At the end, the observables are combined to have a unique response.        for (String permission : permissions) {            mRxPermissionsFragment.log("Requesting permission " + permission);            if (isGranted(permission)) {                // Already granted, or not Android M                // Return a granted Permission object.                // 权限已经被同意或者者不是 Android 6.0 以上版本,创立一个 同意的 Permission 对象。                list.add(Observable.just(new Permission(permission, true, false)));                continue;            }            if (isRevoked(permission)) {                // 权限被拒绝,返回一个 拒绝的 Permission 对象。                list.add(Observable.just(new Permission(permission, false, false)));                continue;            }            PublishSubject<Permission> subject = mRxPermissionsFragment.getSubjectByPermission(permission);            // 假如 subject 不存在,那么创立一个 subject。            if (subject == null) {                unrequestedPermissions.add(permission);                subject = PublishSubject.create();                mRxPermissionsFragment.setSubjectForPermission(permission, subject);            }            list.add(subject);        }                // 还未提起申请的权限进行申请        if (!unrequestedPermissions.isEmpty()) {            String[] unrequestedPermissionsArray = unrequestedPermissions.toArray(new String[unrequestedPermissions.size()]);            requestPermissionsFromFragment(unrequestedPermissionsArray);        }                // 严格按照顺序发射数据        return Observable.concat(Observable.fromIterable(list));    }

onRequestPermissionsResult()

    @TargetApi(Build.VERSION_CODES.M)    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {        super.onRequestPermissionsResult(requestCode, permissions, grantResults);        if (requestCode != PERMISSIONS_REQUEST_CODE) return;        boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];        for (int i = 0; i < permissions.length; i++) {            shouldShowRequestPermissionRationale[i] = shouldShowRequestPermissionRationale(permissions[i]);        }        onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale);    }    void onRequestPermissionsResult(String permissions[], int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {        for (int i = 0, size = permissions.length; i < size; i++) {            log("onRequestPermissionsResult  " + permissions[i]);            // Find the corresponding subject            PublishSubject<Permission> subject = mSubjects.get(permissions[i]);            if (subject == null) {                // No subject found                Log.e(RxPermissions.TAG, "RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");                return;            }            // 发射权限申请结果            mSubjects.remove(permissions[i]);            boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;            subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));            subject.onComplete();        }    }

RxJava 操作符

Observable.just()

just 操作符是将一个对象转化为 Observable 的操作符。这个对象可以是一个数字、字符串或者者是数组对象等,是 RxJava 中快速创立一个 Observable 对象的操作符。假如有 subscriber 订阅的话,那么会依次调用 onNext() 和 OnComplete() 方法。所以这里只是创立了一个 Observable 对象,方便后续的调用。

compose(Transformer)操作符

compose 操作符是对 Observable 对象的整体转化。例如:通过 Transformer,我们可以将 Observable<Object> 对象转换成 Observable<Boolean> 对象了。

    public static ObservableTransformer<String,Boolean> getTransformer(){        return new ObservableTransformer<String, Boolean>() {            @Override            public ObservableSource<Boolean> apply(Observable<String> upstream) {                return upstream.flatMap(new Function<String, ObservableSource<Boolean>>() {                    @Override                    public ObservableSource<Boolean> apply(String s) throws Exception {                        return Observable.just(true);                    }                });            }        };    }
    /**     * 线程切换     * @return     */    public static <T> ObservableTransformer<T,T> getScheduler(){        return new ObservableTransformer<T, T>() {            @Override            public ObservableSource<T> apply(Observable<T> upstream) {                return upstream.subscribeOn(Schedulers.io())                        .observeOn(AndroidSchedulers.mainThread());            }        };    }    

buffer 操作符

buffer 操作符将一个 Observable 变换为另一个,原来的 Observable 正常发射数据,变换产生的 Observable 发射这些数据的缓存集合。buffer将数据缓存到一个集合当中,而后在适当的时机一起发送。
buffer(count) 以列表(List)的形式发射非重叠的缓存,每一个缓存至多包含来自原始Observable的count项数据(最后发射的列表数据可能少于count项)

  • 例如:缓存 2 个数据之后,再发送数据(调用 buffer(count) 函数)
                Observable.just(1,2,3,4,5,6)                        .buffer(2)                        .subscribe(integers -> {                            Log.i(TAG, "accept size: "+integers.size());                            for (Integer integer : integers) {                                Log.i(TAG, "accept: "+integer);                            }                        });
  • 输出结果
2018-12-14 11:16:28.452 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept size: 22018-12-14 11:16:28.452 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 12018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 22018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept size: 22018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 32018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 42018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept size: 22018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 52018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 6
  • 例如:缓存 3 个数据,再发送数据,每次移动 1 步
                Observable.just(1,2,3,4)                        .buffer(3,1)                        .subscribe(integers -> {                            Log.i(TAG, "accept size: "+integers.size());                            for (Integer integer : integers) {                                Log.i(TAG, "accept: "+integer);                            }                        });
  • 输出结果
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 32018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 12018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 22018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 32018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 32018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 22018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 32018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 42018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 22018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 32018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 42018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 12018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 4

concat 操作符

是接收若干个Observables,发射数据是有序的,不会交叉。

Subject

  • 作为 Observable 和 Observer 之间的桥梁
  • 可以当做 Observable
  • 可以当做 Observer

PublishSubject

继承至 Subject,它的 Observer 只会接收到 PublishSubject 被订阅之后发送的数据。示例代码如下;我们只会接收到 publishSubject3 和 publishSubject4;

                PublishSubject<String> publishSubject = PublishSubject.create();                publishSubject.onNext("publishSubject1");                publishSubject.onNext("publishSubject2");                publishSubject.subscribe(new Consumer<String>() {                    @Override                    public void accept(String s) throws Exception {                        Log.i(TAG, "accept: "+s);                    }                });                publishSubject.onNext("publishSubject3");                publishSubject.onNext("publishSubject4");
  • 执行结果
2018-12-14 11:33:18.168 29916-29916/com.luwei.lwbaselib I/LwBaseActivity: accept: publishSubject32018-12-14 11:33:18.168 29916-29916/com.luwei.lwbaselib I/LwBaseActivity: accept: publishSubject4

举一反三

可以看到,在 RxPermissions 这个获取权限的开源框架中,往 Activity 中增加了一个空的 Fragment,这个 fragment 才是用来发起申请权限和解决权限的请求,最后再将结果返回,这样子就避免了我们发送请求之后,还需要在 onRequestPermissionsResult 中进行解决,并判断 requestCode 等繁琐操作。想到这里,我们平常使用 startActivityForResult 时,我们也可以同样采用这样的思路来简化我们的请求。

同样的,我们采用增加空白的 fragment,来做 startActivityForResult 请求,主要的实现类有 SimpleForResult 和 SimpleOnResultFragment,ActivityResultInfo 是请求 model,接下我们先看代码。

SimpleForResult

/** * @Author: chenjianrun * @Time: 2018/12/7 * @Description:   避免调用 startActivity 时,需要 onActivityResult 解决的类 */public class SimpleForResult {    private static final String TAG = "SimpleForResult";    private SimpleOnResultFragment mSimpleOnResultFragment;    public SimpleForResult(AppCompatActivity activity) {        mSimpleOnResultFragment = getOnResultFragment(activity.getSupportFragmentManager());    }    public SimpleForResult(Fragment fragment){        mSimpleOnResultFragment = getOnResultFragment(fragment.getChildFragmentManager());    }    private SimpleOnResultFragment getOnResultFragment(FragmentManager fragmentManager) {        SimpleOnResultFragment simpleOnResultFragment = findSimpleOnResultFragment(fragmentManager);        if (simpleOnResultFragment == null) {            simpleOnResultFragment = new SimpleOnResultFragment();            fragmentManager                    .beginTransaction()                    .add(simpleOnResultFragment, TAG)                    .commitAllowingStateLoss();            fragmentManager.executePendingTransactions();        }        return simpleOnResultFragment;    }    private SimpleOnResultFragment findSimpleOnResultFragment(FragmentManager fragmentManager) {        return (SimpleOnResultFragment) fragmentManager.findFragmentByTag(TAG);    }    public Observable<ActivityResultInfo> startForResult(Intent intent) {        return mSimpleOnResultFragment.startForResult(intent);    }    public Observable<ActivityResultInfo> startForResult(Class<?> clazz) {        Intent intent = new Intent(mSimpleOnResultFragment.getActivity(), clazz);        return startForResult(intent);    }    public void startForResult(Intent intent, Callback callback) {        mSimpleOnResultFragment.startForResult(intent, callback);    }    public void startForResult(Class<?> clazz, Callback callback) {        Intent intent = new Intent(mSimpleOnResultFragment.getActivity(), clazz);        startForResult(intent, callback);    }    public interface Callback {        void onActivityResult(int requestCode, int resultCode, Intent data);    }}

SimpleOnResultFragment

/** * @Author: chenjianrun * @Time: 2018/12/7 * @Description:    真正调用 startActivity 和解决 onActivityResult 的类。 */public class SimpleOnResultFragment extends Fragment {    private Map<Integer, PublishSubject<ActivityResultInfo>> mSubjects = new HashMap<>();    private Map<Integer, SimpleForResult.Callback> mCallbacks = new HashMap<>();    public SimpleOnResultFragment() {    }    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setRetainInstance(true);    }    public Observable<ActivityResultInfo> startForResult(final Intent intent) {        int requestCode = generateRequestCode();        PublishSubject<ActivityResultInfo> subject = PublishSubject.create();        mSubjects.put(requestCode, subject);        startActivityForResult(intent, requestCode);        return subject;    }    public void startForResult(Intent intent, SimpleForResult.Callback callback) {        int requestCode = generateRequestCode();        mCallbacks.put(requestCode, callback);        startActivityForResult(intent, requestCode);    }    @Override    public void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        //rxjava方式的解决        PublishSubject<ActivityResultInfo> subject = mSubjects.remove(requestCode);        if (subject != null) {            subject.onNext(new ActivityResultInfo(requestCode, resultCode, data));            subject.onComplete();        }        //callback方式的解决        SimpleForResult.Callback callback = mCallbacks.remove(requestCode);        if (callback != null) {            callback.onActivityResult(requestCode, resultCode, data);        }    }    private int generateRequestCode(){        Random random = new Random();        for (;;){            int code = random.nextInt(65536);            if (!mSubjects.containsKey(code) && !mCallbacks.containsKey(code)){                return code;            }        }    }}

ActivityResultInfo

package com.luwei.util.forresult;import android.content.Intent;/** * @Author: chenjianrun * @Time: 2018/12/7 * @Description: */public class ActivityResultInfo {    private int requestCode;    private int resultCode;    private Intent data;    public ActivityResultInfo(int requestCode, int resultCode, Intent data) {        this.requestCode = requestCode;        this.resultCode = resultCode;        this.data = data;    }    public int getRequestCode() {        return requestCode;    }    public void setRequestCode(int requestCode) {        this.requestCode = requestCode;    }    public ActivityResultInfo(int resultCode, Intent data) {        this.resultCode = resultCode;        this.data = data;    }    public int getResultCode() {        return resultCode;    }    public void setResultCode(int resultCode) {        this.resultCode = resultCode;    }    public Intent getData() {        return data;    }    public void setData(Intent data) {        this.data = data;    }}

简单使用示例

  • 简单的 Activity 调用
// 简化调用 startActivityForResult 及避免在 onActivityResult 中解决繁琐的结果                SimpleForResult simpleForResult = new SimpleForResult(this);                simpleForResult.startForResult(ToastActivity.class)                        .subscribe((resultInfo) -> {                            if (resultInfo.getData() != null) {                                ToastUtils.showLong(resultInfo.getData().getStringExtra("result"));                            }                        });
  • 调用摄像头
    /**     * 打开摄像头     */    private void openCamera() {        try {            mTmpFile = FileUtils.createTmpFile(this);        } catch (IOException e) {            e.printStackTrace();        }        simpleForResult.startForResult(getOpenCameraIntent(this, mTmpFile))                .subscribe((resultInfo -> {                    if (resultInfo.getResultCode() == RESULT_OK) {                        mHeadUrl = mTmpFile.getAbsolutePath();                        ImageLoaderUtils.loadCircleImage(this, ivHeader, mHeadUrl);                        // 裁剪(假如没有要求可裁剪,也可以不要)                        startPictureZoom(mTmpFile);                    }                }));    }    /**     * 获取打开照相机的 intent,适配 Android 7.0     * @param activity     * @param mTmpFile     * @return     */    public static Intent getOpenCameraIntent(Activity activity,File mTmpFile){        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);        if (intent.resolveActivity(activity.getPackageManager()) != null) {            if (mTmpFile != null && mTmpFile.exists()) {                if (Build.VERSION.SDK_INT >= 24) {                    // 适配 Android 7.0                    intent.putExtra(MediaStore.EXTRA_OUTPUT,                            FileProvider.getUriForFile(activity, activity.getPackageName()+".provider",mTmpFile));                } else {                    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mTmpFile));                }            } else {                Toast.makeText(activity, me.nereo.image_selector.R.string.error_image_not_exist, Toast.LENGTH_SHORT).show();            }        } else {            Toast.makeText(activity, me.nereo.image_selector.R.string.msg_no_camera, Toast.LENGTH_SHORT).show();        }        return intent;    }

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » RxPermissions 源码解析之举一反三

发表回复