RxPermissions 原始碼解析之舉一反三

蘆葦科技App技術團隊發表於2019-02-27

[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:結果處理
@Override
public 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 onCreate
RxView.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 物件。Observable.just(TRIGGER) 是快捷建立一個 Observable 的方式,由於 TRIGGER 是一個空的 Object 物件,所以 TRIGGER 就是一個佔位符而已,Observable.just(TRIGGER) 建立的是一個 Observable,之後通過 compose 將 Observable 轉化為 Observable 並返回。在 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 轉換為 Observable 的操作。我們對 apply 這個方法裡面的程式碼進行簡化一下。

return request(o,permissions)
    .buffer(permissions.length)
    .flatMap(new Function<List<Permission>, ObservableSource<Boolean>>{});
複製程式碼
  • request() 方法返回 Observable 物件
  • buffer(len) 操作符將一個 Observable 變換為 Observable<List>,原來的 Observable 正常發射資料,變換產生的 Observable 發射這些資料的快取集合。buffer 將資料快取到一個集合當中,然後在適當(比如:所有請求的許可權結果都返回了)的時機一起傳送。
  • flatMap() 方法將 Observable<List> 轉化為 Observable

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 物件轉換成 Observable 物件了。

    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: 2
2018-12-14 11:16:28.452 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 1
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 2
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept size: 2
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 3
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 4
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept size: 2
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 5
2018-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: 3
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 1
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 2
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 3
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 3
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 2
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 3
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 4
2018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 2
2018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 3
2018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 4
2018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 1
2018-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: publishSubject3
2018-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 static Map<Integer, PublishSubject<ActivityResultInfo>> mSubjects = new HashMap<>();
    private static 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;
    }

複製程式碼

相關文章