您的当前位置:首页扩展SQLite使其能从apk文件中读取db

扩展SQLite使其能从apk文件中读取db

2023-11-11 来源:哗拓教育

游戏中会大量使用到配置文件,每个项目组根据自己不同的需求会选择不同的存储格式,比如使用Json或者SQLite来存储数据。此处我们只对使用SQLite的情况来做讨论。一般情况下会选择把它放在可读写目录里面,这样SQLite可以直接使用它原来的io API来对db文件进行读取。在PC或者iOS平台上这不是问题。但是如果在Android平台上,游戏安装后还是以一个apk文件的形式存在。如果我们的数据放在了db中,使用SQLite原来自带的io功能是不能进行读取的。这里有3种方式可以供选择:

  1. 在程序第一次启动时,把apk中的所有文件解压出来放到可读写目录中,这样存在的问题是第一次打开程序时会比较慢。
  2. 在需要的读取某个db的时候才把这个文件从apk中解压出来,这样的话可能会导致卡顿,或者使用协程等异步操作来完成,但是这样对于逻辑层的代码书写成本比较高。
  3. 对SQLite做一定的改动,使它可以读取apk中的db文件。

一般大家可能会选择第一种方法,这没有什么好说的。我们接下来看看第3种方法的可能性。

理论

为了实现上述的想法,我们需要两个条件:

  1. android提供对apk中单个文件的读取的能力。
  2. SQLite提供了对io层(不同平台)进行快速修改的能力,这就要求SQLite有比较好的抽象以较少的模块之间的耦合。

当然上述两个条件是满足的,下面我们来具体看看这两个条件。

Android对apk中单个文件的读取能力

Open a new file descriptor that can be used to read the asset data. If the start or length cannot be represented by a 32-bit number, it will be truncated. If the file is large, use AAsset_openFileDescriptor64 instead.

 

Returns < 0 if direct fd access is not possible (for example, if the asset is compressed).

int AAsset_openFileDescriptor    (AAsset * asset, off_t *     outStart, off_t * outLength )

从这个API可以看出,它可以返回一个用于读取当前asset的一个文件描述符。但是如果当前asset被压缩了,那么就回返回一个小于0的值。如果我们想要读取db的话,那么它必须是没有压缩过的。

示例代码大体如下所示:

    AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_UNKNOWN);

    if (NULL == asset)

    {

        //LOGD("file not found! Stop preload file: %s", filename);

        return FILE_NOT_FOUND;

    }

    // open asset as file descriptor

    int fd = AAsset_openFileDescriptor(asset, &start, &length);

    assert(0 <= fd);

    AAsset_close(Asset);

注意,这个fd返回的是整个apk的句柄,start代表这个文件在apk中的偏移,length代表长度,使用的时候要注意。

SQLite对于文件io层的支持

下图是 SQLite官方给出的架构图:

技术分享

我们这里主要关注的就是OS Interface这一层,它使用了VFS这一对象来为不同系统之间的可移植性提供了保证,。具体细节可以参照官网对于VFS的介绍。SQLite目前提供了对类unix系统和windows系统的支持,分别在os_unix.c以及os_win.c中实现的。其中os_unix.c提供了对mac os、iOS、Android以及Linux的支持。如果读取想对这块有一个比较深入了了解可以看官方文档以及查看一些示例如test_demovfs.c等来深入了解。

实现

有了上面的理论支持,那么我们就可以着手可以写代码了。我们目前有两种方案可以选择:

  1. 单独为安卓实现一个VFS。
  2. 在os_unix.c的基础上进行修改。

第一种方案需要写的代码比较多,而且需要读者对SQLite有一个比较深入的了解。所以我们这里选择第二种方案。在原来的os_unix.c的基础上进行改动。

SQLite改造

通过分析os_unix.c文件,我们能得出它主要使用了open() read() write()等io操作。这跟我们上面Android NDK提供的AAsset_openFileDescriptor正好完美的结合起来。我们正好可以使用它返回的句柄进行类似的操作。只不过在Android的情况下特殊一点。

