在应用App的代码中调用如下代码:
context.getExternalFilesDir("");
这段代码,会在主动创建目录:./Android/data/包名/files;
我们来一下这个调用栈:
buildPaths:934, Environment (android.os)
buildExternalStorageAppFilesDirs:131, Environment$UserEnvironment (android.os)
buildExternalStorageAppFilesDirs:686, Environment (android.os)
getExternalFilesDirs:633, ContextImpl (android.app)
getExternalFilesDir:626, ContextImpl (android.app)
getExternalFilesDir:242, ContextWrapper (android.content)
getFile:98, MainActivity (cn.rocky)
access$100:25, MainActivity (cn.rocky)
handleMessage:83, MainActivity$1 (cn.rocky)
dispatchMessage:106, Handler (android.os)
loop:164, Looper (android.os)
main:6548, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:438, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:857, ZygoteInit (com.android.internal.os)
这些代码一个来分析一下:
frameworks/base/core/java/android/content/Context.java
public abstract File getExternalFilesDir(@Nullable String type);
上面的实现代码:frameworks/base/core/java/android/app/ContextImpl.java
@Override
public File getExternalFilesDir(String type) {
// Operates on primary external storage
final File[] dirs = getExternalFilesDirs(type);
return (dirs != null && dirs.length > 0) ? dirs[0] : null;
}
@Override
public File[] getExternalFilesDirs(String type) {
synchronized (mSync) {
File[] dirs = Environment.buildExternalStorageAppFilesDirs(getPackageName());
if (type != null) {
dirs = Environment.buildPaths(dirs, type);
}
return ensureExternalDirsExistOrFilter(dirs);
}
}
/**
* Ensure that given directories exist, trying to create them if missing. If
* unable to create, they are filtered by replacing with {@code null}.
*/
private File[] ensureExternalDirsExistOrFilter(File[] dirs) {
File[] result = new File[dirs.length];
for (int i = 0; i < dirs.length; i++) {
File dir = dirs[i];
if (!dir.exists()) {//判断目录是否存在,如果不存在,走if
if (!dir.mkdirs()) {//创建目录,创建目录失败,走if,否则走else
// recheck existence in case of cross-process race
if (!dir.exists()) {//再次判断目录是否存在,不存在,走if,通过binder服务来创建目录,因为可能是权限不够导致
// Failing to mkdir() may be okay, since we might not have
// enough permissions; ask vold to create on our behalf.
final IStorageManager storageManager = IStorageManager.Stub.asInterface(
ServiceManager.getService("mount"));
try {
final int res = storageManager.mkdirs(
getPackageName(), dir.getAbsolutePath());
if (res != 0) {
Log.w(TAG, "Failed to ensure " + dir + ": " + res);
dir = null;
}
} catch (Exception e) {
Log.w(TAG, "Failed to ensure " + dir + ": " + e);
dir = null;
}
}
}
}
result[i] = dir;
}
return result;
}
frameworks/base/core/java/android/os/Environment.java
public static final String DIR_ANDROID = "Android";
private static final String DIR_DATA = "data";
private static final String DIR_OBB = "obb";
private static final String DIR_FILES = "files";
/**
* Generates the path to an application's files.
* @hide
*/
public static File[] buildExternalStorageAppFilesDirs(String packageName) {
throwIfUserRequired();
return sCurrentUser.buildExternalStorageAppFilesDirs(packageName);
}
/**
* Append path segments to each given base path, returning result.
*
* @hide
*/
public static File[] buildPaths(File[] base, String... segments) {
File[] result = new File[base.length];
for (int i = 0; i < base.length; i++) {
result[i] = buildPath(base[i], segments);
}
return result;
}
/**
* Append path segments to given base path, returning result.
* 循环创建目录结构
* @hide
*/
public static File buildPath(File base, String... segments) {
File cur = base;
for (String segment : segments) {
if (cur == null) {
cur = new File(segment);
} else {
cur = new File(cur, segment);
}
}
return cur;
}
/** {@hide} */
public static class UserEnvironment {
//获取外部的盘符根目录
public File[] getExternalDirs() {
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;
}
//构造目录结构
public File[] buildExternalStorageAppFilesDirs(String packageName) {
return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
}
}
这里的代码比较简单,至此,所有挂载的盘符都已经了对应用的Android/data/包名/files目录;