Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android學習筆記034之數據存儲—SQLite數據庫

Android學習筆記034之數據存儲—SQLite數據庫

編輯:關於Android編程

前面我們介紹了Android數據存儲的兩種方法:文件存儲和SharedPreference存儲,這一篇我們來學習一下Android存儲數據的另外一種方式——SQLite數據庫存儲。

1、SQlite數據庫簡介

現在的主流移動智能設備中,比如Android手機、iPhone手機,平板等都是使用SQLite數據庫作為存儲復雜數據的存儲引擎。那麼什麼是SQLite數據庫呢?

SQLite是D.Richard Hipp用C語言編寫的開源嵌入式數據庫引擎,它支持大多數的SQL92標准,並且可以在所有主要的操作系統上運行。SQLite由以下幾個部分組成:SQL編譯器、內核、後端以及附件。SQLite通過利用虛擬機和虛擬數據庫引擎(VDBE),是調試、修改和擴展SQLite的內核變得更加方便。所有SQL語句都被編譯成易讀的、可以在SQLite虛擬機中執行的程序集。

SQlite數據庫是一個輕量級的關系型數據庫,不需要像其它關系型數據庫一樣需要安裝,Android已經將SQLite數據庫內置在系統中,內置的版本是3.0版本。SQlite支持標准的SQL語法,還支持ACID(數據庫事務)原則,占用資源非常少,非常適合在移動設備中使用。

袖珍型的SQLite數據庫就可以支持高達2TB大小的數據庫,每個數據庫都是以單個文件的形式存在,這些數據都是以B-Tree的數據結構形式存儲在磁盤上。每一個數據庫是一個文件,數據庫中可以包含多個表,表中可以包含多個字段。

SQLite數據庫支持NULL、INTEGER、REAL、TEXT和BLOB數據類型,分別代表空值、整型值、浮點值、字符串文本、二進制對象。SQLite采用動態數據類型,當某個值插入到數據庫時,SQLite將會檢查它的類型,如果該類型與關聯的列不匹配,SQLite則會嘗試將該值轉換成該列的類型,如果不能轉換,則該值將作為本身的類型存儲,SQLite稱這為“弱類型”。但有一個特例,如果是INTEGER PRIMARY KEY,則其他類型不會被轉換,會報一個“datatype missmatch”的錯誤。簡單的說就是:我們可以各種數據類型的數據保存到任何字段中而不用關心字段聲明的數據類型。

下面是Android系統中SQLite數據庫的幾個關鍵類:

SQLiteOpenHelper:數據庫抽象類,我們通過繼承該類,獲取數據庫實例,然後可以重寫數據庫創建、更新版本方法和關閉數據庫

SQLiteDatabase:數據庫訪問類:我們通過獲取這個類的實例來對數據庫做一些CRUD操作

Cursor:游標:類似於JDBC裡的結果集

2、SQLiteOpenHelper創建數據庫與版本管理

我們繼承SQLiteOpenHelper這個類的時候必須要實現兩個方法: onCreate(SQLiteDatabase sqLiteDatabase)和onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1),onCreate方法是我們第一次使用應用的時候生成數據表;onUpgrade方法是數據庫版本發生變更的時候調用。還有必須要實現一個構造方法,下面是代碼示例:

public class DBOpenHelper extends SQLiteOpenHelper {
private static String DB_NAME = "db_test.db";
private static int DB_VERSION = 1;

public DBOpenHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
}

/**
 * 數據庫第一次創建的時候調用
 *
 * @param sqLiteDatabase
 */
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {

}

/**
 * 數據庫版本發生改變的時候調用
 *
 * @param sqLiteDatabase
 * @param i
 * @param i1
 */
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

}
}

  上述代碼啟動會創建一個db_test.db的數據庫文件,在data/data/包名/database目錄下就可以看到創建的數據庫文件,將其導出之後就可以用圖形化工具查看。

3、SQLite數據庫圖形化工具

  SQLite圖形化工具很多,推薦使用SQLite expert,這是官網下載地址。這個圖形化工具使用也很簡單,直接將從Android Device Monitor導出的數據庫文件導入就可以查看數據。這個可視化工具實用不是很復雜,就不在做很多的介紹了。

4、SQLite數據庫使用

  下面我們進行數據庫的基本使用,在Android開發中,我們不需要寫很多SQL語句,Android已經幫我們封裝好了,我們直接調用就可以了。Android提供的CRUD操作的方法介紹:

數據增加:
insert(String table, String nullColumnHack, ContentValues values)