这样下来,我们基本上大体需要改的几个函数和结构体如下所示

  1. struct unixFile 我们需要在其中添加文件在apk中的偏移以及大小 。
  2. posixOpen 通过openFileDescriptor返回文件描述符。
  3. seekAndRead 读取时要加上文件偏移。
  4. unixFileSize 返回前面unixFile中记录的文件大小的值。

以上基本是需要改到的函数,当然根据实现的不同可能具体需要改动的函数不一样。这只是比较粗暴的改法。像我们需要支持从apk里面读取以及从一个散文件里面读取,所以跟上面的改动多少有一些不一样的地方,但是基本思想是通的。当然由于本人对SQLite不了        解,可能有需要改动的地方没有注意到,如果说的有错误希望能及时指正。方法已经说的比较明白了,这里也就不贴代码了。

上面的例子提到的AAssetManager_open在打开时需要一个AAssetManager的对象,这个对象只能从Java里面获取。如果你是直接使用Android开发那么这个对象就比较容易获取,那么如果你是使用Unity或者UE4开发怎么获取这个对象呢。

Unity实现细节

SQLite的修改跟上面是一样的,只是我们在Unity中如何获取这个对象呢。读者可以具体对照一下这个类AndroidJNI AndroidJNIHelper AndroidJavaClass AndroidJavaObject AndroidJavaProxy这几个类。

示例代码如下所示:

IntPtr cls_Activity = (IntPtr)AndroidJNI.FindClass("com/unity3d/player/UnityPlayer");

IntPtr fid_Activity = AndroidJNI.GetStaticFieldID(cls_Activity, "currentActivity", "Landroid/app/Activity;");

IntPtr obj_Activity = AndroidJNI.GetStaticObjectField(cls_Activity, fid_Activity);

 

IntPtr obj_cls = AndroidJNI.GetObjectClass(obj_Activity);

IntPtr asset_func = AndroidJNI.GetMethodID(obj_cls, "getAssets", "()Landroid/content/res/AssetManager;");

jvalue[] asset_array = new jvalue[2]; // <- ?

IntPtr assetManager = AndroidJNI.CallObjectMethod(obj_Activity, asset_func, asset_array);

这样我们就得到了这个AssetManager,这个时候我们就可以通过C#把这个对象传递给SQLite库了。

UE4实现细节

UE4在C++中可以直接拿到AAssetManager对象,具体实现细节UE4已经帮我们做了,具体可以查看AndroidJNI.cpp中的代码。我们拿到AAssetManager这个对象并把它设置给SQLite就可以了。

总结

到此,我们对SQLite扩展读取apk中db的方法已经写完了。由于Android NDK返回了文件描述符以及SQLite提供的OS Interface层让我们很比较容易的实现了对SQLite扩展。由于作者对SQLite原来并没有了解,所以难免有错误之处,如果有错误请及时指正。如果读者想对SQLite有一个比较深入的认识,也可以看看参考文章6。

参考文章:
  1. http://sqlite.org/arch.html
  2. http://www.sqlite.org/vfs.html
  3. https://developer.android.com/ndk/reference/group___asset.html#ga1af4ffd050016e99961e24f550981677
  4. https://docs.unity3d.com/560/Documentation/ScriptReference/AndroidJNI.html
  5. http://answers.unity3d.com/questions/205212/android-file-open.html
  6. http://huili.github.io/summary/READMEG1.html

     

     

扩展SQLite使其能从apk文件中读取db

标签:完成   static   null   esc   class   name   一点   linux   body   

小编还为您整理了以下内容,可能对您也有帮助:

安卓手机如何打开.db文件?

安卓手机打开.db文件可以使用RE文件管理器或SQLite打开并查看。首先需要在网上下载SQLite软件,然后点击想要打开的.db文件就可以打开浏览相关数据库了。

1、首先在电脑安装SQLiteDeveloper,安装后在桌面生成图标,点击图标打开程序。

2、打开程序后,可以看到程序左边的数据库列表。展开可以查看到所有打开过的数据库。

3、点击一个db文件,把这个db直接拖拉进程序窗口。

