Android_StorageManager存储模块

前言

前段时间听了有关于 Android StorageManager 外部存储模块的培训,现将所理解的知识整理起来,横向扩展知识面。

StorageManager 模块是管理 Android 存储的模块,那么我们将会从两方面展开说明:

了解 Android 中的内部存储与外部存储

了解 StorageManager 模块结构

正文

正文部分将从两个方面展开描述:

一、Android 中的存储

这部分比较复杂,说法众说纷纭。这部分会结合官方文档将存储相关知识进行汇总。

二、Storage 存储模块介绍

这部分主要从架构上对 Storage 存储进行描述。

1 Android 中的存储

1.1 Android 存储分类

Android 提供两类物理存储位置:内部存储空间 和 外部存储空间。

内部存储空间通常小于外部存储空间,不过,内部存储空间的数据存储时更加可靠。

内部存储空间目录:包括用于存储持久性文件的专属位置,也包括用于存储缓存数据的其他位置。系统会组织其他应用访问这些位置,并在 Android 10(API 级别为 29)及更高版本中,系统会对这些位置进行加密。这些特征使得这些位置非常适合存储只有应用本身才能访问的敏感数据。

外部存储空间目录:包括用于存储持久性文件的专属位置,也包括用于存储缓存数据的其他位置。虽然其他应用可以在具有适当权限的情况下访问这些目录,但是存储在这些目录中的文件仅供对应应用使用。如果明确其他应用可以访问数据,应将数据存储在外部空间的共享存储空间部分。表示外部存储空间也存在共有部分和私有部分,共有部分数据所有应用在一定权限下可以访问,私有部分数据仅供应用本身访问。

1.2 内部存储

概念:内部存储不是内存。存储在内部存储中的文件只能被自己所访问,且一个应用创建的所有文件都在其应用包名目录下,也就是应用创建的内部存储文件是与应用相关联的,内部存储空间十分有限,当应用卸载后,内部存储的文件也会删除,以管理可贵的内部存储空间。

内部存储空间对于应用来说就是 “/data/data” 目录(实际挂载是在 /data/user/0/ 路径下的,/data/data/ 是软链接到前者),其中有两个文件夹值得关注。

  • /data/app/ 文件夹:存放所有 app 的 apk 文件
  • /data/data/ 文件夹:内部是 app 的包名,存储着应用程序相关的数据(shared_prefs、database、files、cache)

1.3 外部存储

概念:外部存储比较容易混淆,因为比较旧的 Android 版本跟新的 Android 版本是有差别的。在 Android 4.4 之前,手机自带的存储空间是作为内部存储,扩展的 SD 卡是最为外部存储的。但是从 Android 4.4 系统开始,Google 将 Android 的机身存储从概念上分为 “内部Internal” 和 “外部Internal” 两部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xBntJj97-1689213386723)(D:\Workspace\模块分析\原生-StorageManager\图\机身存储结构.png)]

外部存储的空间也有公共和私有的区别。对于应用而言,外部公共空间在一定权限下可以共享,而外部私有空间对于应用来说是无法互相访问的。

而对于可移动存储介质,例如 SD 卡、U盘等外部存储空间,外部存储即 storage 目录或者 mnt 目录。

关于外部存储的挂载,Android 原生中一直变动,在 Android 4.2 之后一直挂载在 /storage/emulated/legacy。而之前的 /storage/sdcard0、/sdcard、/mnt/sdcard 都软链接到 /storage/emulated/legacy 下。

在路径中出现的 “0”,是由于 Google 增加了多用户的概念。

1.3.1 用户空间文件系统 FUSE

FUSE,implementing filesystems in user space,在用户空间实现文件系统。为了实现更灵活的权限管理能力,Android 引入了 fuse 文件系统。

简单讲,用户可通过 fuse 在用户空间来定制实现自己的文件系统。fuse 本身是内核提供的一个功能,内核开启 fuse 支持后,会在 /dev 目录下,生成 fuse 设备节点,应用层可以通过该设备节点完成用户态文件系统。

libfuse 是一个对 fuse 功能封装的库,提供一系列的 api,使用户可以更方便、更简单的使用 fuse 功能,后面的内容都是基于 libfuse 来说明的。fuse 驱动加载完成后,可以在 /dev/ 看到创建了一个名为 fuse 的设备文件,fuse 即通过此设备来中转用户态与内核间的信息。