參數解析:第一個是數據表名;第二個是設置為null值的數據表名;第三個是插入的數據,用ContentValues封裝。

數據刪除:
delete(String table, String whereClause, String[] whereArgs)

參數解析:第一個是數據表名;第二個是where條件;第三個是約束

數據更新:
update(String table, ContentValues values, String whereClause, String[] whereArgs)

參數解析:第一個是數據表名;第二個是改變的數據;第三個是where條件;第四個是約束

數據查詢:
query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)

query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit, CancellationSignal cancellationSignal)

query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)

query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)

參數解析:

table:表名稱

colums:表示要查詢的列所有名稱集

selection:表示WHERE之後的條件語句,可以使用占位符

selectionArgs:條件語句的參數數組

groupBy:指定分組的列名

having:指定分組條件,配合groupBy使用

orderBy:y指定排序的列名

limit:指定分頁參數

distinct:指定“true”或“false”表示要不要過濾重復值

Cursor:返回值,相當於結果集ResultSet

下面通過實現數據庫的基本CRUD操作體會一下:

Activity代碼:

package com.example.datasave;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Devin on 2016/7/20.
 */
public class SQLiteActivity extends AppCompatActivity {
private Button btn_sqlite_insert;
private Button btn_sqlite_delete;
private Button btn_sqlite_update;
private Button btn_sqlite_select;
private SQLiteDatabase mDatabase;
private DBOpenHelper mDBOpenHelper;
private static final String TAG = "SQLiteActivity";

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sqlite);

    mDBOpenHelper = new DBOpenHelper(this);
    mDatabase = mDBOpenHelper.getWritableDatabase();
    btn_sqlite_insert = (Button) findViewById(R.id.btn_sqlite_insert);
    btn_sqlite_insert.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            insert();
        }
    });
    btn_sqlite_delete = (Button) findViewById(R.id.btn_sqlite_delete);
    btn_sqlite_delete.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            delete(5);
        }
    });
    btn_sqlite_update = (Button) findViewById(R.id.btn_sqlite_update);
    btn_sqlite_update.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            update(6);
        }
    });
    btn_sqlite_select = (Button) findViewById(R.id.btn_sqlite_select);
    btn_sqlite_select.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            select();
        }
    });
}

private void select() {
    Cursor cursor = mDatabase.query("student", null, null, null, null, null, null);
    List students = new ArrayList<>();
    while (cursor.moveToNext()) {
        StudentModel model = new StudentModel();
        model.setS_id(cursor.getInt(cursor.getColumnIndex("s_id")));
        model.setName(cursor.getString(cursor.getColumnIndex("name")));
        model.setSex(cursor.getString(cursor.getColumnIndex("sex")));
        model.setGrade(cursor.getString(cursor.getColumnIndex("grade")));
        students.add(model);
        Log.i(TAG, "數據---->>" + model.toString());
    }
    cursor.close();
    ToastUtils.showToast(this, students.toString());
}

private void update(int s_id) {
    ContentValues values = new ContentValues();
    values.put("grade", "2013級信計班");
    mDatabase.update("student", values, "s_id=?", new String[]{"" + s_id + ""});
    ToastUtils.showToast(this, "更新了s_id為" + s_id + "的數據");
}


private void insert() {
    ContentValues values = new ContentValues();
    values.put("name", "李四");
    values.put("sex", "男");
    values.put("grade", "2012級數應班");
    mDatabase.insert("student", null, values);
    ToastUtils.showToast(this, "插入數據成功");
}

private void delete(int position) {
    mDatabase.delete("student", "s_id=?", new String[]{"" + position + ""});
    ToastUtils.showToast(this, "刪除了s_id為" + position + "的數據");
}
}

SQLiteOpenHelper代碼:

package com.example.datasave;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * Created by Devin on 2016/7/20.
 */
public class DBOpenHelper extends SQLiteOpenHelper {
private static String DB_NAME = "db_test.db";
private static int DB_VERSION = 1;


private static final String TEST_TABLE = "CREATE TABLE student (s_id INTEGER PRIMARY KEY AUTOINCREMENT," +
        "name VARCHAR(20),sex VARCHAR(10),grade VARCHAR(100))";

public DBOpenHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
}

/**
 * 數據庫第一次創建的時候調用
 *
 * @param sqLiteDatabase
 */
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
    sqLiteDatabase.execSQL(TEST_TABLE);
}

/**
 * 數據庫版本發生改變的時候調用
 *
 * @param sqLiteDatabase
 * @param i
 * @param i1
 */
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

}
}

布局文件非常簡單,就不再貼代碼了,下面是實現效果圖:

