Android学习笔记之数据存储

Android中有5种数据存储方式,分别为文件存储、SQLite数据库、SharedPreferences、ContentProvider、网络。每种存储方式的特点如下:

  • File文件存储/SD卡:与Java中实现I/O的程序是完全一样的,提供openFileInput()和openFileOutput()方法来读取设备上的文件。
  • SharedPreferences存储:一种轻型的数据存储方式,常用来存储一些简单的配置信息,本质是基于XML文件存储key-value键值对数据。
  • SQLite数据库存储:一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,在存储大量复杂的关系型数据的时可以使用。
  • ContentProvider:四大组件之一,用于数据的存储和共享,不仅可以让不同应用程序之间进行数据共享,还可以选择只对哪一部分数据进行共享,可保证程序中的隐私数据不会有泄漏风险。

1、File文件存储/SD卡

文件存储方式是一种较常用的方法,在Android中读取/写入文件的方法,与Java中实现I/O的程序是完全一样的,提供openFileInput()和openFileOutput()方法来读取设备上的文件。

将数据存储到文件中:

String data = "data to save";
FileOutputStream out = openFileOutput("文件名", 覆盖:MODE_PRIVATE 追加:MODE_APPEND);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
writer.close();

从文件中读取数据:

FileInputStream in = openFileInput("文件名");
BufferedReader reader = new BufferedReader(new InputStreamReader());
StringBuffer content = new StringBuffer();
String line = "";
while ((line.reader.readLine()) != null) {
    content.append(line);
}
reader.close();
return content.toString();

将数据存储到手机内存中


