Delphi和Android Studio混合开发(一)Delphi开发标准.SO给Android Studio用

Delphi自从开始跨平台,支持Windows,Linux,Mac,iOS,Android以后,玩法就多了很多。

实际上Delphi在开发任何平台的代码的时候都是编译成原生的Native Code,在Android平台,Delphi的代码和C++一样是被编译为SO(Linux,Unix上的动态链接库)文件,由一小段JVM代码直接加载.SO,引入原生代码。

这篇文章介绍的是用Delphi开发一个JNI形式的SO给Android调用,好处是SO是ARM的机器码,执行效率比JVM的执行效率要高,而且SO是原生代码并不像Java字节码那样容易被反编译,另外还有一个Delphi编译器产生的代码不是常见的C++编译器产生的,很多反编译工具例如IDA pro这样的,和它内置常见的C++运行时函数匹配不上。这样Delphi产生的SO就兼具了速度和安全的优势。

下面阐述一下做法,先创建一个Android Studio的普通项目。因为这些年转了用Kotlin开发Android以后特别舒服,所以Java很少碰了,下面的Android Studio项目代码都用Kotlin。

创建项目就是普通项目,语言选Kotlin,这些步骤就不陈述了,下面贴出MainActivity的代码。

//注意包名路径
package com.tools.sotest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button

class MainActivity : AppCompatActivity() {

    val btn_test : Button by lazy { findViewById(R.id.btn_test) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //调用Delphi so里面的函数,把返回的结果字符串赋值到Button的文字上
        btn_test.text = test("from Delphi ", " SO" )
    }

    //注意external关键字,表示是外部实现的函数。这是用Delphi给MainActivity实现的一个原生方法,所以没有实现
    private external
    fun test(a: String, b: String): String

    companion object {
        init {
            //加载我们Delphi现实的SO文件,文件实际的名字叫libDSO.so,System.loadLibrary会自动补全前面的lib和扩展名.so
            System.loadLibrary("DSO")
        }
    }
}

再在app的build.gradle中android节添加下面的代码,让系统知道JNI的SO去哪里找

    sourceSets {

        main {

            jniLibs.srcDirs = ['libs']

        }

}

下面是创建Delphi的SO的步骤。

第一步创建一个Dynamic Libray工程,名字就叫DSO吧,表示Delphi SO的意思:

很重要的:把工程文件的libray关键字改成program关键字。

然后在工程的target platforms添加Android 64 bit平台,如果你要Android 32 bit的也可以添加这个,不过这年头不论谷歌还是国内各大应用市场都已经要求是64bit的APP了。

代码很少。

{library DSO;  注意我们创建的是Dynamic Library,动态库,
 但是可能是Delphi编译器或者连接器产生的so入口初始化部分有不同,如果是linbrary的话
 在Java/kotlin中Sysem.loadLibrary会抛出异常。
 因为Android工程,不管是 library,还是program编译产生的都是so,所以模仿普通Android工程,
 把这里改成program,加载就不会有问题。
 所以:如果使用Delphi单独给Android产生so的话第一件事就是把library关键字改成program。
 }
 program DSO;
 uses
 //如果仅仅是简单的so开发System.SysUtils,System.Classes,也不是必须的,这样开发出来的release版SO只有200多KB。复杂的代码还是免不了引入这些的
 //  System.SysUtils,
 //  System.Classes,
 AndroidApi.Jni;
 {$R *.res}
 {
 这是Java的JNI开发规范,C++开发的时候也是一样的。
 Java开头表示是给Java的方法,然后接类的全路径包名,再接方法名。都是把.用_替换。
 第一个参数一定是env: PJNIEnv
 第二个参数,如果是普通成员方法就是一个JNIObject,如果是静态方法就是一个JNIClass
 然后是各种参数。
 至于更多的建议去看看JNI开发规范。
 }
 //com.tools.sotest.MainActivity.test
 function Java_com_tools_sotest_MainActivity_test(env: PJNIEnv; this: JNIObject; a, b: JNIString): JNIString;
 var
   sa, sb: string;
 begin
   sa := JNIStringToString(env, a);
   sb := JNIStringToString(env, b);
   Result := StringToJNIString(env, sa + sb);
 end;
 exports
   Java_com_tools_sotest_MainActivity_test name 'Java_com_tools_sotest_MainActivity_test';
 begin
 end.

直接编译Release版,然后到输出文件夹也就是默认工程文件夹的Android64\Release文件夹中找到我们需要的.so文件。因为代码少,又去掉了对Classes和SysUtils单元的引用,所以.so文件很小,只有290kb。

实际开发情况不太可能避开Classes和SysUtils单元的引用,所以体积不会太小,不过以后自己再写代码,体积增加微乎其微。

在刚才建立的Android Studio工程的app module的libs中创建一个文件夹arm64-v8a(如果你非要32bit或者64bit 32bit混合项目,就在创建一个armeabi-v7a的文件夹)把刚才Delphi产生的32 bit的.so文件复制到这个新建的文件夹中。

就可以直接运行和调试了。运行起来后可以看到界面上按钮的文字变成了From Delphi SO.

以后可以用Delphi类似C++一样给Android程序单独开发一些功能性的so。

上面代码都是Delphi 12开发的,其他版本暂时还没试验。

本文的DEMO链接,内有Android Studio项目和Delphi 的SO项目。

此条目发表在Android, APP, Delphi, 未分类分类目录,贴了, , , , 标签。将固定链接加入收藏夹。