Android 数据持久化技术(即数据存储方式)

来源:互联网 发布:查看端口占用进程 编辑:程序博客网 时间:2024/05/13 17:26

在讨论数据持久化技术之前我们先了解几个概念?

什么是瞬时数据:存储在内存当中,有可能会因为程序的关闭或其他原因导致内存被收回而丢失的数据。

 

为什么采用数据持久化技术:为了保证关键数据在程序退出时不被丢失。

 

什么是数据持久化技术:将内存中的瞬时数据保存到存储设备中,保证手机在关机的情况下数据仍然不会丢失。

 

安卓提供了三种方式用于简单的数据持久化功能:文件储存,SharedPreference存储,数据库储存

 

文件储存

         用于保存一些简单的文本数据或二进制数据。

         使用到的方法:Context类中提供了openFileOutput()方法 和 openFileInput()方法

         openFileOutput()方法 拥有两个参数 第一个是文件名 第二个是文件的操作方式

默认的存储到data/data/<package name> files目录下

文件的操作方式: MODE_PRIVATE当指定同样文件名时会覆盖原文件中的内容

                 MODE_APPEND当该文件已存在时就往文件中追加内容,不会创建新文件

文件存储使用:java流

 

demo:在文本框中输入数据,点击button保存,当程序退出,或者activity销毁时,还能回显保存的数据

代码:

  布局文件

