Android中數據持久化技術包括文件存儲、SharedPreferences以及數據庫存儲,對於大量複雜的關係型數據,數據庫無疑是最合適的選擇。
SQLite是一個輕量級的關係型數據庫,運算速度快,佔用資源少,適合在移動設備上使用。SQLite不僅支持SQL語法,還遵循數據庫的ACID事務,使得本地持久化產生了質的飛躍。
首先我們創建類繼承SQliteOpenHelper抽象類,重寫onCreate和onUpgrade方法實現數據庫的創建和升級。
這裏爲了方便多線程併發訪問數據庫,將類設計爲單例模式。
package com.sdu.runningsdu.Utils;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.util.Log;
/**
* Created by FTDsm on 2018/6/4.
*/
public class DatabaseHelper extends SQLiteOpenHelper {
private static String DB_NAME = "xxx.db";
private static final int DB_VERSION = 1;
private static DatabaseHelper databaseHelper;
public DatabaseHelper(Context context, String name) {
super(context, name, null, DB_VERSION);
if(Build.VERSION.SDK_INT >= 11){
getWritableDatabase().enableWriteAheadLogging();
}
}
/**
* 單例模式
* @param context
* @param name
* @return DatabaseHelper
*/
public static synchronized DatabaseHelper getInstance(Context context, String name) {
if (databaseHelper == null) {
databaseHelper = new DatabaseHelper(context, name);
}
return databaseHelper;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
String userSQL = "create table if not exists user " +
"(sid varchar(20) primary key, " +
"name varchar(255), " +
"password varchar(255), " +
"imagePath varchar(255), " +
"image blob)";
sqLiteDatabase.execSQL(userSQL);
Log.d("database", "create table user");
String friendSQL = "create table if not exists friend " +
"(sid varchar(20) primary key, " +
"name varchar(255), " +
"imagePath varchar(255), " +
"unread integer, " +
"image blob)";
sqLiteDatabase.execSQL(friendSQL);
Log.d("database", "create table friend");
String groupSQL = "create table if not exists groups " +
"(gid integer primary key, " +
"name varchar(255), " +
"creator varchar(255), " +
"imagePath varchar(255), " +
"unread varchar(255), " +
"image blob)";
sqLiteDatabase.execSQL(groupSQL);
Log.d("database", "create table groups");
String groupMemberSQL = "create table if not exists groupmember " +
"(gid integer, " +
"sid varchar(20)," +
"primary key(gid, sid) )";
sqLiteDatabase.execSQL(groupMemberSQL);
Log.d("database", "create table groupmember");
String friendMessageSQL = "create table if not exists friendmessage " +
"(mid integer primary key, " +
"sid varchar(20), " +
"type integer, " + /* 0接收 1發送 */
"content varchar(255), " +
"time timestamp)";
sqLiteDatabase.execSQL(friendMessageSQL);
Log.d("database", "create table friendmessage");
String groupMessageSQL = "create table if not exists groupmessage " +
"(mid integer primary key, " +
"gid integer, " +
"sid varchar(20), " +
"type integer, " +
"content varchar(255), " +
"time timestamp)";
sqLiteDatabase.execSQL(groupMessageSQL);
Log.d("database", "create table groupmessage");
String requestSQL = "create table if not exists request " +
"(rid integer primary key, " +
"receiver varchar(20), " +
"sender varchar(20), " +
"message varchar(255), " +
"time timestamp, " +
"state integer)";
sqLiteDatabase.execSQL(requestSQL);
Log.d("database", "create table request");
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
String userSQL = "drop table if exists user";
sqLiteDatabase.execSQL(userSQL);
String friendSQL = "drop table if exists friend";
sqLiteDatabase.execSQL(friendSQL);
String groupSQL = "drop table if exists group";
sqLiteDatabase.execSQL(groupSQL);
String groupMemberSQL = "drop table if exists groupmember";
sqLiteDatabase.execSQL(groupMemberSQL);
String friendMessageSQL = "drop table if exists friendmessage";
sqLiteDatabase.execSQL(friendMessageSQL);
String groupMessageSQL = "drop table if exists groupmessage";
sqLiteDatabase.execSQL(groupMessageSQL);
String requestSQL = "drop table if exists request";
sqLiteDatabase.execSQL(requestSQL);
onCreate(sqLiteDatabase);
}
@Override
public synchronized void close() {
super.close();
}
}
然後我們創建Database Access Object類,完成數據增刪改查等操作,藉助SQLiteOpenHelper的getReadableDatabase()和getWritableDatabase()方法獲取對數據庫可讀或可寫操作對象。爲了確保數據庫可併發訪問,有兩種解決方案,一是以單例模式實例化SQLiteOpenHelper,每次使用完不執行db.close(),以免另一個操作正在進行時關閉數據庫導致操作失敗,二是每次實例化新的SQLiteOpenHelper,並在使用結束後調用db.close(),這裏推薦第一種,性能更高且節省資源。
這裏以user爲例,介紹數據庫的增刪改查操作。
package com.sdu.runningsdu.Utils;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.sdu.runningsdu.JavaBean.Friend;
import com.sdu.runningsdu.JavaBean.Group;
import com.sdu.runningsdu.JavaBean.Message;
import com.sdu.runningsdu.JavaBean.Request;
import com.sdu.runningsdu.JavaBean.User;
import java.util.ArrayList;
import java.util.List;
/**
* Created by FTDsm on 2018/6/4.
*/
public class MyDAO {
private Context context;
private String name;
private DatabaseHelper databaseHelper;
public MyDAO(Context context, String name) {
this.context = context;
this.name = name;
this.databaseHelper = DatabaseHelper.getInstance(context, name);
Log.d("database", "database name: " + databaseHelper.getDatabaseName());
}
/**
* 查詢所有表格
*/
public void findTable() {
SQLiteDatabase db = this.databaseHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("select name from sqlite_master where type='table' order by name", null);
Log.d("database", "table:\n");
while (cursor.moveToNext()) {
Log.d("database", cursor.getString(0));
}
cursor.close();
}
/**
* 添加用戶
* @param user User對象
*/
public void addUser(User user) {
SQLiteDatabase db = this.databaseHelper.getWritableDatabase();
Object[] objects = new Object[3];
objects[0] = user.getSid();
objects[1] = user.getName();
objects[2] = user.getPassword();
String sql = "insert into user(sid, name, password) values(?,?,?)";
db.execSQL(sql, objects);
Log.d("database", "add user: " + user.getName());
}
/**
* 刪除用戶
* @param sid 用戶id
*/
public void deleteUser(String sid) {
SQLiteDatabase db = this.databaseHelper.getWritableDatabase();
String sql = "delete from user where sid = ?";
db.execSQL(sql, new Object[]{sid});
Log.d("database", "delete user: " + sid);
}
/**
* 更新用戶信息
* @param user User對象
*/
public void updateUser(User user) {
SQLiteDatabase db = this.databaseHelper.getWritableDatabase();
Object[] objects = new Object[3];
objects[0] = user.getName();
objects[1] = user.getPassword();
objects[2] = user.getSid();
String sql = "update user set name=?, password=? where sid=?";
db.execSQL(sql, objects);
Log.d("database", "update user: " + user.getName());
}
/**
* 查詢是否有用戶
* @return 當前是否存在用戶
*/
public boolean hasUser() {
SQLiteDatabase db = this.databaseHelper.getReadableDatabase();
Cursor cursor = db.query("user",
null, null, null, null, null, null);
Log.w("has user", ""+cursor.getCount());
if (cursor.getCount() > 0) {
return true;
}
cursor.close();
return false;
}
/**
* 查找用戶信息
* @param sid 用戶id
* @return User對象
*/
public User findUser(String sid) {
User user = new User();
SQLiteDatabase db = this.databaseHelper.getReadableDatabase();
Cursor cursor = db.query("user",
new String[]{"sid", "name", "password", "imagePath"},
"sid = ?",
new String[]{sid},
null, null, null);
if (cursor.moveToNext()) {
user.setSid(cursor.getString(cursor.getColumnIndex("sid")));
user.setName(cursor.getString(cursor.getColumnIndex("name")));
user.setPassword(cursor.getString(cursor.getColumnIndex("password")));
user.setImagePath(cursor.getString(cursor.getColumnIndex("imagePath")));
}
Log.d("database", "find user: " + user.toString());
cursor.close();
return user;
}
/**
* 查詢所有用戶
* @return User列表
*/
public List<User> findAllUser() {
List<User> users = new ArrayList<>();
SQLiteDatabase db = this.databaseHelper.getReadableDatabase();
Cursor cursor = db.query("user",
new String[]{"sid", "name", "password", "imagePath"},
null, null, null, null, null);
while (cursor.moveToNext()) {
User user = new User();
user.setSid(cursor.getString(cursor.getColumnIndex("sid")));
user.setName(cursor.getString(cursor.getColumnIndex("name")));
user.setPassword(cursor.getString(cursor.getColumnIndex("password")));
user.setImagePath(cursor.getString(cursor.getColumnIndex("imagePath")));
Log.d("database", "find user: " + user.toString());
users.add(user);
}
cursor.close();
return users;
}
}
正如groupmember表中所定義,SQLite聯合主鍵採用如下寫法:
String sql= "create table if not exists groupmember " +
"(gid integer, " +
"sid varchar(20)," +
"primary key(gid, sid) )";
sqLiteDatabase.execSQL(sql);
SQLite允許存儲varchar、char、integer、real、text、blob、date、time等類型,存儲圖片通常使用blob存儲byte數組
/**
* 更新用戶頭像
* @param sid
* @param image
*/
public void updateUserImage(String sid, String imagePath, byte[] image) {
SQLiteDatabase db = this.databaseHelper.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("imagePath", imagePath);
cv.put("image", image);
db.update("user", cv, "sid = ?", new String[]{sid});
}