sqlite3源碼編譯到Android,實現SQLite跨全平臺使用

文/何曉杰Dev(高級Android架構師)著作權歸作者所有,轉載請聯系作者獲得授權。

初看這個標題你可能會不解,SQLite 本身就是一個跨平臺的數據庫,在這里再說跨平臺有什么意義呢?

其實不然,目前我就遇到了一個項目需要使用 SQLite 數據庫,而且我甚至完全不想花多套代碼在不同的平臺上,畢竟每個平臺的包含的相關 SDK 并不一致。舉個簡單的例子,在 Android 上操作 SQLite,需要用到?SQLiteDatabase?這個類,用?Java?來操作;而在 iOS 上,除了需要引入?libsqlite3.tbd?外,還需要引入?sqlite3.h?這個頭文件,使用?Objective-C?來操作,到了 PC 上,雖然都是以使用?sqlite3.h?為主,但是依然會有不一致的地方,比如說種類繁多的編程語言,大多都有不同的封裝,API 不一致這足以讓人頭疼。

因此,在不同的平臺上操作 SQLite,必定會使用不同的代碼。當然了,除了 SQLite 之外,實現相同的功能,在不同平臺上使用不同的代碼也許已經是慣例,大家也習以為常。

請輸入標題 ? ? bcdef

Roll your eggs 的習以為常!作為一個懶人,當這樣一個鍋需要自己背的時候,自然是去找更簡單的解決方案了。目標是一套代碼走天下!

請輸入標題 ? ? abcdefg

那么也不多廢話了,直接上手寫代碼,這里有很多種技術可以選擇,比如說?C++,sqlite3.h?還是很好用的。不過我依然是折騰自己喜歡的?CodeTyphon,因為它有更讓人覺得方便的封裝。

很幸運的是,CodeTyphon?已經自帶了?sqlite3conn?單元,直接引用之即可。關于如何查找可引用的庫,可以看?CTC?的?Typhon-IDE Pkgs?和?FPC Pkgs?這兩頁,你會找到你要的。

0818b9ca8b590ca3270a3433284dd417.png

CTC

首先先制作一個簡單的數據庫吧,用于測試代碼能否正常工作:

$ sqlite3 demo.db

> create table user(id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(32) NOT NULL);

> insert into user(name) value ('ABC');

> insert into user(name) value ('XYZ');

然后根據數據庫結構聲明一個結構體,后面會用于數據傳遞:

type

TDemoRec = record

AId: Integer;

AName: PChar;

end;

與這個結構等價的 C++ 的結構體是這樣的:

struct DemoRec {

int AId;

char* AName;

};

這一瞬間我們會發現原來操作 SQLite 是如此的簡單,在此我定義了一個類,用來保存一些數據:

TSQLite = class

private

FDatabase: TSQLite3Connection;

FQuery: TSQLQuery;

FTransaction: TSQLTransaction;

published

property Database: TSQLite3Connection read FDatabase write FDatabase;

property Transaction: TSQLTransaction read FTransaction write FTransaction;

property Query: TSQLQuery read FQuery write FQuery;

end;

有了這些東西后,就可以方便的玩起來了,比如說執行一個 SQL 語句:

function TSQLite.ExecuteSQL(ASQL: string): Boolean;

begin

FQuery.Close;

FQuery.SQL.Text:= ASQL;

try

FQuery.ExecSQL;

Exit(True);

except

Exit(False);

end;

end;

這段代碼似乎太簡單了,也許我們更加希望在出錯時能夠給出一個原因,那么可以改一下:

function TSQLite.ExecuteSQL(ASQL: string; var AError: string): Boolean;

begin

FQuery.Close;

FQuery.SQL.Text:= ASQL;

try

FQuery.ExecSQL;

Exit(True);

except

on E: Exception do begin

AError:= e.Message;

Exit(False);

end;

end;

end;

好了,現在調用這個方法時,只需要額外傳入一個字符串參數,就可以獲取出錯時的信息。

在這個體系下,要進行查詢也很簡單,需要額外封裝兩個方法:

// 根據 SQL 語句查詢

function TSQLite.Select(ASQL: string; var AError: string): Boolean;

begin

FQuery.Close;

FQuery.SQL.Text:= ASQL;

try

FQuery.Open;

Exit(True);

Except

on E: Exception do begin

AError:= e.Message;

Exit(False);

end;

end;

end;

// 獲取查詢結果的行數

function dbGetSelectResultCount(APath: PChar): Integer;

var

database: TSQLite;

begin

Result := -1;

if (DatabaseExists(string(APath))) then begin

database := GetDatabase(string(APath));

Result := database.Query.RecordCount;

end;

end;

// 獲取指定行號的一條記錄

function dbGetSelectResult(APath: PChar; AIndex: Integer): TDemoRec;

var

