【问题标题】:Android - What is the Best/Efficient way of converting an Encrypted database to a plain database fileAndroid - 将加密数据库转换为普通数据库文件的最佳/有效方法是什么
【发布时间】:2014-08-01 12:06:30
【问题描述】:

我使用 SQLiteCipher 为我的应用程序数据库添加安全性,问题是,它比默认的 android SQLite API 太慢了。于是我切换回了默认的 SQLite API,但我确实需要 SQLCipher 提供的安全性,所以我做的是:

打开我的应用时

  1. 打开加密的数据库文件
  2. 创建一个普通的数据库文件
  3. 将记录从加密数据库传输到普通数据库
  4. 删除加密的数据库文件
  5. 使用普通的数据库文件

当我的应用关闭时,

  1. 打开正常的数据库文件
  2. 创建一个加密的数据库文件
  3. 将记录从普通数据库传输到加密数据库
  4. 删除正常的数据库文件

这没有任何问题,但是传输记录需要一些时间并且会消耗更多(我假设我的数据库文件在实际使用期间将包含数千条记录)。那么我所做的还有其他方式吗? 有没有更有效的方法来做到这一点?蒂亚!

编辑:这里是一些使用 SQLCipher 的代码

主要活动

private final static String phrase = "passW0rd3r";

private EditText            StudNum, StudName, StudCrse;
private DBHelper            dbIns;
SQLiteDatabase              DBFile;
private MainDBHelper        DBHelp;
private MenuItem            msg_app;
final Context               con = this;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    StudNum = (EditText)findViewById(R.id.txtsNum);
    StudName = (EditText)findViewById(R.id.txtsName);
    StudCrse = (EditText)findViewById(R.id.txtCourse);
    StudNum.requestFocus();

    SQLiteDatabase.loadLibs(this);
    dbIns = new DBHelper(this, DB_NAME);
    DBFile = dbIns.getWritableDatabase(phrase);
    DBHelp = new MainDBHelper(this);
//Takes too much time to load, loads around 3 seconds, compared to
//the default SQLite API, which is half a second only
}

MainDBHelper 类

    public class MainDBHelper {

    private static final String DB_NAME = "StudentInfo.db";
    private static final String TABLE_NAME = "StudentInfo";
    private final static String phrase = "passW0rd3r";

    private DBHelper openHelper;
    private SQLiteDatabase database;

    public MainDBHelper(Context context) {
        openHelper = new DBHelper(context, DB_NAME);
        database = openHelper.getWritableDatabase(phrase);
    }
}

DBHelper 类

public class DBHelper extends SQLiteOpenHelper{

    private String DBName;
    public DBHelper(Context context, String DBname) {
        super(context, DBname, null, 5);
        this.DBName = DBname.substring(0,DBname.length()-3);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onCreate(SQLiteDatabase arg0) {
        // TODO Auto-generated method stub
        arg0.execSQL("CREATE TABLE " + DBName + " (_id INTEGER PRIMARY KEY, " +
                "Stud_Num TEXT, Stud_Name TEXT, Stud_Crse TEXT)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
        // TODO Auto-generated method stub
        arg0.execSQL("DROP TABLE IF EXISTS " + DBName);
        onCreate(arg0);
    }
}

使用相同数据库连接的额外类

DisplayMessageActivity 类 - 这个加载大约 2 秒,只是为了在列表视图中显示 3 条记录。如果我使用默认的 SQLite API,它可以在一秒钟内加载多达 10 条记录

public class DisplayMessageActivity extends Activity {

        private ListAdapter             listAdapt;
        private MainDBHelper            DBHelp;

        private EditText                edtName, edtSnum, edtCrse;
        private TextView                dName, dSnum, dCrse, ssn;
        private ListView                lst_snum;
        private SearchView              sView;
        private MenuItem                sItem;

        private View                    lView, diagView;
        private AlertDialog.Builder     DBuilder;
        private AlertDialog             ADiag;
        private String                  StudentNumber, edSname, edSnum, edCrse;


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_display_message);
            DBHelp = new MainDBHelper(this);

            lst_snum = (ListView)findViewById(R.id.lv_test);

            refreshList();
            createADiag(this);

            lst_snum.setOnItemLongClickListener(new OnItemLongClickListener(){
                @Override
                public boolean onItemLongClick(AdapterView<?> parent, View view,
                        int position, long id) {
                    // TODO Auto-generated method stub
                    lView = view;
                    ssn = (TextView)view.findViewById(R.id.studNum);
                    ADiag.show();
                    return false;
                }
            });
        }