1
2
3
4
5
6
7
8
9
10
11
<EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
       />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="save"
        android:onClick="click"
        />

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class MainActivity extends Activity {
 
    private EditText et;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et = (EditText) findViewById(R.id.et);
        String inputText = save();<br>     //回显数据
        if(!TextUtils.isEmpty(inputText)) {
            et.setText(save());
        }
    }
    //读取保存数据
    public String save() {
        BufferedReader reader = null;
        try {
            FileInputStream in = openFileInput("save");
            reader = new BufferedReader(new InputStreamReader(in));
            StringBuffer buffer = new StringBuffer();
            String len = "";
            while ((len = reader.readLine()) != null) {
                buffer.append(len);
            }
            return buffer.toString();
        catch (FileNotFoundException e) {
            e.printStackTrace();
        catch (IOException e) {
            e.printStackTrace();
        finally {
            try {
                if(reader!=null){
                    reader.close();
                }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "";
    }
    //点击保存数据
    public void click(View v) {
        BufferedWriter writer = null;
        String text = et.getText().toString();
 
        if (!TextUtils.isEmpty(text)) {
            try {
                FileOutputStream out = openFileOutput("save", MODE_PRIVATE);
                writer = new BufferedWriter(new OutputStreamWriter(out));
                writer.write(text);
            catch (FileNotFoundException e) {
                e.printStackTrace();
            catch (IOException e) {
                e.printStackTrace();
            finally {
                if (writer != null) {
                    try {
                        writer.close();
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

  


 

 SharedPreferences储存

  sharedPreferences是采用键值对的方式存储数据的,它的储存方式比文件储存简单易用。

  使用到的方法:getSharedPreferences() 此方法接受两个参数,

         第一个参数是文件名,如果文件不存在则会创建一个。

          默认的储存路径是:data/data/<package name>/shared_prefs/下

         第二个参数是指定操作模式:MODE_PRIVATE和MODE_MULTI_PROCESS

         MODE_PRIVATE表示只有当前应用程序可以对sharedPreferences文件读写。

         MODE_MULTI_PROCESS 用于会有多个进程中对同一个sharedPreferences文件读取。

demo:通过sharePreferences来实现记住密码的功能,当我们点击登录时,如果勾选checkbox记住密码,则会保存密码,下次启动应用会回显应用的用户名密码,当我们不勾选checkbox时,不会保存用户名密码,同时如果保存的有用户名密码的话,会删除保存的用户名密码。(这里不做验证密码的操作)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<EditText
       android:id="@+id/et_username"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:hint="请输入用户名" />
    
   <EditText
       android:id="@+id/et_password"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="textPassword"
       android:hint="请输入密码" />   
        
   <CheckBox
       android:id="@+id/cb_remember"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="记住密码"
       />
   <Button
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="登陆"
       android:onClick="land"/>

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class MainActivity extends Activity {
 
    private EditText et_username;
    private EditText et_password;
    private SharedPreferences sPref;
    private CheckBox cb_remember;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_username = (EditText) findViewById(R.id.et_username);
        et_password = (EditText) findViewById(R.id.et_password);
        cb_remember = (CheckBox) findViewById(R.id.cb_remember);
        sPref = getSharedPreferences("save", MODE_PRIVATE);
         
        String username =  sPref.getString("username""");
        String password =  sPref.getString("password""");
        //如果保存的有用户名密码的话回显
        if(!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) {
            et_username.setText(username);
            et_password.setText(password);
        }
    }
     
    //登陆
    public void land(View view) {
        String username = et_username.getText().toString();
        String password = et_password.getText().toString();
        //判断用户名密码不能为空
        if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
            Toast.makeText(this"用户名或密码为空", 0).show();
        else {
            //checkbox勾上时记录密码
            if (cb_remember.isChecked()) {
                Toast.makeText(this"登陆成功", 0).show();
                sPref.edit().putString("username", username).commit();
                sPref.edit().putString("password", password).commit();
            }else{
                //checkbox不勾上时清清除用户名密码
                Toast.makeText(this"登陆成功", 0).show();
                sPref.edit().remove("username").commit();
                sPref.edit().remove("password").commit();
            }
        }
    }
}

  在真实的保存用户名密码的时候我们可以使用md5加密算法,对用户名密码进行加密,再下一个博客中来展示如何使用md5


 

数据库储存

  当我们需要储存大量复杂的关系型数据的时候,前两种方法就有点力不从心了,例如保存短息,联系人信息等,这个时候我们就可以使用安卓内置的数据库。

  安卓系统内置了SQLLite数据库,它支持SQL语法,还遵循数据库的ACID事务,是一款轻量级的数据库

  

创建数据库

  1创建数据库需要继承SQLiteOpenHelper,我们需要重写它的两个方法,onCreate()和onUpgrade().分别在这连个方法中创建和升级数据库

  2SQLiteOpenHelper中有两个非常重要的实例方法,getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建和打开一个现有的数据库。当磁盘空间满的时候getReadableDatabase()会打开一个只读的数据库,getWritableDatabase()会出现异常。磁盘空间未满的时候都是创建或打开一个可读可写的数据库。

    数据库文件会存放在data/data/<package name>/databases/

   3SQLiteOpenHelper的构造方法,四个参数 第一个Context 第二个 数据库名 第三个 查询数据时返回一个自定义的Cursor,一般都传null , 第四个是数据库的版本号

   4在SQLite数据库中数据类型 integer表示整形 real表示浮点型 text 表示文本类型 blob表示二进制类型

 demo: 我们在一个demo中展示创建数据库,升级数据库,实现数据库的增删改查功能 ,和使用事物功能,对每个功能的使用做了注释。

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//布局文件<br><!--  创建数据库-->
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="create_database"
        android:text="创建数据库" />
 <!--  添加数据-->  
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="insert_database"
        android:text="添加数据" />
 
    <!--  更新数据-->  
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="update_database"
        android:text="更新数据" />
      <!--  删除数据-->  
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="delete_database"
        android:text="删除数据" />
       <!--  查询数据-->  
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="query_database"
        android:text="查询数据" />
       <!--  使用事务-->  
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="Transaction_database"
        android:text="使用事务" />

  

1
//MySQLiteOpenHelper帮助类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<br>public class MySQLiteOpenHelper extends SQLiteOpenHelper {
     
    public static final String Create_contact = "create table person(_id integer primary key autoincrement, " +
            "name char(10), " +
            "salary char(20), " +
            "phone integer(20))";
 
 
    public MySQLiteOpenHelper(Context context, String name,
            CursorFactory factory, int version) {
        super(context, name, factory, version);
        mcontext = context;
    }
 
    @Override
    public void onCreate(SQLiteDatabase db) {
        //当创建数据库的时候会调用此方法在此方法中创建表
        db.execSQL(Create_contact);
         
        Toast.makeText(mcontext, "联系人数据库创建了", 0).show();
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
         
 
    }
 
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//增删改查事物的功能<br>public class MainActivity extends Activity {
 
    private MySQLiteOpenHelper dbHelper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建SQLiteOpenHelper实例
        dbHelper = new MySQLiteOpenHelper(this"contact.db"null, 1);
    }
    //创建数据库
    public void create_database(View view) {
        dbHelper.getWritableDatabase();
    }
    //添加数据
    public void insert_database(View view) {
        SQLiteDatabase  db = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        //开始组装第一条数据
        values.put("name""ben");
        values.put("salary""10000");
        values.put("phone", 1384567);
        //第一个参数是名 第二个参数未指定添加数据的情况下给某些可为空的列自动赋值NULL,我们不使用这个功能传null 第三个ContentValues对象
        db.insert("person"null, values);
        //也可以使用sql语句,达到同样效果
        //db.execSQL("insert into person (name,salary,phone) values(?,?,?)", new String []{"ben","10000","1384567"});
        values.clear();
        values.put("name""jack");
        values.put("salary""15000");
        values.put("phone", 1857545);
        db.insert("person"null, values);
        //也可以使用sql语句,达到同样效果
        //db.execSQL("insert into person (name,salary,phone) values(?,?,?)", new String[]{"jack","15000","1857545"});
    }
     
    //更新数据
    public void update_database(View view) {
        SQLiteDatabase  db = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("salary""15000");
        db.update("person", values, "name=?"new String[]{"ben"});
        //也可以使用sql语句,达到同样效果
        //db.execSQL("update person set salary=? where name=?", new String[]{"15000","ben"});
    }
     
    //删除数据
    public void delete_database(View view) {
        SQLiteDatabase  db = dbHelper.getWritableDatabase();
        db.delete("person""name=?"new String[]{"ben"});
        //也可以使用sql语句,达到同样效果
        //db.execSQL("delete from person where name=?", new String[]{"ben"});
    }
     
    //查询数据
    public void query_database(View view) {
        SQLiteDatabase  db = dbHelper.getWritableDatabase();
        //参数以此表示 表名 列名 where约束条件 占位符填充值 groudby having orderBy
        Cursor cursor = db.query("person"nullnullnullnullnullnull);
        while(cursor.moveToNext()) {
            String name = cursor.getString(cursor.getColumnIndex("name"));
            String salary = cursor.getString(cursor.getColumnIndex("salary"));
            int phone = cursor.getInt(cursor.getColumnIndex("phone"));
            //也可以使用sql语句,达到同样效果
            //db.rawQuery("select * from person",null);
            System.out.println(name+"--"+salary+"--"+phone);
        }
        cursor.close();
        //也可以使用sql语句,达到同样效果
        //db.execSQL("delete from person where name=?", new String[]{"ben"});
    }
     
    //事务可以保证一系列操作要么全部完成,要么都不完成
    public void Transaction_database(View view) {
        SQLiteDatabase  db = dbHelper.getWritableDatabase();
        //开启事物
        db.beginTransaction();
        try {
            db.delete("person"null,null);
            if(true){
                //在这里手动抛出异常,让事务失败,那么一条语句都没有执行成功
                throw new NullPointerException();
            }
            ContentValues values = new ContentValues();
            values.put("name""ben");
            values.put("salary""10000");
            values.put("phone", 1384567);
            db.insert("person"null, values);
            //事务执行成功
            db.setTransactionSuccessful();
        catch (Exception e) {
            e.printStackTrace();
        }finally{
            db.endTransaction();//关闭事物
        }
    }
    

  升级数据库。

  需求:现在需要升级数据库,版本1里面有一张表,版本2中有一张表,用户升级分两种情况

  如果用户现在的版本号是1,那么他则只需要创建一张表,如果用户直接安装版本2,则直接创建2张表(在update中使用switch代码有体现)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
 
    public static final String Create_contact = "create table person(_id integer primary key autoincrement, "
            "name char(10), " "salary char(20), " "phone integer(20))";
    public static final String Create_sms = "create table sms(_id integer primary key autoincrement, "
            "name char(10), " "body char(20), " "time integer(20))";
    private Context mcontext;
 
    public MySQLiteOpenHelper(Context context, String name,
            CursorFactory factory, int version) {
        super(context, name, factory, version);
        mcontext = context;
    }
 
    @Override
    public void onCreate(SQLiteDatabase db) {
        // 当创建数据库的时候会调用此方法在此方法中创建表
        db.execSQL(Create_contact);
        db.execSQL(Create_sms); // 更新数据库时,如果用户直接安装最新版本,则同时将两张表创建好
        Toast.makeText(mcontext, "联系人数据库创建了", 0).show();
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {
        case 1:
            db.execSQL(Create_sms);// 如果用户从1版本升级过来的话,就不会创建两张表而是再升级中添加一张表
        default:
 
        }
 
    }
 
}

  

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MySQLiteOpenHelper helper = new MySQLiteOpenHelper(this"person.db"null, 2);
        helper.getWritableDatabase();
    }
     
}

新需求:这次要给contact表和sms表之间建立联系 给contact表中添加一个category_id的字段。 用户升级直接升级到3版本,或者从2版本升级过来。

在这里我们发现一个特别的地方,switch中case都没有break,这是为了每次升级修改都能被执行到。比如用户从从1版本升级到3版本,则case1 case2 中的逻辑都会执行到,使用这种方法可以保证数据时最新的,同时原来的数据也不会丢失

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MySQLiteOpenHelper helper = new MySQLiteOpenHelper(this"person.db"null, 3);
        helper.getWritableDatabase();
    }
     
}

  

复制代码
public class MySQLiteOpenHelper extends SQLiteOpenHelper {    public static final String Create_contact = "create table person(_id integer primary key autoincrement, "            + "name char(10), " + "salary char(20), " + "phone integer(20),category_id integer)";    public static final String Create_sms = "create table sms(_id integer primary key autoincrement, "            + "name char(10), " + "body char(20), " + "time integer(20))";    private Context mcontext;    public MySQLiteOpenHelper(Context context, String name,            CursorFactory factory, int version) {        super(context, name, factory, version);        mcontext = context;    }    @Override    public void onCreate(SQLiteDatabase db) {        // 当创建数据库的时候会调用此方法在此方法中创建表        db.execSQL(Create_contact);        db.execSQL(Create_sms); // 更新数据库时,如果用户直接安装最新版本,则同时将两张表创建好        Toast.makeText(mcontext, "联系人数据库创建了", 0).show();    }    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        switch (oldVersion) {        case 1:            db.execSQL(Create_sms);// 如果用户从1版本升级过来的话,就不会创建两张表而是再升级中添加一张表        case 2:            db.execSQL("alter table person add column category_id integer");// 如果用户从2版本升级过来的话,就不会创建两张表而是再升级中添加一张表        default:        }    }}
复制代码

  最后的升级数据库的最佳写法 来自郭霖著的第一行代码,我把其中的代码提炼出来,如果希望得到更详细的解释,可以查看第一行代码这本书

原创粉丝点击