标签:show end 笔记 检测 小米 com text 设置 bool
最近在项目上因为6.0运行时权限吃了亏,发现之前对运行时权限的理解不足,决定回炉重造,重新学习一下Android Permission。
进入正题:
在Android系统中,权限分为三种:正常权限、危险权限和特殊权限:
SYSTEM_ALERT_WINDOW
和 WRITE_SETTINGS,从官方文档和实际应用可以知道,这两个权限必须在清单中声明,在需要使用的时候发送用户授权的intent,并在指定的方法中回调授权申请情况。(这里在后面详细分析)
READ_CONTACTS
权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。READ_CONTACTS
权限,然后它又请求 WRITE_CONTACTS
,系统将立即授予该权限。targetSdkVersion
是 22 或更低版本,则系统会在安装时要求用户授予权限。(参考自https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous)
危险权限和权限组-->https://developer.android.com/guide/topics/security/permissions.html#perm-groups
正常权限----------->https://developer.android.com/guide/topics/security/normal-permissions.html
所有Android版本的应用都需要在Androidmanifest.xml中声明正常权限和危险权限。当然在不同的版本中声明的影响会有所不同。
将<uses-permission>作为顶级<manifest>元素的子项加入到Androidmanifest.xml中:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.snazzyapp"> <uses-permission android:name="android.permission.SEND_SMS"/> <application ...> ... </application> </manifest>
根据系统的不同,对于危险权限有不同的处理方式:
那么在6.0下该怎么处理危险权限的申请呢?
可以分为三步:
1.检查权限
2.请求权限
3.请求回调处理
为了兼容旧版本,这里使用ContextCompat的API,详细的信息看http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0830/3387.html的“用兼容库使代码兼容旧版”部分
// Assume thisActivity is the current activity int permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR);
如果应用尚未所需的权限,则需要调用requestPermissions()方法请求权限。这个方法是异步执行的,它会调用一个系统标准Android对话框。会在onRequestPermissionsResult中获得授权结果:
// 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. } }
对于shouldShowRequsetPermissionRationale()方法的使用解释:
对于这个方法的使用场景,官方文档原话:
您可以采用的一个方法是仅在用户已拒绝某项权限请求时提供解释。如果用户继续尝试使用需要某项权限的功能,但继续拒绝权限请求,则可能表明用户不理解应用为什么需要此权限才能提供相关功能。对于这种情况,比较好的做法是显示解释。
当用户响应时,系统将调用应用的 onRequestPermissionsResult()
方法,向其传递用户响应。您的应用必须重写该方法,以了解是否已获得相应权限。回调会将您传递的相同请求代码传递给 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; } // other ‘case‘ lines to check for other // permissions this app might request } }
总结:到这里,需要说的是,当用户拒绝了权限申请后,应用应该采取适当的操作,例如弹出一个对话框解释为什么需要该权限。
当用户指示系统不再要求提供该权限时,这种情况下,应用使用requestPermissions()再次请求权限时,系统都会立即拒绝请求,并回调onRequestPermissionsResult()回调方法,并传递PERMISSION_DENIED。这意味着当您调用 requestPermissions()
时,您不能假设已经发生与用户的任何直接交互。
封装后的基类代码:BaseActivity
public class BaseActivity extends AppCompatActivity { private HashMap<Integer,RequestPermissionCallback> permissionCallbackHashMap=new HashMap<>(); private List<Integer> requestCodes=new ArrayList<>(); /** * 申请成功的回调接口 */ public interface RequestPermissionCallback{ void onRequestCallback(); } private String[] findDeniedPermissions(String...permissions){ List<String> permissionList=new ArrayList<>(); for (String perm:permissions){ if (ContextCompat.checkSelfPermission(this,perm)!= PackageManager.PERMISSION_GRANTED){ permissionList.add(perm); } } return permissionList.toArray(new String[permissionList.size()]); } protected void hasPermissions(int requestCode,RequestPermissionCallback callback,String... permissions){ permissionCallbackHashMap.put(requestCode,callback); requestCodes.add(requestCode); String[] deniedPermissions=findDeniedPermissions(permissions); if (deniedPermissions.length>0){ ActivityCompat.requestPermissions(this,permissions,requestCode); }else { callback.onRequestCallback(); } } private boolean verifyPermissions(int [] grantResults){ for (int result:grantResults){ if (result!=PackageManager.PERMISSION_GRANTED){ return false; } } return true; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); boolean grandResult=verifyPermissions(grantResults); for (int code:requestCodes){ if (code==requestCode){ if (grandResult){ permissionCallbackHashMap.get(code).onRequestCallback(); }else { ActivityCompat.requestPermissions(this,permissions, requestCode); } } } } }
BaseFragment:
public abstract class BaseFragment extends Fragment { private HashMap<Integer,BaseActivity.RequestPermissionCallback> permissionCallbackHashMap=new HashMap<>(); private List<Integer> requestCodes=new ArrayList<>(); private Context mContext; @Override public void onAttach(Context context) { super.onAttach(context); this.mContext=mContext; } public Context getmContext() { return mContext; } /** * 申请成功的回调接口 */ public interface RequestPermissionCallback{ void onRequestCallback(); } private String[] findDeniedPermissions(String...permissions){ List<String> permissionList=new ArrayList<>(); for (String perm:permissions){ if (ContextCompat.checkSelfPermission(getmContext(),perm)!= PackageManager.PERMISSION_GRANTED){ permissionList.add(perm); } } return permissionList.toArray(new String[permissionList.size()]); } protected void hasPermissions(int requestCode, BaseActivity.RequestPermissionCallback callback, String... permissions){ permissionCallbackHashMap.put(requestCode,callback); requestCodes.add(requestCode); String[] deniedPermissions=findDeniedPermissions(permissions); if (deniedPermissions.length>0){ /** * 这里要直接用Fragment的requestPermissions(), * 使用ActivityCompat的requestPermissions()会回调到Activity的onRequestPermissionsResult() */ requestPermissions(permissions,requestCode); }else { callback.onRequestCallback(); } } private boolean verifyPermissions(int [] grantResults){ for (int result:grantResults){ if (result!=PackageManager.PERMISSION_GRANTED){ return false; } } return true; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); boolean grandResult=verifyPermissions(grantResults); for (int code:requestCodes){ if (code==requestCode){ if (grandResult){ permissionCallbackHashMap.get(code).onRequestCallback(); }else { requestPermissions(permissions, requestCode); } } } } }
※这里有一个坑:在Fragment中申请权限要用Fragment自带的requestPermissions,否则会导致结果回调到activity的onRequestPermissionsResult而收不到申请结果。
关于Fragment嵌套的情况看:http://blog.csdn.net/qfanmingyiq/article/details/52561658
如果应用非要申请某个权限的话,可以用以下代码:(慎用,有引起用户反感的可能):
public abstract class BaseActivity extends AppCompatActivity { private HashMap<Integer,RequestPermissionCallback> permissionCallbackHashMap=new HashMap<>(); private List<Integer> requestCodes=new ArrayList<>(); /** * 申请成功的回调接口 */ public interface RequestPermissionCallback{ void onRequestCallback(); } private String[] findDeniedPermissions(String...permissions){ List<String> permissionList=new ArrayList<>(); for (String perm:permissions){ if (ContextCompat.checkSelfPermission(this,perm)!= PackageManager.PERMISSION_GRANTED){ permissionList.add(perm); } } return permissionList.toArray(new String[permissionList.size()]); } protected void hasPermissions(int requestCode, RequestPermissionCallback callback, String... permissions){ permissionCallbackHashMap.put(requestCode,callback); requestCodes.add(requestCode); String[] deniedPermissions=findDeniedPermissions(permissions); if (deniedPermissions.length>0){ ActivityCompat.requestPermissions(this,permissions,requestCode); }else { callback.onRequestCallback(); } } private boolean verifyPermissions(int [] grantResults){ for (int result:grantResults){ if (result!=PackageManager.PERMISSION_GRANTED){ return false; } } return true; } /** * 当被用户拒绝授权并且出现不再提示时(小米在第一拒绝后再次申请就不会出现请求对话框了,不过对于call phone权限不同,这点再看), * shouldShowRequestPermissionRationale也会返回false,若实在必须申请权限时可以使用方法检测, * 可能会引起用户的厌恶感,慎用 * @param permissions * @return */ protected boolean verifyShouldShowRequestPermissions(String[] permissions){ for (String permission:permissions){ if (!ActivityCompat.shouldShowRequestPermissionRationale(this,permission)){ return false; } } return true; } /** * 显示提示信息,与verifyShouldShowRequestPermissions(String[] permissions)搭配使用,提醒用户需要授权,并打开应用详细设置页面 * * @since 2.5.0 * */ protected void showMissingPermissionDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("提示"); builder.setMessage("当前应用缺少必要权限。\\n\\n请点击\\\"设置\\\"-\\\"权限\\\"-打开所需权限。"); // // 拒绝, 退出应用 // builder.setNegativeButton(R.string.cancel, // listener // ); builder.setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { startAppSettings(); } }); builder.setCancelable(false); builder.show(); } /** * 启动应用的设置 * * @since 2.5.0 * */ private void startAppSettings() { Intent intent = new Intent( Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); boolean grandResult=verifyPermissions(grantResults); for (int code:requestCodes){ if (code==requestCode){ if (grandResult){ permissionCallbackHashMap.get(code).onRequestCallback(); }else { if (verifyShouldShowRequestPermissions(permissions)){ final int code1=code; final String[] permission=permissions; AlertDialog.Builder builder=new AlertDialog.Builder(this); builder.setMessage("定位功能需要定位权限,请授权。") .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { ActivityCompat.requestPermissions(BaseActivity.this,permission, code1); } }); }else { showMissingPermissionDialog(); } } } } } }
这里需要指出的是,shouldShowRequestPermissionRationale()被放到onRequestPermissionsResult中进行判断,这样就能在系统直接返回PERMISSION_DENIED这个回调结果时判断是否应该弹出解释窗口或者在用户选择了不再提示之后弹出应用设置页面。
这里有一段关于官方文档的原话:
在某些情况下,您可能需要帮助用户了解您的应用为什么需要某项权限。例如,如果用户启动一个摄影应用,用户对应用要求使用相机的权限可能不会感到吃惊,但用户可能无法理解为什么此应用想要访问用户的位置或联系人。在请求权限之前,不妨为用户提供一个解释。请记住,您不需要通过解释来说服用户;如果您提供太多解释,用户可能发现应用令人失望并将其移除。
所以这个方法要慎用。
代码地址:https://github.com/liberty2015/PermissionStudy
参考:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0830/3387.html
http://blog.csdn.net/lmj623565791/article/details/50709663
http://blog.csdn.net/qfanmingyiq/article/details/52561658
https://developer.android.com/guide/topics/security/permissions.html
https://developer.android.com/training/permissions/requesting.html
标签:show end 笔记 检测 小米 com text 设置 bool
原文地址:http://www.cnblogs.com/libertycode/p/6122741.html