Android-外置sd卡的使用

本人开发是在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后的版本存在的问题,为了解决这个问题,网上找资料,资料很少,但是还是找到了一个https://blog.csdn.net/rookie_wei/article/details/80460795,参照这个文章,我在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了,不再有权限问题。

猜你喜欢

转载自blog.csdn.net/songqinging/article/details/87936749