Android SQLite使用詳解和多執行緒併發訪問
阿新 • • 發佈:2019-02-02
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});
}