数据库操作、基本每个android应用都有涉及,有些应用需要对数据库进行加密,不让root用户去访问,有些应用表之间联系紧密要并发处理等,那么怎么建立一个比较好的成熟的框架,方便之后的应用都可以拿来直接用呢。下面介绍下我一直使用的数据库,本人使用的是Android studio 开发工具。
我们都知道android sqlite是存储在我们应用的内部存储data/data/里面的,一般用户是查看不到的、但对于root的手机是可以查看我们的数据库的,为了也防止root用户查看、我们就要对数据库进行加密。我们要使用SQLCipher ,SQLCipher是一SQLite基础之上进行扩展的开源数据库,它主要是在SQLite的基础之上增加了数据加密功能,我们在数据库中使用,可以加大我们数据库的安全性。
这个数据库的下载地址是:https://s3.amazonaws.com/sqlcipher/SQLCipher+for+Android+v2.2.2.zip
接下来介绍SQLCipher的使用
package com.test.dbencrypt.db; import android.content.Context; import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteOpenHelper; /** * Created by fuweiwei on 2015/11/19. * 数据库访问类,因此数据库使用了加密,所以这里使用的并不是Android API中的SQLiteOpenHelper,而是net.sqlcipher.database包下的SQLiteOpenHelper */ public class MyDatabaseHelper extends SQLiteOpenHelper implements TableColumns{ private final static String DB_NAME = "db_demo.db"; private final static int DB_VERSION = 1; //建表sql语句 public static final String CREATE_TABLE_USER = "CREATE TABLE IF NOT EXISTS " + USER_COLUMNS.TABLE_USER +"(" + "_id integer primary key autoincrement," + USER_COLUMNS.NAME +" text," + USER_COLUMNS.PASSWORD +" text" +")"; public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } public MyDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { sqLiteDatabase.execSQL(CREATE_TABLE_USER); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { } }需要主要的是我们继承的sqliteOpenHelper 不是android Api中的,是net.sqlcipher.database包下的sqlteOpenHelper。有人注意到了我们这里实现了接口TableColumns,那我们看下这个接口是做什么用的
package com.test.dbencrypt.db; /** * Created by fuweiwei on 2015/11/19. * 数据库表、字段(方便以后统一管理) */ public interface TableColumns { public static interface USER_COLUMNS { public static final String TABLE_USER="user"; public static final String NAME="Name";//名字 public static final String PASSWORD = "Password";// 密码 } }其实这个这只是一个统一管理数据库表名和字段的接口,因为在开发中我们时不时会改我们的表名和字段名,放在一起就不需要一处处去改了,这也是跟以前有十几年开发经验老大学的。
package com.test.dbencrypt.db; import android.content.Context; import net.sqlcipher.database.SQLiteDatabase; import java.util.concurrent.atomic.AtomicInteger; /** * Created by fuweiwei on 2015/11/19. * 数据库操作基类 */ public class DBDao { protected static MyDatabaseHelper dbHelper; protected SQLiteDatabase db; //数据库加密的key protected static String DB_Key ="secret_key"; //数据可打开关闭标记器,可解决重复打开关闭数据库的问题 protected AtomicInteger mOpenCounter = new AtomicInteger(); protected DBDao(Context context) { if (dbHelper == null) { synchronized (DBDao.class) { if (dbHelper == null) { //将SQLCipher所依赖的so库加载进来 SQLiteDatabase.loadLibs(context); dbHelper = new MyDatabaseHelper(context); } } } } /** * 获得一个可写数据库 * @return SQLiteDatabase */ protected synchronized SQLiteDatabase getWritableDatabase(){ if(mOpenCounter.incrementAndGet() == 1) { db = dbHelper.getWritableDatabase(DB_Key); } return db; } /** * 获得一个可读数据库 * @return SQLiteDatabase */ protected synchronized SQLiteDatabase getReadableDatabase(){ if(mOpenCounter.incrementAndGet() == 1) { db = dbHelper.getReadableDatabase(DB_Key); } return db; } /** * 关闭数据库连接 */ protected synchronized void closeDatabase(){ if(mOpenCounter.decrementAndGet() == 0) { db.close(); } } }
SQLiteDatabase.loadLibs(context);这段代码必须要加,相当于初始化,因为我们要对数据库加密,那么必须要有一个秘钥,在数据库解密的时候我们需要这个秘钥,需要注意的是SQLCipher提供的Api跟android原生的数据库的Api是一模一样的,操作起来几乎一模一样。
Leak found
Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed
package com.test.dbencrypt.db; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import com.test.dbencrypt.bean.UserBean; import net.sqlcipher.database.SQLiteDatabase; import java.util.ArrayList; import java.util.List; /** * Created by fuweiwei on 2015/11/19. * 用户表操作类 */ public class DBUserDao extends DBDao implements TableColumns.USER_COLUMNS { private static DBUserDao INSTANCE; private Context mContext; protected DBUserDao(Context context) { super(context); } public static DBUserDao getInstance(Context context){ if(INSTANCE==null){ synchronized (DBUserDao.class) { if(INSTANCE==null){ INSTANCE = new DBUserDao(context); } } } return INSTANCE; } /** * 添加一条用户 * @param userBean */ public void addUser(UserBean userBean){ if(userBean==null){ return; } SQLiteDatabase db = getWritableDatabase(); ContentValues values = new ContentValues(); values.put("name", userBean.getName()); values.put("password", userBean.getPassword()); db.insert(TABLE_USER,NAME,values); closeDatabase(); } /** * 获取所有用户 * @return */ public List<UserBean> getUsers(){ List<UserBean> list = new ArrayList<>(); SQLiteDatabase db = getWritableDatabase(); String sql = "select *from "+TABLE_USER; Cursor cursor = db.rawQuery(sql,null); if(cursor != null && cursor.moveToFirst()){ UserBean bean = new UserBean(); bean.setName(cursor.getString(cursor.getColumnIndex(NAME))); bean.setPassword(cursor.getString(cursor.getColumnIndex(PASSWORD))); list.add(bean); } if(cursor!=null){ cursor.close(); } closeDatabase(); return list; } }
package com.test.dbencrypt.bean; import java.io.Serializable; /** * Created by fuweiwei on 2015/11/19. */ public class UserBean implements Serializable{ private String name; private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
MainActivity:
package com.test.dbencrypt; import android.content.Context; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.test.dbencrypt.bean.UserBean; import com.test.dbencrypt.db.DBUserDao; import java.util.List; public class MainActivity extends FragmentActivity implements View.OnClickListener{ private Button mBtnAdd; private Button mBtnLook; private Context mContext = this; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } public void initView(){ mBtnAdd = (Button) findViewById(R.id.btn_add); mBtnLook = (Button) findViewById(R.id.btn_look); mBtnAdd.setOnClickListener(this); mBtnLook.setOnClickListener(this); } @Override public void onClick(View view) { int id = view.getId(); switch (id){ case R.id.btn_add: UserBean userBean = new UserBean(); userBean.setName("vgeer"); userBean.setPassword("123456"); DBUserDao.getInstance(mContext).addUser(userBean); Toast.makeText(mContext,"添加成功",Toast.LENGTH_SHORT).show(); break; case R.id.btn_look: List<UserBean> list = DBUserDao.getInstance(mContext).getUsers(); StringBuffer stringBuffer = new StringBuffer(); for (UserBean userBean1:list){ stringBuffer.append("用户:"+userBean1.getName()+","); stringBuffer.append("密码:"+userBean1.getPassword()+"。"); } Toast.makeText(mContext,stringBuffer.toString(),Toast.LENGTH_SHORT).show(); break; } } }
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="添加" android:id="@+id/btn_add" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="查看" android:id="@+id/btn_look" android:layout_marginTop="32dp" android:layout_below="@+id/btn_add" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> </RelativeLayout>以上就是我经常使用的数据库框架,基本每个应用都可以搬过来直接用,以后也会把我认为好点东西分享给大家。