private void saveTosdcard() {
        String str = "data to save";
        //文件输出流
        FileOutputStream fos = null;
        //设置文件路径 ,第一个参数是文件保存的路径,null放在根目录下,第二个参数是文件名
        File file = new File(getExternalFilesDir(null), "/data666.txt");
        try {
            //传入参数第一是file,第二可以传boolean表示是否追加
            fos = new FileOutputStream(file,true);
            fos.write(str.getBytes());
            Toast.makeText(MainActivity.this, "保存到SD卡成功!", Toast.LENGTH_SHORT).show();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    // 关闭输入流
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
  • getExternalFilesDir(null):路径在/storage/sdcard0/Android/data/包名/files/文件名
  • Environment.getExternalStorageDirectory():路径在/storage/sdcard0/文件名
删除getExternalFilesDir路径文件
private void deleteSocket() {
    try {
        // 找到文件所在的路径并删除该文件
        File file = new File(getExternalFilesDir(null), "/dataSocketHiMate.txt");
        if (file.exists()) {
            file.delete();
        } else {
            Toast.makeText(MainActivity.this, "没有文件可以删除", Toast.LENGTH_SHORT).show();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

2、SharedPreferences

SharePreferences是一种轻型的数据存储方式,常用来存储一些简单的配置信息,如int、string、boolean、float和long。它的本质是基于XML文件存储key-value键值对数据。

  • 保存位置: data/data/程序包名/share_prefs/
  • 主要用途:
    1. 保存应用的设置 例如:设置静音,下次进入还是静音
    2. 判断是否是第一次登陆
    3. 保存登录用户名密码等情形

将数据存储到SharedPreferences

  • 步骤1. 调用 getSharedPreferences() 方法获得 SharedPreferences 对象,提供两个参数,指定文件名和操作模式。MODE_PRIVATE只有当前程序才能读写。
  • 步骤2. 调用 SharedPreferences 对象的 edit() 方法来获取 SharedPreferences.Editor 对象。
  • 步骤3. 调用Edit接口的形如put某某某()方法以键值对形式保存某某某类型的数据。
  • 步骤4. 调用Edit接口的commit()方法提交键值对。
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
editor.putString("name", "Tom");
editor.putInt("age", 28);
editor.putBoolean("married", false);
editor.remove("married");
editor.commit();

从SharedPreferences中读取数据

SharedPreferences pref  = getSharedPreferences("data",MODE_PRIVATE);
String name = pref.getString("name","默认值");
int age = pref.getInt("age",0);
boolean married = pref.getBoolean("married",false);

3、SQLite数据库

SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,在存储大量复杂的关系型数据的时可以使用,比前面学过的只适用于存储简单数据的两种存储方式要好很多。

创建数据库

自定义帮助类并继承SQLiteOpenHelper,并重写两个方法:onCreate()和 onUpgrade(),分别在这两个方法中去实现创建、升级数据库的逻辑。还需要一个构造方法,这里用含有四个参数的构造方法就可以

public class MyDatabaseHelper extends SQLiteOpenHelper {
    private Context mContext;
    public static final String CREATE_BOOK = "create table Book(" +
            "id integer primary key autoincrement," +
            "author text," +
            "price readl," +
            "price integer," +
            "name text)";
    public MyDatabaseHelper(Context context, String name,
                            SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //升级数据库的话先修改版本号
        //drop删除表,delete是删除数据
        db.execSQL("drop table if exists Book");
        onCreate(db);
    }
}

数据库进行增删改查的操作

public class Main2Activity extends AppCompatActivity {
    private MyDatabaseHelper dbHepler;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        dbHepler = new MyDatabaseHelper(this, "BookStore.db", null, 1);
        //都是打开和创建,区别在于空间满的情况w是出现异常,R是只读
        //dbHepler.getWritableDatabase();
        dbHepler.getReadableDatabase();
        SQLiteDatabase db = dbHepler.getWritableDatabase();

        //添加
        ContentValues values = new ContentValues();
        values.put("name", "tom");
        values.put("pages", 464);
        values.put("price", 14.23);
        db.insert("Book", null, values);
        values.clear();
        //更新
        ContentValues values = new ContentValues();
        values.put("name", "HHH");
        db.update("Book", values, "price>?", new String[]{"10"});
        //删除
        db.delete("Book",  "price>?", new String[]{"10"});
        //查询
        Cursor c = db.query("Book",null,null,null,null,null,null);
        String s = c.getString(c.getColumnIndex("name"));
        c.close();
    }
}

sqlite数据导出到csv、txt

String sql = "select * from location";
Cursor cursor = db.rawQuery(sql, null);
//如果要其他格式的话,只需修改文件后缀名即可
ExportToCSV(cursor, "test666.csv");

核心代码块ExportToCSV()

private void ExportToCSV(Cursor c, String fileName) {
        int rowCount = 0;
        int colCount = 0;
        FileWriter fw;
        BufferedWriter bfw;
        //获取SD卡路径
        File sdCardDir = Environment.getExternalStorageDirectory();
        File saveFile = new File(sdCardDir, fileName);
        try {
            rowCount = c.getCount();
            colCount = c.getColumnCount();
            fw = new FileWriter(saveFile);
            bfw = new BufferedWriter(fw);
            if (rowCount > 0) {
                c.moveToFirst();
                // 写入表头
                for (int i = 0; i < colCount; i++) {
                    if (i != colCount - 1)
                        bfw.write(c.getColumnName(i) + ',');
                    else
                        bfw.write(c.getColumnName(i));
                }

                // 写好表头后换行
                bfw.newLine();
                // 写入数据
                for (int i = 0; i < rowCount; i++) {
                    c.moveToPosition(i);
                    Log.d(TAG, "正在导出第" + (i + 1) + "条");
                    for (int j = 0; j < colCount; j++) {
                        if (j != colCount - 1) {
                            bfw.write(c.getString(j) + ',');
                        } else
                            bfw.write(c.getString(j));
                    }
                    // 写好每条记录后换行
                    bfw.newLine();
                }
            }
            // 将缓存数据写入文件
            bfw.flush();
            Log.d(TAG, "ExportToCSV: " + bfw.toString());
            // 释放缓存
            bfw.close();
            Log.d(TAG, "导出完毕!");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            c.close();
        }
    }

4、ContentProvider

并不能用户存储数据。主要用于不同应用程序之间共享数据,只是为我们存储以及添加数据制定统一的接口而已。

5、网络存储数据

通过网络上提供的存储空间来上传(存储)或下载(获取)我们存储在网络空间中的数据信息

6、共享数据的方式

1.File, 2.Sqlite,3.Content Provider,4.Service,5.Broadcast Receiver,6.Intent

7、了解SQLite中的事务处理吗?是如何做的?

使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务。多用于大量数据操作时,能明显减少耗时。

 SQLiteDatabase db = dbHepler.getWritableDatabase();
 db.beginTransaction();//开始事务
 try{    
     db.execSQL(...);  
     db.execSQL(...); 
     //调用此方法会在执行到endTransaction() 时提交当前事务,如果不调用此方法会回滚事务  
     db.setTransactionSuccessful(); 
} finally{ 
    //由事务的标志决定是提交事务,还是回滚事务  
    db.endTransaction();
} 
db.close();

8、事务的属性

  • 原子性(Atomicity):确保工作单位内的所有操作都成功完成,否则,事务会在出现故障时终止,之前的操作也会回滚到以前的状态。
  • 一致性(Consistency):确保数据库在成功提交的事务上正确地改变状态。
  • 隔离性(Isolation):使事务操作相互独立和透明。
  • 持久性(Durability):确保已提交事务的结果或效果在系统发生故障的情况下仍然存在。

9、使用SQLite做批量操作有什么好的方法吗?

即使用事务处理进行优化,默认SQLite的数据库插入操作,如果没有采用事务的话,它每次写入提交,就会触发一次事务操作,而这样几千条的数据,就会触发几千个事务的操作,这就是时间耗费的根源

10、如果现在要删除SQLite中表的一个字段如何做?

SQLite数据库只允许增加表字段而不允许修改和删除表字段,只能采取复制表思想,即创建一个新表保留原表想要的字段、再将原表删除。

11、使用SQLite时会有哪些优化操作?

  1. 使用事务做批量操作:具体操作见上。
  2. 及时关闭Cursor,避免内存泄漏。
  3. 耗时操作异步化:数据库的操作属于本地IO,通常比较耗时,建议将这些耗时操作放入异步线程中处理。
  4. ContentValues的容量调整:ContentValues内部采用HashMap来存储Key-Value数据,ContentValues初始容量为8,扩容时翻倍。因此建议对ContentValues填入的内容进行估量,设置合理的初始化容量,减少不必要的内部扩容操作
  5. 使用索引加快检索速度:对于查询操作量级较大、业务对要求查询要求较高的推荐使用索引

12、Android 中有哪几种解析xml的类,官方推荐哪种?以及它们的原理和区别。

Android 提供了三种解析XML的方式:SAX(Simple API XML)DOM(Document Object Model)Pull解析

  • Dom解析:将XML文件的所有内容读取到内存中(内存的消耗比较大),然后允许您使用DOM API遍历XML树、检索所需的数据。
  • Sax解析:Sax是一个解析速度快并且占用内存少的xml解析器,Sax解析XML文件采用的是事件驱动,它并不需要解析完整个文档,而是按内容顺序解析文档的过程。
  • Pull解析:Pull解析器的运行方式与 Sax 解析器相似。它提供了类似的事件,可以使用一个switch对感兴趣的事件进行处理。

本文参考资料:

  • 《Android开发艺术探索》
  • 《第一行代码》
  • https://www.jianshu.com/p/718aa3c1a70b

你可能感兴趣的:(Android学习笔记之数据存储)