Android--数据持久化之内部存储Sdcard存储

休言女子非英物,夜夜龙泉壁上鸣。这篇文章主要讲述Android--数据持久化之内部存储Sdcard存储相关的知识,希望能为你提供帮助。
前言
之前一直在讲androidUI的内容,但是还没有完结,之后会慢慢补充。今天讲讲其他的,关于数据持久化的内容。对于一个应用程序而言,不可避免的要能够对数据进行存储,Android程序也不例外。而在Android中,提供了几种实现数据持久化的方法。后面会分别介绍。
在Android中,可以使用几种方式实现数据持久化:

  • Shared Preferences:共享参数形式,一种以Key-Value的键值对形式保存数据的方式,Android内置的,一般应用的配置信息,推荐使用此种方式保存。
  • Internal Storage:使用Android设备自带的内存存储数据。
  • External Storage:使用外部存储设备存储数据,一般是指Sdcard。
  • SQLite Databases:以SQLite数据库存储结构化的数据。
  • Network Connection:使用基于网络的服务获取数据,可以参见另外一篇博客:Android--Apache HttpClient。
后面几天会分别介绍以上几种方式实现的数据持久化,对于SharedPreferences而言,之前写过一篇博客,但是自己不是很满意,之后有时间会再重新写一份关于SharedPreferences的博客,有兴趣的朋友可以先去看看,Android--使用SharedPreferences。今天先介绍Internal Storage以及External Storage。
Internal Storage
内部存储,在Android中,开发者可以直接使用设备的内部存储器中保存文件,默认情况下,以这种方式保存的和数据是只能被当前程序访问,在其他程序中是无法访问到的,而当用户卸载该程序的时候,这些文件也会随之被删除。
使用内部存储保存数据的方式,基本上也是先获得一个文件的输出流,然后以write()的方式把待写入的信息写入到这个输出流中,最后关闭流即可,这些都是java中IO流的操作。具体步骤如下:
  • 使用Context.openFileOutput()方法获取到一个FileOutputStream对象。
  • 把待写入的内容通过write()方法写入到FileOutputStream对象中。
  • 最后使用close()关闭流。
上面介绍的Context.openFileOutput()方法有两个重载函数,它们的签名分别是:
  • FileOutputStream openFileOutput(String name):以MODE_PRIVATE的模式打开name文件。
  • FileOutputStream openFileOutput(String name,int mode):以mode的模式打开name文件。
【Android--数据持久化之内部存储Sdcard存储】上面第二个重载函数中,mode为一个int类型的数据,这个一般使用Context对象中设置好的常量参数,有如下几个:
  • MODE_APPEND:以追加的方式打开一个文件,使用此模式写入的内容均追加在原本内容的后面。
  • MODE_PRIVATE:私有模式(默认),如果文件已经存在会重新创建并替换原文件,如果不存在直接创建。
  • MODE_WORLD_READABLE:以只读的方式打开文件。
  • MODE_WORLD_WRITEABLE:以只写的方式打开文件。
还有几个方法需要特别注意一下,这几个方法对于文件关系提供了更好的支持,配合上面介绍的方式,就可以对文件的数据进行常规的CRUD操作(增删改查),方法如下:
  • File getFIlesDir():获取文件系统的绝对路径。
  • boolean deleteFile(String name):删除一个指定文件名为name的文件。
  • String[] fileList():当前应用内部存储路径下的所有文件名。
  讲了这么多,下面通过一个简单的Demo来演示一下上面提到的内容。在这个Demo中,指定文件名和内容,既可创建文件,并且可以对其内容进行追加、修改、删除、查询等操作。
布局代码:
Android--数据持久化之内部存储Sdcard存储

