近段时间对Delphi开发Android程序进行了一些尝试,发现了一些问题,也试着去解决这些问题,特将这些解决方法记录下来,以便自己和他人翻阅。由于本人接触Android程序时间有限,许多问题都是边开写代码边在网上找一些资料,我这篇文章也是集成一些网上的办法,结合自己的研究(本文所用开发工具:Delphi 10.2)。下面详细讲解:
wxsqlite3是开源的sqlite3加密模块,比较有名,对于我们来说,能不花钱的方法才是最好的,在Android下使用wxsqlite3必须将其编译成安卓支持的SO文件。网上有很多关于安卓下sqlite3加密的方法,但基本上是Java代码,而且需要Java开发工具,对于我这种delphier来说,Java虽接触过,仍是小白一个,只好另寻他法。最终找到用ndk-build编译so的方法,Delphi开发安卓必须要ndk,参照网上的方法反复折腾,始终是不得其法 。经过一番尝试,还是让我这种小白大致明白了问题所在,ndk-build编译so必须把所有C语言源码放在jni目录之下,然后直接调用命令即可。对于使用Java开发的人来说,是再简单不过了,让我们这种delphier情何以堪。
其中还有两个关键点:一是Android.mk文件的编写。ndk-build是依赖这个文件进行编译的,一并放在jni目录下即可。结构如下:
LOCAL_PATH := $(call my-dir)
#清理变量定义
include $(CLEAR_VARS)
#模块名称
LOCAL_MODULE := wxsqlite3
#库文件名称
LOCAL_MODULE_FILENAME := libwxsqlite3
#源文件
LOCAL_SRC_FILES := sqlite3secure.c
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
#头文件目录u
LOCAL_C_INCLUDES := $(LOCAL_PATH)
#构建动态库
include $(BUILD_SHARED_LIBRARY)
以上已经进行了注释。这是最简单的应用,对于我们编译wxsqlite3足够了,网上还有许多复杂的方法,我认为简单就是好,so文件仅是用于Delphi来调用的。
第二个关键点是使用ndk-build命令编译C源码。Android.mk写好了,且C源码全部放在jni目录下后,我们返加jni的上一级目录,在这个目录下编写一个cmd文件执行命令。为了看到编译的过程,用记事本写入“cmd.exe”,存为cmd扩展名的文件。执行后进入dos界面,输入:%NDKPATH%\ndk-build回车即可,其中“%NDKPATH%”是你的NDK-build所在目录,我的是在“D:\Program Files\Embarcadero\Studio\19.0\PlatformSDKs\android-ndk-r9c”这里,如果目录有空格要将“Program Files”改为“Progra~1”才行。经常用ndk-build的话,可以将路径添加到环境变量里。这样编译成功后,so文件就放在“..\libs\armeabi”下,注意,这样编译的是不带加密功能的。如何带加密功能,网上的办法是在C源码里添加“#define SQLITE_HAS_CODEC”,经我反复测试根本不行。最后打开C源码研究了一下,才知道这里有一个坑,我下载的是最新的源码,较以前的源码可能有较大改动,其实应该是在sqlite3secure.c文件顶端添加“#define SQLITE_HAS_CODEC 1”即可,默认的是AES128加密,应该也够用了。如果要用AES256,需要将codec.h文件里的第46行“#define CODEC_TYPE CODEC_TYPE_AES128”改成AES256就行了,或是在这个文件的顶端加入“#define CODEC_TYPE CODEC_TYPE_AES256”也行。顺便提一句,Android.mk文件也可以直接定义预编译指令,就不需要改C源码,有兴趣的同志可以在网上搜一下,我没有试过。
procedure LoadLib;//用来加载so动态链接库
begin
{$IFDEF ANDROID}
SQLiteDLL:=GetFilesDir+'/libwxsqlite.so';//注意这里,声明的时候不要用const SQLiteDLL='....',定义一个全局变量
{$ENDIF}
hlib:=LoadLibrary(PChar(SQLiteDLL));
end;
function GetProc(const Name: string):Pointer;//动态调用
begin
if hlib<>0 then
begin
Result:=GetProcAddress(hLib, PChar(Name));
end;
end;
procedure InitFunctions;//每个动态调用的函数获得指针
begin
sqlite3_open16 := GetProc('sqlite3_open16');
//.........................................
end;
在第三个函数里,把所有需要调用的都写进去就可以了。这里就是修改的核心。
TSQLite3_key=function(pDb: TSQLiteDB; // Database handle
pKey: PChar; // Database PassWord (UTF-8)
nKey: Integer // Database sizeofpassword
): integer;cdecl;
Tsqlite3_rekey =function (
pDb: TSQLiteDB; // Database handle
pKey: PChar; // Database PassWord (UTF-8)
nKey: Integer // Database sizeofpassword
): integer; c
因为修改幅度较大,这里仅说一下更改的关键地方。