database: TSQLite;

tmp: string;

begin

Inc(AIndex);

if (DatabaseExists(string(APath))) then begin

database := GetDatabase(string(APath));

if (database.Query.RecordCount >= AIndex) then begin

database.Query.RecNo:= AIndex;

Result.AId:= database.Query.FieldByName('id').AsInteger;

tmp := database.Query.FieldByName('name').AsString;

Result.AName:= StrAlloc(tmp.Length);

strcopy(Result.AName, PChar(tmp));

end;

end;

end;

接下來就是導出函數了,作為一個跨平臺的庫,它需要被其他程序調用,那么必定有導出函數,而不同的平臺下,所需要的函數形態是不一樣的,特別是由于 Android 使用 JNI 來調用動態庫,導出函數必須符合 JNI 的規范。

下面的例子很好的說明了導出函數的方法:

// iOS, PC

function dbGetSelectResultCount(APath: PChar): Integer; cdecl;

function dbGetSelectResult(APath: PChar; AIndex: Integer): TDemoRec; cdecl;

// Android

function Java_com_sqlite_sample_NativeAPI_dbGetSelectResultCount(env: PJNIEnv; obj: jobject; APath: jstring): jint; stdcall;

function Java_com_sqlite_sample_NativeAPI_dbGetSelectResult(env: PJNIEnv; obj: jobject; APath: jstring; AIndex: jint): jobject; stdcall;

唯一需要注意的是調用協定,用于 JNI 的必須設為?stdcall,而其他的均設為?cdecl。

那么再下一步就是編譯,直接使用?FPC?跨平臺編譯器即可,編譯方法很簡單:

$ fpc64 -Fisqlite -Fusqlite sample.lpr

此時即可以在 Mac 端生成?libsample.dylib?以及在 Linux 端生成?libsample.so。

要跨平臺編譯的話,稍微麻煩一點,但是也比想象中簡單很多:

$ export ANDROID_LIB=/usr/local/codetyphon/binLibraries/android-5.0-api21-arm/

$ export FPC=/usr/local/codetyphon/fpc/fpc64/bin/x86_64-linux/fpc

$ ${FPC} -Tandroid -Parm -Fl${ANDROID_LIB} -Fiqslite -Fusqlite sample.lpr

此時即可生成一個供 Android 系統使用的,arm 架構的?libsample.so,通過更換?-P?后面的參數,也可以編譯 x86,mips 等架構的 so。

完成后再看一下 iOS 的庫要怎么編譯。由于 iOS 已不再允許動態加載 dylib,我們必須把代碼編譯為靜態庫,也就是?.a?文件,并且靜態鏈接到 iOS 項目內。

$ export FPC_ROOT=/usr/local/lib/fpc/3.1.1

$ export FPC=${FPC_ROOT}/ppcrossa64

$ ${FPC} -Tdarwin -dIPHONEALL -Cn -Fisqlite -Fusqlite sample.lpr

$ ar -q libsample.a `grep "\.o$" link.res`

$ ranlib libsample.a

此時可以得到一個用于 64 位真機的?libsample.a?文件,若是要在 32 位的 iOS 和模擬器上完成兼容,還必須再另外編譯兩個?.a。

32 位真機:替換編譯器為 ppcrossarm

模擬器:替換編譯器為 ppcx64,并替換 -T 參數為 iphonesim

當我們得到了 3 個不同架構的?.a?后,有些時候需要將它們合并,使用如下命令來合并之:

lipo -create libsample_A64.a libsample_ARM.a libsample_EMU.a -output libsample.a

這樣就得到了一個融合了的?.a,它可以用于各種場合。

現在一切都準備好了,看看如何使用我們做好的庫吧,以上述的?dbGetSelectResultCount?和?dbGetSelectResult?為例,分別講述在各平臺的使用方法。

Android:

package com.sqlite.sample;

public class NativeAPI {

static { ?System.loadLibrary("sample"); }

public static native int dbGetSelectResultCount(String APath);

public static native DemoRec dbGetSelectResult(String APath, int AIndex);

}

iOS:

extern int dbGetSelectResultCount(const char* APath);

extern struct DemoRec dbGetSelectResult(const char* APath, int AIndex);

PC(以 C++ 為例):

typedef int (*dbSelectResultCount)(const char* APath);

typedef struct DemoRec (*dbSelectResult)(const char* APath, int AIndex);

void* handle = dlopen("./libsample.so", RTLD_LAZY);

dbSelectResultCount mSelectResultCount = (dbSelectResultCount) dlsym(handle, "dbGetSelectResultCount");

dbSelectResult mSelectResult = (dbSelectResult) dlsym(handle, "dbGetSelectResult");

