欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
擴展SQLite使其能從apk文件中讀取db

擴展SQLite使其能從apk文件中讀取db

2017-07-17 08:16 by 風(fēng)戀殘雪, 81 閱讀, 0 評論, 收藏, 編輯

游戲中會(huì )大量使用到配置文件,每個(gè)項目組根據自己不同的需求會(huì )選擇不同的存儲格式,比如使用Json或者SQLite來(lái)存儲數據。此處我們只對使用SQLite的情況來(lái)做討論。一般情況下會(huì )選擇把它放在可讀寫(xiě)目錄里面,這樣SQLite可以直接使用它原來(lái)的io API來(lái)對db文件進(jìn)行讀取。在PC或者iOS平臺上這不是問(wèn)題。但是如果在A(yíng)ndroid平臺上,游戲安裝后還是以一個(gè)apk文件的形式存在。如果我們的數據放在了db中,使用SQLite原來(lái)自帶的io功能是不能進(jìn)行讀取的。這里有3種方式可以供選擇:

  1. 在程序第一次啟動(dòng)時(shí),把apk中的所有文件解壓出來(lái)放到可讀寫(xiě)目錄中,這樣存在的問(wèn)題是第一次打開(kāi)程序時(shí)會(huì )比較慢。
  2. 在需要的讀取某個(gè)db的時(shí)候才把這個(gè)文件從apk中解壓出來(lái),這樣的話(huà)可能會(huì )導致卡頓,或者使用協(xié)程等異步操作來(lái)完成,但是這樣對于邏輯層的代碼書(shū)寫(xiě)成本比較高。
  3. 對SQLite做一定的改動(dòng),使它可以讀取apk中的db文件。

  一般大家可能會(huì )選擇第一種方法,這沒(méi)有什么好說(shuō)的。我們接下來(lái)看看第3種方法的可能性。

理論

為了實(shí)現上述的想法,我們需要兩個(gè)條件:

  1. android提供對apk中單個(gè)文件的讀取的能力。
  2. SQLite提供了對io層(不同平臺)進(jìn)行快速修改的能力,這就要求SQLite有比較好的抽象以較少的模塊之間的耦合。

  當然上述兩個(gè)條件是滿(mǎn)足的,下面我們來(lái)具體看看這兩個(gè)條件。

Android對apk中單個(gè)文件的讀取能力

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 )

從這個(gè)API可以看出,它可以返回一個(gè)用于讀取當前asset的一個(gè)文件描述符。但是如果當前asset被壓縮了,那么就回返回一個(gè)小于0的值。如果我們想要讀取db的話(huà),那么它必須是沒(méi)有壓縮過(guò)的。

示例代碼大體如下所示:

    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);

注意,這個(gè)fd返回的是整個(gè)apk的句柄,start代表這個(gè)文件在apk中的偏移,length代表長(cháng)度,使用的時(shí)候要注意。

SQLite對于文件io層的支持

下圖是 SQLite官方給出的架構圖:

我們這里主要關(guān)注的就是OS Interface這一層,它使用了VFS這一對象來(lái)為不同系統之間的可移植性提供了保證,。具體細節可以參照官網(wǎng)對于VFS的介紹。SQLite目前提供了對類(lèi)unix系統和windows系統的支持,分別在os_unix.c以及os_win.c中實(shí)現的。其中os_unix.c提供了對mac os、iOS、Android以及Linux的支持。如果讀取想對這塊有一個(gè)比較深入了了解可以看官方文檔以及查看一些示例如test_demovfs.c等來(lái)深入了解。

實(shí)現

有了上面的理論支持,那么我們就可以著(zhù)手可以寫(xiě)代碼了。我們目前有兩種方案可以選擇:

  1. 單獨為安卓實(shí)現一個(gè)VFS。
  2. 在os_unix.c的基礎上進(jìn)行修改。

  第一種方案需要寫(xiě)的代碼比較多,而且需要讀者對SQLite有一個(gè)比較深入的了解。所以我們這里選擇第二種方案。在原來(lái)的os_unix.c的基礎上進(jìn)行改動(dòng)。