4、显示注册数据库,点击【确定】,就可以用SQLiteDeveloper管理这个db文件了。

5、展开左边数据库列表,找到刚才注册的db文件。点击右键菜单的【打开数据库】。

6、打开数据库后可以看到这个db文件中有很多的数据表,选择其中一个,右键点击【查询数据】即可。

安卓(Android)是一种基于Linux的自由及开放源代码的操作系统。主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。

安卓手机如何打开.db文件?

安卓手机打开.db文件可以使用RE文件管理器或SQLite打开并查看。首先需要在网上下载SQLite软件,然后点击想要打开的.db文件就可以打开浏览相关数据库了。

1、首先在电脑安装SQLiteDeveloper,安装后在桌面生成图标,点击图标打开程序。

2、打开程序后,可以看到程序左边的数据库列表。展开可以查看到所有打开过的数据库。

3、点击一个db文件,把这个db直接拖拉进程序窗口。

4、显示注册数据库,点击【确定】,就可以用SQLiteDeveloper管理这个db文件了。

5、展开左边数据库列表,找到刚才注册的db文件。点击右键菜单的【打开数据库】。

6、打开数据库后可以看到这个db文件中有很多的数据表,选择其中一个,右键点击【查询数据】即可。

安卓(Android)是一种基于Linux的自由及开放源代码的操作系统。主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。

安卓中怎么读取sqlite的db文件的数据

安装SQLite Expert Professional 可以在网上下载,我下载的是试用版,没有找注册码,但是试用不影响使用,反正用几次查看到数据库操作没有错误就不用这个工具了。当然也可以使用Eclipse插件DDMS来查看,也可以使用Android工具包中的adb工具来查看。android项目中的sqlite数据库位于/data/data/项目包/databases中。先介绍使用DDMS导出sqlite数据库的操作和使用adb工具访问sqlite数据库。

首先打开android项目的调试模式,然后找到显示DDMS

切换到DDMS,显示File Explorer窗口,找到/data/data/ 如下图1 ,

然后找到程序包的文件夹,打开databases,就能看到sqlite数据库文件了。选择将其导出。如下图2.

这样就把sqlite数据库文件以文件的方式导出来了,然后使用sqlite界面管理工具如SQLite Expert Professional可以打开该数据库了。其他 sqlite界面管理工具如sqlite administrator、sqlite man或者firefox插件sqlite manager也可以打开该数据库。

使用adb工具访问sqlite数据库

Android Debug Bridge(ADB)是Android的一个通用调试工具,它可以更新设备或模拟器中的代码,可以管理预定端口,可以在设备上运行shell命令,我们 知道android是基于Linux内核,它的内部文件结构也是采用linux文件组织方式,因此访问它的文件结构需要使用shell。这次我们就会用 shell来访问android应用中的sqlite数据库文件。

1、运行cmd,切换到android-sdk目录,运行adb.exe,加上参数shell,出现#号就代表进入了shell命令模式,注意adb要在Android模拟器运行时才能进入shell:如下图

进入数据库所在目录:

shell命令记住两个基本命令ls和cd,类似windows命令提示行中的dir和cd,代表列出当前目录下文件列表和进入到指定目录。了解这两个命令之后,就可以找到data/data/项目包名/databases 如下图1, 找到数据库文件如下图2。

使用sqlite管理工具来进行数据库操作

键入sqlite3 数据库名就进入了sqlite管理模式了。

如下图

设置sqlite环境变量:

在android的sdk中自带了sqlite3.exe,这是sqlite的官方管理工具,它是一个命令行工具。为了使用方便,将其路径注册到系 统环境变量path中,即将;%Android_Home%加在Path中,这样只样运行sqlite3,就能直接打开sqlite管理工具了。

如下图:

8.使用sqlite管理数据库:

sqlite命令行工具默认是以;结束语句的。所以如果只是一行语句,要在末尾加;,或者在下一行中键入;,这样sqlite命令才会被执行。

sqlite常用命令:

.tables--查看数据库的表列表

.exit--退出sqlite命令行

其他命令可随时.help查看帮助。sql命令可直接在此命令行上执行即可:

显示全文