android6.0的系统为什么在androiddtistudio运行系统中运行不了

在 SegmentFault,解决技术问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
一线的工程师、著名开源项目的作者们,都在这里:
获取验证码
已有账号?
标签:至少1个,最多5个
英文: 译文:
android M 的名字官方刚发布不久,最终正式版即将来临!android在不断发展,最近的更新 M 非常不同,一些主要的变化例如运行时权限将有颠覆性影响。惊讶的是android社区鲜有谈论这事儿,尽管这事很重要或许在不远的将来会引发很严重的问题。这是今天我写这篇博客的原因。这里有一切关于android运行时权限你需要知道的,包括如何在代码中实现。现在亡羊补牢还不晚。
新运行时权限
android的权限系统一直是首要的安全概念,因为这些权限只在安装的时候被询问一次。一旦安装了,app可以在用户毫不知晓的情况下访问权限内的所有东西。难怪一些坏蛋利用这个缺陷恶意收集用户数据用来做坏事了!android小组也知道这事儿。7年了!权限系统终于被重新设计了。在android6.0棉花糖,app将不会在安装的时候授予权限。取而代之的是,app不得不在运行时一个一个询问用户授予权限。
注意权限询问对话框不会自己弹出来。开发者不得不自己调用。如果开发者要调用的一些函数需要某权限而用户又拒绝授权的话,函数将抛出异常直接导致程序崩溃。
另外,用户也可以随时在设置里取消已经授权的权限。
你或许已经感觉到背后生出一阵寒意。。。如果你是个android开发者,意味着要完全改变你的程序逻辑。你不能像以前那样直接调用方法了,你不得不为每个需要的地方检察权限,否则app就崩溃了!是的。我不能哄你说这是简单的事儿。尽管这对用户来说是好事,但是对开发者来说就是噩梦。我们不得不修改编码不然不论短期还是长远来看都是潜在的问题。这个新的运行时权限仅当我们设置targetSdkVersion to 23(这意味着你已经在23上测试通过了)才起作用,当然还要是M系统的手机。app在6.0之前的设备依然使用旧的权限系统。
已经发布了的app会发生什么
新运行时权限可能已经让你开始恐慌了。“hey,伙计!我三年前发布的app可咋整呢。如果他被装到android 6.0上,我的app会崩溃吗?!?”莫慌张,放轻松。android小队又不傻,肯定考虑到了这情况。如果app的targetSdkVersion 低于 23,那将被认为app没有用23新权限测试过,那将被继续使用旧有规则:用户在安装的时候不得不接受所有权限,安装后app就有了那些权限咯!
然后app像以前一样奔跑!注意,此时用户依然可以取消已经同意的授权!用户取消授权时,android 6.0系统会警告,但这不妨碍用户取消授权。
问题又来了,这时候你的app崩溃吗?善意的主把这事也告诉了android小组,当我们在targetSdkVersion 低于23的app调用一个需要权限的函数时,这个权限如果被用户取消授权了的话,不抛出异常。但是他将啥都不干,结果导致函数返回值是null或者0.
别高兴的太早。尽管app不会调用这个函数时崩溃,返回值null或者0可能接下来依然导致崩溃。好消息(至少目前看来)是这类取消权限的情况比较少,我相信很少用户这么搞。如果他们这么办了,后果自负咯。但从长远看来,我相信还是会有大量用户会关闭一些权限。我们app不能再新设备完美运行这是不可接受的。怎样让他完美运行呢,你最好修改代码支持最新的权限系统,而且我建议你立刻着手搞起!代码没有成功改为支持最新运行时权限的app,不要设置targetSdkVersion 23 发布,否则你就有麻烦了。只有当你测试过了,再改为targetSdkVersion 23 。警告:现在你在android studio新建项目,targetSdkVersion 会自动设置为 23。如果你还没支持新运行时权限,我建议你首先把targetSdkVersion 降级到22
PROTECTION_NORMAL类权限
当用户安装或更新应用时,系统将授予应用所请求的属于 PROTECTION_NORMAL 的所有权限(安装时授权的一类基本权限)。这类权限包括:
android.permission.ACCESS_LOCATION_EXTRA_COMMANDSandroid.permission.ACCESS_NETWORK_STATEandroid.permission.ACCESS_NOTIFICATION_POLICYandroid.permission.ACCESS_WIFI_STATEandroid.permission.ACCESS_WIMAX_STATEandroid.permission.BLUETOOTHandroid.permission.BLUETOOTH_ADMINandroid.permission.BROADCAST_STICKYandroid.permission.CHANGE_NETWORK_STATEandroid.permission.CHANGE_WIFI_MULTICAST_STATEandroid.permission.CHANGE_WIFI_STATEandroid.permission.CHANGE_WIMAX_STATEandroid.permission.DISABLE_KEYGUARDandroid.permission.EXPAND_STATUS_BARandroid.permission.FLASHLIGHTandroid.permission.GET_ACCOUNTSandroid.permission.GET_PACKAGE_SIZEandroid.permission.INTERNETandroid.permission.KILL_BACKGROUND_PROCESSESandroid.permission.MODIFY_AUDIO_SETTINGSandroid.permission.NFCandroid.permission.READ_SYNC_SETTINGSandroid.permission.READ_SYNC_STATSandroid.permission.RECEIVE_BOOT_COMPLETEDandroid.permission.REORDER_TASKSandroid.permission.REQUEST_INSTALL_PACKAGESandroid.permission.SET_TIME_ZONEandroid.permission.SET_WALLPAPERandroid.permission.SET_WALLPAPER_HINTSandroid.permission.SUBSCRIBED_FEEDS_READandroid.permission.TRANSMIT_IRandroid.permission.USE_FINGERPRINTandroid.permission.VIBRATEandroid.permission.WAKE_LOCKandroid.permission.WRITE_SYNC_SETTINGScom.android.alarm.permission.SET_ALARMcom.android.launcher.permission.INSTALL_SHORTCUTcom.android.launcher.permission.UNINSTALL_SHORTCUT
只需要在AndroidManifest.xml中简单声明这些权限就好,安装时就授权。不需要每次使用时都检查权限,而且用户不能取消以上授权。
让你的app支持新运行时权限
是时候让我们的app支持新权限模型了,从设置compileSdkVersion and targetSdkVersion 为 23开始吧.
compileSdkVersion 23
defaultConfig {
targetSdkVersion 23
例子,我想用一下方法添加联系人。
private static final String TAG = "Contacts";
private void insertDummyContact() {
// Two operations are needed to insert a new contact.
ArrayList&ContentProviderOperation& operations = new ArrayList&ContentProviderOperation&(2);
// First, set up a new raw contact.
ContentProviderOperation.Builder op =
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null);
operations.add(op.build());
// Next, set the name for the contact.
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE,
monDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.monDataKinds.StructuredName.DISPLAY_NAME,
"__DUMMY CONTACT from runtime permissions sample");
operations.add(op.build());
// Apply the operations.
ContentResolver resolver = getContentResolver();
resolver.applyBatch(ContactsContract.AUTHORITY, operations);
} catch (RemoteException e) {
Log.d(TAG, "Could not add a new contact: " + e.getMessage());
} catch (OperationApplicationException e) {
Log.d(TAG, "Could not add a new contact: " + e.getMessage());
上面代码需要WRITE_CONTACTS权限。如果不询问授权,app就崩了。下一步像以前一样在AndroidManifest.xml添加声明权限。
&uses-permission android:name="android.permission.WRITE_CONTACTS"/&
下一步,不得不再写个方法检查有没有权限。如果没有弹个对话框询问用户授权。然后你才可以下一步创建联系人。
权限被分组了,如下表:
同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_CONTACTS被授权了,app也有READ_CONTACTS和GET_ACCOUNTS了。源码中被用来检查和请求权限的方法分别是Activity的checkSelfPermission和requestPermissions。这些方法api23引入。
final private int REQUEST_CODE_ASK_PERMISSIONS = 123;
private void insertDummyContactWrapper() {
int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
REQUEST_CODE_ASK_PERMISSIONS);
insertDummyContact();
如果已有权限,insertDummyContact()会执行。否则,requestPermissions被执行来弹出请求授权对话框,如下:
不论用户同意还是拒绝,activity的onRequestPermissionsResult会被回调来通知结果(通过第三个参数),grantResults,如下:
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_PERMISSIONS:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
insertDummyContact();
// Permission Denied
Toast.makeText(MainActivity.this, "WRITE_CONTACTS Denied", Toast.LENGTH_SHORT)
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
这就是新权限模型工作过程。代码真复杂但是只能去习惯它。。。为了让app很好兼容新权限模型,你不得不用以上类似方法处理所有需要的情况。如果你想捶墙,现在是时候了。。。
处理 “不再提醒”
如果用户拒绝某授权。下一次弹框,用户会有一个“不再提醒”的选项的来防止app以后继续请求授权。
如果这个选项在拒绝授权前被用户勾选了。下次为这个权限请求requestPermissions时,对话框就不弹出来了,结果就是,app啥都不干。这将是很差的用户体验,用户做了操作却得不到响应。这种情况需要好好处理一下。在请求requestPermissions前,我们需要检查是否需要展示请求权限的提示通过activity的shouldShowRequestPermissionRationale,代码如下:
final private int REQUEST_CODE_ASK_PERMISSIONS = 123;
private void insertDummyContactWrapper() {
int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {
showMessageOKCancel("You need to allow access to Contacts",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
REQUEST_CODE_ASK_PERMISSIONS);
requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
REQUEST_CODE_ASK_PERMISSIONS);
insertDummyContact();
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
当一个权限第一次被请求和用户标记过不再提醒的时候,我们写的对话框被展示。后一种情况,onRequestPermissionsResult 会收到PERMISSION_DENIED ,系统询问对话框不展示。
一次请求多个权限
当然了有时候需要好多权限,可以用上面方法一次请求多个权限。不要忘了为每个权限检查“不再提醒”的设置。修改后的代码:
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
private void insertDummyContactWrapper() {
List&String& permissionsNeeded = new ArrayList&String&();
final List&String& permissionsList = new ArrayList&String&();
if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
permissionsNeeded.add("GPS");
if (!addPermission(permissionsList, Manifest.permission.READ_CONTACTS))
permissionsNeeded.add("Read Contacts");
if (!addPermission(permissionsList, Manifest.permission.WRITE_CONTACTS))
permissionsNeeded.add("Write Contacts");
if (permissionsList.size() & 0) {
if (permissionsNeeded.size() & 0) {
// Need Rationale
String message = "You need to grant access to " + permissionsNeeded.get(0);
for (int i = 1; i & permissionsNeeded.size(); i++)
message = message + ", " + permissionsNeeded.get(i);
showMessageOKCancel(message,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
insertDummyContact();
private boolean addPermission(List&String& permissionsList, String permission) {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
// Check for Rationale Option
if (!shouldShowRequestPermissionRationale(permission))
如果所有权限被授权,依然回调onRequestPermissionsResult,我用hashmap让代码整洁便于阅读。
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
Map&String, Integer& perms = new HashMap&String, Integer&();
// Initial
perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.READ_CONTACTS, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.WRITE_CONTACTS, PackageManager.PERMISSION_GRANTED);
// Fill with results
for (int i = 0; i & permissions. i++)
perms.put(permissions[i], grantResults[i]);
// Check for ACCESS_FINE_LOCATION
if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
// All Permissions Granted
insertDummyContact();
// Permission Denied
Toast.makeText(MainActivity.this, "Some Permission is Denied", Toast.LENGTH_SHORT)
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
条件灵活的,你自己设置。有的情况,一个权限没有授权,就不可用;但是也有情况,能工作,但是表现的是有所限制的。对于这个我不做评价,你自己设计吧。
用兼容库使代码兼容旧版
以上代码在android 6.0以上运行没问题,但是23 api之前就不行了,因为没有那些方法。粗暴的方法是检查版本
if (Build.VERSION.SDK_INT &= 23) {
// Marshmallow+
// Pre-Marshmallow
但是太复杂,我建议用v4兼容库,已对这个做过兼容,用这个方法代替:
ContextCompat.checkSelfPermission()
被授权函数返回PERMISSION_GRANTED,否则返回PERMISSION_DENIED ,在所有版本都是如此。
ActivityCompat.requestPermissions()这个方法在M之前版本调用,OnRequestPermissionsResultCallback 直接被调用,带着正确的 PERMISSION_GRANTED或者 PERMISSION_DENIED 。
ActivityCompat.shouldShowRequestPermissionRationale()在M之前版本调用,永远返回false。
用v4包的这三方法,完美兼容所有版本!这个方法需要额外的参数,Context or Activity。别的就没啥特别的了。下面是代码:
private void insertDummyContactWrapper() {
int hasWriteContactsPermission = ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_CONTACTS);
if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.WRITE_CONTACTS)) {
showMessageOKCancel("You need to allow access to Contacts",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[] {Manifest.permission.WRITE_CONTACTS},
REQUEST_CODE_ASK_PERMISSIONS);
ActivityCompat.requestPermissions(MainActivity.this,
new String[] {Manifest.permission.WRITE_CONTACTS},
REQUEST_CODE_ASK_PERMISSIONS);
insertDummyContact();
后两个方法,我们也可以在Fragment中使用,用v13兼容包:FragmentCompat.requestPermissions() and FragmentCompat.shouldShowRequestPermissionRationale().和activity效果一样。
第三方库简化代码
以上代码真复杂。为解决这事,有许多第三方库已经问世了,速度真快!我试了很多最终找到了个满意的。他和我上面做的一样,只是简化了代码。灵活易扩展,试一下吧。如果不满足你可以找些其他的。
如果我的app还开着呢,权限被撤销了,会发生什么
权限随时可以被撤销。
当app开着的时候被撤消了会发生什么呢?我试过了发现这时app会突然终止 terminated。app中的一切都被简单粗暴的停止了,因为terminated!对我来说这可以理解,因为系统如果允许它继续运行(没有某权限),这会召唤弗雷迪到我的噩梦里。或许更糟…
我相信你对新权限模型已经有了清晰的认识。我相信你也意识到了问题的严峻。但是我们没得选择。新运行时权限已经在棉花糖中被使用了。我们没有退路。我们现在唯一能做的就是保证app适配新权限模型.欣慰的是只有少数权限需要运行时权限模型。大多数常用的权限,例如,网络访问,属于Normal Permission 在安装时自动会授权,当然你要声明,以后无需检查。因此,只有少部分代码你需要修改。
两个建议:
严肃对待新权限模型
如果你代码没支持新权限,不要设置targetSdkVersion 23 。尤其是当你在Studio新建工程时,不要忘了修改!
说一下代码修改。这是大事,如果代码结构被设计的不够好,你需要一些很蛋疼的重构。每个app都要被修正。如上所说,我们没的选择。。。列出所有你需要请求的权限所有情形,如果A被授权,B被拒绝,会发生什么。blah,blah。
祝重构顺利。把它列为你需要做的大事,从现在就开始着手做,以保证M正式发布的时候没有问题。
希望本文对你有用,祝你好运。。。
2 收藏&&|&&23
你可能感兴趣的文章
20 收藏,6k
3 收藏,1.3k
1 收藏,1.3k
收藏。。。在用户体验度上是越来越专业了
收藏。。。在用户体验度上是越来越专业了
请问楼主是怎么获取这些信息的?我把自己的手机刷到6.0后各种问题,一直找不到问题,直到看到了您的文章,才知道是因为targetapi的原因,多谢楼主了!还请指点一二
请问楼主是怎么获取这些信息的?我把自己的手机刷到6.0后各种问题,一直找不到问题,直到看到了您的文章,才知道是因为targetapi的原因,多谢楼主了!还请指点一二
分享到微博?
我要该,理由是:18:22 提问
Android 6.0项目,程序不能运行,继承AppCompatActivity就不行!!!
之前eclipse只是4.4,后来网上下载一些东西。按照设置都完成后。自己建了个demo也可以运行。唯独当我在MainActivity中改变extends Activity
变为extends AppCompatActivity。奇怪了,Demo直接闪屏报错。下面贴出damo项目:
这个是Demo项目列表
这个是activity内容:
这个是报错原因:
求大神告诉我什么原因!为什么继承了AppCompatActivity就不能运行了?这个是V7包下的。我检查了我引用的V7包,反编译看到里面是有这个的。
按赞数排序
clean一下project试试?
不要继续用eclipse了,改用Android Studio吧
其他相关推荐android6.0权限获取
更详细直观的文章:
工具:Android studio
编译环境要求:targetSdkVersion
& & compileSdkVersion
& & buildToolsVersion
& & defaultConfig {
& applicationId "com.example.user.xxxxxx"
& minSdkVersion 15
& targetSdkVersion 23
& versionCode 1
& versionName "1.0"
& & buildTypes {
& release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
测试手机系统:Android M(6.0)。
File file = new File("/sdcard/test");
& & if(file.mkdir())
& Toast.makeText(this, "Success",
Toast.LENGTH_LONG).show();
& Toast.makeText(this, "fail",
Toast.LENGTH_LONG).show();
当这段代码在android6.0系统上运行时,file.mkdir()一直都会返回false。(如果targetSdkVersion不是23就不会有这个问题)
原因:android6.0对应用获取权限增加了限制。有些权限不能单单只在AndroidManifest.xml声明,还要在代码中获取。
解决方法:
先获取权限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
PackageManager.PERMISSION_GRANTED) {
//申请WRITE_EXTERNAL_STORAGE权限
ActivityCompat.requestPermissions(this, new
String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
& WRITE_EXTERNAL_STORAGE_REQUEST_CODE);
这时会弹出对话框让我们选择是否要获取WRITE_EXTERNAL_STORAGE权限
接着在回调方法中根据我们的选择来做相应的处理
& & public void
onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
& super.onRequestPermissionsResult(requestCode,
permissions, grantResults);
& if (requestCode ==
WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
& & // Permission Granted
& & & } else
& & // Permission Denied
& & Toast.makeText(this,
"Permission Denied", Toast.LENGTH_LONG).show();
参考文章:
//android-6-permission/
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。Android6.0运行时权限处理透析
从 Android 6 0(API 级别 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。此方法可以简化应用安装过程,因为用户在安装或更新应用时不需要授予权限。它还让用户可以对应用的功能进行更多控制
6.0的权限定义:
从 Android 6.0(API 级别 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。此方法可以简化应用安装过程,因为用户在安装或更新应用时不需要授予权限。它还让用户可以对应用的功能进行更多控制;例如,用户可以选择为相机应用提供相机访问权限,而不提供设备位置的访问权限。用户可以随时进入应用的“Settings”屏幕调用权限。
权限分为两类:正常权限和危险权限(运行时权限,一下统称危险权限):
正常权限不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。
危险权限会授予应用访问用户机密数据的权限。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。
所有危险的 Android 系统权限都属于权限组。如果设备运行的是 Android 6.0(API 级别 23),并且应用的是 23 或更高版本,则当用户请求危险权限时系统会发生以下行为:
如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。
如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了权限,然后它又请求,系统将立即授予该权限。
任何权限都可属于一个权限组,包括正常权限和应用定义的权限。但权限组仅当权限危险时才影响用户体验。可以忽略正常权限的权限组。
如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的权限组,而不告知具体权限。 低于6.0的应用是 22的版本运行时是强制用户允许在AndroidManifest.xml声明的权限的,而6.0以后,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。
表 1.危险权限和权限组。
现在我们写一个小例子验证一下:
一、首先目标版本设置为23
强调,在低于23版本也可以使用V7包来调用检测权限和申请权限的方法,还能在回调接收到,但是,也会出现回调返回的状态不准的问题,在下就被这个坑坑了好久。targetSdkVersion如果是23以下,调用ActivityCompat.requestPermissions(),会弹出权限选择对话框,但是选择拒绝授权,onRequestPermissionsResult中的返回值却是PERMISSION_GRANTED,但选择同意授权,会把应用关闭重新开启当前activity,而不会调用onRequestPermissionsResult中的方法,所以不要在targetSdkVersion设置为23以下,又把complierSdkversion设置为23,这样会出现上述的问题。最好的方式是把targetSdkVersion也设置为23,就可以解决。一切完美运行。
详情请看:
按照如下设置:
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.qinglin.permissiondemo"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
二、声明需要的权限
首先,必须在AndroidManifest.xml文件里面声明需要的权限,否则在使用到相应的权限的时候强行申请权限会崩溃的,这也许是android系统的保护机制吧。
如果您的应用需要危险权限,则每次执行需要这一权限的操作时您都必须检查自己是否具有该权限。用户始终可以自由调用此权限,因此,即使应用昨天使用了相机,它不能假设自己今天仍具有该权限。
要检查您是否具有某项权限,请调用方法。例如,以下代码段显示了如何检查 Activity 是否具有在日历中进行写入的权限:
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
1、解释应用为什么需要权限
在某些情况下,您可能需要帮助用户了解您的应用为什么需要某项权限。例如,如果用户启动一个摄影应用,用户对应用要求使用相机的权限可能不会感到吃惊,但用户可能无法理解为什么此应用想要访问用户的位置或联系人。在请求权限之前,不妨为用户提供一个解释。请记住,您不需要通过解释来说服用户;如果您提供太多解释,用户可能发现应用令人失望并将其移除。
您可以采用的一个方法是仅在用户已拒绝某项权限请求时提供解释。如果用户继续尝试使用需要某项权限的功能,但继续拒绝权限请求,则可能表明用户不理解应用为什么需要此权限才能提供相关功能。对于这种情况,比较好的做法是显示解释。
为了帮助查找用户可能需要解释的情形,Android 提供了一个实用程序方法,即。如果应用之前请求过此权限但用户拒绝了请求,此方法将返回true。
注:如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了Don't ask again选项,此方法将返回false。如果设备规范禁止应用具有该权限,此方法也会返回false。
2、请求您需要的权限
如果应用尚无所需的权限,则应用必须调用一个方法,以请求适当的权限。应用将传递其所需的权限,以及您指定用于识别此权限请求的整型请求代码。此方法异步运行:它会立即返回,并且在用户响应对话框之后,系统会使用结果调用应用的回调方法,将应用传递的相同请求代码传递到。
以下代码可以检查应用是否具备读取用户联系人的权限,并根据需要请求该权限:
// 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.
// 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.
3、处理权限请求响应(onRequestPermissionsResult回调)
当应用请求权限时,系统将向用户显示一个对话框。当用户响应时,系统将调用应用的
方法,向其传递用户响应。您的应用必须替换该方法,以了解是否已获得相应权限。回调会将您传递的相同请求代码传递给 。例如,如果应用请求
访问权限,则它可能采用以下回调方法:
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PluginUtil.BAIDU_READ_LOCATION_STATE: {
// 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.
Toast.makeText(this, "用户允许此权限", Toast.LENGTH_LONG).show();
if(!ActivityCompat.shouldShowRequestPermissionRationale(this,permissions[0])){
//用户拒绝了此申请权限,并勾选了不再询问
ToastUtil.showToast(this,"点击权限,并打开全部权限");
PluginUtil.gotoAppDetailSettingIntent(this);
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this, "用户拒绝此权限", Toast.LENGTH_LONG).show();
// other 'case' lines to check for other
// permissions this app might request
系统显示的对话框说明了您的应用需要访问的;它不会列出具体权限。例如,如果您请求
权限,系统对话框只显示您的应用需要访问设备的联系人。用户只需要为每个权限组授予一次权限。如果您的应用请求该组中的任何其他权限(已在您的应用清单中列出),系统将自动授予应用这些权限。当您请求此权限时,系统会调用您的
回调方法,并传递 ,如果用户已通过系统对话框明确同意您的权限请求,系统将采用相同方式操作。
权限功能规范化(统一创建一个工具类PermissionUtils)
由于权限的功能会在多个地方反复使用,为了提高复用性,必然需要建立一个工具类,把常用方法写在这个工具类里面,方便调用。
以下是我的工具类封装PermissionUtils,共享给大家:
public class PermissionUtils { private static final String SCHEME = "package"; public static final int BAIDU_READ_LOCATION_STATE = 100; //自定义一个权限获取码,用于回调函数中做对应处理 public static void opendPermissionSetting(Context context) { String deviceInfo = getDeviceInfo(); LogUtil.i("hql", "deviceInfo:" + deviceInfo); if ("Xiaomi".equals(Build.MANUFACTURER)) { gotoMiuiPermission(context); } else if ("Meizu".equals(Build.MANUFACTURER)) { gotoMeizuPermission(context); } else if ("HUAWEI".equals(Build.MANUFACTURER)) { gotoHuaweiPermission(context); } else { if (Build.VERSION.SDK_INT &= 23) {// 用于判断是否为Android 6.0系统以上版本 gotoAppDetailSettingIntent(context); } else { gotoAppSettingIntent(context); } } } public static void needPermission(Activity context, int requestCode, String[] permissions) { try { // 申请一个(或多个)权限,并提供用于回调返回的获取码(用户定义) ActivityCompat.requestPermissions(context, permissions, requestCode); } catch (Exception e) { e.printStackTrace(); LogUtil.e("hql", e.toString()); } } // 判断权限集合是否都拥有,只要有一个缺少就返回false public static boolean isHasPermission(Context context, String... permissions) { for (String permission : permissions) { if (!isHasPermission(context, permission)) { } } } // 判断是否拥有权限 private static boolean isHasPermission(Context context, String permission) { Context mContext = context.getApplicationContext(); return ContextCompat.checkSelfPermission(mContext, permission) == PackageManager.PERMISSION_GRANTED; } /** * 跳转到miui的权限管理页面 */ public static void gotoMiuiPermission(Context context) { Intent i = new Intent("miui.intent.action.APP_PERM_EDITOR"); ComponentName componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); i.setComponent(componentName); i.putExtra("extra_pkgname", context.getPackageName()); try { context.startActivity(i); } catch (Exception e) { e.printStackTrace(); gotoAppSettingIntent(context); } } /** * 跳转到魅族的权限管理系统 */ public static void gotoMeizuPermission(Context context) { Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); try { context.startActivity(intent); } catch (Exception e) { e.printStackTrace(); gotoAppSettingIntent(context); } } /** * 华为的权限管理页面 */ public static void gotoHuaweiPermission(Context context) { try { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity");//华为权限管理 intent.setComponent(comp); context.startActivity(intent); } catch (Exception e) { e.printStackTrace(); gotoAppSettingIntent(context); } } /** * 打开应用详情页面intent */ public static void gotoAppDetailSettingIntent(Context context) { Intent localIntent = new Intent(); localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT &= 9) { localIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); localIntent.setData(Uri.fromParts(SCHEME, context.getPackageName(), null)); } else if (Build.VERSION.SDK_INT &= 8) { localIntent.setAction(Intent.ACTION_VIEW); localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails"); localIntent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName()); } context.startActivity(localIntent); } /** * 打开系统设置界面 */ public static void gotoAppSettingIntent(Context context) { Intent intent = new Intent(Settings.ACTION_SETTINGS);//系统设置界面 context.startActivity(intent); } }
Android 6.0运行时权限勾选不再询问后该如何处理?
当第一次请求权限申请被拒绝后再进行第二次申请时,对话框中会多出一个 不再询问 的复选框。如果勾选了该复选框并且拒绝请求,那么以后将无法再申请该权限。也就是说在调用 requestPermissions() 后,onRequestPermissionsResult() 会立刻被调用并且申请结果为 PERMISSION_DENIED 。 其实这个时候还是有一根救命稻草的。
1、首先需要判断用户是否勾选了不再询问,使用方法:ActivityCompat.shouldShowRequestPermissionRationale(this,permissions[0])。
2、ActivityCompat 位于 support.v7 包中,因为运行时权限是 6.0 的新特性,使用该类可以省略对版本的判断当权限申请被拒绝并且shouldShowRequestPermissionRationale() 返回 false 就表示勾选了不再询问。转到设置界面现在我们唯一能做的就是跳转到我们 App 的设置界面,让用户手动开启权限了。
权限最佳做法
应用如果一味要求用户提供授权,可能会让用户无所适从。如果用户发现应用难以使用,或者担心应用会滥用其信息,他们可能不愿意使用该应用,甚至会将其完全卸载。以下最佳做法有助于避免此类糟糕的用户体验。
考虑使用 intent
许多情况下,您可以使用以下两种方式之一来让您的应用执行某项任务。您可以将应用设置为要求提供权限才能执行操作。或者,您可以将应用设置为使用 intent,让其他应用来执行任务。
例如,假设应用需要使用设备相机才能够拍摄照片。应用可以请求
权限,以便允许其直接访问相机。然后,应用将使用 Camera API 控制相机并拍摄照片。利用此方法,您的应用能够完全控制摄影过程,并支持您将相机 UI 整合至应用中。
不过,如果您无需此类完全控制,则可以使用
intent 来请求图像。发送该 intent 时,系统会提示用户选择相机应用(如果没有默认相机应用)。用户使用选定的相机应用拍摄照片,该相机应用会将照片返回给应用的
同样,如果您需要拨打电话、访问用户的联系人或要执行其他操作,可以通过创建适当的 intent 来完成,或者您也可以请求相应的权限并直接访问相应的对象。每种方法各有优缺点。
如果使用权限:
您的应用可在您执行操作时完全控制用户体验。不过,如此广泛的控制会增加任务的复杂性,因为您需要设计适当的 UI。
系统会在运行或安装应用时各提示用户提供一次权限(具体取决于用户的 Android 版本)。之后,应用即可执行操作,不再需要用户进行其他交互。不过,如果用户不授予权限(或稍后撤销权限),您的应用将根本无法执行操作。
如果使用 intent:
您无需为操作设计 UI。处理 intent 的应用将提供 UI。不过,这意味着您无法控制用户体验。用户可能与您从未见过的应用交互。
如果用户没有适用于操作的默认应用,则系统会提示用户选择一款应用。如果用户未指定默认处理程序,则他们每次执行此操作时都必须处理一个额外对话框。
仅要求您需要的权限
每次您要求权限时,实际上是在强迫用户作出决定。您应尽量减少提出这些请求的次数。如果用户运行的是 Android 6.0(API 级别 23)或更高版本,则每次用户尝试要求提供权限的新应用功能时,应用都必须中断用户的操作并发起权限请求。如果用户运行的是较早版本的 Android,则在安装应用时需要为应用的每一权限请求给予授权;如果列表过长或看起来不合适,用户可能会决定不安装该应用。为此,您应尽量减少应用需要的权限数。
例如,很多情况下应用可以通过使用 intent 来避免请求权限。如果某项功能并非应用的核心功能,不妨考虑将相关工作交给其他应用来执行,如 中所述。
不要让用户感到无所适从
如果用户运行的是 Android 6.0(API 级别 23)或更高版本,则用户必须在应用运行时为其授权。如果您的应用一次要求用户提供多项权限,用户可能会感到无所适从并因此退出应用。您应根据需要请求权限。
某些情况下,一项或多项权限可能是应用所必需的。在这种情况下,合理的做法是,在应用启动之后立即要求提供这些权限。例如,如果您运行摄影应用,应用需要访问设备的相机。在用户首次启动应用时,他们不会对提供相机使用权限的要求感到惊讶。但是,如果同一应用还具备与用户联系人共享照片的功能,您不应在应用首次启动时要求用户提供
权限,而应等到用户尝试使用“共享”功能之后,再要求提供该权限。
如果应用提供了教程,则合理的做法是,在教程结束时请求提供应用的必要权限。
解释需要权限的原因:
系统在您调用
时显示的权限对话框将说明应用需要的权限,但不会解释为何需要这些权限。某些情况下,用户可能会感到困惑。因此,最好在调用
之前向用户解释应用需要相应权限的原因。
例如,摄影应用可能需要使用位置服务,以便能够为照片添加地理标签。通常,用户可能不了解照片能够包含位置信息,并且对摄影应用想要了解具体位置感到不解。因此在这种情况下,应用最好在调用
之前告知用户此功能的相关信息。
告知用户的一种办法是将这些请求纳入应用教程。这样,教程可以依次显示应用的每项功能,并在显示每项功能时解释需要哪些相应的权限。例如,摄影应用的教程可以演示其“与您的联系人共享照片”功能,然后告知用户需要为应用授予权限才能查看用户的联系人。然后,应用可以调用 ,要求用户提供该访问权限。当然,并非所有用户都会按照教程操作,因此您仍需在应用的正常操作期间检查和请求权限
测试两种权限模式
从 Android 6.0(API 级别 23)开始,用户是在运行时而不是在应用安装时授予或撤销应用权限。因此,您应在多种不同条件下测试应用。在低于 Android 6.0 的版本中,您可以认为如果应用得到运行,它就可以得到在应用清单中声明的全部权限。在新的权限模式中,这一推断不再成立。
以下提示可帮助您识别在运行 API 级别 23 或更高级别的设备上与权限有关的代码问题:
识别应用的当前权限和相关的代码路径。
在各种受权限保护的服务和数据中测试用户流程。
使用授予或撤销权限的各种组合进行测试。例如,相机应用可能会在清单中列出 、 和 。您应在测试应用时逐一打开和关闭这些权限,确保应用可以妥善处理所有权限配置。请记住,自 Android 6.0 起,用户可以打开或关闭任何应用的权限,即使面向 API 级别 22 或更低级别的应用也是如此。
工具从命令行管理权限:
按组列出权限和状态:
$ adb shell pm list permissions -d -g
授予或撤销一项或多项权限:
$ adb shell pm [grant|revoke]

我要回帖

更多关于 androidstudio运行apk 的文章

 

随机推荐