1. 程式人生 > >Android 資料持久化技術(即資料儲存方式)

Android 資料持久化技術(即資料儲存方式)

在討論資料持久化技術之前我們先了解幾個概念?

什麼是瞬時資料:儲存在記憶體當中,有可能會因為程式的關閉或其他原因導致記憶體被收回而丟失的資料。

 

為什麼採用資料持久化技術:為了保證關鍵資料在程式退出時不被丟失。

 

什麼是資料持久化技術:將記憶體中的瞬時資料儲存到儲存裝置中,保證手機在關機的情況下資料仍然不會丟失。

 

安卓提供了三種方式用於簡單的資料持久化功能:檔案儲存,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中展示建立資料庫,升級資料庫,實現資料庫的增刪改查功能 ,和使用事物功能,對每個功能的使用做了註釋。

  



//佈局檔案<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
 

//增刪改查事物的功能<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", null, null, null, null, null, null);
        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
 
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:
 
        }
 
    }
 
}
  

 
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 中的邏輯都會執行到,使用這種方法可以保證資料時最新的,同時原來的資料也不會丟失

 
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:

        }

    }

}
--------------------- 
作者:ITluochen 
來源:CSDN 
原文:https://blog.csdn.net/itluochen/article/details/52605392 
版權宣告:本文為博主原創文章,轉載請附上博文連結!