下图是一个简单的 APP 访问 /storage 下面的U盘的流程图,可以看到,APP 对U盘文件的读写,并不是直接在 APP 的进程内打开的,而是通过 FuseDaemon 访问的具体的文件。所以当出现 U盘 移除的时候,如果使用的 APP 没有及时释放占用的文件句柄的时候,就会出现 MediaProvider 被 kill 的情况,而不是使用的 APP 被直接 KILL。但是 MediaProvider 由于实现了 FuseDasemon,所以在 MediaProvider 被 kill 的时候,会重启,重新建立 fuse 的连接,这个时候,会把所有的挂载的存储都卸载了重新挂载一遍。

在这里插入图片描述

1.4 Android 中应用内外部存储路径

对于应用访问内外部存储路径,对如下 api 进行输出。

public void getStorageDirPath() {
        // 获取应用在内部存储的根路径
        File file = getContext().getDataDir();
        Log.d("Context().getDataDir()" , file.getAbsolutePath());

        // 获取应用在内部存储的 files 路径
        File file1 = getContext().getFilesDir();
        Log.d("Context().getFilesDir()" , file1.getAbsolutePath());

        // 获取应用在内部存储的 cache 缓存路径
        File file2 = getContext().getCacheDir();
        Log.d("Context().getCacheDir()" , file2.getAbsolutePath());

        // 获取外部存储根路径
        File file3 = Environment.getExternalStorageDirectory();
        Log.d("Environment.getExternalStorageDirectory()" , file3.getAbsolutePath());

        // 获取外部存储具体公共路径
        /*
         * 参数如下:
         * public static String DIRECTORY_MUSIC = "Music"
         * public static String DIRECTORY_PODCASTS = "Podcasts"
         * public static String DIRECTORY_RINGTONES = "Ringtones"
         * public static String DIRECTORY_ALARMS = "Alarms"
         * public static String DIRECTORY_NOTIFICATIONS = "Notifications"
         * public static String DIRECTORY_PICTURES = "Pictures"
         * public static String DIRECTORY_MOVIES = "Movies"
         * public static String DIRECTORY_DOWNLOADS = "Download"
         * public static String DIRECTORY_DCIM = "DCIM"
         * public static String DIRECTORY_DOCUMENTS = "Documents"
         */
        File file4 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
        Log.d("Environment.getExternalStoragePublicDirectory(Music)" , file4.getAbsolutePath());

        // 获取应用在外部存储中的 Files 路径
        File[] file5 = getContext().getExternalFilesDirs("");
        for (File file6 : file5) {
            Log.d("Context().getExternalFilesDirs()" , file6.getAbsolutePath());
        }

        // 获取应用在外部存储中的 cache 缓存路径
        File file7 = getContext().getExternalCacheDir();
        Log.d("Context().getExternalCacheDir()" , file7.getAbsolutePath());
    }

--------------------------------------------------------------------------------------------------------------------------------

输出如下:
D/Context().getDataDir(): /data/user/0/com.example.androidstudiotest
D/Context().getFilesDir(): /data/user/0/com.example.androidstudiotest/files
D/Context().getCacheDir(): /data/user/0/com.example.androidstudiotest/cache
D/Environment.getExternalStorageDirectory(): /storage/emulated/0
D/Environment.getExternalStoragePublicDirectory(Music): /storage/emulated/0/Music
D/Context().getExternalFilesDirs(): /storage/emulated/0/Android/data/com.example.androidstudiotest/files
D/Context().getExternalCacheDir(): /storage/emulated/0/Android/data/com.example.androidstudiotest/cache

表格如下:

存储区域方法备注
Internal Storage 内部存储Context.getDataDir()/data/user/0/包名
Context.getFilesDir()/data/user/0/包名/files
Context.getCacheDir()/data/user/0/包名/cache
External Storage 外部存储Environment.getExternalStoragePublicDirectory(String type)/storage/emulated/0/type
Context.getExternalFilesDirs()/storage/emulated/0/Android/data/包名/files
Context.getExternalCacheDir()/storage/emulated/0/Android/data/包名/cache

2 Storage 存储模块介绍

2.1 StorageManagerService 简介

Android 外部存储空间由 Vold 服务和 StorageManagerService 系统服务共同管理。外部实体存储卷的装载由 Vold 处理,准备好后上报给 StorageManagerService,然后再将其提供给应用。在 Android 8.0 及以后,MountService 服务已经更名为 StorageManagerServic,并且 StorageManagerService 与 Vold 的通信由 socket 变更为 binder 方式。