可以看到,不論在哪個平臺上,最終得到的 API 都是一致的,這樣就統一了調用方式。在此基礎上,要做二次封裝也是非常方便。另外,由于代碼耦合幾乎沒有,也能夠很方便的對 SQLite 的底層庫的邏輯進行修改,只要 API 不變,就不會影響上層的調用。

以下是一個完整的調用代碼,以 iOS 端為例,其他各端均一致:

// 復制數據庫文件

NSString * originPath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"db"];

NSString * destPath = [ViewController getDocumentPath];

NSString * dbFile = [destPath stringByAppendingPathComponent:@"demo.db"];

[ViewController copyFile:originPath destFile:dbFile];

// 打開數據庫

int b = dbOpen([dbFile UTF8String]);

printf("Open Database => %d\n", b);

// 執行查詢

b = dbSelect([dbFile UTF8String], "select * from user");

printf("Select => %d\n", b);

// 獲取查詢結果的行數

int count = dbGetSelectResultCount([dbFile UTF8String]);

printf("Select Rows => %d\n", count);

// 取出查到的每一條數據

for (int i = 0; i < count; i++) {

struct DemoRec r = dbGetSelectResult([dbFile UTF8String], i);

printf("Data %d => {id => %d, name => %s}\n", i, r.AId, r.AName);

}

// 關閉數據庫

b = dbClose([dbFile UTF8String]);

printf("Close Database => %d\n", b);

這段代碼的輸出為:

0818b9ca8b590ca3270a3433284dd417.png

可以看到,調用成功,并且正確的傳遞了數據。在其他平臺上的效果也是完全一樣的。

這個用于演示的項目已經開源,請訪問我的 github 獲取,地址:

https://github.com/rarnu/cross_sqlite

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275458.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275458.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275458.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在Red Hat 4 AS U7上安裝oracle10gR2

軟件&#xff1a;Red Hat 4 AS U7, Oracle 10g R2 for linux32, VMWare 7, Windows 7詳細步驟清單&#xff1a;在Red Hat 4 AS U7上安裝oracle10gR2 1. 硬件需求&#xff1a; &#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1…

illustrator下載_平面設計:16個Illustrator快捷方式可加快工作流程

illustrator下載I know, I know — keyboard shortcuts sound so nerdy, and you’re a graphic designer, not an IT Director, why should you learn keyboard shortcuts?我知道&#xff0c;我知道—鍵盤快捷鍵聽起來很書呆&#xff0c;而且您是圖形設計師&#xff0c;而不是…

手把手教你五分鐘扒個源碼寫個無敵外掛

大家好&#xff0c;我是若川。源碼共讀《1個月&#xff0c;200人&#xff0c;一起讀了4周源碼》 活動進行到第五期了&#xff0c;歡迎點鏈接加我微信 ruochuan12 報名參加。前言前段時間群里分享了一個小游戲&#xff0c;多次懷疑自己的眼睛以后&#xff0c;嘗試去寫個外掛。中…

Kubernetes 1.14重磅來襲,多項關鍵特性生產可用

走過了突飛猛進的2018年&#xff0c;Kubernetes在2019年終于迎來了第一個大動作&#xff1a;Kubernetes 1.14版本的正式發布&#xff01;Kubernetes 本次發布的 1.14 版本&#xff0c;包含了 31 項增強&#xff0c;其中 10 項為 GA&#xff0c;12 項進入 beta 試用階段&#xf…

中英文

http://it.freesion.com/3220/4888028/13606306/#轉載于:https://www.cnblogs.com/yqskj/articles/2082326.html

open ai gpt_讓我們來談談將GPT-3 AI推文震撼到核心的那條推文

open ai gpt重點 (Top highlight)“設計師”插件 (The ‘Designer’ plugin) A couple days ago, a tweet shared by Jordan Singer turned the heads of thousands of designers. With the capabilities of GPT-3 (from OpenAI), he shared a sample of what he was able to c…

我歷時3年才寫了10余篇源碼文章,但收獲了100w+閱讀

你好&#xff0c;我是若川。最近來了一些讀者朋友&#xff0c;在這里簡單介紹自己的經歷&#xff0c;也許對你有些啟發。之前發過這篇文章&#xff0c;現在修改下聲明原創&#xff0c;方便保護版權。最近組織了源碼共讀活動1個月&#xff0c;200人&#xff0c;一起讀了4周源碼&…

android onlescan 參數,Android BLE:從iOS外設廣告時,在onLeScan()回調中檢索服務UUID

我正在使用Nexus 4(4.4 kitkat)作為中央和iPad作為外設.外圍設備有廣告服務.廣告包有一些數據(22字節)的服務UUID.當我嘗試從Android掃描外圍設備時,iPad外圍設備被發現.但是當我嘗試從回調中的scanRecord參數獲取服務UUID時,我找不到它.我得到的是外設發送的20byte數據.當我嘗…

