Android 運行時許可權處理

来源:https://www.cnblogs.com/2eggs/archive/2018/08/22/9516032.html
-Advertisement-
Play Games

轉載請標明出處: http://blog.csdn.net/lmj623565791/article/details/50709663; 本文出自:【張鴻洋的博客】 一、概述 隨著Android 6.0發佈以及普及,我們開發者所要應對的主要就是新版本SDK帶來的一些變化,首先關註的就是許可權機制的變化 ...


轉載請標明出處: 
http://blog.csdn.net/lmj623565791/article/details/50709663; 
本文出自:【張鴻洋的博客】

一、概述

隨著Android 6.0發佈以及普及,我們開發者所要應對的主要就是新版本SDK帶來的一些變化,首先關註的就是許可權機制的變化。對於6.0的幾個主要的變化,查看查看官網的這篇文章http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0-changes.html,其中當然包含Runtime Permissions

ok,本篇文章目的之一就是對運行時許可權處理的一個介紹,以及對目前許可權相關的庫的一些瞭解。

當然非常推薦閱讀官網許可權相關文章:

本文也是在上述文章基礎上理解、實驗以及封裝。

二、運行時許可權的變化及特點

對於6.0以下的許可權及在安裝的時候,根據許可權聲明產生一個許可權列表,用戶只有在同意之後才能完成app的安裝,造成了我們想要使用某個app,就要默默忍受其一些不必要的許可權(比如是個app都要訪問通訊錄、簡訊等)。而在6.0以後,我們可以直接安裝,當app需要我們授予不恰當的許可權的時候,我們可以予以拒絕(比如:單機的象棋對戰,請求訪問任何許可權,我都是不同意的)。當然你也可以在設置界面對每個app的許可權進行查看,以及對單個許可權進行授權或者解除授權。

新的許可權機制更好的保護了用戶的隱私,Google將許可權分為兩類,一類是Normal Permissions,這類許可權一般不涉及用戶隱私,是不需要用戶進行授權的,比如手機震動、訪問網路等;另一類是Dangerous Permission,一般是涉及到用戶隱私的,需要用戶進行授權,比如讀取sdcard、訪問通訊錄等。

  • Normal Permissions如下
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • Dangerous Permissions:
group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.USE_SIP
  permission:android.permission.PROCESS_OUTGOING_CALLS
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

可以通過adb shell pm list permissions -d -g進行查看。

看到上面的dangerous permissions,會發現一個問題,好像危險許可權都是一組一組的,恩,沒錯,的確是這樣的,

那麼有個問題:分組對我們的許可權機制有什麼影響嗎?

的確是有影響的,如果app運行在Android 6.x的機器上,對於授權機制是這樣的。如果你申請某個危險的許可權,假設你的app早已被用戶授權了同一組的某個危險許可權,那麼系統會立即授權,而不需要用戶去點擊授權。比如你的app對READ_CONTACTS已經授權了,當你的app申請WRITE_CONTACTS時,系統會直接授權通過。此外,對於申請時彈出的dialog上面的文本說明也是對整個許可權組的說明,而不是單個許可權(ps:這個dialog是不能進行定製的)。

不過需要註意的是,不要對許可權組過多的依賴,儘可能對每個危險許可權都進行正常流程的申請,因為在後期的版本中這個許可權組可能會產生變化。

三、相關API

好在運行時相關的API也比較簡單,所以適配起來並不會非常痛苦。

