日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

sqlite3源码编译到Android,实现SQLite跨全平台使用

發(fā)布時(shí)間:2023/12/9 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 sqlite3源码编译到Android,实现SQLite跨全平台使用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文/何曉杰Dev(高級(jí)Android架構(gòu)師)著作權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)。

初看這個(gè)標(biāo)題你可能會(huì)不解,SQLite 本身就是一個(gè)跨平臺(tái)的數(shù)據(jù)庫,在這里再說跨平臺(tái)有什么意義呢?

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

因此,在不同的平臺(tái)上操作 SQLite,必定會(huì)使用不同的代碼。當(dāng)然了,除了 SQLite 之外,實(shí)現(xiàn)相同的功能,在不同平臺(tái)上使用不同的代碼也許已經(jīng)是慣例,大家也習(xí)以為常。

請(qǐng)輸入標(biāo)題 ? ? bcdef

Roll your eggs 的習(xí)以為常!作為一個(gè)懶人,當(dāng)這樣一個(gè)鍋需要自己背的時(shí)候,自然是去找更簡單的解決方案了。目標(biāo)是一套代碼走天下!

請(qǐng)輸入標(biāo)題 ? ? abcdefg

那么也不多廢話了,直接上手寫代碼,這里有很多種技術(shù)可以選擇,比如說?C++,sqlite3.h?還是很好用的。不過我依然是折騰自己喜歡的?CodeTyphon,因?yàn)樗懈屓擞X得方便的封裝。

很幸運(yùn)的是,CodeTyphon?已經(jīng)自帶了?sqlite3conn?單元,直接引用之即可。關(guān)于如何查找可引用的庫,可以看?CTC?的?Typhon-IDE Pkgs?和?FPC Pkgs?這兩頁,你會(huì)找到你要的。

CTC

首先先制作一個(gè)簡單的數(shù)據(jù)庫吧,用于測試代碼能否正常工作:

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

然后根據(jù)數(shù)據(jù)庫結(jié)構(gòu)聲明一個(gè)結(jié)構(gòu)體,后面會(huì)用于數(shù)據(jù)傳遞:

type

TDemoRec = record

AId: Integer;

AName: PChar;

end;

與這個(gè)結(jié)構(gòu)等價(jià)的 C++ 的結(jié)構(gòu)體是這樣的:

struct DemoRec {

int AId;

char* AName;

};

這一瞬間我們會(huì)發(fā)現(xiàn)原來操作 SQLite 是如此的簡單,在此我定義了一個(gè)類,用來保存一些數(shù)據(jù):

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;

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

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

begin

FQuery.Close;

FQuery.SQL.Text:= ASQL;

try

FQuery.ExecSQL;

Exit(True);

except

Exit(False);

end;

end;

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

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;

好了,現(xiàn)在調(diào)用這個(gè)方法時(shí),只需要額外傳入一個(gè)字符串參數(shù),就可以獲取出錯(cuò)時(shí)的信息。

在這個(gè)體系下,要進(jìn)行查詢也很簡單,需要額外封裝兩個(gè)方法:

// 根據(jù) 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;

// 獲取查詢結(jié)果的行數(shù)

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;

// 獲取指定行號(hào)的一條記錄

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;

接下來就是導(dǎo)出函數(shù)了,作為一個(gè)跨平臺(tái)的庫,它需要被其他程序調(diào)用,那么必定有導(dǎo)出函數(shù),而不同的平臺(tái)下,所需要的函數(shù)形態(tài)是不一樣的,特別是由于 Android 使用 JNI 來調(diào)用動(dòng)態(tài)庫,導(dǎo)出函數(shù)必須符合 JNI 的規(guī)范。

下面的例子很好的說明了導(dǎo)出函數(shù)的方法:

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

唯一需要注意的是調(diào)用協(xié)定,用于 JNI 的必須設(shè)為?stdcall,而其他的均設(shè)為?cdecl。

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

$ fpc64 -Fisqlite -Fusqlite sample.lpr

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

要跨平臺(tái)編譯的話,稍微麻煩一點(diǎn),但是也比想象中簡單很多:

$ 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

此時(shí)即可生成一個(gè)供 Android 系統(tǒng)使用的,arm 架構(gòu)的?libsample.so,通過更換?-P?后面的參數(shù),也可以編譯 x86,mips 等架構(gòu)的 so。

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

$ 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

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

32 位真機(jī):替換編譯器為 ppcrossarm

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

當(dāng)我們得到了 3 個(gè)不同架構(gòu)的?.a?后,有些時(shí)候需要將它們合并,使用如下命令來合并之:

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

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

現(xiàn)在一切都準(zhǔn)備好了,看看如何使用我們做好的庫吧,以上述的?dbGetSelectResultCount?和?dbGetSelectResult?為例,分別講述在各平臺(tái)的使用方法。

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

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

以下是一個(gè)完整的調(diào)用代碼,以 iOS 端為例,其他各端均一致:

// 復(fù)制數(shù)據(jù)庫文件

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

NSString * destPath = [ViewController getDocumentPath];

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

[ViewController copyFile:originPath destFile:dbFile];

// 打開數(shù)據(jù)庫

int b = dbOpen([dbFile UTF8String]);

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

// 執(zhí)行查詢

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

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

// 獲取查詢結(jié)果的行數(shù)

int count = dbGetSelectResultCount([dbFile UTF8String]);

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

// 取出查到的每一條數(shù)據(jù)

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

}

// 關(guān)閉數(shù)據(jù)庫

b = dbClose([dbFile UTF8String]);

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

這段代碼的輸出為:

可以看到,調(diào)用成功,并且正確的傳遞了數(shù)據(jù)。在其他平臺(tái)上的效果也是完全一樣的。

這個(gè)用于演示的項(xiàng)目已經(jīng)開源,請(qǐng)?jiān)L問我的 github 獲取,地址:

https://github.com/rarnu/cross_sqlite

總結(jié)

以上是生活随笔為你收集整理的sqlite3源码编译到Android,实现SQLite跨全平台使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。