文章图片
1 < ?xml version="1.0" encoding="utf-8"?> 2 < LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3android:layout_width="match_parent" 4android:layout_height="match_parent" 5android:orientation="vertical" > 6 7< TextView 8android:layout_width="match_parent" 9android:layout_height="wrap_content" 10android:text="file name:" /> 11 12< EditText 13android:id="@+id/etInternalFilename" 14android:layout_width="match_parent" 15android:layout_height="wrap_content" /> 16 17< TextView 18android:layout_width="match_parent" 19android:layout_height="wrap_content" 20android:text="Content:" /> 21 22< EditText 23android:id="@+id/etInternalContent" 24android:layout_width="match_parent" 25android:layout_height="wrap_content" /> 26 27< LinearLayout 28android:layout_width="match_parent" 29android:layout_height="wrap_content" 30android:orientation="horizontal" > 31 32< Button 33android:id="@+id/btnInternalSave" 34android:layout_width="wrap_content" 35android:layout_height="wrap_content" 36android:text="save" /> 37 38< Button 39android:id="@+id/btnInternalDelete" 40android:layout_width="wrap_content" 41android:layout_height="wrap_content" 42android:text="delete" /> 43 44< Button 45android:id="@+id/btnInternalAppend" 46android:layout_width="wrap_content" 47android:layout_height="wrap_content" 48android:text="append" /> 49 50< Button 51android:id="@+id/btnInternalQuery" 52android:layout_width="wrap_content" 53android:layout_height="wrap_content" 54android:text="query" /> 55< /LinearLayout> 56 < !-- 以一个ListView的形式展示当前程序内部存储路径下的所有文件 --> 57< ListView 58android:id="@+id/lvInternalData" 59android:layout_width="match_parent" 60android:layout_height="fill_parent" > 61< /ListView> 62 63 < /LinearLayout>

Android--数据持久化之内部存储Sdcard存储

文章图片
专门写一个内部存储的操作类,对其实现CRUD操作:
Android--数据持久化之内部存储Sdcard存储

