Android 6.0 執行時許可權處理完全解析
轉載請標明出處:
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的講解就跟著申請許可權步驟一起了:
-
在AndroidManifest檔案中新增需要的許可權。
這個步驟和我們之前的開發並沒有什麼變化,試圖去申請一個沒有宣告的許可權可能會導致程式崩潰。
-
檢查許可權
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就需要進行申請授權了。 -
申請授權
ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS);
- 1
- 2
- 3
該方法是非同步的,第一個引數是Context;第二個引數是需要申請的許可權的字串陣列;第三個引數為requestCode,主要用於回撥的時候檢測。可以從方法名
requestPermissions
以及第二個引數看出,是支援一次性申請多個許可權的,系統會通過對話方塊逐一詢問使用者是否授權。 -
處理許可權申請回撥
@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)處理許可權回撥
對於回撥,因為要根據是否授權去執行不同的事情,所以很多框架也需要些一連串的程式碼,或者和前面的申請程式碼耦合。不過這個框架還是比較方便的,也是我選擇它來講解的原因。
回撥主要做的事情:
- 瞭解是否授權成功。
- 根據授權情況進行回撥。
對於第一條邏輯都一樣;對於第二條,因為涉及到兩個分支,每個分支執行不同的方法。
對於第二條,很多框架選擇將兩個分支的方法在申請許可權的時候進行註冊,然後在回撥中根據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的使用。
相關文章
- Android 6.0 在執行時請求許可權Android
- Android 6.0 執行時許可權管理最佳實踐Android
- Android許可權處理分類Android
- Android6.0------許可權申請管理(單個許可權和多個許可權申請)Android
- Android6.0許可權的動態適配Android
- 原生Android之(6.0及以上)許可權申請Android
- Jenkins執行遠端Windows批處理的許可權問題JenkinsWindows
- android 6.0許可權機制的簡單封裝(支援批量申請許可權)Android封裝
- android 6.0許可權申請機制(簡單案例)Android
- 「Android6.0許可權適配| 掘金技術徵文 」Android
- Android 採用AOP方式封裝6.0許可權管理Android封裝
- Linux資料夾執行許可權不夠怎麼辦?如何處理?Linux
- Vue2.0-token許可權處理Vue
- linux 檔案許可權 s 許可權和 t 許可權解析Linux
- Android SELinux許可權AndroidLinux
- Android 通知許可權Android
- win10組策略錯誤沒有許可權執行此操作處理方法Win10
- Docker容器執行時許可權和Linux系統功能DockerLinux
- Android呼叫相簿、相機(相容6.0、7.0、8.0)所需新增的許可權Android
- Android6.0動態許可權最簡單的解決方法Android
- android permission 許可權與安全機制解析(下)Android
- 【Android】22.0 許可權處理(四)——應用程式版本控制:Git的使用AndroidGit
- android動態許可權到自定義許可權框架Android框架
- android 許可權庫EasyPermissionsAndroid
- 普通使用者許可權執行dockerDocker
- Linux檔案讀、寫、執行許可權Linux
- 微信、企微小程式使用taro對位置許可權進行處理
- 前後端分離下前端許可權處理後端前端
- android 許可權元件設計Android元件
- php執行shell指令碼需要sudo許可權PHP指令碼
- Windows自動使用管理員許可權執行bat批處理,手動選擇啟停服務WindowsBAT
- 授權許可權服務設計解析
- Android 中的危險許可權Android
- Android動態許可權總結Android
- Android property屬性許可權新增Android
- android強制申請許可權Android
- Laravel 日誌有時候有許可權有時候沒有許可權?Laravel
- 許可權處理 - 用redis實現分散式session~ (cookie && session )Redis分散式SessionCookie
- sqlplus / as sysdba 提示許可權不足(ORA-01031)問題處理SQL