SQLite改造

通過(guò)分析os_unix.c文件,我們能得出它主要使用了open() read() write()等io操作。這跟我們上面Android NDK提供的AAsset_openFileDescriptor正好完美的結合起來(lái)。我們正好可以使用它返回的句柄進(jìn)行類(lèi)似的操作。只不過(guò)在A(yíng)ndroid的情況下特殊一點(diǎn)。

這樣下來(lái),我們基本上大體需要改的幾個(gè)函數和結構體如下所示

  1. struct unixFile 我們需要在其中添加文件在apk中的偏移以及大小 。
  2. posixOpen 通過(guò)openFileDescriptor返回文件描述符。
  3. seekAndRead 讀取時(shí)要加上文件偏移。
  4. unixFileSize 返回前面unixFile中記錄的文件大小的值。

  以上基本是需要改到的函數,當然根據實(shí)現的不同可能具體需要改動(dòng)的函數不一樣。這只是比較粗暴的改法。像我們需要支持從apk里面讀取以及從一個(gè)散文件里面讀取,所以跟上面的改動(dòng)多少有一些不一樣的地方,但是基本思想是通的。當然由于本人對SQLite不了        解,可能有需要改動(dòng)的地方?jīng)]有注意到,如果說(shuō)的有錯誤希望能及時(shí)指正。方法已經(jīng)說(shuō)的比較明白了,這里也就不貼代碼了。

上面的例子提到的AAssetManager_open在打開(kāi)時(shí)需要一個(gè)AAssetManager的對象,這個(gè)對象只能從Java里面獲取。如果你是直接使用Android開(kāi)發(fā)那么這個(gè)對象就比較容易獲取,那么如果你是使用Unity或者UE4開(kāi)發(fā)怎么獲取這個(gè)對象呢。

Unity實(shí)現細節

SQLite的修改跟上面是一樣的,只是我們在Unity中如何獲取這個(gè)對象呢。讀者可以具體對照一下這個(gè)類(lèi)AndroidJNI AndroidJNIHelper AndroidJavaClass AndroidJavaObject AndroidJavaProxy這幾個(gè)類(lèi)。

示例代碼如下所示:

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);

這樣我們就得到了這個(gè)AssetManager,這個(gè)時(shí)候我們就可以通過(guò)C#把這個(gè)對象傳遞給SQLite庫了。

UE4實(shí)現細節

UE4在C++中可以直接拿到AAssetManager對象,具體實(shí)現細節UE4已經(jīng)幫我們做了,具體可以查看AndroidJNI.cpp中的代碼。我們拿到AAssetManager這個(gè)對象并把它設置給SQLite就可以了。

總結

到此,我們對SQLite擴展讀取apk中db的方法已經(jīng)寫(xiě)完了。由于A(yíng)ndroid NDK返回了文件描述符以及SQLite提供的OS Interface層讓我們很比較容易的實(shí)現了對SQLite擴展。由于作者對SQLite原來(lái)并沒(méi)有了解,所以難免有錯誤之處,如果有錯誤請及時(shí)指正。如果讀者想對SQLite有一個(gè)比較深入的認識,也可以看看參考文章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.  

     

作者: 風(fēng)戀殘雪

出處: http://www.cnblogs.com/ghl_carmack

關(guān)于作者:專(zhuān)注游戲引擎,關(guān)注VR,對操作系統、編譯原理有深厚興趣!

本文版權歸作者和博客園共有,歡迎轉載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出, 原文鏈接,否則保留追究法律責任的權利。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Android開(kāi)發(fā)常用工具匯總
APK文件查看、修改和簽名 - dospy智能手機網(wǎng) 塞班 安卓Android 蘋(píng)果iPh...
【轉】Android 調試橋(adb),很方便很強大 - 一他糊涂 - 博客園
Android Shell 常用命令 - BLUE OCEAN STRATEGY - Ja...
Android 獲取ROOT權限原理解析
Android開(kāi)發(fā)調試工具ADB的使用
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久