文章图片
1 package com.example.internal; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import java.io.FileNotFoundException; 7 import java.io.FileOutputStream; 8 import java.io.IOException; 9 10 import android.content.Context; 11 import android.os.Environment; 12 import android.util.Log; 13 14 public class MyInternalStorage { 15//需要保存当前调用对象的Context 16private Context context; 17 18public MyInternalStorage(Context context) { 19this.context = context; 20} 21/** 22* 保存内容到内部存储器中 23* @param filename 文件名 24* @param content 内容 25*/ 26public void save(String filename, String content) throws IOException { 27// FileOutputStream fos=context.openFileOutput(filename, 28// Context.MODE_PRIVATE); 29File file = new File(context.getFilesDir(), filename); 30FileOutputStream fos = new FileOutputStream(file); 31 32fos.write(content.getBytes()); 33fos.close(); 34} 35/** 36*通过文件名获取内容 37* @param filename 文件名 38* @return 文件内容 39*/ 40public String get(String filename) throws IOException { 41FileInputStream fis = context.openFileInput(filename); 42ByteArrayOutputStream baos = new ByteArrayOutputStream(); 43byte[] data = https://www.songbingjia.com/android/new byte[1024]; 44int len = -1; 45while ((len = fis.read(data)) != -1) { 46baos.write(data, 0, len); 47} 48return new String(baos.toByteArray()); 49} 50/** 51* 以追加的方式在文件的末尾添加内容 52* @param filename 文件名 53* @param content 追加的内容 54*/ 55public void append(String filename, String content) throws IOException { 56FileOutputStream fos = context.openFileOutput(filename, 57Context.MODE_APPEND); 58fos.write(content.getBytes()); 59fos.close(); 60} 61/** 62* 删除文件 63* @param filename 文件名 64* @return 是否成功 65*/ 66public boolean delete(String filename) { 67return context.deleteFile(filename); 68} 69/** 70* 获取内部存储路径下的所有文件名 71* @return 文件名数组 72*/ 73public String[] queryAllFile() { 74return context.fileList(); 75} 76 77 }

Android--数据持久化之内部存储Sdcard存储

文章图片
Activity代码:
Android--数据持久化之内部存储Sdcard存储

文章图片
1 package com.example.datastoragedemo; 2 3 import java.io.IOException; 4 5 import com.example.internal.MyInternalStorage; 6 7 import android.app.Activity; 8 import android.os.Bundle; 9 import android.view.View; 10 import android.view.View.OnClickListener; 11 import android.widget.AdapterView; 12 import android.widget.AdapterView.OnItemClickListener; 13 import android.widget.ArrayAdapter; 14 import android.widget.Button; 15 import android.widget.EditText; 16 import android.widget.ListView; 17 import android.widget.Toast; 18 19 public class InternalStorageActivity extends Activity { 20private EditText etFilename, etContent; 21private Button btnSave, btnQuery, btnDelete, btnAppend; 22private ListView lvData; 23 24@Override 25protected void onCreate(Bundle savedInstanceState) { 26// TODO Auto-generated method stub 27super.onCreate(savedInstanceState); 28setContentView(R.layout.activity_internalstorage); 29 30etFilename = (EditText) findViewById(R.id.etInternalFilename); 31etContent = (EditText) findViewById(R.id.etInternalContent); 32btnSave = (Button) findViewById(R.id.btnInternalSave); 33btnQuery = (Button) findViewById(R.id.btnInternalQuery); 34btnDelete = (Button) findViewById(R.id.btnInternalDelete); 35btnAppend = (Button) findViewById(R.id.btnInternalAppend); 36lvData = https://www.songbingjia.com/android/(ListView) findViewById(R.id.lvInternalData); 37 38btnSave.setOnClickListener(click); 39btnQuery.setOnClickListener(click); 40btnDelete.setOnClickListener(click); 41btnAppend.setOnClickListener(click); 42 43} 44 45private View.OnClickListener click = new OnClickListener() { 46 47@Override 48public void onClick(View v) { 49MyInternalStorage myInternal = null; 50String filename = null; 51String content = null; 52switch (v.getId()) { 53case R.id.btnInternalSave: 54filename = etFilename.getText().toString(); 55content = etContent.getText().toString(); 56myInternal = new MyInternalStorage(InternalStorageActivity.this); 57try { 58myInternal.save(filename, content); 59Toast.makeText(InternalStorageActivity.this,"保存文件成功", 60Toast.LENGTH_SHORT).show(); 61} catch (IOException e) { 62// TODO Auto-generated catch block 63e.printStackTrace(); 64Toast.makeText(InternalStorageActivity.this, "保存文件失败", 65Toast.LENGTH_SHORT).show(); 66} 67 68break; 69 70case R.id.btnInternalDelete: 71filename = etFilename.getText().toString(); 72myInternal = new MyInternalStorage(InternalStorageActivity.this); 73myInternal.delete(filename); 74Toast.makeText(InternalStorageActivity.this, "删除文件成功", 75Toast.LENGTH_SHORT).show(); 76break; 77case R.id.btnInternalQuery: 78myInternal = new MyInternalStorage(InternalStorageActivity.this); 79String[] files = myInternal.queryAllFile(); 80ArrayAdapter< String> fileArray = new ArrayAdapter< String> ( 81InternalStorageActivity.this, 82android.R.layout.simple_list_item_1, files); 83lvData.setAdapter(fileArray); 84Toast.makeText(InternalStorageActivity.this, "查询文件列表", 85Toast.LENGTH_SHORT).show(); 86break; 87case R.id.btnInternalAppend: 88filename = etFilename.getText().toString(); 89content = etContent.getText().toString(); 90myInternal = new MyInternalStorage(InternalStorageActivity.this); 91try { 92myInternal.append(filename, content); 93Toast.makeText(InternalStorageActivity.this, "文件内容追加成功", 94Toast.LENGTH_SHORT).show(); 95} catch (IOException e) { 96// TODO Auto-generated catch block 97e.printStackTrace(); 98Toast.makeText(InternalStorageActivity.this, "文件内容追加失败", 99Toast.LENGTH_SHORT).show(); 100} 101break; 102} 103 104} 105}; 106 107private OnItemClickListener itemClick = new OnItemClickListener() { 108 109@Override 110public void onItemClick(AdapterView< ?> parent, View view, int position, 111long id) { 112ListView lv = (ListView) parent; 113ArrayAdapter< String> adapter = (ArrayAdapter< String> ) lv 114.getAdapter(); 115String filename = adapter.getItem(position); 116etFilename.setText(filename); 117MyInternalStorage myInternal = new MyInternalStorage( 118InternalStorageActivity.this); 119String content; 120try { 121content = myInternal.get(filename); 122etContent.setText(content); 123} catch (IOException e) { 124// TODO Auto-generated catch block 125e.printStackTrace(); 126} 127 128} 129}; 130 131 }

Android--数据持久化之内部存储Sdcard存储

文章图片
效果展示,在示例中,先添加三个文件,最后删除一个,分别查询文件列表:
Android--数据持久化之内部存储Sdcard存储

文章图片
Android--数据持久化之内部存储Sdcard存储

文章图片
Android--数据持久化之内部存储Sdcard存储

文章图片
Android--数据持久化之内部存储Sdcard存储

文章图片

使用内部存储的方式进行数据持久化,文件的地址将保存在/data/data/< package_name> /files/路径下,上面创建了三个文件,最后删掉了一个,如果是使用的模拟器,可以直接在File Explorer中查看:
Android--数据持久化之内部存储Sdcard存储

文章图片

 
缓存(cache)
既然提到了内部存储,这里再简单的说说关于缓存文件(cache files)。cache files的操作与操作内部存储中的文件方式基本一致,只是获取文件的路径有说不同。如果需要使用缓存的方式进行数据持久话,那么需要使用Context.getCacheDir()方法获取文件保存的路径。
对于缓存文件而言,当设备内部内存存储空间不足的时候,Android会有自动删除的机制删除这些缓存文件,用来恢复可用空间,所以对于缓存文件而言,内容一般最好控制在1MB之下,并且也不要存放重要的数据,因为很可能下次去取数据的时候,已经被Android系统自动清理了。
External Storage
使用外部存储实现数据持久化,这里的外部存储一般就是指的是sdcard。使用sdcard存储的数据,不限制只有本应用访问,任何可以有访问Sdcard权限的应用均可以访问,而Sdcard相对于设备的内部存储空间而言,会大很多,所以一般比较大的数据,均会存放在外部存储中。
使用SdCard存储数据的方式与内部存储的方式基本一致,但是有三点需要注意的:
  • 第一点,需要首先判断是否存在可用的Sdcard,这个可以使用一个访问设备环境变量的类Environment进行判断,这个类提供了一系列的静态方法,用于获取当前设备的状态,在这里获取是否存在有效的Sdcard,使用的是Environment.getExternalStorageState()方法,返回的是一个字符串数据,Environment封装好了一些final对象进行匹配,除了Environment.MEDIA_MOUNTED外,其他均为有问题,所以只需要判断是否是Environment.MEDIA_MOUNTED状态即可。
  • 第二点,既然转向了Sdcard,那么存储的文件路径就需要相对变更,这里可以使用Envir.getExternalStorageDirectory()方法获取当Sdcard的根目录,可以通过它访问到相应的文件。
  • 第三点,需要赋予应用程序访问Sdcard的权限,Android的权限控制尤为重点,在Android程序中,如果需要做一些越界的操作,均需要对其进行授权才可以访问。在AndroidManifest.xml中添加代码:< uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
因为访问Sdcard的方式和访问内部存储的方式差不多,这里就展示一个Save的方法,用于保存文件,其他CRUD操作,这里就不再一一给出了。
Android--数据持久化之内部存储Sdcard存储

文章图片
1 public void saveToSdcard(String filename, String content) throws IOException { 2 3if (Environment.MEDIA_MOUNTED.equals(Environment 4.getExternalStorageState())) { 5Log.i("main", "本设备有存储卡!"); 6File file = new File(Environment.getExternalStorageDirectory(), 7filename); 8FileOutputStream fos = null; 9fos = new FileOutputStream(file); 10fos.write(content.getBytes()); 11fos.close(); 12} 13}

Android--数据持久化之内部存储Sdcard存储

文章图片
而如果使用SdCard存储文件的话,存放的路径在Sdcard的根目录下,如果使用模拟器运行程序的话,创建的文件在/mnt/sdcard/目录下:
Android--数据持久化之内部存储Sdcard存储

文章图片

补充:对于现在市面上很多Android设备,自带了一个大的存储空间,一般是8GB或16GB,并且又支持了Sdcard扩展,对于这样的设备,使用Enviroment.getExternalStorageDirectory()方法只能获取到设备自带的存储空间,对于另外扩展的Sdcard而言,需要修改路径。
源码下载
 
总结
以上就介绍了内部存储、外部存储、缓存存储的方式方法。开发者可以根据需要选择不同的方式进行数据持久化。
请支持原创,尊重原创,转载请注明出处。谢谢。
 

    推荐阅读