API的講解就跟著申請許可權步驟一起了:

  1. 在AndroidManifest文件中添加需要的許可權。

    這個步驟和我們之前的開發並沒有什麼變化,試圖去申請一個沒有聲明的許可權可能會導致程式崩潰。

  2. 檢查許可權

    if (ContextCompat.checkSelfPermission(thisActivity,
                    Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {
    }else{
        //
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    這裡涉及到一個API,ContextCompat.checkSelfPermission,主要用於檢測某個許可權是否已經被授予,方法返回值為PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。當返回DENIED就需要進行申請授權了。

  3. 申請授權

     ActivityCompat.requestPermissions(thisActivity,
                    new String[]{Manifest.permission.READ_CONTACTS},
                    MY_PERMISSIONS_REQUEST_READ_CONTACTS);
    • 1
    • 2
    • 3

    該方法是非同步的,第一個參數是Context;第二個參數是需要申請的許可權的字元串數組;第三個參數為requestCode,主要用於回調的時候檢測。可以從方法名requestPermissions以及第二個參數看出,是支持一次性申請多個許可權的,系統會通過對話框逐一詢問用戶是否授權。

  4. 處理許可權申請回調

    @Override
    public void onRequestPermissionsResult(int requestCode,
            String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
    
                } else {
    
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    ok,對於許可權的申請結果,首先驗證requestCode定位到你的申請,然後驗證grantResults對應於申請的結果,這裡的數組對應於申請時的第二個許可權字元串數組。如果你同時申請兩個許可權,那麼grantResults的length就為2,分別記錄你兩個許可權的申請結果。如果申請成功,就可以做你的事情了~

當然,到此我們的許可權申請的不走,基本介紹就如上述。不過還有個API值得提一下:

// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
        Manifest.permission.READ_CONTACTS)) 
    // Show an expanation to the user *asynchronously* -- don't block
    // this thread waiting for the user's response! After the user
    // sees the explanation, try again to request the permission.

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

這個API主要用於給用戶一個申請許可權的解釋,該方法只有在用戶在上一次已經拒絕過你的這個許可權申請。也就是說,用戶已經拒絕一次了,你又彈個授權框,你需要給用戶一個解釋,為什麼要授權,則使用該方法。

那麼將上述幾個步驟結合到一起就是:

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

四、簡單的例子

這裡寫一個簡單的例子,針對於運行時許可權。相信大家在最開始接觸Android的時候,都利用Intent實驗了打電話、發簡訊等功能。

我們看看直接撥打電話在Android 6.x的設備上許可權需要如何處理。

當然代碼非常簡單:

package com.zhy.android160217;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity
{

    private static final int MY_PERMISSIONS_REQUEST_CALL_PHONE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void testCall(View view)
    {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED)
        {

            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CALL_PHONE},
                    MY_PERMISSIONS_REQUEST_CALL_PHONE);
        } else
        {
            callPhone();
        }
    }

    public void callPhone()
    {
        Intent intent = new Intent(Intent.ACTION_CALL);
        Uri data = Uri.parse("tel:" + "10086");
        intent.setData(data);
        startActivity(intent);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
    {

        if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE)
        {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
                callPhone();
            } else
            {
                // Permission Denied
                Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();
            }
            return;
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

在Android 6.x上運行是,點擊testCall,即會彈出授權視窗,如何你Allow則直接撥打電話,如果Denied則Toast彈出”Permission Denied”。

例子很簡單,不過需要註意的是,對於Intent這種方式,很多情況下是不需要授權的甚至許可權都不需要的,比如:你是到撥號界面而不是直接撥打電話,就不需要去申請許可權;打開系統圖庫去選擇照片;調用系統相機app去牌照等。更多請參考Consider Using an Intent 
.

當然,上例也說明瞭並非所有的通過Intent的方式都不需要申請許可權。一般情況下,你是通過Intent打開另一個app,讓用戶通過該app去做一些事情,你只關註結果(onActivityResult),那麼許可權是不需要你處理的,而是由打開的app去處理。

五、封裝

(1)申請許可權

雖然許可權處理並不複雜,但是需要編寫很多重覆的代碼,所以目前也有很多庫對用法進行了封裝,大家可以在github首頁搜索:android permission,對比了幾個庫的使用方式,發現https://github.com/lovedise/PermissionGen這個庫據我所見相比較而言使用算是比較簡單的,那麼就以這個庫的源碼為基礎來講解,大家有興趣可以多看幾個庫的源碼。

封裝的代碼很簡單,正如大家的對上面的許可權代碼所見,沒有特別複雜的地方。

對於申請許可權的代碼,原本的編寫為:

if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED)
{

    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.CALL_PHONE},
            MY_PERMISSIONS_REQUEST_CALL_PHONE);
} else
{
    callPhone();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

大家可以看到,對於其他的許可權,其實申請的邏輯是類似的;唯一不同的肯定就是參數,那麼看上面的代碼,我們需要3個參數:Activity|Fragment許可權字元串數組int型申請碼

也就是說,我們只需要寫個方法,接受這幾個參數即可,然後邏輯是統一的。

public static void needPermission(Fragment fragment, int requestCode, String[] permissions)
{
    requestPermissions(fragment, requestCode, permissions);
}

@TargetApi(value = Build.VERSION_CODES.M)
private static void requestPermissions(Object object, int requestCode, String[] permissions)
{
    if (!Utils.isOverMarshmallow())
    {
        doExecuteSuccess(object, requestCode);
        return;
    }
    List<String> deniedPermissions = Utils.findDeniedPermissions(getActivity(object), permissions);

    if (deniedPermissions.size() > 0)
    {
        if (object instanceof Activity)
        {
            ((Activity) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
        } else if (object instanceof Fragment)
        {
            ((Fragment) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
        } else
        {
            throw new IllegalArgumentException(object.getClass().getName() + " is not supported");
        }

    } else
    {
        doExecuteSuccess(object, requestCode);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

Utils.findDeniedPermissions其實就是check沒有授權的許可權。

#Utils
@TargetApi(value = Build.VERSION_CODES.M)
public static List<String> findDeniedPermissions(Activity activity, String... permission)
{
    List<String> denyPermissions = new ArrayList<>();
    for (String value : permission)
    {
        if (activity.checkSelfPermission(value) != PackageManager.PERMISSION_GRANTED)
        {
            denyPermissions.add(value);
        }
    }
    return denyPermissions;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

那麼上述的邏輯就很清晰了,需要的3種參數傳入,先去除已經申請的許可權,然後開始申請許可權。

ok,我相信上面代碼,大家掃一眼就可以瞭解了。

(2)處理許可權回調

對於回調,因為要根據是否授權去執行不同的事情,所以很多框架也需要些一連串的代碼,或者和前面的申請代碼耦合。不過這個框架還是比較方便的,也是我選擇它來講解的原因。

回調主要做的事情:

  1. 瞭解是否授權成功。
  2. 根據授權情況進行回調。

對於第一條邏輯都一樣;對於第二條,因為涉及到兩個分支,每個分支執行不同的方法。

對於第二條,很多框架選擇將兩個分支的方法在申請許可權的時候進行註冊,然後在回調中根據requestCode進行匹配執行,不過這樣需要在針對每次申請進行對象管理。

不過這個框架採取了一種很有意思的做法,它利用註解去確定需要執行的方法,存在兩個註解:

@PermissionSuccess(requestCode = 100)
@PermissionFail(requestCode = 100)
  • 1
  • 2

利用反射根據授權情況+requestCode即可找到註解標註的方法,然後直接執行即可。

大致的代碼為:

@Override public void onRequestPermissionsResult(int requestCode, String[] permissions,
      int[] grantResults) {
    PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}

private static void requestResult(Object obj, int requestCode, String[] permissions,
                                  int[] grantResults)
{
    List<String> deniedPermissions = new ArrayList<>();
    for (int i = 0; i < grantResults.length; i++)
    {
        if (grantResults[i] != PackageManager.PERMISSION_GRANTED)
        {
            deniedPermissions.add(permissions[i]);
        }
    }

    if (deniedPermissions.size() > 0)
    {
        doExecuteFail(obj, requestCode);
    } else
    {
        doExecuteSuccess(obj, requestCode);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

首先根據grantResults進行判斷成功還是失敗,對於成功則:

private static void doExecuteSuccess(Object activity, int requestCode)
{
    Method executeMethod = Utils.findMethodWithRequestCode(activity.getClass(),
            PermissionSuccess.class, requestCode);

    executeMethod(activity, executeMethod);
}

#Utils
public static <A extends Annotation> Method findMethodWithRequestCode(Class clazz,  Class<A> annotation, int requestCode)
{
    for (Method method : clazz.getDeclaredMethods())
    {
        if (method.isAnnotationPresent(annotation))
        {
            if (isEqualRequestCodeFromAnntation(method, annotation, requestCode))
            {
                return method;
            }
        }
    }
    return null;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

根據註解和requestCode找到方法,然後反射執行即可。失敗的邏輯類似,不貼代碼了。

ok,到此我們的運行時許可權相對於早起版本的變化、特點、以及如何處理和封裝都介紹完了。

不過對於上面講解的庫,肯定有人會說:運行時反射會影響效率,沒錯,不過我已經在上述代碼的基礎上將運行時註解改成Annotation Processor的方式了,即編譯時註解,這樣的話,就不存在反射損失效率的問題了。本來準備fork修改,然後PR,結果寫完,改動太大,估計PR是不可能通過了,所以另起項目了,也方便後面的做一些擴展和維護。

詳見庫:https://github.com/hongyangAndroid/MPermissions.

六、MPermissions用法

對外的介面和PermissionGen基本一致,因為申請只需要三個參數,拋棄了使用原本類庫的單例的方式,直接一個幾個靜態方法,簡單整潔暴力。

貼一個用法:

public class MainActivity extends AppCompatActivity
{

    private Button mBtnSdcard;
    private static final int REQUECT_CODE_SDCARD = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnSdcard = (Button) findViewById(R.id.id_btn_sdcard);
        mBtnSdcard.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
    {
        MPermissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }


    @PermissionGrant(REQUECT_CODE_SDCARD)
    public void requestSdcardSuccess()
    {
        Toast.makeText(this, "GRANT ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
    }

    @PermissionDenied(REQUECT_CODE_SDCARD)
    public void requestSdcardFailed()
    {
        Toast.makeText(this, "DENY ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

是不是簡單明瞭~~對於onRequestPermissionsResult所有的Activity都是一致的,所以可以放到BaseActivity中去。此外,在Fragment中使用的方式一致,詳見demo。

詳見庫:https://github.com/hongyangAndroid/MPermissions.

至於為什麼不直接介紹MPermission的源碼,因為主要涉及到Annotation Processor,所以以這種方式引出,後面考慮單篇博文介紹下我目前所會的編譯時註解的相關做法以及API的使用。

最後感謝PermissionGen庫,祝大家元宵節快樂~!


 

版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/lmj623565791/article/details/50709663
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Android ConstraintLayout詳解 https://www.jianshu.com/p/a8b49ff64cd3 1. 概述 在本篇文章中,你會學習到有關ConstraintLayout -- 一種構建於彈性Constraints(約束)系統的新型Android Layout。最終 ...
  • 在C語言中,編程都是面向過程的編程,每一個代碼塊都嚴格按照從上至下的順序執行,在代碼塊之間同樣也是這樣, 但是在OC中往往不是這樣,OC和C++、java等語言一樣,都是面向對象的編程語言,在代碼的執行上有很大的跳躍性。 OC是C語言的超集,對C語言所有的功能進行了封裝,例如,OC對C語言的字元串進 ...
  • 一個老鳥發的公司內部整理的 Android 學習路線圖 2017年09月12日 17:13:27 閱讀數:20449 一個老鳥發的公司內部整理的 Android 學習路線圖 2017年09月12日 17:13:27 閱讀數:20449 一個老鳥發的公司內部整理的 Android 學習路線圖 一個老鳥 ...
  • 引用 在項目的build.gradle添加依賴 RecyclierView使用的基本方法 recyclerView.setAdapter(); 添加適配器(必須) recyclerView.setLayoutManager(); 選擇一種佈局(必須) recyclerAdapter.setHeade ...
  • 1、微信小程式獲取Access_token: access_token具體時效看官方文檔。 using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System. ...
  • "Android項目刮刮獎詳解(四)" 前言 我們已經成功實現了刮刮獎的功能了,本期是擴展篇,我們把這個View直接定義成開源控制項,發佈到JitPack上,以後有需要也可以直接使用,關於自定義控制項的知識,不瞭解的同學可以看這下麵我之前寫的這兩篇 "Android 自定義控制項" "Android開發— ...
  • 1 package com.geli_2.sujie.sujiegeili2testbutton; 2 3 import android.os.Bundle; 4 import android.support.v7.app.AppCompatActivity; 5 import android.vi... ...
  • NSDate * senddate=[NSDate date]; NSDateFormatter *dateformatter=[[NSDateFormatter alloc] init]; [dateformatter setDateFormat:@"yyyy"]; // 今年2016 NSStr ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...