解决:Android使用自带sqlite开发时,apk中创建的数据库外部的进程是没有权限去读/写的,而且无法如何读取指定目录下的db文件

SQLiteOpenHelper是Android框架为我们提供的一个非常好的数据库打开、升级与关闭的工具类。但是这个工具类会自动把db文件创建到“ /data/data/com.*.*(package name)/” 目录下,这么做可能是与Android文件系统的设计思路有关。

但是在实战过程中,我们可能有各种原因需要自定义db文件路径(例如db文件较大放到sd卡更安全等等),相信很多人都遇到了这个需求,网上也有很多解决方法,这些方法大多是抛弃Android框架为我们提供的SQLiteOpenHelper类,自己重头写一个DbHelper类完成自定义路径的数据库打开关闭等。这么做虽然可以解决问题,但并不是一个最好的方法,因为自己写的DbHelper可靠性和功能自然难和google巨匠相。


本文提出一种方法,通过继承和添加代码,并复用SQLiteOpenHelper的代码,来解决自定义db路径的问题。

首先我们来分析一下SQLiteOpenHelper的源代码。getReadableDatabase()和getWritableDatabase()在内部都是调用getDatabaseLocked()。getDatabaseLocked()的源代码很容易理解,分析得知:

  • 如果以只读方式打开,是通过mContext.getDatabasePath(mName)来获取db文件的路径并使用SQLiteDatabase.openDatabase()直接打开数据库;
  • 如果以读写方式打开,是通过mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ? Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0, mFactory, mErrorHandler)打开或创建数据库。

所以我们需要改变mContext的行为。Android框架提供了一个ContextWrapper类,是Context的一个代理,可以通过继承的方式拉改变Context的行为,所以我们继承ContextWrapper,代码如下:


class CustomPathDatabaseContext extends ContextWrapper{

                private String mDirPath;
                
                public CustomPathDatabaseContext(Context base, String dirPath) {
                        super(base);
                        this.mDirPath = dirPath;
                }
                
                @Override
                public File getDatabasePath(String name) 
                {
                    File result = new File(mDirPath + File.separator + name);

                    if (!result.getParentFile().exists())
                    {
                        result.getParentFile().mkdirs();
                    }

                    return result;
                }
                
                @Override 
                public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory)
                {
                        return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory);
                }
                @Override 
                public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler){
                       return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name).getAbsolutePath(), factory, errorHandler);
                }


        }




上述代码很简单了,就不用多说明了吧,然后我们在继承SQLiteOpenHelper时这么写就可以了:
class YourDbHelper extends SQLiteOpenHelper{

        public YourDbHelper(Context context, String name, CursorFactory factory,
                        int version) {
                super(new CustomPathDatabaseContext(context, getDirPath()), name, factory, version);
        }

        /**
         * 获取db文件在sd卡的路径
         * @return
         */
        private static String getDirPath(){
                //TODO 这里返回存放db的文件夹的绝对路径
                return "";
        }
        
        @Override
        public void onCreate(SQLiteDatabase db) {
                
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                
        }
        
}



如此一来,我们既可以自定义db文件路径,又可以复用SQLiteOpenHelper十分好用的功能了~


另外需要注意的是,有些应用可能是有一个已建好表的db文件放在assets中,应用运行时先判断db文件是否存在,如果不存在则从assets中复制到自定义路径。这种情况通常都是在PC端使用SQLiteSpy诸如此类的工具写sql建表,使用这种方法的小伙伴们别忘了在建表时执行  PRAGMA schema_version = 1   这句sql(当然了版本号取决于您的需求) , 否则SQLiteOpenHelper还是会触发onCreate的~看了SQLiteOpenHelper什么时候触发onCreate的源码就明白怎么回事了~




        另补充: 之前的代码有个小问题,CustomPathDatabaseContext 在重写方法时,不但要重写openOrCreateDatabase(String name, int mode, CursorFactory factory),这个是android2.x调用的方法,还需要重写openOrCreateDatabase(String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler),这个是android4.x调用的方法~~
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值