Android开发中Environment类的探究和学习
参考的博客:http://blog.youkuaiyun.com/android_dong/article/details/38686283
比较高深的一片:http://blog.youkuaiyun.com/u012889434/article/details/45578059
http://blog.youkuaiyun.com/wangsf1112/article/details/51427007
大牛的文章:http://gityuan.com/2016/07/23/android-io-arch/
在使用Encironment类进行外部存储的时候,需要判断外部设备是不是可以使用;
getExternalStorageState();//来获取外部设备的存储的状态
下面是Envrionment类中关于外部存储设备的状态的定义:
/**
* Unknown storage state, such as when a path isn't backed by known storage
* media.(未知的存储状态,如路径不支持已知存储媒体。)
*/
public static final String MEDIA_UNKNOWN = "unknown";
/**
* Storage state if the media is not present.(存储状态如果媒体不存在。)
*/
public static final String MEDIA_REMOVED = "removed";
/**
* Storage state if the media is present but not mounted.(存储状态,如果设备存在但是没有挂载)
*/
public static final String MEDIA_UNMOUNTED = "unmounted";
/**
* Storage state if the media is present and being disk-checked.(存储状态如果媒体存在并且被磁盘检查。)
*/
public static final String MEDIA_CHECKING = "checking";
/**
* Storage state if the media is present but is blank or is using an
* unsupported filesystem.(存储状态如果媒体存在,但是空白或正在使用一个不支持的文件系统。)
*/
public static final String MEDIA_NOFS = "nofs";
/**
* Storage state if the media is present and mounted at its mount point with
* read/write access.(存储状态,如果介质存在,并在它的挂载点上安装读/写访问。)
*/
public static final String MEDIA_MOUNTED = "mounted";
/**
* Storage state if the media is present and mounted at its mount point with
* read-only access.(存储状态,如果媒体存在并在其挂载点上使用只读访问)
*/
public static final String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
/**
* Storage state if the media is present not mounted, and shared via USB
* mass storage.(存储状态如果媒体没有挂载,并且通过USB存储进行共享。)
*/
public static final String MEDIA_SHARED = "shared";
/**
* Storage state if the media was removed before it was unmounted.(存储状态,如果媒体在卸载之前被删除。)
*/
public static final String MEDIA_BAD_REMOVAL = "bad_removal";
/**
* Storage state if the media is present but cannot be mounted. Typically
* this happens if the file system on the media is corrupted.(存储状态如果媒体存在,但不能挂载。通常情况下,如果媒体上的文件系统被破坏了。)
*/
public static final String MEDIA_UNMOUNTABLE = "unmountable";
/**
* Storage state if the media is in the process of being ejected.(存储状态如果媒体处于被弹出的过程中。)
*/
public static final String MEDIA_EJECTING = "ejecting";
具体代码如下:
//在获取外置的设备的存贮的路劲的时候需要先判断外部存贮的是否可用(挂载和可以使用的状态)
String exStorageState = Environment.getExternalStorageState(); // 获取外部设备的状态
if (exStorageState == null || exStorageState.equals(Environment.MEDIA_MOUNTED)
|| exStorageState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
Environment.getExternalStorageDirectory();// 这样一般获取的是 /storage/emulated/0
}
我们看看Envrionment的获取存储路劲的方法的运行过程;
// 在Envrionment中的方法
public static File getExternalStorageDirectory() {
throwIfUserRequired();
return sCurrentUser.getExternalDirs()[0];
}
我们看一个定义:
private static UserEnvironment sCurrentUser;
这个类型是UserEnvrionment类型的,我们看看这个类.这个类是定义在Envrionment内部的一个类;我们直接在源码上边看边解释
/** {@hide} */ //我们可以看到对外是隐藏的
public static class UserEnvironment {
private final int mUserId;
public UserEnvironment(int userId) {
mUserId = userId;
}
public File[] getExternalDirs() { //关键的方法StorageManager后面还会使用到 volume是指(卷)
final StorageVolume[] volumes = StorageManager.getVolumeList(mUserId,
StorageManager.FLAG_FOR_WRITE);
final File[] files = new File[volumes.length];
for (int i = 0; i < volumes.length; i++) { // 开始循环遍历获取的卷的列表
files[i] = volumes[i].getPathFile();
}
return files;
}
// 下面的是提供的方法,暂时不去细看实现过程. 你可以看到调用的是上面方法了!
@Deprecated
public File getExternalStorageDirectory() {
return getExternalDirs()[0];
}
@Deprecated
public File getExternalStoragePublicDirectory(String type) {
return buildExternalStoragePublicDirs(type)[0];
}
public File[] buildExternalStoragePublicDirs(String type) {
return buildPaths(getExternalDirs(), type);
}
public File[] buildExternalStorageAndroidDataDirs() {
return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA);
}
public File[] buildExternalStorageAndroidObbDirs() {
return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB);
}
public File[] buildExternalStorageAppDataDirs(String packageName) {
return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName);
}
public File[] buildExternalStorageAppMediaDirs(String packageName) {
return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_MEDIA, packageName);
}
public File[] buildExternalStorageAppObbDirs(String packageName) {
return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB, packageName);
}
public File[] buildExternalStorageAppFilesDirs(String packageName) {
return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
}
public File[] buildExternalStorageAppCacheDirs(String packageName) {
return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
}
}
看到这里我们需要看看StorageManager类:
/**
* StorageManager is the interface to the systems storage service. The storage
* manager handles storage-related items such as Opaque Binary Blobs (OBBs).
* <p>(StorageManager是系统存储服务的接口。存储管理器处理与存储相关的项目,如不透明的二进制Blobs(OBBs)。)
*
* OBBs contain a filesystem that maybe be encrypted on disk and mounted
* on-demand from an application. OBBs are a good way of providing large amounts
* of binary assets without packaging them into APKs as they may be multiple
* gigabytes in size. However, due to their size, they're most likely stored in
* a shared storage pool accessible from all programs. The system does not
* guarantee the security of the OBB file itself: if any program modifies the
* OBB, there is no guarantee that a read from that OBB will produce the
* expected output.
* (OBBs包含一个文件系统,可能在磁盘上进行加密,并从应用程序上按需安装。OBBs是提供大量二进制资产的好方法,
* 无需将其打包成apk,因为它们的大小可能是多个g。但是,由于它们的大小,它们很可能存储在所有程序中都可以访问的共享存储中。
* 系统不能保证OBB文件本身的安全性:如果有任何程序修改OBB,就不能保证从OBB读取的数据将产生预期的输出。)
*
*/
public class StorageManager {
// 其他代码
}
直接上关键代码:
/** {@hide} */
public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
// stub 出来了
final IMountService mountService = IMountService.Stub.asInterface(
ServiceManager.getService("mount"));
try {
String packageName = ActivityThread.currentOpPackageName();
if (packageName == null) {
// Package name can be null if the activity thread is running but the app
// hasn't bound yet. In this case we fall back to the first package in the
// current UID. This works for runtime permissions as permission state is
// per UID and permission realted app ops are updated for all UID packages.
String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
android.os.Process.myUid());
if (packageNames == null || packageNames.length <= 0) {
return new StorageVolume[0];
}
packageName = packageNames[0];
}
final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
if (uid <= 0) {
return new StorageVolume[0];
}
return mountService.getVolumeList(uid, packageName, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
我们看到实质获取的是StorageVolume数组;
public final class StorageVolume implements Parcelable {
private final String mId;
private final int mStorageId;
private final File mPath;
private final String mDescription;
private final boolean mPrimary;
private final boolean mRemovable;
private final boolean mEmulated;
private final long mMtpReserveSize;
private final boolean mAllowMassStorage;
private final long mMaxFileSize;
private final UserHandle mOwner;
private final String mFsUuid;
private final String mState;
//其他代码
}
继续查看(IMountService接口中):
下面的就是在这个接口中的源码:
public interface IMountService extends IInterface {
public StorageVolume[] getVolumeList(int uid, String packageName, int flags)
throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
StorageVolume[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(uid);
_data.writeString(packageName);
_data.writeInt(flags);
mRemote.transact(Stub.TRANSACTION_getVolumeList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArray(StorageVolume.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
// 其他代码
}
最后展示常用的Envrionment的获取的路劲:
总结的知识
我们一般使用Envrionment来获取设备的存储路劲,但是有些设备返回的是外部设备的地址,例如/storage/emulate/0有的返回的是内置存储的外置.我们直接使用StorageManger来获取所有存储的路劲,我们上面的源码中可以看到,实质就是使用了StorageManger来获取相应的路劲的.
使用StorageManger来获取设备的所有的存储的路劲
第一步: 获取StorageManager(通过服务来获取) 第二步,反射获取方法 第三部执行方法.
private void getAllStoragerPath() {
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
try {
Class<?>[] paramClasses = {};
Method getVolumePathsMethod = StorageManager.class.getMethod("getVolumePaths", paramClasses);
getVolumePathsMethod.setAccessible(true);
Object[] params = {};
Object invoke = getVolumePathsMethod.invoke(storageManager, params);
for (int i = 0; i < ((String[])invoke).length; i++) {
Log.d(TAG, "getAllStoragerPath: "+((String[])invoke)[i]);
}
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
还需要注意的Envrionment也提供了Android标准目录的路径:
DIRECTORY_ALARMS 系统提醒铃声存放的标准目录。
DIRECTORY_DCIM 相机拍摄照片和视频的标准目录。
DIRECTORY_DOWNLOADS 下载的标准目录。
DIRECTORY_MOVIES 电影存放的标准目录。
DIRECTORY_MUSIC 音乐存放的标准目录。
DIRECTORY_NOTIFICATIONS 系统通知铃声存放的标准目录。
DIRECTORY_PICTURES 图片存放的标准目录
DIRECTORY_PODCASTS 系统广播存放的标准目录。
DIRECTORY_RINGTONES 系统铃声存放的标准目录。
File externalStoragePublicDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
// 最后的结果是 storage/emulated/0/Music
Log.d(TAG, "svaeData: ceshi"+externalStoragePublicDirectory.toString());