Invoke SqlCipher using Flutter ffi

来自WHY42
imported>Riguz2020年11月4日 (三) 00:00的版本 (目前在flutter中调用sqlite有成熟的插件例如sqflite,而我需要sqlcipher,并同时加载fts5扩展,现有的插件并不能直接支持。因此需要创建一个插件来做这个事情。在以前平台集成相当麻烦,而现在有了ffi之后,可以直接调用原生代码,虽然还在试验阶段但终究是大势所趋。)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)

目前在flutter中调用sqlite有成熟的插件例如sqflite,而我需要sqlcipher,并同时加载fts5扩展,现有的插件并不能直接支持。因此需要创建一个插件来做这个事情。在以前平台集成相当麻烦,而现在有了ffi之后,可以直接调用原生代码,虽然还在试验阶段但终究是大势所趋。


源码编译

sqlite源码编译比较简单,但是如果要运行其测试,在mac上还是有些折腾。sqlite3运行测试需要tcl8.6,自带的无法使用(/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tcl.framework/) 下载Tcl8.6并编译安装:

cd tcl8.6.10/unix
./configure
make
sudo make install

然后可以跑sqlite的测试`make test`。

而要编译sqlcipher,也有一些选项需要设置,

cd sqlcipher/build
./configure --enable-tempstore=no \
  --with-crypto-lib=none \
  --enable-fts5 \
  CFLAGS="-DSQLCIPHER_CRYPTO_OPENSSL -DSQLITE_TEMP_STORE=3 -DSQLITE_HAS_CODEC -I/usr/local/opt/openssl/include/" \
  LDFLAGS="/usr/local/opt/openssl/lib/libcrypto.a"

在flutter中集成

创建插件

首先需要创建一个插件:

flutter create \
  --platforms=android,ios \
  --org=com.riguz \
  --template=plugin \
  -i swift \
  -a java \
  native_sqlcipher

flutter升级之后,必须指定`--platforms=android,ios`才会生成ios和android的响应工程。


平台集成

生成整合的代码

最直接的方式是将sqlcipher(其中已经包含了sqlite的代码)整合到一个文件中(称之为“amalgamation”版本,运行速度会更快一些)

git clone https://github.com/sqlcipher/sqlcipher.git
cd sqlcipher
mkdir build
../configure --with-crypto-lib=none --enable-fts5
make sqlite3.c

生成完之后,会得到一个sqlite3.c和sqlite3.h。如果希望在mac上也编译出来,可以

./configure --enable-tempstore=no \
  --with-crypto-lib=none \
  --enable-fts5 \
  CFLAGS="-DSQLCIPHER_CRYPTO_OPENSSL -DSQLITE_TEMP_STORE=3 -DSQLITE_HAS_CODEC -I/usr/local/opt/openssl/include/" \
  LDFLAGS="/usr/local/opt/openssl/lib/libcrypto.a"

ios集成

将sqlite3.c和sqlite3.h代码拷贝到插件的ios/Classes目录中,然后需要修改插件的podspec文件(单纯在xcode里面修改无法保存,每次pod install之后就会丢失),需要将一些参数加进去:

Pod::Spec.new do |s|
  s.name             = 'native_sqlcipher'
  # ...

  # 尽管之前configure的时候已经指定了SQLITE_ENABLE_FTS5,但是这里还需要再设置一次才能将fts5扩展编译进去
  s.frameworks = 'Security'
  s.xcconfig = { 'OTHER_CFLAGS' => '-DSQLITE_ENABLE_FTS5 -DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=3 -DSQLCIPHER_CRYPTO_CC -DNDEBUG' }
  # ...
end

安卓集成

编译OpenSSL

下载openssl,当前最新为1.1.1h。其编译说明可以参照NOTES.ANDROID文档。

export ANDROID_NDK_HOME=/Users/hfli/Library/Android/sdk/ndk/21.3.6528147
	PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin:$ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin:$PATH
  ./Configure android-arm64 -D__ANDROID_API__=29
	make

对于其他平台需要重新configure然后编译:

#./Configure android-arm -D__ANDROID_API__=29
#./Configure android-arm64 -D__ANDROID_API__=29
#./Configure android-x86 -D__ANDROID_API__=29
./Configure android-x86_64 -D__ANDROID_API__=29
make clean
make

默认生成的so是带有后缀的,(类似*.so.1.1),因为安卓打包的时候不支持,解决办法是在make的时候覆盖掉参数:

make SHLIB_VERSION_NUMBER= SHLIB_EXT=.so

集成sqlcipher

调用

参见Binding to native code using dart:ffi

兼容性

SqlCipher依赖一个非标准的选项,但是这个选项最近已经被移除了,SqlCipher目前包含的sqlite的版本为3.31.0,而sqlite最新为3.33.0。目前尚不知道后续的支持计划。

参考: