硬件资源

因为移动设备的硬件配置各不相同,为了防止使用了不存在的设备资源,所以要对设备的硬件情况进行检查。一般情况下,前置摄像头、部分传感器在低端手机上是没有的,像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对象,从中获得每个进程的内存使用情况。实现的示例代码如下:

 //获取设备的内存总大小,单位KBpublic 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;}//获取当前的内存剩余大小,单位KBpublic 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使用的内存大小,单位KBpublic 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;// 用户IDint 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开发笔记的完整目录

Android开发笔记(七十九)资源与权限校验相关推荐

  1. Android开发笔记(十九)底部标签栏TabBar

    底部标签页实现思路 现在的APP,大多在页面底部显示标签栏Tabbar,用于切换不同栏目的页面.Tabbar起源于iOS,iOS的Tabbar自动位于页面下方,可是Android搬过来的时候做了改动, ...

  2. android开发标签栏应该设置多少,Android开发笔记(十九)底部标签栏TabBar

    底部标签页实现思路 现在的APP,大多在页面底部显示标签栏Tabbar,用于切换不同栏目的页面.Tabbar起源于iOS,iOS的Tabbar自动位于页面下方,可是Android搬过来的时候做了改动, ...

  3. Android开发笔记(十五)淡入淡出动画TransitionDrawable

    说到淡入淡出动画,可能大家会想到补间动画里面的AlphaAnimation,不过这个深浅动画只能对透明度做渐变效果,也就是只能对一个图形做深浅的颜色变换.如果我们想要从A图片逐渐变为B图片,也就是要实 ...

  4. Android开发笔记(九十九)圆形转盘

    圆形转盘的实现思想 圆形转盘的运用场景常见的有:抽奖转盘.圆形菜单列表.热点客户端环状列表等等.对于圆形转盘的编码实现,主要难点除了手势的触摸控制之外,就在于旋转角度的计算了.下面是旋转角度计算的解决 ...

  5. Android开发笔记(十八)书籍翻页动画PageAnimation

    前面几节的动画都算简单,本文就介绍一个复杂点的动画--书籍翻页动画.Android有自带的翻页动画ViewPager,不过ViewPager只实现了平移效果.即便使用补间组合动画或者属性动画,也只是把 ...

  6. Android开发笔记(十六)秋千摇摆动画SwingAnimation

    上节博主介绍了AlphaAnimation和淡入淡出动画的使用,其实AlphaAnimation只是四种补间动画中的一种.那么为了加深对其他补间动画的理解,我想说说旋转动画RotateAnimatio ...

  7. Android开发笔记(十四)圆弧进度动画CircleAnimation

    一个好看的APP,都有不少精致的动画效果.熟练运用各种动画技术,可让我们的APP灼灼生辉.Android在技术上把动画分为了三类,分别是帧动画FrameAnimation.补间动画TweenAnima ...

  8. Android开发笔记(十二)测量尺寸与下拉刷新

    尺寸测量的配置 控件宽和高的设置方式 大家知道,自定义视图的目的就是要在屏幕上显示期望的图案,那在绘制图案之前,我们得先知道这个图案的尺寸(如宽多少高多少). 一般在xml中给控件的宽和高有三种赋值方 ...

  9. 【Visual C++】游戏开发笔记三十九 浅墨DirectX教程之七 他山之石:几种几何体的快捷绘制法

    本篇文章里,我们对Direct3D之中几种几何体的简洁绘制方法进行了详细的剖析,最后依旧是提供文章配套的详细注释的demo源代码的欣赏,并在文章末尾提供了源代码下载.(这标题有些歧义的,这个几种是修饰 ...

  10. Android开发笔记(十)常用的图片加工操作

    APP开发自然少不了各种图片的加工处理,虽然说加工图片是美工干的活,但是码农会些简单的操作总归是好事.一些简单的加工如缩放.旋转.裁剪.格式转换,使用ACDSee就够用了,不过有些稍微复杂的操作得借助 ...

最新文章

  1. Babel的配置和使用
  2. ANDROID_NDK的path,如何在Android Studio中设置NDK_PROJECT_PATH
  3. Android -- ImageLoader本地缓存
  4. 【Lucene3.6.2入门系列】第10节_Tika
  5. 一种基于memcache或redis缓存架构的验证码
  6. uoj#267. 【清华集训2016】魔法小程序(乱搞)
  7. 拓端tecdat|R语言无监督学习:PCA主成分分析可视化
  8. visio绘图与processon绘图比较
  9. 薪酬体系:了解越多,满意越高
  10. wxPython色环电阻计算器
  11. 【科研人必备】各大英文期刊投稿必备搜索/文章相关推荐投稿搜索方法
  12. LiveZilla管理员密码忘记了如何恢复?How can I change the administrator password
  13. 【常用办公软件】万彩办公大师教程丨全能图片转换工具
  14. 推荐一款Android高清平板应用--豆果美食
  15. 华为大数据云管理平台实测
  16. canvas里 阿里云服务器oss图片跨域处理
  17. 电视行业迎来市场“拐点” TCL曲面电视表现最为抢眼
  18. ERP : 经济批量
  19. 不露脸也可以做自媒体短视频,简单罗列几个易上手的领域
  20. Content-Type: application/vnd.ms-excel 操作文件

热门文章

  1. Leetcode每日一题:181.employees-earning-more-than-their-managers(超过经理收入的员工)
  2. Java并发(八)——Fork/Join
  3. 花书+吴恩达深度学习(十三)卷积神经网络 CNN 之运算过程(前向传播、反向传播)
  4. git 入门教程之 git 私服搭建教程
  5. 两年前端菜鸟回忆篇(1)
  6. 7、Fiddler的HTTP统计视图
  7. python wxpython backend wxagg_在wxPython中使用draw()(复制轴)绘制不正确
  8. 软考的一些心得分享, 写在信息系统项目管理师通过之后
  9. Spring Boot项目在GitHub上初始化及控管步骤
  10. Perl -- 使用Perl 读取和发送邮件