android 各种存储目录使用总结之Context#getExternalFilesDir

Posted 小刘学安卓

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 各种存储目录使用总结之Context#getExternalFilesDir相关的知识,希望对你有一定的参考价值。

Context类中定义了获取手机系统目录的一些api。这里做一个总结:

一、api 注释翻译

1、File getExternalFilesDir(@Nullable String type)

@Nullable
public abstract File getExternalFilesDir(@Nullable String type);
    /**
     * Returns the absolute path to the directory on the primary shared/external
     * storage device where the application can place persistent files it owns.
     * These files are internal to the applications, and not typically visible
     * to the user as media.
     * <p>
     * This is like @link #getFilesDir() in that these files will be deleted
     * when the application is uninstalled, however there are some important
     * differences:
     * <ul>
     * <li>Shared storage may not always be available, since removable media can
     * be ejected by the user. Media state can be checked using
     * @link Environment#getExternalStorageState(File).
     * <li>There is no security enforced with these files. For example, any
     * application holding
     * @link android.Manifest.permission#WRITE_EXTERNAL_STORAGE can write to
     * these files.
     * </ul>
     * <p>
     * If a shared storage device is emulated (as determined by
     * @link Environment#isExternalStorageEmulated(File)), it's contents are
     * backed by a private user data partition, which means there is little
     * benefit to storing data here instead of the private directories returned
     * by @link #getFilesDir(), etc.
     * <p>
     * Starting in @link android.os.Build.VERSION_CODES#KITKAT, no permissions
     * are required to read or write to the returned path; it's always
     * accessible to the calling app. This only applies to paths generated for
     * package name of the calling application. To access paths belonging to
     * other packages,
     * @link android.Manifest.permission#WRITE_EXTERNAL_STORAGE and/or
     * @link android.Manifest.permission#READ_EXTERNAL_STORAGE are required.
     * <p>
     * On devices with multiple users (as described by @link UserManager),
     * each user has their own isolated shared storage. Applications only have
     * access to the shared storage for the user they're running as.
     * <p>
     * The returned path may change over time if different shared storage media
     * is inserted, so only relative paths should be persisted.
     * <p>
     * Here is an example of typical code to manipulate a file in an
     * application's shared storage:
     * </p>
     * @sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
     * private_file
     * <p>
     * If you supply a non-null <var>type</var> to this function, the returned
     * file will be a path to a sub-directory of the given type. Though these
     * files are not automatically scanned by the media scanner, you can
     * explicitly add them to the media database with
     * @link android.media.MediaScannerConnection#scanFile(Context, String[], String[], android.media.MediaScannerConnection.OnScanCompletedListener)
     * MediaScannerConnection.scanFile. Note that this is not the same as
     * @link android.os.Environment#getExternalStoragePublicDirectory
     * Environment.getExternalStoragePublicDirectory(), which provides
     * directories of media shared by all applications. The directories returned
     * here are owned by the application, and their contents will be removed
     * when the application is uninstalled. Unlike
     * @link android.os.Environment#getExternalStoragePublicDirectory
     * Environment.getExternalStoragePublicDirectory(), the directory returned
     * here will be automatically created for you.
     * <p>
     * Here is an example of typical code to manipulate a picture in an
     * application's shared storage and add it to the media database:
     * </p>
     * @sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
     * private_picture
     *
     * @param type The type of files directory to return. May be @code null
     *            for the root of the files directory or one of the following
     *            constants for a subdirectory:
     *            @link android.os.Environment#DIRECTORY_MUSIC,
     *            @link android.os.Environment#DIRECTORY_PODCASTS,
     *            @link android.os.Environment#DIRECTORY_RINGTONES,
     *            @link android.os.Environment#DIRECTORY_ALARMS,
     *            @link android.os.Environment#DIRECTORY_NOTIFICATIONS,
     *            @link android.os.Environment#DIRECTORY_PICTURES, or
     *            @link android.os.Environment#DIRECTORY_MOVIES.
     * @return the absolute path to application-specific directory. May return
     *         @code null if shared storage is not currently available.
     * @see #getFilesDir
     * @see #getExternalFilesDirs(String)
     * @see Environment#getExternalStorageState(File)
     * @see Environment#isExternalStorageEmulated(File)
     * @see Environment#isExternalStorageRemovable(File)
     */

        函数api描述信息翻译如下:

        getExternalFilesDir函数返回主要的共享/外部存储设备的目录的绝对路径,在这些路径上应用程序可以存放应用程序自身的持久性文件。这些持久性文件是程序内部的,通常不会作为媒体对用户可见。

        当应用被卸载后,这些文件也会被删除掉,这有点类似于getFilesDir()函数。然而它们之间也有一些重要的不同点,如下:

  • 共享存储可能不总是可用的,因为用户可以弹出可移动媒体设备。可以使用 Environment#getExternalStorageState(File)来检查媒体状态;
  • 这些文件没有安全强制,比如,任何应用具有android.Manifest.permission#WRITE_EXTERNAL_STORAGE权限都可以更改这些文件;

        如果共享存储设备是被模拟的(由Environment#isExternalStorageEmulated(File)决定),那么它的内容被私有用户数据分区支持,这意味着在这里存储数据是有一点好处的,而不是使用getFilesDir()返回的私有目录。

        从android.os.Build.VERSION_CODES#KITKAT (sdk19,即android 4.4)开始,读取和写人这些返回的目录都不再需要任何权限。这些目录总是可被当前正在调用该函数的app访问的。这仅适用于为调用应用程序的包名称生成的路径。要访问属于其他包名的路径,需要获取android.Manifest.permission#WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限。

        在多用户的设备上,每一个用户拥有自己独立的共享存储目录。应用程序仅仅可以访问当前正在运行用户的共享目录。

        如果插入不同的共享存储介质,返回的路径可能会随着时间而改变,因此只应保留相对路径。

        这里有一个在应用程序共享存储上操作文件通用代码的例子private_file,见链接:http://androidxref.com/9.0.0_r3/xref/development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java

        如果你传入了一个非空类型的参数到该函数中,返回的路径将会是给定目录的子目录。尽管媒体扫描仪不会自动扫描这些文件,但你可以使用MediaScannerConnection.scanFile明确地把它们添加到媒体数据库中。

        需要注意它不同于Environment.getExternalStoragePublicDirectory(),后者提供被所有应用程序共享的媒体目录。这里返回的目录仅仅被应用程序自己拥有,并且当应用程序被卸载时这些目录也被删除。不同于Environment.getExternalStoragePublicDirectory(),这里返回的目录将会为你自动创建。

        这里有一个在应用程序共享存储中操作图片和增加图片到媒体数据库中的通用代码的例子private_picture。见链接http://androidxref.com/9.0.0_r3/xref/development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java

        type参数说明:

        type代表要返回的文件目录的类型,有可能为null或其他值。null代表文件目录的根目录,其他取值代表根目录的子目录,分别为:

Environment#DIRECTORY_MUSIC

Environment#DIRECTORY_PODCASTS

Environment#DIRECTORY_RINGTONES

Environment#DIRECTORY_ALARMS

Environment#DIRECTORY_NOTIFICATIONS

Environment#DIRECTORY_PICTURES

Environment#DIRECTORY_MOVIES        

        返回值说明:应用程序指定目录的绝对路径,如果共享存储当前不可用可能返回null。

参考:

Context#getFilesDir
Context#getExternalFilesDirs(String)
Environment#getExternalStorageState(File)
Environment#isExternalStorageEmulated(File)
Environment#isExternalStorageRemovable(File)

二、参考文章

都快写完了,还是没介绍函数返回的具体的路径是什么, 这里我补充一下吧:

  1. 该方法用于获得外部存储,地址为/storage/emulated/0/Android/data/packageName/files
  2. 该方法可传一个String类型的参数,表述为该路径下的文件夹,没有该文件夹会自动创建
  3. 使用方法
    ​String path=context.getExternalFilesDir(null).getAbsolutePath();
    File file=new File(path);
    //输出:path:/storage/emulated/0/Android/data/backageName/files
    
    String path2=context.getExternalFilesDir("UniApp").getAbsolutePath();
    File file2=new File(path2);
    //path:/storage/emulated/0/Android/data/packageName/files/UniApp
    //如uniapp文件夹没有,则会自动创建
    
    String path3=context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
    File file3=new File(path3);
    //path:/storage/emulated/0/Android/data/packageName/files/Download
    
    
    String path4=context.getExternalFilesDir("").getAbsolutePath()+"/hhhhhh";
    File file4=new File(path4);
    //path:storage/emulated/0/Android/data/packageName/files/hhhhhh
    //如果没有“hhhhhh”文件夹,则file4.exists()==false;
    
    ​

    4.该目录主要是用来存放应用私有的一些文件。这个目录有几个特性:

自动删除
        当应用卸载的时候,这些文件会被删除。当然前提是sd卡可用。

其他应用访问
        这些文件可以被其他应用访问,但是前提是其他应用有外部存储权限。

        但是注意在Android 11以后即使有存储权限,其他应用也不允许访问这些文件了。

存储权限
        在Android api 19(即4.4)之后,本应用即使没有存储权限,也可以访问这些文件,虽然它们是在sd卡上。官方文档如下:

Starting in @link android.os.Build.VERSION_CODES#KITKAT, no permissions
are required to read or write to the returned path; it’s always
accessible to the calling app. This only applies to paths generated for
package name of the calling application. To access paths belonging to
other packages,

        鉴于目前大部分App的minSdkVersion都是19或以上,所以当使用这个目录的时候,是没必要再请求存储权限的。

三、与getExternalFilesDirs的区别

        我们来看看getExternalFilesDirs的注释:

* @return the absolute paths to application-specific directories. Some
*         individual paths may be @code null if that shared storage is
*         not currently available. The first path returned is the same as
*         @link #getExternalFilesDir(String).

        getExternalFilesDirs函数返回的是:应用程序指定目录集合的绝对路径。如果共享存储当前不可用,一些独立路径集合可能为null。返回的第一个路径和getExternalFilesDir(String)是一样的。

        下面我通过打断点调试查看下getExternalFilesDirs返回路径数组具体取值包含哪些内容:

 参考文章:

1、getExternalFilesDir到底是什么_BennuCTech的博客-CSDN博客_getexternalfilesdir

2、【Android】getExternalFilesDir()内部参数_聪明努力的积极向上的博客-CSDN博客_android getexternalfilesdir

3、Android 兼容系列文章五之存储卡 - 掘金

Android开发之assets目录下资源使用总结

预前知识:

Android资源文件分类:


Android资源文件大致可以分为两种:

第一种是res目录下存放的可编译的资源文件:

    这种资源文件系统会在R.Java里面自动生成该资源文件的ID,所以访问这种资源文件比较简单,通过R.XXX.ID即可;

 第二种是assets目录下存放的原生资源文件:

      因为系统在编译的时候不会编译assets下的资源文件,所以我们不能通过R.XXX.ID的方式访问它们。那我么能不能通过该资源的绝对路径去访问它们呢?因为apk安装之后会放在/data/app/**.apk目录下,以apk形式存在,asset/res和被绑定在apk里,并不会解压到/data/data/YourApp目录下去,所以我们无法直接获取到assets的绝对路径,因为它们根本就没有。

还好android系统为我们提供了一个AssetManager工具类。

      查看官方API可知,AssetManager提供对应用程序的原始资源文件进行访问;这个类提供了一个低级别的API,它允许你以简单的字节流的形式打开和读取和应用程序绑定在一起的原始资源文件。

AssetManager类


概述:

       提供对应用程序的原始资源文件进行访问;这个类提供了一个低级别的API,它允许你以简单的字节流的形式打开和读取和应用程序绑定在一起的原始资源文件。通过getAssets()方法获取AssetManager对象。

AssetManager类常用方法:

Public Methods

final String[]

list(String path)

返回指定路径下的所有文件及目录名。

final InputStream

open(String fileName)

使用 ACCESS_STREAMING模式打开assets下的指定文件。.

final InputStream

open(String fileName, int accessMode)

使用显示的访问模式打开assets下的指定文件.

应用实例


1.加载assets目录下的网页:

//加载assets/win8_Demo/目录下的index.html网页

webView.loadUrl("file:///android_asset/win8_Demo/index.html");

说明:这种方式可以加载assets目录下的网页,并且与网页有关的cssjs,图片等文件也会的加载。

技术分享图片

2.访问assets目录下的资源文件:

       AssetManager.open(String filename),返回的是一个InputSteam类型的字节流,这里的filename必须是文件比如

(aa.txt;img/semll.jpg),而不能是文件夹。

3.获取assets的文件及目录名:

//获取assets目录下的所有文件及目录名,content(当前的上下文如ActivityServiceContextWrapper的子类的

都可以)

String fileNames[] =context.getAssets().list(path);     

4.将assets下的文件复制到SD卡:

[java] view plain copy
 print?
  1. /**  
  2.  *  从assets目录中复制整个文件夹内容  
  3.  *  @param  context  Context 使用CopyFiles类的Activity 
  4.  *  @param  oldPath  String  原文件路径  如:/aa  
  5.  *  @param  newPath  String  复制后路径  如:xx:/bb/cc  
  6.  */   
  7. public void copyFilesFassets(Context context,String oldPath,String newPath) {                      
  8.          try {  
  9.         String fileNames[] = context.getAssets().list(oldPath);//获取assets目录下的所有文件及目录名  
  10.         if (fileNames.length > 0) {//如果是目录  
  11.             File file = new File(newPath);  
  12.             file.mkdirs();//如果文件夹不存在,则递归  
  13.             for (String fileName : fileNames) {  
  14.                copyFilesFassets(context,oldPath + "/" + fileName,newPath+"/"+fileName);  
  15.             }  
  16.         } else {//如果是文件  
  17.             InputStream is = context.getAssets().open(oldPath);  
  18.             FileOutputStream fos = new FileOutputStream(new File(newPath));  
  19.             byte[] buffer = new byte[1024];  
  20.             int byteCount=0;                 
  21.             while((byteCount=is.read(buffer))!=-1) {//循环从输入流读取 buffer字节          
  22.                 fos.write(buffer, 0, byteCount);//将读取的输入流写入到输出流  
  23.             }  
  24.             fos.flush();//刷新缓冲区  
  25.             is.close();  
  26.             fos.close();  
  27.         }  
  28.     } catch (Exception e) {  
  29.         // TODO Auto-generated catch block  
  30.         e.printStackTrace();  
  31.         //如果捕捉到错误则通知UI线程  
  32.                    MainActivity.handler.sendEmptyMessage(COPY_FALSE);  
  33.     }                             
  34. }  
  35.           
技术分享图片技术分享图片

5.使用assets目录下的图片资源:

[java] view plain copy
 print?
  1. InputStream is=getAssets().open("wpics/0ZR424L-0.jpg");  
  2. Bitmap bitmap=BitmapFactory.decodeStream(is);  
  3. imgShow.setImageBitmap(bitmap);  
技术分享图片

6.播放assets目录下的音乐

首先,获取通过openFd()的方法获取asset目录下指定文件的AssetFileDescriptor对象

其次,通过MediaPlayer对象的setDataSource (FileDescriptorfd, longoffset, long length)方法加载音乐文件。

最后,调用prepare方法准备音乐,start方法开始播放音乐。

预备知识:

AssetFileDescriptor简介:

        在AssetManager中一项的文件描述符。这提供你自己打开的FileDescriptor可用于读取的数据,以及在文件中的

偏移量和长度的该项的数据。

可以通过AssetManageropenFd()的方法获取asset目录下指定文件的AssetFileDescriptor对象。

 常用方法:

Public Methods

FileInputStream

createInputStream()

为asset创建并返回一个自动关闭的输入流。

FileOutputStream

createOutputStream()

为asset创建并返回一个自动关闭的输出流。

FileDescriptor

getFileDescriptor()

返回可用于读取文件中的数据的FileDescriptor对象。

long

getLength()

返回该asset中项的数据的总字节数。

long

getStartOffset()

返回asset中项的数据字节开始偏移。

具体代码:

[java] view plain copy
 print?
  1. // 打开指定音乐文件,获取assets目录下指定文件的AssetFileDescriptor对象  
  2. AssetFileDescriptor afd = am.openFd(music);  
  3. mPlayer.reset();  
  4. // 使用MediaPlayer加载指定的声音文件。  
  5. mPlayer.setDataSource(afd.getFileDescriptor(),  
  6.     afd.getStartOffset(), afd.getLength());  
  7. // 准备声音  
  8. mPlayer.prepare();  
  9. // 播放  
  10. mPlayer.start();  





以上是关于android 各种存储目录使用总结之Context#getExternalFilesDir的主要内容,如果未能解决你的问题,请参考以下文章

Android之SharedPreferences使用

总结系列-Android文件存储相关

总结系列-Android文件存储相关

Android开发之assets目录下资源使用总结

Android之四大组件六大布局五大存储 总结

Android开发之assets目录下资源使用总结