您的位置:首页 > 移动开发 > Android开发

Android学习日记——数据存储5部曲之文件存储

2014-10-28 00:14 405 查看
二、文件存储

1简介:文件存储类似与JAVA的IO流技术对文件进行读写。需要注意的是:Android是基于Linux架构的,所以除了应用的私有目录/data/data/其他的系统文件因为权限问题无法进行读写

2存储区域:内部存储与外部存储

2.1内部存储:一般指设备自带的非易失性存储器,有如下特点:

永远可用。

存储在内部存储区域的数据默认情况下只对你的app可用。无论是用户或者是其他app都不能访问你的数据。

当用户卸载你的app时,系统会自动移除app在内部存储上的所有文件。

2.2外部存储:指可拆卸的存储介质,比如微型的SD卡。有如下特点:

不一定一直可以访问,因为用户可以拆卸外部存储设备。

存储在外部存储的文件是全局可读的,没有访问限制,不受你的控制。可以和其他apps分享数据,用户使用电脑也可以访问在外部存储中的文件。

当用户卸载你的app时,只有当你把文件存储在以 getExternalFilesDir().获得的路径下时,系统才会帮你自动移除。

2.3注意:

一些设备把永久的存储区域分为"internal"和"external"的分区,所以即便没有可拆卸的存储介质,这些设备永远都有两种存储区域,并且不管外部存储区到底是可拆卸的还是内置的,APIs的行为是一致的。

默认情况下app是安装在内存上的,可以通过在manifest中指定android:installLocation 属性来安排app的安装位置。具体见AppInstall Location.

3不同存储方式的存储操作
3.1内部存储(在手机内存读写文件):与JAVA的IO流相同的操作,只是Android提供了两个特殊的方法来获得流。

// 打开文件输入流
FileInputStream fis = openFileInput("YOU_FILE_NAME");
//打开文件输出流
FileOutputStream fos = openFileOutput("YOU_FILE_NAME", MODE_APPEND);
如果需要缓存一些文件,你应该使用createTempFile().比如,下面的例子从一个URL中提取了文件名,然后利用该文件名创建了一个文件存储在应用的内部缓存路径下:
public File getTempFile(Context context, String url) {
File file;
try {

4000
String fileName = Uri.parse(url).getLastPathSegment();
file = File.createTempFile(fileName, null, context.getCacheDir());
catch (IOException e) {
// Error while creating file
}
return file;
}


3.2外部存储(读写SD卡)
步骤:
1调用Environment的getExternalStorageState()方法判断是否有SD卡且应用程序是否具有读写SD卡的权限。

2调用Environment的getExternalStorageDirectory()方法来获取外部存储器,也就是SD卡的目录

3使用FileInputStream,FileOutputStream,FileReader,FileWriter读写SD卡里的文件

为了能够读写SD卡上的文件,应该在应用程序的清单文件中添加相应权限。

<!-- 在SD卡中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 向SD卡写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 向SD卡读取数据权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
PS:如果你的应用包含了WRITE_EXTERNAL_STORAGE 权限,它隐式地包含了读取权限。
因为外部存储很有可能不可用,所以每次使用前都需要检查可用性。


/* 检测是否存在SD卡并是否支持读写 */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}

/*检测是否存在最少能支持读取的SD卡 */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}

回顾与总结:

1每个应用程序所在的包都会有一个私有的存储数据的目录,只有属于这个包中的应用程序才有写入的权限,每个包中应用程序的私有数据目录位于 Android系统中的绝对路径/data/data/<package>/目录中,除了私有目录,应用程序还拥有/sdcard目录即 Android设备上的SD卡的写入权限。文件系统中其他的系统目录,第三方应用程序都是不可写的。

那么问题来了:某个应用私有目录下的文件写入时为可读写的,那么其他应用又该如何读写呢?

解:获得其他应用的上下文Context,顺理成章获得应用私有目录的引用,顺利用IO流读写。