第 8 章 容器網絡 - 061 - flannel 的連通與隔離

flannel 的連通與隔離 測試 bbox1 和 bbxo2 的連通性&#xff1a; bbox1 能夠 ping 到位于不同 subnet 的 bbox2&#xff0c;通過 traceroute 分析一下 bbox1 到 bbox2 的路徑。 1&#xff09; bbox1 與 bbox2 不是一個 subnet&#xff0c;數據包發送給默認網關 10.2.9.1&#…

Javascript 檢測 頁面是否在iframe中

//檢測是否在iframe中if(self.frameElement ! null && (self.frameElement.tagName "IFRAME" || self.frameElement.tagName "iframe")){parent.parent.location "login.jsp";}轉載于:https://www.cnblogs.com/kenkofox/archive/2011…

寫給前端的算法進階指南,我是如何兩個月零基礎刷200題 等推薦

大家好&#xff0c;我是若川。話不多說&#xff0c;這一次花了幾小時精心為大家挑選了20余篇好文&#xff0c;供大家閱讀學習。本文閱讀技巧&#xff0c;先粗看標題&#xff0c;感興趣可以都關注一波&#xff0c;一起共同進步。前端從進階到入院作者ssh就職于字節跳動基礎工程團…

計算機視覺筆記本推薦_視覺靈感:Mishti筆記本

計算機視覺筆記本推薦The Mishti Notebook is a project close to my heart, wherein I experimented with screen printing techniques at the Print Labs at the National Institute of Design, Ahmedabad. Dating back to the year 2012 when the NID Print Labs was first …

Google工程師:如何看待程序員普遍缺乏數據結構和算法知識?

出處&#xff1a;極客時間《數據結構與算法之美》很多技術人都很迷茫&#xff0c;覺得自己做的項目沒有技術含量&#xff0c;成天就是賣苦力。技術的東西&#xff0c;日新月異&#xff0c;有些人總在忙于追求熱點新技術&#xff0c;東學學、西學學&#xff0c;平時泛泛地看技術…

android guide 中文版,Sky Guide

Sky Guide是一款能讓小伙伴們觀察銀河的手機軟件&#xff0c;尤其是喜歡行星、星座的小伙伴們來講&#xff0c;這款軟件能很好的幫助小伙伴們觀看這些&#xff0c;讓小伙伴們體驗不一樣的觀星樂趣&#xff0c;因此想要觀看的小伙伴們&#xff0c;趕緊來試試吧。軟件介紹&#x…

Kinect for Windows SDK發布

轉載請注明出處為KlayGE游戲引擎&#xff0c;本文地址為http://www.klayge.org/2011/06/17/kinect-for-windows-sdk%e5%8f%91%e5%b8%83/ 前一段時間Microsoft研究院宣布了Kinect for Windows SDK。在眾人的期盼下&#xff0c;Kinect for Windows SDK Beta終于發布了&#xff01…

layui選項卡嵌套選項卡_在ProtoPie中使用嵌套組件構建選項卡欄

layui選項卡嵌套選項卡One of the powerful features of ProtoPie is the ability to build fully portable and interactive UI components. We are going to make use of nested components, SVG icons, and layout constraints to build a tab bar UI component that is sel…

50行代碼串行Promise,koa洋蔥模型原來這么有趣?

1. 前言大家好&#xff0c;我是若川&#xff0c;最近組織了源碼共讀活動《1個月&#xff0c;200人&#xff0c;一起讀了4周源碼》&#xff0c;感興趣的可以加我微信 ruochuan12 參與&#xff0c;長期交流學習。之前寫的《學習源碼整體架構系列》 包含jQuery、underscore、lodas…

如何定位死循環或高CPU使用率(linux)

如何定位死循環或高CPU使用率(linux) 確定是CPU過高 使用top觀察是否存在CPU使用率過高現象 找出線程 對CPU使用率過高的進程的所有線程進行排序 ps H -e -o pid,tid,pcpu,cmd --sortpcpu |grep xxx 得到如下結果,其中線程2909使用了7.8%的CPU. 2907 2913 0.0 ./xxx 2907…

js 用迭代器模式優雅的處理遞歸問題

2019獨角獸企業重金招聘Python工程師標準>>> 什么是迭代器 循環數組或對象內每一項值&#xff0c;在 js 里原生已經提供了一個迭代器。 var arr [1, 2, 3] arr.forEach(function (item) {console.log(item) })實現一個迭代器 var iterator function (arr, cb) {fo…

如何抓取html請求,請求獲取網頁的response,獲取網頁的html 怎么那么慢

HttpEntity multipart builder.build();httppost.setEntity(multipart);long start System.currentTimeMillis();// 發送請求response httpclient.execute(httppost);long end System.currentTimeMillis();System.out.println("查詢upsfreight消耗的時間是(毫秒):&quo…