代码路径分布:

层级结构主要文件代码路径
应用 api 接口层StorageManager.javaandroid/frameworks/base/core/java/android/os/storage
系统 framework 层StorageManagerService.javaandroid/frameworks/base/services/core/java/com/android/server
Vold 服务VoldNativeService.cppandroid/system/vold

2.2 StorageManagerService 架构

下图是 StorageManagerService 的架构图

在这里插入图片描述

图中描述了 StorageManagerService 模块的架构,上层 framework 服务 StorageManagerService 是由 SystemService 在启动阶段开启;Vold 服务在 init 阶段由 rc 文件启动,StorageManagerService 与 Vold 服务通过 aidl 的方式交互,Vold 服务中 VoldNativeService 实现了 aidl 接口,是作为 aidl 的服务端,但实际处理是在 VolumeManager 中实现,NetlinkManager 是 VolumeManager 与 驱动层通信事件上报的处理类,NetlinkManager 和 kernel 建立 socket 通讯,监听 kernel 的 uevent 事件,当驱动检测到有 U盘 接入/接出时,会上报 event 事件给到 NetlinkHandler 处理,进入挂载/卸载流程。

Android 提供了一系列广播,应用可以通过这些广播来获取当前U盘状态

广播含义
Intent.ACTION_MEDIA_CHECKING检查
Intent.ACTION_MEDIA_MOUNTED挂载
Intent.ACTION_MEDIA_UNMOUNTABLE无法挂载,挂载失败(常见是U盘挂载节点已经存在无法继续挂载)
Intent.ACTION_MEDIA_EJECT硬件弹出
Intent.ACTION_MEDIA_UNMOUNTED卸载
Intent.ACTION_MEDIA_REMOVEDvolume已经移除,代表移除流程已经走完
Intent.ACTION_MEDIA_BAD_REMOVALvolume已经移除,代表移除流程已经走完,可能节点没有卸载干净

2.3 StorageManagerService 启动

2.3.1 SystemServer 阶段

SystemServer 会在 startOtherServices() 阶段启动 StorageManagerService 服务

路径:android/frameworks/base/services/java/com/android/server/SystemServer.java

	traceBeginAndSlog("StartStorageManagerService");
	try {
    	/*
   		 * NotificationManagerService is dependant on StorageManagerService,
   		 * (for media / usb notifications) so we must start StorageManagerService first.
   		 */
    	mSystemServiceManager.startService(STORAGE_MANAGER_SERVICE_CLASS);
        storageManager = IStorageManager.Stub.asInterface( ServiceManager.getService("mount"));
    } catch (Throwable e) {
         reportWtf("starting StorageManagerService", e);
    }
    traceEnd();
    traceBeginAndSlog("StartStorageStatsService");
2.3.2 StorageManagerService 阶段
2.3.2.1 onStart()

SystemManagerService 通过反射构建 LifeCycle 并调用 onStart() 方法

路径:android/frameworks/base/services/core/java/com/android/server/StorageManagerService.java

public static class Lifecycle extends SystemService {
        private StorageManagerService mStorageManagerService;

        public Lifecycle(Context context) {
            super(context);
        }

        @Override
        public void onStart() {
            mStorageManagerService = new StorageManagerService(getContext());
            publishBinderService("mount", mStorageManagerService);
            mStorageManagerService.start();
        }

        @Override
        public void onBootPhase(int phase) {
            if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
                mStorageManagerService.servicesReady();
            } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                mStorageManagerService.systemReady();
            } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
                mStorageManagerService.bootCompleted();
            }
        }

        @Override
        public void onSwitchUser(int userHandle) {
            mStorageManagerService.mCurrentUserId = userHandle;
        }

        @Override
        public void onUnlockUser(int userHandle) {
            mStorageManagerService.onUnlockUser(userHandle);
        }

        @Override
        public void onCleanupUser(int userHandle) {
            mStorageManagerService.onCleanupUser(userHandle);
        }
    }
2.3.2.2 StorageManagerService()

StorageManagerService 启动

路径:android/frameworks/base/services/core/java/com/android/server/StorageManagerService.java

