本人开发是在Android 7.0版本上,并且是可以修改系统源码的,项目需要使用外置的sd卡,不是内置的存储卡。
1.外置sd路径的获取
需要从外置sd卡上读写文件,首先需要拿到外置sd卡的路径,结果发现开放的API是无法拿到外置sd卡路径的。
一般情况,我们都会调用Environment.getExternalStorageDirectory().getAbsolutePath()来获取sd卡的路径的,但是这个拿到的路径是内部存储的路径。所以我们只能另辟蹊径,参照Android原生应用可以找到获取外置sd卡的方法。通过StorageManager类来获取,这个类需要在系统内部的app(和系统一起编译的那些APP)才能调用,如果要在三方APP开发中调用,那只能通过反射机制了,这里就不介绍反射的部分。
通过StorageManager获取sd卡路径,为了支持在Android studio上开发调用,我的做法是,首先在系统上自定义了一个系统进程,需要使用到aidl,并且支持通过Context.getSystemService(进程名)获取进程实例,再在系统的进程类中增加getSdcardPath()方法(如果你的APP是直接放在Android源码上编译,也可以在你的APP代码上直接加上这个方法),方法实现如下:
public String getSdcardPath() throws RemoteException {
StorageManager storageManager = mContext.getSystemService(StorageManager.class);
if (null == storageManager) {
return null;
}
String path = null;
StorageVolume volumes[] = storageManager.getVolumeList();
if (volumes != null) {
for (StorageVolume volume : volumes) {
if (!Environment.MEDIA_MOUNTED.equals(storageManager.getVolumeState(volume.getPath()))) {
continue;
}
if(volume.isRemovable()) {
path = volume.getPath();
break;
}
}
}
return path;
}
在系统的进程类中增加getSdcardPath方法后,就可以在Android studio工具上,先通过获取到你定义的系统进程类实例,然后调用getSdcardPath获取外置sd卡的路径。
另外一个获取外置sd卡的方式是通过反射,这个就可以直接在三方APP开发中进行了,实现代码如下:
public static String getStoragePath(Context mContext, boolean is_removale) {
StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = null;
try {
storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isRemovable = storageVolumeClazz.getMethod("isRemovable");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String path = (String) getPath.invoke(storageVolumeElement);
boolean removable = (Boolean) isRemovable.invoke(storageVolumeElement);
if (is_removale == removable) {
return path;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
2.存储权限配置
外置sd卡路径获取到后,便可以对其进行读写了,当然你必须配置权限。
首先在AndroidManifest.xml中配置:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
然后在代码中编写动态权限代码,这块可以去网上搜索相关资料来实现。这里不做介绍。
上面完成外置sd卡路径的获取和存储权限的配置工作,执行对文件的读写操作时,会出现异常。
报出如下异常log:
java.io.FileNotFoundException: /storage/7B6F-1BE2/face_recognition_pic/IMG_2019-02-26_08.10.49.jpg (Permission denied) 仍然是一个权限的问题,这个是6.0后的版本存在的问题,为了解决这个问题,网上找资料,资料很少,但是还是找到了一个,,我在frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java类中的方法grantPermissionsLPw中,在case PermissionInfo.PROTECTION_SIGNATURE:代码块的语句allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);后面增加如下代码:
allowedSig = allowedSig || (perm.equals("android.permission.MOUNT_UNMOUNT_FILESYSTEMS")
|| perm.equals("android.permission.WRITE_MEDIA_STORAGE"))
&& pkg.packageName.equals("你的APP包名");
然后在APP的AndroidManifest.xml中增加权限配置:
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
这样配置好就可以直接运行APP了,不再有权限问题。