Android 6.x 运行时权限

有个队友离职了,导致最近忙的手打后脚跟,也没时间写博客了。

好不容易这两天闲了一点,赶快更新一篇。

关于Android6.0以上运行时权限,做过的估计都头疼了两天,做完了之后发现也不过如此。

我在做的时候却因为种种原因,导致已经发版上线的版本在Android6.0以上系统出现问题不能正常使用,确切的说不包含Android6.0,因为6.0的手机有的可以使用。不得已只有紧急再发一版,把android:targetSdkVersion="23"改为android:targetSdkVersion="22"表示我们还没有准备好适配6.0以上的手机。

出现这个问题的原因一部分是因为我们的测试机最高的系统版本就是6.0,包括华为P9、mate8,还有同事的一部小米,上线之前测试均能正常使用。另一本分原因是当时本来说是下一版再适配6.0,而我的一位队友却已经把sdk替换到了23,在测试机上运行之后发现读取短信验证码和系统联系人的功能不能使用,然后就在网上找找代码搞了一下发现没有什么问题就发版上线了。上线之后收到用户投诉之后才发现事情的严重性,只得紧急发版并且跟新提示6.0以上用户必须卸载后重新下载安装,因为我们的应用打开时在引导页要拿到用户的设备id,并且想持续更新应用的话还需要拿到sd卡存储的写入权限,而线上的版本对于6.0以上的手机并没有像用户申请这两个权限,所以Android6.0的手机必须重新安装。

对于为什么在Android6.0手机上并没有出现的权限问题而到了用户那里却出现了呢?终于在同时那里找到了两个Android6.0.1的手机发现了同样的问题,调试之后发现问题确实很严重,个人看法是Android6.0以上的系统越高对权限问题的处理就会越严格。问题已经发生只能正视它然后解决它,网上看了一些帖子,下了两个demo,终于找到了针对于我的程序能够比较好处理的办法。

言归正传,由于Android的特殊性,使用Android手机的用户一旦安装了一些不怀好意的app,开发者能做的事情确实是有点多了,对这部分手机的用户造成的隐患很大。而google那边也一直在致力于解决这个问题,终于Android6.0棉花糖推出,app将不会在安装时一刀切的授予应用权限,取而代之的是一些对用户来说比较危险的权限,app不得不在运行时一个一个询问用户,用户授予之后才能够获得。

一般权限,PROTECTION_NORMAL类权限,应用安装时自动授予,不需询问授权

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
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_WIMAX_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.CHANGE_WIMAX_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET_PACKAGE_SIZE
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.SUBSCRIBED_FEEDS_READ
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT

危险权限,应用中需要此类权限是需要向用户申请授权,从下图大家可以看到这类权限是分组的,目前来看同一组内的某一权限被授权之后同一组的其他权限也会同样被授权,但是我不建议大家依赖于这个做法图省事,以后说不定这个分组会取消。还是用到了哪个权限就老老实实去向用户申请比较好。


还有几个特殊权限和其他权限,一般用不到,这里就不多说了。

首先,大家可以先对自己工程里用到的权限进行分类分组,像这样:


然后是权限问题的具体处理,代码献上:

需要申请权限的页面:

	@Override
	protected void onResume() {
		super.onResume();
		// 缺少权限时, 进入权限配置页面
		if (mPermissionsChecker.lacksPermissions(PERMISSIONS)) {
			startPermissionsActivity();
		}else {
			doNext();
		}
	}
	
	private void startPermissionsActivity() {
		PermissionsActivity.startActivityForResult(this, getResources().getString(R.string.string_help_text2), REQUEST_CODE,
				PERMISSIONS);
	}
权限处理中间页面:
public class PermissionsActivity extends BaseActivity {
	public static final int PERMISSIONS_GRANTED = 0;
	// 权限授权
	public static final int PERMISSIONS_DENIED = 1;
	// 权限拒绝
	private static final int PERMISSION_REQUEST_CODE = 0;
	// 系统权限管理页面的参数
	private static final String EXTRA_PERMISSIONS = "me.szf.permission.extra_permission";
	private static final String HELP_TEXT = "me.szf.permission.help_text";
	// 权限参数
	private static final String PACKAGE_URL_SCHEME = "package:";
	// 权限检测器
	private PermissionsChecker mChecker;
	private boolean isRequireCheck;
	private String stringHelpText = "";

	// 是否需要系统权限检测
	// 启动当前权限页面的公开接口
	public static void startActivityForResult(Activity activity, String helpText,
			int requestCode, String... permissions) {
		Intent intent = new Intent(activity, PermissionsActivity.class);
		intent.putExtra(EXTRA_PERMISSIONS, permissions);
		intent.putExtra(HELP_TEXT, helpText);
		ActivityCompat.startActivityForResult(activity, intent, requestCode,
				null);
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (getIntent() == null || !getIntent().hasExtra(EXTRA_PERMISSIONS)) {
			throw new RuntimeException("PermissionsActivity需要使用静态startActivityForResult方法启动!");
		}
		setContentView(R.layout.activity_permissions);
		mChecker = new PermissionsChecker(this);
		isRequireCheck = true;
	}

	@Override
	protected void onResume() {
		super.onResume();
		if (isRequireCheck) {
			String[] permissions = getPermissions();
			stringHelpText = getIntent().getStringExtra(HELP_TEXT);
			if (mChecker.lacksPermissions(permissions)) {
				requestPermissions(permissions);// 请求权限
			} else {
				allPermissionsGranted();// 全部权限都已获取
			}
		} else {
			isRequireCheck = true;
		}
	}

	// 返回传递的权限参数
	private String[] getPermissions() {
		return getIntent().getStringArrayExtra(EXTRA_PERMISSIONS);
	}

	// 请求权限兼容低版本
	private void requestPermissions(String... permissions) {
		ActivityCompat.requestPermissions(this, permissions,
				PERMISSION_REQUEST_CODE);
	}

	// 全部权限均已获取
	private void allPermissionsGranted() {
		setResult(PERMISSIONS_GRANTED);
		finish();
	}

	/**
	 * 用户权限处理, 
	 * 如果全部获取, 则直接过. 
	 * 如果权限缺失, 则提示Dialog.
	 * @param requestCode 请求码
	 * @param permissions 权限 
	 * @param grantResults 结果
	 */
	@Override
	public void onRequestPermissionsResult(int requestCode,
			String[] permissions, int[] grantResults) {
		if (requestCode == PERMISSION_REQUEST_CODE
				&& hasAllPermissionsGranted(grantResults)) {
			isRequireCheck = true;
			allPermissionsGranted();
		} else {
			isRequireCheck = false;
			showMissingPermissionDialog();
		}
	}

	// 含有全部的权限
	private boolean hasAllPermissionsGranted(int[] grantResults) {
		for (int grantResult : grantResults) {
			if (grantResult == PackageManager.PERMISSION_DENIED) {
				return false;
			}
		}
		return true;
	}

	// 显示缺失权限提示
	private void showMissingPermissionDialog() {
		
		int width = getWindowManager().getDefaultDisplay().getWidth();
		LayoutInflater inflater = LayoutInflater.from(PermissionsActivity.this);
		View view = inflater.inflate(R.layout.permission_dialog, null);

		TextView tvContent = (TextView) view
				.findViewById(R.id.tv_permission_content);

		LinearLayout llContainer = (LinearLayout) view
				.findViewById(R.id.ll_permission_container);

		Button btnSetting = (Button) view.findViewById(R.id.btn_setting);
		Button btnQuit = (Button) view.findViewById(R.id.btn_quit);

		final Dialog dialog = new Dialog(PermissionsActivity.this,
				R.style.dialog_view);
		dialog.setCancelable(false);
		dialog.setContentView(llContainer, new LinearLayout.LayoutParams(
				(int) (width * 0.8), LinearLayout.LayoutParams.WRAP_CONTENT));
		tvContent.setText(stringHelpText);
		btnSetting.setOnClickListener(new android.view.View.OnClickListener() {

					@Override
					public void onClick(View v) {
						dialog.dismiss();
						startAppSettings();
					}
				});
		
		btnQuit.setOnClickListener(new android.view.View.OnClickListener() {

			@Override
			public void onClick(View v) {
				dialog.dismiss();
				setResult(PERMISSIONS_DENIED);
				sendBroadcast(new Intent("com.pica.mobilebusiness.exit"));
			}
		});

		dialog.show();
		
		
		
		/*final ReminderDialog2 dialog = new ReminderDialog2(
				PermissionsActivity.this, stringHelpText);
		dialog.setTitle(getResources().getString(R.string.help));
		dialog.setEnterTextLeft(getResources().getString(R.string.quit));
		dialog.setEnterTextRight(getResources().getString(R.string.setting));
		dialog.setCancelable(false);
		dialog.getEnterButtonLeft().setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				dialog.dismiss();
				setResult(PERMISSIONS_DENIED);
				sendBroadcast(new Intent("com.pica.mobilebusiness.exit"));
			}
		});
		dialog.getEnterButtonRight().setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				dialog.dismiss();
				startAppSettings();
			}
		});
		dialog.show();*/
	}

	// 启动应用的设置
	private void startAppSettings() {
		Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
		intent.setData(Uri.parse(PACKAGE_URL_SCHEME + getPackageName()));
		startActivity(intent);
	}
}
权限检测器:
public class PermissionsChecker {
	private final Context mContext;

	public PermissionsChecker(Context context) {
		mContext = context.getApplicationContext();
	}

	// 判断权限集合
	public boolean lacksPermissions(String... permissions) {
		for (String permission : permissions) {
			if (lacksPermission(permission)) {
				return true;
			}
		}
		return false;
	}

	// 判断是否缺少权限 
	private boolean lacksPermission(String permission) {
		return ContextCompat.checkSelfPermission(mContext, permission) == PackageManager.PERMISSION_DENIED;
	}
}

大家可以根据自己项目的实际情况作为参考,demo地址: http://download.csdn.net/detail/cunjicsdn/9695519


                                                                                       



猜你喜欢

转载自blog.csdn.net/cunjicsdn/article/details/53374595