public StorageManagerService(Context context) {
        sSelf = this;

        // 前面先是读取一些属性状态,其中关于FUSE下面会稍微介绍一下
        // Snapshot feature flag used for this boot
        SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
                SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));

        // If there is no value in the property yet (first boot after data wipe), this value may be
        // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if
        // different
        mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
        mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean(
                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
        mContext = context;
        mResolver = mContext.getContentResolver();
        mCallbacks = new Callbacks(FgThread.get().getLooper());
        mLockPatternUtils = new LockPatternUtils(mContext);
        // 创建名为“StorageManagerService”的线程,并创建对应的Handler
        HandlerThread hthread = new HandlerThread(TAG);
        hthread.start();
        mHandler = new StorageManagerServiceHandler(hthread.getLooper());
        // mObbActionHandler对应“android.io”线程
        // Add OBB Action Handler to StorageManagerService thread.
        mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());

        mStorageSessionController = new StorageSessionController(mContext, mIsFuseEnabled);

        // 启动installd服务
        mInstaller = new Installer(mContext);
        mInstaller.onStart();

        // Initialize the last-fstrim tracking if necessary
        File dataDir = Environment.getDataDirectory();
        File systemDir = new File(dataDir, "system");
        mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
        //判断/data/system/last-fstrim文件,不存在则创建,存在则更新最后修改时间
        if (!mLastMaintenanceFile.exists()) {
            // Not setting mLastMaintenance here means that we will force an
            // fstrim during reboot following the OTA that installs this code.
            try {
                (new FileOutputStream(mLastMaintenanceFile)).close();
            } catch (IOException e) {
                Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
            }
        } else {
            mLastMaintenance = mLastMaintenanceFile.lastModified();
        }

        // 读取data/system/storage.xml配置
        mSettingsFile = new AtomicFile(
                new File(Environment.getDataSystemDirectory(), "storage.xml"), "storage-settings");

        synchronized (mLock) {
            readSettingsLocked();
        }

        LocalServices.addService(StorageManagerInternal.class, mStorageManagerInternal);

        // 监听ACTION_USER_ADDED、ACTION_USER_REMOVED广播
        final IntentFilter userFilter = new IntentFilter();
        userFilter.addAction(Intent.ACTION_USER_ADDED);
        userFilter.addAction(Intent.ACTION_USER_REMOVED);
        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);

        // 内部私有volume的路径为/data,该volume通过dumpsys mount是不会显示的
        synchronized (mLock) {
            addInternalVolumeLocked();
        }

        // Add ourself to the Watchdog monitors if enabled.
        if (WATCHDOG_ENABLE) {
            Watchdog.getInstance().addMonitor(this);
        }

        // 汽车应用支持
        mIsAutomotive = context.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_AUTOMOTIVE);
    }

2.4 AIDL 接口层

Vold 的 aidl 文件定义在 /system/vold/binder/android/os/ 目录下,有三个文件:IVold.aidl、IVoldListener.aidl、IVoldTaskListener.aidl。

在这里插入图片描述

在 Android.bp 中的编译如下

路径:android/system/vold/Android.bp

filegroup {
    name: "vold_aidl",
    srcs: [
        "binder/android/os/IVold.aidl",
        "binder/android/os/IVoldListener.aidl",
        "binder/android/os/IVoldTaskListener.aidl",
    ],
}

cc_library_static {
    name: "libvold_binder",
    defaults: ["vold_default_flags"],

    srcs: [
        ":vold_aidl",
    ],
    shared_libs: [
        "libbinder",
        "libutils",
    ],
    aidl: {
        local_include_dirs: ["binder"],
        include_dirs: ["frameworks/native/aidl/binder"],
        export_aidl_headers: true,
    },
}

2.5 Vold 服务层

2.5.1 Vold 模块编译
路径:android/system/vold/Android.bp

cc_binary {
    name: "vold",
    defaults: [
        "vold_default_flags",
        "vold_default_libs",
    ],

    srcs: ["main.cpp"],
    static_libs: ["libvold"],
    product_variables: {
        arc: {
            static_libs: [
                "arc_services_aidl",
                "libarcappfuse",
                "libarcobbvolume",
            ],
        },
    },
    init_rc: [
        "vold.rc",
        "wait_for_keymaster.rc",
    ],

    required: [
        "mke2fs",
        "vold_prepare_subdirs",
        "wait_for_keymaster",
    ],

    shared_libs: [
        "android.hardware.health.storage@1.0",
        "libhidltransport",
    ],
}