<span style="white-space:pre">			</span>Context c = createPackageContext("Package_Name", Context.CONTEXT_IGNORE_SECURITY);
File test =new File( c.getFilesDir(),"Your_File");
if(test.exists()){Log.d("ReadOtherPreferences.test","true"+test.getAbsolutePath()+" and"+test.getCanonicalPath());}
//获得File引用后,就用经典的IO流来处理就行了,不要用Android的openFileInput(name)方法,这些都只针对本身应用私有目录的
FileInputStream f = new FileInputStream(test);
BufferedReader bf = new BufferedReader(new InputStreamReader(f));
反思:虽然以上理论上是可行的,但是想要获得已经可以读写的其他应用程序的引用仍然需要其他应用的包名与文件名,但这在实际上却很难实现,因为我们很难获得其他应用的包名与文件名(如果我们能轻易知道,某种意义上我们不是能够自由读取其他应用的文件名,不管这个应用支不支持读写,这个应用本身设计上就是不安全的,想想信息泄漏)所以最好使用openFIleInput或openFileOupt方法时最好把权限设为MODE_PRIVATE模式。

2我们知道Android是基于LInux架构的,所以文件权限管理很严格,本身应用只有私有目录与SD卡才能进行访问。使用Android自有的输入流与输出流也一样,只能对/data/data/<package_name>/files里的文件进行读写

那么问题来了:经典的IO流能否读写本身应用包下的其他目录呢?当然毫无疑问,经典IO流也是无法访问其他系统目录和第三方应用文件的

解:测试:读写缓存文件cache,通过经典IO流(通过一个按钮来写入)

File cache = new File(getCacheDir(),"in.txt");

Log.d("org.crazyit.io.write", "cache路径是否存在"+cache.exists());

FileOutputStream fos = new FileOutputStream(cache, true);

PrintStream ps = new PrintStream(fos);

ps.println(content);

Log.d("org.crazyit.io.write", "写入成功");

ps.close();

2次测试后,显示写入成功,证明经典IO流能够对应用下其他目录进行读写





换个方式,用经典IO流新建新目录新文件,即使用File cache = new File("/txt/in.txt")与File cache = new File("in.txt");File cache = new File(getCacheDir()+"/txt","in.txt");分别测试,发现找不到File,自此新建新文件夹失败

那么问题来了:如何在私有目录下新建文件夹呢?

解:使用File类自带的办法mkdir()创建文件夹,createNewFile创建文件

//新建文件夹
File cache = new File("/data/data/org.crazyit.io/test");
if(!cache.exists()){
cache.mkdir();
}
//新建文件a
File cache = new File("/data/data/org.crazyit.io/test"+"/2.txt");
if(!cache.exists()){
cache.createNewFile();
}
测试结果如下:



反思:经典IO流对与应用本身data/data/<package_name>/file(随意)拥有读写权限,也就是比系统自带的流权限要更宽点,由于是本身应用,包名也很容易能得到,但是对于经典IO流的File的声明,应用不能像自带的流一样自动定位到应用根路径,即类似File f = new File("1.txt");这种形式就不行了,一定要全路径File f = new File("/data/data/<package_name>/File_Name")

3外部存储不像内部存储一样,有严格的权限限制,任何文件都可以被任何用户任何应用访问。但是外部存储也有私有和公有的概念,不同于内部存储的权限管理,本质上外部存储的私有和公有都是可以被所有用户访问的,但是区别在与应用删除的时候,私有的会随着应用删除而删除,而公有会自动保留下来。例如下载时的安装包我们会放在私有目录下,照相应用拍摄的图片会放在公有目录下。如果要确定绝对的私有公有,那么就要修改文件的权限了   PS:获得sd卡目录的简单格式 File sdFile = new File("/sdcard");

那么问题来了:如何创建私有目录与公有目录呢?

解:只要有路径path,获得File的引用,轻而易举创建目录,与第2点没什么区别,相反不用绝对路径反而更简单了,网上大把例子

私有目录:getExternalFilesDir()

公有库目录:getExternalStoragePublicDirectory();

公有根目录:getExternalStorageDirectory();

那么问题来了:能创建目录,文件,必然要有删除,那我们要怎么删除呢?

解:内部存储的私有文件:直接就通过Context.deleteFile()方法删了

外部存储的不管私有还是公有:获得文件的File引用,直接调用delete()删了

反思:注意缓存要定期清理

问题来了:如何获得空间容量

解:经测试,以下两个办法得出的容量大小跟网上的方法测试出来的是一样的

SD卡:获得SD卡路径,剩余空间 getFreeSpace() 和总空间getTotalSpace()

手机:获得Data路径,剩余空间 getFreeSpace() 和总空间getTotalSpace()

引用:http://vinny-w.iteye.com/blog/1339827

http://blog.csdn.net/zoe6553/article/details/6119698

http://www.cnblogs.com/hanyonglu/archive/2012/03/01/2374894.html

http://www.cnblogs.com/mengdd/archive/2013/05/02/3055465.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android