• 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文件。

      上面第二个重载函数中,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中,指定文件名和内容,既可创建文件,并且可以对其内容进行追加、修改、删除、查询等操作。  

      布局代码:

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

       专门写一个内部存储的操作类,对其实现CRUD操作:  

     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
    16     private Context context;
    17 
    18     public MyInternalStorage(Context context) {
    19         this.context = context;
    20     }
    21     /**
    22      * 保存内容到内部存储器中
    23      * @param filename 文件名
    24      * @param content 内容
    25      */
    26     public void save(String filename, String content) throws IOException {
    27         // FileOutputStream fos=context.openFileOutput(filename,
    28         // Context.MODE_PRIVATE);
    29         File file = new File(context.getFilesDir(), filename);
    30         FileOutputStream fos = new FileOutputStream(file);
    31 
    32         fos.write(content.getBytes());
    33         fos.close();
    34     }
    35     /**
    36      *  通过文件名获取内容
    37      * @param filename 文件名
    38      * @return 文件内容
    39      */
    40     public String get(String filename) throws IOException {
    41         FileInputStream fis = context.openFileInput(filename);
    42         ByteArrayOutputStream baos = new ByteArrayOutputStream();
    43         byte[] data = new byte[1024];
    44         int len = -1;
    45         while ((len = fis.read(data)) != -1) {
    46             baos.write(data, 0, len);
    47         }
    48         return new String(baos.toByteArray());
    49     }
    50     /**
    51      * 以追加的方式在文件的末尾添加内容
    52      * @param filename 文件名
    53      * @param content 追加的内容
    54      */
    55     public void append(String filename, String content) throws IOException {
    56         FileOutputStream fos = context.openFileOutput(filename,
    57                 Context.MODE_APPEND);
    58         fos.write(content.getBytes());
    59         fos.close();
    60     }
    61     /**
    62      * 删除文件
    63      * @param filename 文件名
    64      * @return 是否成功
    65      */
    66     public boolean delete(String filename) {
    67         return context.deleteFile(filename);
    68     }
    69     /**
    70      * 获取内部存储路径下的所有文件名
    71      * @return 文件名数组
    72      */
    73     public String[] queryAllFile() {
    74         return context.fileList();
    75     }
    76 
    77 }

       Activity代码:

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

      效果展示,在示例中,先添加三个文件,最后删除一个,分别查询文件列表:

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

    缓存(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操作,这里就不再一一给出了。

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

      而如果使用SdCard存储文件的话,存放的路径在Sdcard的根目录下,如果使用模拟器运行程序的话,创建的文件在/mnt/sdcard/目录下:

      补充:对于现在市面上很多Android设备,自带了一个大的存储空间,一般是8GB或16GB,并且又支持了Sdcard扩展,对于这样的设备,使用Enviroment.getExternalStorageDirectory()方法只能获取到设备自带的存储空间,对于另外扩展的Sdcard而言,需要修改路径。

      源码下载

    总结

      以上就介绍了内部存储、外部存储、缓存存储的方式方法。开发者可以根据需要选择不同的方式进行数据持久化。

      请支持原创,尊重原创,转载请注明出处。谢谢。

  • 相关阅读:
    IDETalk
    servlet概述
    过滤器(Filter)
    ieda常用快捷键
    UUID
    JRebel 7.0.10 for intellij IDEA 2017.1
    BP神经网络(手写数字识别)
    遗传算法解决TSP问题
    [CODEVS1258]关路灯
    [NOIP2007]统计数字
  • 原文地址:https://www.cnblogs.com/plokmju/p/Android_Storage.html
Copyright © 2020-2023  润新知