从 vold 二进制可执行文件的编译配置中发现,服务入口是 main.cpp 文件,启动 rc 为 vold.rc 和 wait_for_keymaster.rc。

2.5.2 rc 文件
路径:android/system/vold/vold.rc

service vold /system/bin/vold \
        --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
        --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
    class core
    ioprio be 2
    writepid /dev/cpuset/foreground/tasks
    shutdown critical
    group root reserved_disk

vold 可执行文件在 /system/bin/ 目录下。

2.5.3 服务入口文件

vold 服务的入口文件是 main.cpp。

路径:android/system/vold/main.cpp

int main(int argc, char** argv) {
    
    // 初始化日志相关服务
    atrace_set_tracing_enabled(false);
    setenv("ANDROID_LOG_TAGS", "*:d", 1);  // Do not submit with verbose logs enabled
    android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));

    LOG(INFO) << "Vold 3.0 (the awakening) firing up";

    ATRACE_BEGIN("main");

    LOG(DEBUG) << "Detected support for:"
               << (android::vold::IsFilesystemSupported("exfat") ? " exfat" : "")
               << (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")
               << (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")
               << (android::vold::IsFilesystemSupported("ntfs") ? " ntfs" : "")
               << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");

	// 定义 VolumeManager、NetlinkManager 对象
	// VolumeManager 对象是 vold aidl 接口的实际处理类
	// NetlinkManager 对象是用于 vold 与 驱动通信的处理类
    VolumeManager* vm;
    NetlinkManager* nm;

    parse_args(argc, argv);

    sehandle = selinux_android_file_context_handle();
    if (sehandle) {
        selinux_android_set_sehandle(sehandle);
    }

    // 建立 /dev/block/vold 文件夹
    mkdir("/dev/block/vold", 0755);

    /* For when cryptfs checks and mounts an encrypted filesystem */
    klog_set_level(6);

    /* Create our singleton managers */
    // 初始化 VolumeManager 对象
    if (!(vm = VolumeManager::Instance())) {
        LOG(ERROR) << "Unable to create VolumeManager";
        exit(1);
    }

    // 初始化 NetlinkManager 对象
    if (!(nm = NetlinkManager::Instance())) {
        LOG(ERROR) << "Unable to create NetlinkManager";
        exit(1);
    }

    // debug 属性
    if (android::base::GetBoolProperty("vold.debug", false)) {
        vm->setDebug(true);
    }

    // 调用 volumeManager 类的 start() 函数
    if (vm->start()) {
        PLOG(ERROR) << "Unable to start VolumeManager";
        exit(1);
    }

    bool has_adoptable;
    bool has_quota;
    bool has_reserved;

    if (process_config(vm, &has_adoptable, &has_quota, &has_reserved)) {
        PLOG(ERROR) << "Error reading configuration... continuing anyways";
    }

    android::hardware::configureRpcThreadpool(1, false /* callerWillJoin */);

    ATRACE_BEGIN("VoldNativeService::start");
    if (android::vold::VoldNativeService::start() != android::OK) {
        LOG(ERROR) << "Unable to start VoldNativeService";
        exit(1);
    }
    ATRACE_END();

    LOG(DEBUG) << "VoldNativeService::start() completed OK";

    ATRACE_BEGIN("NetlinkManager::start");
    // 调用 NetlinkManager 对象的 start() 函数
    if (nm->start()) {
        PLOG(ERROR) << "Unable to start NetlinkManager";
        exit(1);
    }
    ATRACE_END();

    // This call should go after listeners are started to avoid
    // a deadlock between vold and init (see b/34278978 for details)
    android::base::SetProperty("vold.has_adoptable", has_adoptable ? "1" : "0");
    android::base::SetProperty("vold.has_quota", has_quota ? "1" : "0");
    android::base::SetProperty("vold.has_reserved", has_reserved ? "1" : "0");

    // Do coldboot here so it won't block booting,
    // also the cold boot is needed in case we have flash drive
    // connected before Vold launched
    coldboot("/sys/block");

    ATRACE_END();

    android::IPCThreadState::self()->joinThreadPool();
    LOG(INFO) << "vold shutting down";

    exit(0);
}

3 ChatGPT 对话

问:Android 内部存储与外部存储

ChatGPT 答:

Android设备的存储分为内部存储和外部存储。

内部存储是指设备的固定存储空间,通常是设备的闪存存储器。它用于存储操作系统、应用程序和用户数据。内部存储对于应用程序来说是私有的,只有应用程序本身才能访问和修改其存储的数据。内部存储通常具有较小的容量,一般在几十GB到几百GB之间。

外部存储是指设备上可移动的存储介质,如SD卡或USB存储设备。它用于存储用户的媒体文件、文档和其他数据。外部存储对于应用程序来说是共享的,多个应用程序可以访问和修改外部存储的数据。外部存储通常具有较大的容量,可以根据用户的需求进行扩展。

在Android中,可以使用内部存储和外部存储来保存和获取数据。对于应用程序的私有数据,通常使用内部存储;对于用户共享的数据,可以使用外部存储。可以使用Android的API来访问和管理内部存储和外部存储的数据。

问:Android 内部存储与外部存储的区别

ChatGPT 答:

Android的内部存储和外部存储有以下区别:

  1. 访问权限:内部存储对于应用程序是私有的,只有应用程序本身才能访问和修改其存储的数据。外部存储是共享的,多个应用程序可以访问和修改外部存储的数据。

  2. 存储容量:内部存储通常具有较小的容量,一般在几十GB到几百GB之间。外部存储通常具有较大的容量,可以根据用户的需求进行扩展。

  3. 数据保留:当应用程序被卸载时,内部存储中的数据会被删除。外部存储中的数据不会被自动删除,除非用户手动删除。

  4. 移动性:内部存储是设备的固定存储空间,无法移动到其他设备上。外部存储是可移动的存储介质,可以将存储的数据从一个设备移动到另一个设备。

  5. 备份和恢复:内部存储中的数据通常不会被包含在设备的备份中,因为这些数据可以通过重新安装应用程序来恢复。外部存储中的数据可以包含在设备的备份中,以便在需要时进行恢复。

总的来说,内部存储适合存储应用程序的私有数据,而外部存储适合存储用户的共享数据和媒体文件。根据具体的需求和数据类型,可以选择使用内部存储或外部存储来存储和管理数据。

问:Android 哪些是内部存储,哪些是外部存储

ChatGPT 答:

在Android设备中,一般情况下,以下是内部存储和外部存储的常见示例:

内部存储:

  1. /data目录:这是Android设备的内部存储的根目录,包含应用程序的私有数据、数据库、缓存等。
  2. /data/data/<package_name>/目录:每个应用程序都有一个私有目录,用于存储该应用程序的数据和文件。
  3. /data/app目录:这是存储已安装应用程序的目录,包括应用程序的APK文件和相关的数据。

外部存储:

  1. SD卡:外部存储通常指的是设备上的可移动存储介质,如SD卡。它可以用来存储用户的媒体文件、文档和其他数据。
  2. 外部存储目录:在Android设备上,通常有一个名为"sdcard"或"external"的目录,用于表示外部存储的根目录。这个目录可以是内置的存储卡,也可以是外部插入的SD卡或USB存储设备。
  3. 公共存储目录:Android提供了一些公共的存储目录,如Music、Pictures、Downloads等,用于存储特定类型的媒体文件。

需要注意的是,具体的存储路径可能因设备和Android版本的不同而有所变化。可以使用Android的API来获取正确的内部存储和外部存储路径。

问:Android 内部存储路径

ChatGPT 答:

在Android设备中,内部存储的根目录路径是"/data"。每个应用程序都有一个私有目录,其路径格式为"/data/data/<package_name>“,其中”<package_name>"是应用程序的包名。在该私有目录下,应用程序可以创建子目录来存储其数据和文件。

例如,如果应用程序的包名是"com.example.app",那么其私有目录的路径将是"/data/data/com.example.app"。

需要注意的是,由于内部存储是应用程序的私有空间,其他应用程序无法直接访问或修改该目录下的数据。只有具有相同用户ID(UID)的应用程序才能访问对方的内部存储数据。

此外,Android还提供了一些特定的目录,用于存储不同类型的数据,例如:

  1. /data/data/<package_name>/databases:用于存储应用程序的数据库文件。
  2. /data/data/<package_name>/cache:用于存储应用程序的缓存文件。
  3. /data/app:用于存储已安装应用程序的APK文件和相关数据。

需要注意的是,内部存储的数据在应用程序被卸载时会被删除,因此不适合存储需要长期保存的重要数据。对于需要长期保存的数据,应该考虑使用外部存储或其他持久性存储解决方案。

问:Android 外部存储路径

ChatGPT 答:

在Android设备中,外部存储的路径可以有多个,具体取决于设备的配置和外部存储介质的类型。以下是一些常见的外部存储路径示例:

  1. SD卡路径:如果设备有SD卡插槽,外部存储的路径通常是"/mnt/sdcard"或"/storage/sdcard"。需要注意的是,具体路径可能因设备和Android版本的不同而有所变化。

  2. 外置SD卡路径:如果设备支持外置SD卡,外部存储的路径通常是"/mnt/external_sd"或"/storage/extSdCard"。类似地,具体路径可能因设备和Android版本的不同而有所变化。

  3. USB存储路径:如果设备支持USB存储设备,外部存储的路径通常是"/mnt/usb_storage"或"/storage/usb"。同样,具体路径可能因设备和Android版本的不同而有所变化。

需要注意的是,外部存储的路径可能因设备和Android版本的不同而有所变化。为了获取正确的外部存储路径,可以使用Android的API来动态获取路径,而不是硬编码路径。

此外,Android还提供了一些公共的存储目录,例如:

  1. Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS):用于存储文档文件。
  2. Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES):用于存储图片文件。
  3. Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC):用于存储音乐文件。
  4. Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS):用于存储下载文件。

