You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android 10以下媒体存储方案是否合理?Android 10+替代方案咨询

聊天应用媒体存储方案分析与Android 10+适配指南

嘿,针对你这个聊天应用的媒体存储问题,我来给你详细拆解下:

一、Android 10及以下方案的正确性

你的方案在Android 10(API 29)及以下是完全靠谱的,理由如下:

  • 这个版本范围内,getExternalStorageDirectory()是官方认可的外部存储访问方式,你可以自由创建应用专属文件夹(比如/sdcard/你的应用包名/chat_media/),这类文件属于应用专属空间,卸载应用时会被自动清理,完全符合聊天媒体的存储需求。
  • 把文件的URI路径存在SQLite里做离线访问是合理的,因为本地路径在这些版本里能直接通过File类读写,不需要额外的权限绕弯。
  • 把URI路径发去服务器的逻辑也没问题,用户卸载重装后,只要服务器能返回这个路径,只要文件没被用户手动删除,就能直接通过路径访问或重新下载。

不过要提个小细节:别忘了申请WRITE_EXTERNAL_STORAGE权限,而且Android 6.0+要做运行时动态请求,不然会碰权限异常。

二、Android 10及以上的替代方案(废弃getExternalStorageDirectory()后的适配)

从Android 10开始,Google推了Scoped Storage(分区存储),旧的根目录访问方法被废弃了。结合你的聊天应用需求,推荐两种适配思路:

方案1:用应用专属外部存储目录(最贴近你的原有逻辑)

这个方案不用折腾复杂的MediaStore操作,和你之前的习惯最像:

  • Context.getExternalFilesDir(String type)获取应用专属的外部存储目录,比如存图片的话可以这么写:
    File mediaDir = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "chat_media");
    // 音频就换DIRECTORY_AUDIO,视频换DIRECTORY_MOVIES
    if (!mediaDir.exists()) {
        mediaDir.mkdirs();
    }
    
  • 这个目录的路径大概是/sdcard/Android/data/你的应用包名/files/Pictures/chat_media/,属于应用私有的外部空间,卸载会自动清掉,而且Android 10+不需要WRITE_EXTERNAL_STORAGE权限。
  • 存到SQLite的话直接用文件的绝对路径就行,离线访问时直接通过File类打开。
  • 发去服务器的话,建议别发绝对路径,改成发相对路径(比如chat_media/xxx.jpg),因为不同设备的根路径可能不一样,用户重装后,你用getExternalFilesDir()拼上相对路径就能找到文件。

方案2:用MediaStore共享存储(适合需要其他应用访问的场景)

如果你的聊天媒体需要被系统相册或者其他应用识别到,那可以用MediaStore来存:

  • 以图片为例,插入到MediaStore的代码大概是这样:
    ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.DISPLAY_NAME, "chat_image.jpg");
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    // Android 10+要设置RELATIVE_PATH指定存储子目录
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/你的应用名/chat_media");
    }
    Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
    // 把媒体数据写入到uri对应的输出流
    try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) {
        // 这里写入你的文件数据就行
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  • 存到SQLite的应该是这个Uri的字符串形式(比如content://media/external/images/media/1234),离线访问时用ContentResolver.openInputStream(uri)读取文件。
  • 发去服务器的话,不能直接发这个content uri(其他设备识别不了),建议发文件的唯一标识(比如文件名加哈希值),用户重装后,要么通过MediaStore查询对应文件,要么从服务器重新下载。

三、跨版本兼容的小提醒

  • Build.VERSION.SDK_INT判断系统版本,分情况执行不同的存储逻辑就行。
  • 如果选方案1,要知道Android 11+里这个目录的文件其他应用访问不了,刚好符合聊天内容的隐私需求。
  • 服务器同步的时候,尽量别存绝对路径,改用相对路径或者唯一标识,这样在不同版本和设备上都能兼容。

内容的提问来源于stack exchange,提问作者lifelonglearner

火山引擎 最新活动