\<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPtPJ09rKx8K81sZHSUajrMvZtsixyL3Pwv2jrNXi0fm+zb/J0tS7+bG+yrXP1sr9vt2/4rXEQ1JVRLLZ1/ehozwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9wPg0KPGgzIGlkPQ=="5sqlite數據庫事務">5、SQLite數據庫事務

  上面我們介紹了SQLite的基本操作,現在我們來介紹一下SQLite數據庫的事務,首先,來了解一下什麼是事務?

什麼是事務?

  數據事務是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性。簡單的說,就是多個操作捆綁到一起執行,只有所有的操作都成功了,事務才算執行完畢,才會提交,只要有一個操作沒有完成,事務都會回滾,所有的操作都不回提交。舉一個例子就是:張三向李四轉賬1000塊,張三的賬戶上要減少1000塊,李四的賬戶上要增加1000塊,只有兩個操作都完成,事務才會提交。假如張三的賬戶減少了1000塊,但是李四的賬戶上沒有增加1000塊;或者張三的賬戶沒有減少1000塊,但是李四的賬戶增加了1000塊,這顯然都是不可以的。

比如下面的例子代碼:

/**
 * 事務測試
 */
public void payment() {
    SQLiteDatabase db = mDBOpenHelper.getWritableDatabase();
    //開啟事務 
    db.beginTransaction();
    try {
        db.execSQL("update person set amount=amount-10 where personid=?", new Object[]{1});
        db.execSQL("update person set amount=amount+10 where personid=?", new Object[]{2});
        //設置事務標志為成功,當結束事務時就會提交事務 
        db.setTransactionSuccessful();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //結束事務 
        db.endTransaction();
    }
}

簡單的說就是:只有寫在事務裡的所有數據庫操作都成功,事務才會提交,否則,事務回滾回原來的狀態。在data/data/<包名>/database/目錄下,我們創建數據庫的時候也會創建一個xxx.db-journal讓數據庫支持事務的臨時日志文件。

6、SQLite數據庫二進制大文件保存和讀取

  一般情況下,我們很少在數據庫裡面存儲大文件,視頻、音頻、圖片等我們都是保存路徑,但是有些特殊情況下,我們需要將大文件保存到數據庫裡面,下面我們就實現一個簡單的例子:

SQLiteDatabase db = mDBService.getWritableDatabase(); // 得到數據庫
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
((BitmapDrawable) iv.getDrawable()).getBitmap().compress(
CompressFormat.PNG, 100, baos);//壓縮為PNG格式,100表示跟原圖大小一樣
Object[] args = new Object[] {baos.toByteArray() };
db.execSQL(INSERT_SQL, args);
baos.close();
db.close();
} catch (Exception e) {
e.printStackTrace();
}
}

//3.從數據庫中取圖片
public void getPhoto() {
String SELECT_SQL = "SELECT photo FROM launcher";
ImageView appIcon = (ImageView) v.findViewById(R.id.appicon);//v是我在類中定義的一個view對象,跟前面保存圖片一樣
byte[] photo = null;
mDBService = new DBService(getContext());
SQLiteDatabase db = mDBService.getReadableDatabase();
Cursor mCursor = db.rawQuery(SELECT_SQL, null);
if (mCursor != null) {
if (mCursor.moveToFirst()) {//just need to query one time
photo = mCursor.getBlob(mCursor.getColumnIndex("photo"));//取出圖片
}
}
if (mCursor != null) {
mCursor.close();
}
db.close();
ByteArrayInputStream bais = null;
if (photo != null) {
bais = new ByteArrayInputStream(photo);
appIcon.setImageDrawable(Drawable.createFromStream(bais, "photo"));//把圖片設置到ImageView對象中
} 
//appIcon顯示的就是之前保存到數據庫中的圖片
}

7、SimpleCursorAdapter綁定數據庫數據

  這種比較少用,我們就實現一個簡單的例子:

Map map = new HashMap();

    ListView listView = (ListView) this.findViewById(R.id.list);
    Cursor cursor = getContentResolver().query(
            ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
    if(cursor != null){

      startManagingCursor(cursor);

   }       
    ListAdapter adapter = new SimpleCursorAdapter(this,
            android.R.layout.simple_list_item_1, cursor,
            new String[] { PhoneLookup.DISPLAY_NAME },
            new int[] { android.R.id.text1 });
    listView.setAdapter(adapter);
    stopManagingCursor();

SQLite數據庫就簡單介紹到這裡,網絡上有很多好的數據庫操作框架,後面再介紹。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved