下面这张图展示了 Android 系统的主要组成部分: 可以很明显看出,Android 系统架构由 5 部分组成,分别是:Linux Kernel、Android Runtime、Libraries、Application Framework、Applications。第二部分将详细介绍这 5 个部分。
现在我们拿起手术刀来剖析各个部分。其实这部分 SDK 文档已经帮我们做得很好了,我们要做的就是拿来主义,然后再加上自己理解。下面自底向上分析各层。
Android 基于 Linux 2.6 提供核心系统服务,例如:安全、内存管理、进程管理、网络堆栈、驱动模型。Linux Kernel 也作为硬件和软件之间的抽象层,它隐藏具体硬件细节而为上层提供统一的服务。 如果你学过计算机网络知道 OSI/RM,就会知道分层的好处就是使用下层提供的服务而为上层提供统一的服务,屏蔽本层及以下层的差异,当本层及以下层发生了变化不会影响到上层。也就是说各层各司其职,各层提供固定的 SAP(Service Access Point),专业点可以说是高内聚、低耦合。 如果你只是做应用开发,就不需要深入了解 Linux Kernel 层。
Android 包含一个核心库的集合,提供大部分在 Java 编程语言核心类库中可用的功能。每一个 Android 应用程序是 Dalvik 虚拟机中的实例,运行在他们自己的进程中。Dalvik 虚拟机设计成,在一个设备可以高效地运行多个虚拟机。Dalvik 虚拟机可执行文件格式是. dex,dex 格式是专为 Dalvik 设计的一种压缩格式,适合内存和处理器速度有限的系统。
大多数虚拟机包括 JVM 都是基于栈的,而 Dalvik 虚拟机则是基于寄存器的。两种架构各有优劣,一般而言,基于栈的机器需要更多指令,而基于寄存器的机器指令更大。dx 是一套工具,可以將 Java .class 转换成 .dex 格式。一个 dex 文件通常会有多个. class。由于 dex 有時必须进行最佳化,会使文件大小增加 1-4 倍,以 ODEX 结尾。
Dalvik 虚拟机依赖于 Linux 内核提供基本功能,如线程和底层内存管理。
Android 包含一个 C/C++ 库的集合,供 Android 系统的各个组件使用。这些功能通过 Android 的应用程序框架(application framework)暴露给开发者。下面列出一些核心库: - 系统 C 库——标准 C 系统库(libc)的 BSD 衍生,调整为基于嵌入式 Linux 设备 - 媒体库——基于 PacketVideo 的 OpenCORE。这些库支持播放和录制许多流行的音频和视频格式,以及静态图像文件,包括 MPEG4、 H.264、 MP3、 AAC、 AMR、JPG、 PNG - 界面管理——管理访问显示子系统和无缝组合多个应用程序的二维和三维图形层 - LibWebCore——新式的 Web 浏览器引擎, 驱动 Android 浏览器和内嵌的 web 视图 - SGL——基本的 2D 图形引擎 - 3D 库——基于 OpenGL ES 1.0 APIs 的实现。库使用硬件 3D 加速或包含高度优化的 3D 软件光栅 - FreeType ——位图和矢量字体渲染 - SQLite ——所有应用程序都可以使用的强大而轻量级的关系数据库引擎
通过提供开放的开发平台,Android 使开发者能够编制极其丰富和新颖的应用程序。开发者可以自由地利用设备硬件优势、访问位置信息、运行后台服务、设置闹钟、向状态栏添加通知等等,很多很多。
开发者可以完全使用核心应用程序所使用的框架 APIs。应用程序的体系结构旨在简化组件的重用,任何应用程序都能发布他的功能且任何其他应用程序可以使用这些功能(需要服从框架执行的安全限制)。这一机制允许用户替换组件。
所有的应用程序其实是一组服务和系统,包括: - 视图(View)——丰富的、可扩展的视图集合,可用于构建一个应用程序。包括包括列表、网格、文本框、按钮,甚至是内嵌的网页浏览器 - 内容提供者(Content Providers)——使应用程序能访问其他应用程序(如通讯录)的数据,或共享自己的数据 - 资源管理器(Resource Manager)——提供访问非代码资源,如本地化字符串、图形和布局文件 - 通知管理器(Notification Manager)——使所有的应用程序能够在状态栏显示自定义警告 - 活动管理器(Activity Manager)——管理应用程序生命周期, 提供通用的导航回退功能
Android 装配一个核心应用程序集合,包括电子邮件客户端、SMS 程序、日历、地图、浏览器、联系人和其他设置。所有应用程序都是用 Java 编程语言写的。更加丰富的应用程序有待我们去开发!
从上面我们知道 Android 的架构是分层的,非常清晰,分工很明确。Android 本身是一套软件堆叠 (Software Stack),或称为「软件叠层架构」,叠层主要分成三层:操作系统、中间件、应用程序。从上面我们也看到了开源的力量,一个个熟悉的开源软件在这里贡献了自己的一份力量。
- public void showBluetoothCheckDialog(final Context context) {
- dialog = new AlertDialog.Builder(context).create();// 创建一个AlertDialog对象viewReconnectBluetooth = ((Activity) context).getLayoutInflater().inflate(R.layout.dialog_confirm_bluetooth, null);// 自定义布局messagetext = (TextView) viewReconnectBluetooth.findViewById(R.id.message);dialog.setCancelable(false);// dialog弹出后会点击屏幕或物理返回键,dialog不消失messagetext.setText("当前是蓝牙模式,但是蓝牙设备已断开连接,请选择操作。");View reconnect = viewReconnectBluetooth.findViewById(R.id.reconnect);reconnect.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(context,
- DeviceListActivity.class);context.startActivity(intent);dialog.dismiss();}
- });View cancel = viewReconnectBluetooth.findViewById(R.id.cancel);cancel.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // nothing to do
- dialog.dismiss();}
- });View shutdown = viewReconnectBluetooth.findViewById(R.id.shutdown);shutdown.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // Auto-generated method stub
- Util.savePreferences(Constants.BLUE_TOOTH,0, context);bluetoothAdapter.disable();app.setVersion(app.getVersion() +1);dialog.dismiss();Toast.makeText(context,"蓝牙模式已关闭", Toast.LENGTH_SHORT).show();}
- });dialog.setView(viewReconnectBluetooth,0,0,0,0);// 把自定义的布局设置到dialog中,注意,布局设置一定要在show之前。从第二个参数分别填充内容与边框之间左、上、右、下、的像素// Service&Application弹出对话框或WindowManager添加view时,要设置window type为TYPE_SYSTEM_ALERT
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);dialog.show();// 一定要先show出来再设置dialog的参数,不然就不会改变dialog的大小了}
MediaStore.Images.Media.getBitmap(ContentResolver cr,Uri url) 方法可以从一个已知的图片 Uri 中获得图片的 bitmap 对象,其中 ContentResolver 通常可以通过在 Activity 中调用的 getContentResolver() 方法中获取。Uri 地址就是类似 content://media/external/images/media/2 的地址,也就是 Content Provider 定义的地址形式。但是这种获取 bitmap 的方式在图片较大的时候并不可取。为什么呢?
从 getBitmap 的参数可以看出,我们没有传递所要得到的 bitmap 大小信息,那么 getBitmap 的返回的 bitmap 究竟有多大我们就无从知晓,完全取决于 getBitmap 本身,最乐观的结果是 getBitmap 能智能的判断我们的需求,返回一个合适的 bitmap,但是可能性很小。到底 getBitmap 中做了什么还是直接去看他的实现源码吧:
android.provider.MediaStore 中找到 Images.Media 的 getBitmap 方法:
- public static finalBitmapgetBitmap(ContentResolver cr, Uri url)throwsFileNotFoundException, IOException {
- InputStream input = cr.openInputStream(url);
- Bitmap bitmap = BitmapFactory.decodeStream(input);
- input.close();returnbitmap;
- }
其实它很简单很粗暴,返回的是原始大小的 bitmap。
MediaStore.Images.Media 自身的 getBitmap 不是很可靠。那我们来自定义一个 getBitmap 吧:
取名叫 getThumbnail,他能返回一个指定大小的 bitmap:
- public staticBitmapgetThumbnail(Uri uri,intsize)throwsFileNotFoundException, IOException{
- InputStream input =this.getContentResolver().openInputStream(uri);
- BitmapFactory.Options onlyBoundsOptions =newBitmapFactory.Options();
- onlyBoundsOptions.inJustDecodeBounds =true;
- onlyBoundsOptions.inDither=true;//optionalonlyBoundsOptions.inPreferredConfig=Bitmap.Config.ARGB_8888;//optionalBitmapFactory.decodeStream(input,null, onlyBoundsOptions);
- input.close();if((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1))return null;intoriginalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;doubleratio = (originalSize > size) ? (originalSize / size) :1.0;
- BitmapFactory.Options bitmapOptions =newBitmapFactory.Options();
- bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
- bitmapOptions.inDither=true;//optionalbitmapOptions.inPreferredConfig=Bitmap.Config.ARGB_8888;//optionalinput =this.getContentResolver().openInputStream(uri);
- Bitmap bitmap = BitmapFactory.decodeStream(input,null, bitmapOptions);
- input.close();returnbitmap;
- }private static int getPowerOfTwoForSampleRatio(doubleratio){intk = Integer.highestOneBit((int)Math.floor(ratio));if(k==0)return 1;else returnk;
- }
- private String getPath(Uri uri) {
- String[] projection = {
- MediaStore.Video.Media.DATA
- };
- Cursor cursor = managedQuery(uri, projection, null, null, null);
- int column_index = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
- cursor.moveToFirst();
- return cursor.getString(column_index);
- }
- Uri uri = Uri.parse(path);
- Stringpath =file.getPath()
- File file=new File(path)
- /**
- * 将图片Bitmap保存到本地返回File
- * @parambmp
- */
- publicFilesaveBmpToFile(Bitmap bmp) {// 将图片保存到本地时进行压缩开始时间Log.v("TakePhoteActivity.this","将图片保存到本地时进行压缩开始时间"+getTime());
- ByteArrayOutputStream baos =newByteArrayOutputStream();intoptions =100;
- bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);// 系统时间
- longdateTaken = System.currentTimeMillis();
- SimpleDateFormat formatter =newSimpleDateFormat("yyyy-MM-dd HH.mm.ss.SSS");
- String formatStr =formatter.format(dateTaken);// 图像名称String filename = formatStr +".JPEG";
- File parent =newFile(PATH);if(!parent.exists()) {
- parent.mkdirs();
- }
- File file =newFile(PATH, filename);if(file.exists()){
- file.delete();
- }try{
- FileOutputStream fos =newFileOutputStream(file);
- fos.write(baos.toByteArray());
- fos.flush();
- fos.close();
- }catch(Exception e) {
- e.printStackTrace();
- }returnfile;
- }
- /**
- * 存储图像并将信息添加入媒体数据库
- */private Uri insertImage(ContentResolver cr, String name, long dateTaken,
- String directory, String filename, Bitmap source, byte[] jpegData) {
- OutputStream outputStream = null;String filePath = directory + filename;try {
- File dir = new File(directory);if (!dir.exists()) {
- dir.mkdirs();}
- File file = new File(directory, filename);if (file.createNewFile()) {
- outputStream = new FileOutputStream(file);if (source != null) {
- source.compress(Bitmap.CompressFormat.JPEG,100, outputStream);} else {
- outputStream.write(jpegData);}
- }
- } catch (FileNotFoundException e) {
- Log.e(TAG, e.getMessage());return null;} catch (IOException e) {
- Log.e(TAG, e.getMessage());return null;} finally {
- if (outputStream != null) {
- try {
- outputStream.close();} catch (Throwable t) {
- }
- }
- }
- try {
- ContentValues values = new ContentValues(7);values.put(MediaStore.Images.Media.TITLE, name);values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken);values.put(MediaStore.Images.Media.MIME_TYPE,"image/JPEG");values.put(MediaStore.Images.Media.DATA, filePath);return cr.insert(IMAGE_URI, values);} catch (Exception e) {/**
- * @Description: 用户禁用了下载服务时,通过如下代码进入"启用/禁用"下载管理程序界面
- * @Author: BG235144/AMOSCXY
- * @Data 2017/3/2 15:34
- */String packageName ="com.android.providers.downloads";Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);intent.setData(Uri.parse("package:"+ packageName));startActivity(intent);Toast.makeText(TakePhoteActivity.this,"请开启下载服务!", Toast.LENGTH_SHORT).show();ContentValues values = new ContentValues(7);values.put(MediaStore.Images.Media.TITLE, name);values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken);values.put(MediaStore.Images.Media.MIME_TYPE,"image/JPEG");values.put(MediaStore.Images.Media.DATA, filePath);return cr.insert(IMAGE_URI, values);}
- }
- // 图片存储路径
- public static finalString PATH = Environment.getExternalStorageDirectory()
- .toString() +"/AndroidMedia/";// 系统时间
- longdateTaken = System.currentTimeMillis();
- SimpleDateFormat formatter =newSimpleDateFormat("yyyy-MM-dd HH.mm.ss.SSS");
- String formatStr = formatter.format(dateTaken);// 图像名称String filename = formatStr +".JPEG";byte[] jpegData = Bitmap2Bytes(bitmap);// 保存图片Uri uri = insertImage(getContentResolver(), filename, dateTaken, PATH, bitmap, jpegData);/**
- * Bitmap → byte[]
- * @parambm
- * @return*/
- public byte[]Bitmap2Bytes(Bitmap bm) {
- ByteArrayOutputStream baos =newByteArrayOutputStream();
- bm.compress(Bitmap.CompressFormat.PNG,100, baos);returnbaos.toByteArray();
- }
- Bitmap bt = BitmapFactory.decodeFile(file.getPath()); //图片地址
- Bitmap mBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
- Uri uri = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(), bitmap, null,null));
- privateBitmapzoomImage(Bitmap bm,intnewWidth,intnewHeight){// 获得图片的宽高
- intwidth = bm.getWidth()intheight = bm.getHeight();// 计算缩放比例
- floatscaleWidth = ((float) newWidth) / width;floatscaleHeight = ((float) newHeight) / height;// 取得想要缩放的matrix参数Matrix matrix =newMatrix();
- matrix.postScale(scaleWidth, scaleHeight);// 得到新的图片Bitmap newbm = Bitmap.createBitmap(bm,0,0, width, height, matrix,true);returnnewbm;
- }
- privateBitmapcomp(Bitmap image) {
- Matrix matrix =newMatrix();
- Bitmap bitmap = image;while(bitmap.getWidth() >512|| bitmap.getHeight() >1024) {
- matrix.setScale(0.9f,0.9f);
- bitmap = Bitmap.createBitmap(bitmap,0,0, bitmap.getWidth(), bitmap.getHeight(), matrix,true);
- }returnbitmap;
- }
- /**
- * 将图片保存到本地时进行压缩
- *
- * @parambmp
- */
- publicBitmapcompressBmpToFile(Bitmap bmp) {
- ByteArrayOutputStream baos =newByteArrayOutputStream();intoptions =60;
- bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);// 系统时间
- longdateTaken = System.currentTimeMillis();
- SimpleDateFormat formatter =newSimpleDateFormat("yyyy-MM-dd HH.mm.ss.SSS");
- String formatStr = formatter.format(dateTaken);// 图像名称String filename = formatStr +".JPEG";
- File parent =newFile(PATH);if(!parent.exists()) {
- parent.mkdirs();
- }
- File file =newFile(PATH, filename);if(file.exists()) {
- file.delete();
- }try{
- FileOutputStream fos =newFileOutputStream(file);
- fos.write(baos.toByteArray());
- fos.flush();
- fos.close();
- }catch(Exception e) {
- e.printStackTrace();
- }
- String filePath = PATH + filename;
- Bitmap scanBitmap = BitmapFactory.decodeFile(filePath);returnscanBitmap;
- }
- /**
- * 将图片保存到本地时进行压缩
- * @parambmp
- */
- publicBitmapcompressBmpToFile(Bitmap bmp){
- ByteArrayOutputStream baos =newByteArrayOutputStream();intoptions =80;//个人喜欢从80开始,bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);while(baos.toByteArray().length /1024>50) {
- baos.reset();
- options -=5;
- bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
- }// 系统时间
- longdateTaken = System.currentTimeMillis();// 图像名称String filename = DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken)
- .toString() +".jpg";
- File file =newFile(PATH, filename);try{
- FileOutputStream fos =newFileOutputStream(file);
- fos.write(baos.toByteArray());
- fos.flush();
- fos.close();
- }catch(Exception e) {
- e.printStackTrace();
- }
- String filePath=PATH + filename;
- Bitmap bitmap=BitmapFactory.decodeFile(filePath);returnbitmap;
- }
Curzibn/Luban
- private byte[]Bitmap2Bytes(Bitmap bm){
- ByteArrayOutputStream baos =newByteArrayOutputStream();
- bm.compress(Bitmap.CompressFormat.PNG,100, baos);returnbaos.toByteArray();
- }
- privateBitmapBytes2Bimap(byte[] b){if(b.length!=0){returnBitmapFactory.decodeByteArray(b,0, b.length);
- }else{return null;
- }
- }
- Resources res=getResources();Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic);
- public static final intCHOOSE_PHOTO =2;
- Button chooseFromAlbum.setOnClickListener(newView.OnClickListener() {@Override
- public void onClick(View v) {if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(MainActivity.this,newString[]{ Manifest.permission. WRITE_EXTERNAL_STORAGE },1);
- }else{
- openAlbum();
- }
- }
- });private void openAlbum() {
- Intent intent =newIntent("android.intent.action.GET_CONTENT");
- intent.setType("image/*");
- startActivityForResult(intent, CHOOSE_PHOTO);// 打开相册}@Override
- public void onRequestPermissionsResult(intrequestCode, String[] permissions,int[] grantResults) {switch(requestCode) {case 1:if(grantResults.length >0&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- openAlbum();
- }else{
- Toast.makeText(this,"权限被拒绝", Toast.LENGTH_SHORT).show();
- }break;default:
- }
- }@Override
- protected void onActivityResult(intrequestCode,intresultCode, Intent data) {switch(requestCode) {caseCHOOSE_PHOTO:if(resultCode == RESULT_OK) {// 判断手机系统版本号
- if(Build.VERSION.SDK_INT >=19) {// 4.4及以上系统使用这个方法处理图片handleImageOnKitKat(data);
- }else{// 4.4以下系统使用这个方法处理图片handleImageBeforeKitKat(data);
- }
- }break;default:break;
- }
- }@TargetApi(19)private void handleImageOnKitKat(Intent data) {
- String imagePath =null;
- Uri uri = data.getData();
- Log.d("TAG","handleImageOnKitKat: uri is "+ uri);if(DocumentsContract.isDocumentUri(this, uri)) {// 如果是document类型的Uri,则通过document id处理String docId = DocumentsContract.getDocumentId(uri);if("com.android.providers.media.documents".equals(uri.getAuthority())) {
- String id = docId.split(":")[1];// 解析出数字格式的idString selection = MediaStore.Images.Media._ID +"="+ id;
- imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
- }else if("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
- Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
- imagePath = getImagePath(contentUri,null);
- }
- }else if("content".equalsIgnoreCase(uri.getScheme())) {// 如果是content类型的Uri,则使用普通方式处理imagePath = getImagePath(uri,null);
- }else if("file".equalsIgnoreCase(uri.getScheme())) {// 如果是file类型的Uri,直接获取图片路径即可imagePath = uri.getPath();
- }
- displayImage(imagePath);// 根据图片路径显示图片}private void handleImageBeforeKitKat(Intent data) {
- Uri uri = data.getData();
- String imagePath = getImagePath(uri,null);
- displayImage(imagePath);
- }privateStringgetImagePath(Uri uri, String selection) {
- String path =null;// 通过Uri和selection来获取真实的图片路径Cursor cursor = getContentResolver().query(uri,null, selection,null,null);if(cursor !=null) {if(cursor.moveToFirst()) {
- path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
- }
- cursor.close();
- }returnpath;
- }private void displayImage(String imagePath) {if(imagePath !=null) {
- Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
- ImageView picture.setImageBitmap(bitmap);
- }else{
- Toast.makeText(this,"获取图片失败", Toast.LENGTH_SHORT).show();
- }
- }
- /**
- * @Class: NotificationsUtils
- * @Description:
- * @author: BG235144/AMOSCXY
- * @Date: 2017/3/13
- */
- public class NotificationsUtils{
- private static finalString CHECK_OP_NO_THROW ="checkOpNoThrow";private static finalString OP_POST_NOTIFICATION ="OP_POST_NOTIFICATION";@SuppressLint("NewApi")public static boolean isNotificationEnabled(Context context) {
- AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- ApplicationInfo appInfo = context.getApplicationInfo();
- String pkg = context.getApplicationContext().getPackageName();intuid = appInfo.uid;
- Class appOpsClass =null;/* Context.APP_OPS_MANAGER */
- try{
- appOpsClass = Class.forName(AppOpsManager.class.getName());
- Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE,
- String.class);
- Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);intvalue = (Integer) opPostNotificationValue.get(Integer.class);return((Integer) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED);
- }catch(ClassNotFoundException e) {
- e.printStackTrace();
- }catch(NoSuchMethodException e) {
- e.printStackTrace();
- }catch(NoSuchFieldException e) {
- e.printStackTrace();
- }catch(InvocationTargetException e) {
- e.printStackTrace();
- }catch(IllegalAccessException e) {
- e.printStackTrace();
- }return false;
- }
- }
注意:Build.VERSION.SDK_INT>=19 时才能使用
- Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");String pkg ="com.android.settings";Stringcls="com.android.settings.applications.InstalledAppDetails";intent.setComponent(new ComponentName(pkg,cls));intent.setData(Uri.parse("package:"+ mContext.getPackageName()));startActivity(intent);
权限组名 | 权限名 |
---|---|
CALENDAR | READ_CALENDAR 、WRITE_CALENDAR |
CAMERA | CAMERA |
CONTACTS | READ_CONTACTS 、WRITE_CONTACTS、 GET_ACCOUNTS |
LOCATION | ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION |
MICROPHONE | RECORD_AUDIO |
PHONE | READ_PHONE_STATE、CALL_PHONE、READ_CALL_LOG、WRITE_CALL_LOG、ADD_VOICEMAIL、USE_SIP、PROCESS_OUTGOING_CALLS |
SENSOPS | BODY_SENSORS |
SMS | SEND_SMS、RECEIVE_SMS、READ_SMS、RECEIVE_WAP_PUSH、RECEIVE_SMS |
STORAGE | READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE |
- if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(MainActivity.this, new String[] {
- Manifest.permission.WRITE_EXTERNAL_STORAGE
- },
- 1);
- } else {
- // 获取权限后要执行的事件
- openAlbum();
- }
- // 检测到还没有获取到权限时执行,申请获取权限
- @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- switch (requestCode) {
- case
来源: http://blog.csdn.net/amoscxy/article/details/72357900