Android开发笔记(七十九)资源与权限校验
2016-03-14 10:50
537 查看
硬件资源
因为移动设备的硬件配置各不相同,为了防止使用了不存在的设备资源,所以要对设备的硬件情况进行检查。一般情况下,前置摄像头、部分传感器在低端手机上是没有的,像SD卡也可能因为用户没插卡使得找不到SD卡资源。下面是校验这些硬件设备的说明:SD卡
Android4.0之后增加了多存储卡的支持,故一般手机有内置存储卡和外置存储卡(即SD卡),其中外置存储卡便是可选的。获取各个存储卡的磁盘路径,可通过系统服务STORAGE_SERVICE构造StorageManager对象,再使用反射机制调用getVolumePaths内部方法获得。磁盘路径符合Environment.getExternalStorageDirectory().getPath()的,就是默认的内置存储卡,否则就是外置存储卡。具体的示例代码如下:public static String[] getVolumePaths(Context ctx) { String[] paths = null; StorageManager storMgr = (StorageManager) ctx.getSystemService(Activity.STORAGE_SERVICE); Method method = null; try { method = storMgr.getClass().getMethod("getVolumePaths"); } catch (NoSuchMethodException e) { e.printStackTrace(); return paths; } try { paths = (String[]) method.invoke(storMgr); } catch (Exception e) { e.printStackTrace(); } return paths; }
前置摄像头
后置摄像头对手机来说是标配,但前置摄像头就有部分低端机不支持。摄像头的详细介绍参见《Android开发笔记(五十六)摄像头拍照》。检查前置摄像头是否存在,可通过获取摄像头个数来判断,个数多于一个就表示有前置摄像头。示例代码如下:
private void checkCamera() { int cameraCount = Camera.getNumberOfCameras(); mDesc = String.format("%s\n\n摄像头个数=%d", mDesc, cameraCount); for (int i=0; i<cameraCount; i++) { Camera camera = Camera.open(i); Parameters params = camera.getParameters(); List<Size> sizes = params.getSupportedPreviewSizes(); mDesc = String.format("%s\n%s摄像头支持的分辨率有%d种", mDesc, (i==0)?"前置":"后置", sizes.size()); for (int j=0; j<sizes.size(); j++) { Size size = sizes.get(j); mDesc = String.format("%s\n分辨率%d为:宽%d*高%d", mDesc, j+1, size.width, size.height); } camera.release(); } tv_check_hardware.setText(mDesc); }
传感器
Android的传感器种类繁多,可是大多数手机都只支持少数几种,所以使用传感功能前要先校验当前设备是否存在对应的传感器。传感器的详细介绍参见《Android开发笔记(五十九)巧用传感器》。获取当前支持的传感器列表的示例代码如下:
private String[] mSensorType = { "加速度", "磁场", "方向", "陀螺仪", "光线", "压力", "温度", "距离", "重力", "线性加速度", "旋转矢量", "湿度", "环境温度", "无标定磁场", "无标定旋转矢量", "未校准陀螺仪", "特殊动作", "步行检测", "计步器", "地磁旋转矢量"}; private void checkSensor() { SensorManager sensroMgr = (SensorManager) getSystemService(Context.SENSOR_SERVICE); List<Sensor> sensorList = sensroMgr.getSensorList(Sensor.TYPE_ALL); mDesc = String.format("%s\n\n传感器个数=%d", mDesc, sensorList.size()); for (int i=0; i<sensorList.size(); i++) { Sensor sensor = sensorList.get(i); mDesc = String.format("%s\n传感器%d的类型=%s,名称=%s", mDesc, i+1, mSensorType[sensor.getType()-1], sensor.getName()); } tv_check_hardware.setText(mDesc); }
存储资源
由于移动设备上资源有限,因此常常需要判断当前的剩余资源是否足够。比如说,发现剩余内存较低,则app不再进行大量消耗内存的操作,避免设备死机;又比如发现剩余磁盘空间不足,则app不再存储个头较大的图片或视频,避免设备爆盘;再比如发现当前应用的流量消耗较大,则app自动减少联网操作,避免被用户拉入黑名单。剩余内存
获取设备的剩余内存大小,以及内存总量,可通过系统服务ACTIVITY_SERVICE构造ActivityManager对象,从中获得每个进程的内存使用情况。实现的示例代码如下://获取设备的内存总大小,单位KB public static long getMemoryTotalSize() { long totalSize; // /proc/meminfo读出的内核信息进行解释 String path = "/proc/meminfo"; String content = null; BufferedReader br = null; try { br = new BufferedReader(new FileReader(path), 8); String line; if ((line = br.readLine()) != null) { content = line; } } catch (Exception e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (Exception e) { e.printStackTrace(); } } } int begin = content.indexOf(':'); int end = content.indexOf('k'); // 截取字符串信息 content = content.substring(begin + 1, end).trim(); totalSize = Integer.parseInt(content); return totalSize; } //获取当前的内存剩余大小,单位KB public static long getMemoryLeftSize(Context ctx) { ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); am.getMemoryInfo(mi); return mi.availMem / 1024; } //获取本app使用的内存大小,单位KB public static int getAppUserdMemory(Context ctx) { int userdMemory = 0; String packageName = ctx.getPackageName(); ActivityManager actMgr = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); // 获得系统里正在运行的所有进程 List<RunningAppProcessInfo> runningList = actMgr.getRunningAppProcesses(); for (RunningAppProcessInfo runningAppProcessInfo : runningList) { // 进程ID号 int pid = runningAppProcessInfo.pid; // 用户ID int uid = runningAppProcessInfo.uid; // 进程名 String processName = runningAppProcessInfo.processName; if (processName.equals(packageName) != true) { continue; } // 占用的内存 int[] pids = new int[] { pid }; MemoryInfo[] infoList = actMgr.getProcessMemoryInfo(pids); if (infoList.length > 0) { MemoryInfo info = infoList[0]; userdMemory = info.dalvikPrivateDirty; } } return userdMemory; } }
剩余磁盘空间
磁盘分内部存储和外部存储(即SD卡)两种,内部存储的磁盘路径由下面代码获得:String path = Environment.getDataDirectory().getPath();
外部存储的默认磁盘路径由下面代码获得:
String path = Environment.getExternalStorageDirectory().getPath();
获取磁盘剩余空间,以及总空间,实现的示例代码如下:
//获取指定路径的总空间,单位字节 public static long getStorageTotalSize(String path) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { return getStorageTotalSizeNew(path)/1024; } else { return getStorageTotalSizeOld(path)/1024; } } @SuppressWarnings("deprecation") private static long getStorageTotalSizeOld(String path) { File sdcardDir = new File(path); StatFs sf = new StatFs(sdcardDir.getPath()); long blockSize = sf.getBlockSize(); long blockCount = sf.getBlockCount(); return blockSize*blockCount; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private static long getStorageTotalSizeNew(String path) { File sdcardDir = new File(path); StatFs sf = new StatFs(sdcardDir.getPath()); long blockSize = sf.getBlockSizeLong(); long blockCount = sf.getBlockCountLong(); return blockSize*blockCount; } //获取指定路径的剩余空间,单位字节 public static long getStorageLeftSize(String path) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { return getStorageLeftSizeNew(path)/1024; } else { return getStorageLeftSizeOld(path)/1024; } } @SuppressWarnings("deprecation") private static long getStorageLeftSizeOld(String path) { File sdcardDir = new File(path); StatFs sf = new StatFs(sdcardDir.getPath()); long blockSize = sf.getBlockSize(); long blockCount = sf.getAvailableBlocks(); return blockSize*blockCount; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private static long getStorageLeftSizeNew(String path) { File sdcardDir = new File(path); StatFs sf = new StatFs(sdcardDir.getPath()); long blockSize = sf.getBlockSizeLong(); long blockCount = sf.getAvailableBlocksLong(); return blockSize*blockCount; }
剩余流量
Android的流量数据保存在系统文件中,每次开机都会清零,所以查看系统文件得到的已使用流量,其实只是本次开机后的流量数据。系统级别的流量文件路径是/proc/net/dev,应用级别的流量文件路径是/proc/uid_stat/uid/tcp_rcv(注意中间的“uid”要替换为数字的应用id)。不想解析文件的话,也可以使用Android的工具类TrafficStats来读取流量,该工具的常用方法如下:
getTotalRxBytes : 获取接收流量的总字节数。
getTotalTxBytes : 获取发送流量的总字节数。
getMobileRxBytes : 获取移动接收流量的总字节数。(包括2G/3G/4G流量,不包括wifi流量)
getMobileTxBytes : 获取移动发送流量的总字节数。
getUidRxBytes : 获取本进程接收流量的总字节数。本进程的应用ID可调用Process.myUid()获得。
getUidTxBytes : 获取本进程发送流量的总字节数。
权限校验
获取权限列表
查看app申请了哪些permission权限,可通过下面代码来检查:private String[] mPerArray; PackageManager pm = getPackageManager(); try { PackageInfo pack = pm.getPackageInfo(getPackageName(), PackageManager.GET_PERMISSIONS); mPerArray = pack.requestedPermissions; } catch (NameNotFoundException e) { e.printStackTrace(); } if (mPerArray!=null && mPerArray.length>0) { mDesc = String.format("%s\n当前请求的权限个数=%d", mDesc, mPerArray.length); for (int i=0; i<mPerArray.length; i++) { mDesc = String.format("%s\n权限%d的名称=%s", mDesc, i+1, mPerArray[i]); } } else { mDesc = String.format("%s\n请求权限列表失败", mDesc); } tv_check_permission.setText(mDesc);
不过即使app申请了必要的权限,运行时仍有可能出错,原因除了缺少对应的硬件之外,还可能是相关功能未开启,甚至可能是安全软件强行屏蔽了部分权限。检查功能的开关状态(例如数据连接、GPS等),具体例子参见《Android开发笔记(五十五)手机设备基本操作》。如果是被安全软件屏蔽权限,则app很可能会扔出运行时异常,此时在代码中加入异常捕获情节,即可即时判断拥有权限与否。
下面是几个常用业务场景的权限检查例子:
检查定位权限
判断是否能够正常定位,除了检查功能开关状态,还要检查是否存在定位提供者。定位功能的详细介绍参见《Android开发笔记(四十六)手机相关事件》。检查定位权限的示例代码如下:
if (checkValid("android.permission.ACCESS_FINE_LOCATION") != true) { return; } else if (checkValid("android.permission.ACCESS_COARSE_LOCATION") != true) { return; } else { if (SwitchUtil.getMobileDataStatus(this) == true || SwitchUtil.getGpsStatus(this) == true) { LocationManager locMgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE); Criteria criteria = new Criteria(); String bestProvider = locMgr.getBestProvider(criteria, true); if (bestProvider != null) { tv_check_permission.setText("正常定位"); } else { Log.d(TAG, "bestProvider is null"); tv_check_permission.setText("无法获取定位提供者"); } } else { tv_check_permission.setText("无法定位,GPS和数据连接均未开启"); } }
检查拍照权限
判断是否能够正常拍照,如无法拍照则app在执行Camera的open方法时,(即打开摄像头时)会扔出异常“java.lang.RuntimeException: Fail to connect to camera service”。拍照功能的详细介绍参见《Android开发笔记(五十六)摄像头拍照》。检查拍照权限的示例代码如下:
if (checkValid("android.permission.CAMERA") != true) { return; } else { try { Camera camera = Camera.open(); camera.release(); tv_check_permission.setText("正常拍照"); } catch (Exception e) { e.printStackTrace(); tv_check_permission.setText("拍照失败:" + e.getMessage()); } }
检查录音权限
判断是否能够正常录音,如无法录音则app在执行MediaRecorder的setAudioSource方法时,(即打开麦克风时)会扔出异常“java.lang.RuntimeException: setAudioSource failed.”。录音功能的详细介绍参见《Android开发笔记(五十七)录像录音与播放》。检查录音权限的示例代码如下:
if (checkValid("android.permission.RECORD_AUDIO") != true) { return; } else { try { MediaRecorder mRecorder = new MediaRecorder(); mRecorder.setAudioSource(AudioSource.MIC); // 如被关闭录音权限,则setAudioSource就会扔出异常 mRecorder.setAudioSamplingRate(10); // 设置音频的采样率,单位赫兹(Hz) mRecorder.setAudioChannels(2); // 设置音频的声道数。1表示单声道,2表示双声道 mRecorder.setAudioEncodingBitRate(1000); // 设置音频每秒录制的字节数 mRecorder.setOutputFormat(OutputFormat.DEFAULT); mRecorder.setAudioEncoder(AudioEncoder.AMR_NB); mRecorder.setMaxDuration(3000); mRecorder.setOutputFile(createRecordDir().getAbsolutePath()); mRecorder.prepare(); mRecorder.release(); tv_check_permission.setText("正常录音"); } catch (Exception e) { e.printStackTrace(); tv_check_permission.setText("录音失败:" + e.getMessage()); } }
点此查看Android开发笔记的完整目录
相关文章推荐
- 启动activity时,有个一闪而过界面
- Android 最火的快速开发框架XUtils
- Android IPC机制之ContentProvider
- Android 实现颜色选取圆盘
- android studio mipmap 目录和 drawable 目录有什么区别
- android中activity切换时白屏问题
- Android 让你一分钟打一千个渠道包,丢掉该死的打包工具!
- android studio 图片错误 libpng error: Not a PNG file
- Android 异步操作AsyncTask
- Android 5.x特性概览三
- android闪关灯的开启和关闭方法代码实例
- Gradle依赖的统一管理
- Android设备抓包命令
- Android中JNI起源及存在的必要性
- Android Studio中导入第三方库工程的方法
- Android 6.0 Permission权限与安全机制
- 修改Android系统目录的读写权限命令
- Android IPC机制之Messenger
- Android二维码ZXing扫描区域大小的调整,提高扫描速度
- android--textview实现文字伪自适应