        private void refreshList(){
            listAdapt = null;
            listAdapt = new ListAdapter(this, DBHelp.getTimeRecordList());
            lst_snum.setAdapter(listAdapt);
        }
    }

更新:我已经阅读了一些与我的相关的问题,并且我真的看到有些问题还面临使用 SQLCipher 的性能问题,一个答案说“缓存数据库”只能用于一次连接,可以用于多个同一个应用程序上的活动,问题是,我不知道这样做

【问题讨论】:

  • “它比默认的 android SQLite API 太慢了”——我还没有听说过这方面的主要问题。加密开销并没有那么糟糕,因此如果使用 SQLCipher 很慢,那么使用普通 SQLite 可能会很慢(例如,强制表扫描的查询)。您可以考虑使用PRAGMA cipher_profile 进行试验,看看是否可以帮助您查明问题。而且由于Android中没有“应用已关闭”的概念,因此您提出的方法将无法可靠地工作。
  • 嗯,我的代码似乎有些奇怪,那么如果你说它很快(我会用我的代码更新我的问题)。那 PRAGMA cipher_profile 是否需要我构建源代码?关于应用程序关闭的概念,我开发了我的应用程序,以便每当用户离开它时它会自动退出/完成(通过activityForResult),所以我想我的应用程序可以在完全退出之前加密数据库。感谢您的回答
  • 为什么不简单地在退出时加密整个文件?并在启动时解密?
  • “PRAGMA cipher_profile 需要我构建源代码吗?” ——我不知道。 “我开发了我的应用程序,以便在用户离开它时(通过activityForResult)自动退出/完成,所以我猜我的应用程序可以在完全退出之前加密数据库”——不可靠。
  • @greenapps,你是什么意思?有代码吗?

标签: android database sqlite sqlcipher


【解决方案1】:

对于优化 SQLCipher 性能,有一些非常重要的准则:

  • 不要重复打开和关闭连接,因为密钥派生非常昂贵,这是设计使然
  • 使用事务来包装插入/更新/删除操作。除非在事务范围内执行,否则每个操作都将发生在它自己的事务中,这会使事情变慢几个数量级
  • 确保您的数据已规范化(即,使用良好的做法将数据分离到多个表中以消除冗余)。不必要的数据重复会导致数据库膨胀,这意味着 SQLCipher 需要操作更多页面
  • 确保所有用于搜索或连接条件的列都已编入索引。如果不这样做,SQLCipher 将需要对大量页面执行完整的数据库扫描
  • 如果您进行大量删除、更新等操作,请定期抽真空以确保数据库紧凑。

最后,为了进一步诊断特定查询语句的性能,有几个选项。首先,我建议像上面提到的 CommonsWare 一样运行 PRAGMA cipher_profile,您可以阅读更多关于 here 的用法。这将为您提供在数据库上执行的查询的日志以及它们各自的执行时间(以毫秒为单位)。接下来,您对一些可能表现不佳的查询运行explain query plan 命令?解释查询命令的输出描述为here

【讨论】:

  • 这解释了我需要知道的,一个非常有用的答案。关于您所说的第一个准则,我只想问一件事,我如何将一个数据库连接用于我的其他活动?我是 Android SQLite 新手,刚开始学习一段时间,谢谢!
  • 没关系,找到This,我想这样就可以了
猜你喜欢
  • 2013-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多