这些公共存储目录的路径可能因设备和Android版本的不同而有所变化。可以使用上述API来获取正确的公共存储目录路径。

问:Google 原生对于内部存储与外部存储的官方解释

ChatGPT 答:

根据Google官方的解释,内部存储和外部存储在Android中具有不同的特点和用途。

内部存储(Internal Storage)是应用程序的私有存储空间,用于存储应用程序的数据和文件。每个应用程序都有一个私有目录,其他应用程序无法直接访问或修改该目录下的数据。内部存储的数据在应用程序被卸载时会被删除,因此不适合存储需要长期保存的重要数据。内部存储通常用于存储应用程序的数据库文件、缓存文件和其他私有数据。

外部存储(External Storage)是设备共享的存储空间,用于存储用户和应用程序共享的数据和文件。外部存储可以是设备上的SD卡、外置SD卡或USB存储设备。外部存储的数据可以被用户和其他应用程序访问,因此适合存储需要与其他应用程序共享或需要长期保存的数据。外部存储通常用于存储用户的文档、图片、音乐、下载文件等。

需要注意的是,外部存储的可用性和容量可能因设备和存储介质的不同而有所变化。而且,外部存储的数据可能会受到用户的操作或设备的限制而被删除或修改。因此,在使用外部存储时,应该考虑数据的备份和恢复策略,以确保数据的安全性和可靠性。

总结来说,内部存储适合存储应用程序的私有数据和临时文件,而外部存储适合存储用户和应用程序共享的数据和长期保存的重要文件。根据具体的需求和使用场景,开发人员可以选择合适的存储方式来管理和操作数据。

问:怎么区分 Android 的内部存储与外部存储

ChatGPT 答:

在Android中,可以通过以下方法来区分内部存储和外部存储:

  1. 获取根目录路径:

    • 内部存储的根目录路径可以使用Context.getFilesDir()方法获取。该方法返回一个File对象,表示应用程序的私有目录。
    • 外部存储的根目录路径可以使用Environment.getExternalStorageDirectory()方法获取。该方法返回一个File对象,表示设备的外部存储根目录。
  2. 判断路径是否为内部存储:

    • 可以使用File.getAbsolutePath()方法获取文件或目录的绝对路径,并与内部存储的根目录路径进行比较。如果路径以内部存储的根目录路径开头,则可以判断为内部存储。
    • 例如,可以使用file.getAbsolutePath().startsWith(context.getFilesDir().getAbsolutePath())来判断路径是否为内部存储。
  3. 判断路径是否为外部存储:

    • 可以使用File.getAbsolutePath()方法获取文件或目录的绝对路径,并与外部存储的根目录路径进行比较。如果路径以外部存储的根目录路径开头,则可以判断为外部存储。
    • 例如,可以使用file.getAbsolutePath().startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())来判断路径是否为外部存储。

需要注意的是,具体的内部存储和外部存储路径可能因设备和Android版本的不同而有所变化。为了获取准确的内部存储和外部存储路径,建议使用上述方法来动态获取路径,而不是硬编码路径。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值