操作系统:android

在编译android下的launcher前,请先根据“开始机器人开发”写的,在win10下编译出studio,并运行studio,导入launcher私有包,这时在projectfiles/android才会有launcher目录。而且,此时不建议你就开始编译andorid的launcher,还是在win10下继续编译出launcher,确定在win10下运行launcher没问题了,再编译android版本的launcher。kdesktop也类似操作。

一、编译:Android Studio 2021.2.1 Patch2 + NDK-r24

  • NDK版本至少r24,对更高的版本,我没测试过。若想避免因NDK版本造成意外问题,就用-r24。
  • Android Studio版本至少2021.2.1 Patch2,对更高版本,我没测试过。若想避免因Android Studio版本造成意外问题,就用2021.2.1 Patch2。

用于编译的操作系统用64位Win10或以上,要安装的除了Android Studio、NDK,不必再安装其它工具。运行命令行的终端可直接使用系统自带的cmd.exe,不必专门使用类Unit命令行cygwin等。

安装NDK

下载的NDK应该是一个zip包,像android-ndk-r24-windows.zip。

解压缩后,理论上你可以放在硬盘上任何目录。个人建议把它作为子目录下放在Android SDK目录下,目录名“android-ndk-r24”则改为ndk-bundle。

图1 Android SDK下的子目录ndk-bundle存放着NDK

在图1,“C:\Users\ancientcc\AppData\Local\Android\Sdk”是Android SDK目录,子目录“ndk-bundle”存放着NDK。

Rose包含不少和处理器相关汇编代码,编译出的app只支持真机,不支持模拟器。

不论launcher,还是kdesktop,默认只编译arm64-v8a。

下面以编译launcher为例描述编译过程。注:不同于windows,在android,可以第一个就编译launcher或kdesktop,不必先编译出studio。studio在android设备上运行没啥实际意义,可能编译还会报错。

1.1 目录结构

除非已经了解为什么要这么设置目录,否则不要去修改要求的目录结构。

图2 顶层目录结构
  • apps:源码目录。<apps>/projectfiles/android下存放着编译、链接时生成的文件。
  • linker:编译、链接时须要的头文件、so库文件。对android,so文件放在linker/android目录下。
  • scripts:为方便编译过程而写的脚本文件。android_2_ndk.bat/android_2_app.bat用于把编译生成的各个库复制到指定的位置。
  • SDL:多媒体开发库sdl。内中开源包在开发过程中,很少需要改动,专门放在一个目录。

总的来说,开发包须要编译的分两个部分:sdl和app。不论sdl还是app都要用NDK,在编译它们之前首先要设对环境变量。用文本编辑器打开<scripts>/android_set_variable.bat,这文件由Studio自动生成,第一次时,你须根据本地电脑设对NDK相关路径。

<scripts>/android_set_variable.bat
------
set NDK=C:\Users\ancientcc\AppData\Local\Android\sdk\ndk-bundle

set _APP_SRC = C:\ddksample\apps-src
set SCRIPTS=C:\ddksample\apps-src\scripts

set SDL_sdl=C:\ddksample\apps-src\SDL\SDL2-2.0.12\android
set SDL_sdl_so_path_32=c:\ddksample\apps-src\linker\android\lib-android-private\armeabi-v7a
set SDL_sdl_so_path_64=c:\ddksample\apps-src\linker\android\lib-android-private\arm64-v8a
set SDL_image=C:\ddksample\apps-src\SDL\SDL2_image-2.0.5\android
set SDL_mixer=C:\ddksample\apps-src\SDL\SDL2_mixer-2.0.4\android
set SDL_ttf=C:\ddksample\apps-src\SDL\SDL2_ttf-2.0.15\android
set libvpx=C:\ddksample\apps-src\SDL\libvpx\projectfiles\android
set lib3rdparty=C:\ddksample\apps-src\apps\projectfiles\android\3rdparty 
set libleagor_basic=c:\ddksample\apps-src\apps\projectfiles\android\libleagor_basic

set studio=C:\ddksample\apps-src\apps\projectfiles\android\studio
set launcher=C:\ddksample\apps-src\apps\projectfiles\android\launcher
set kdesktop=C:\ddksample\apps-src\apps\projectfiles\android\kdesktop

运行DOS命令行后,须先运行这个bat以让环境中存在以上定义的变量。

cd c:\apps\apps-src\scripts
android_set_variable.bat

注:查看以上变量是否被设置,可在命令行运行“set”。

Set

1.2 SDL

编译libSDL2.so

cd %SDL_sdl%
%NDK%/ndk-build

要确认编译是否成功,可以察看<SDL>/SDL2-2.x.x/android/libs/arm64-v8a下是否生成了一个新的libSDL2.so文件。

到现在要编译的库都是不依赖其它库的库,接下要编译依赖其它库的库,像SDL2_ttf要依赖以上编译出的libSDL2.so,为接下编译时不报错,须复制编译出的*.so到ndk能找到的库目录。

cd %SCRIPTS%
android_2_ndk.bat

注:如果报出不能复制libSDL2_image.so,libSDL2_mixer.so,libSDL2_ttf.so,可忽略这几个错误。

编译libSDL2_image.so

cd %SDL_image%
%NDK%/ndk-build

要确认编译是否成功,可以察看<SDL>/SDL2_image-2.x.x/android/libs/arm64-v8a下是否生成了一个新的libSDL2_image.so文件。

编译libSDL2_mixer.so

cd %SDL_mixer%
%NDK%/ndk-build

要确认编译是否成功,可以察看<SDL>/SDL2_mixer-2.x.x/android/libs/arm64-v8a下是否生成了一个新的libSDL2_mixer.so文件。

编译libSDL2_ttf.so

cd %SDL_ttf%
%NDK%/ndk-build

要确认编译是否成功,可以察看<SDL>/SDL_ttf-2.x.x/android/libs/arm64-v8a下是否生成了一个新的libSDL2_ttf.so文件。

1.3 lib3rdparty.so、libmain.so

为接下编译lib3rdparty.so、libmain.so不报错,复制编译出的*.so到ndk能找到的库目录

cd %SCRIPTS%
android_2_ndk.bat

注:此时如果报错的话,应该只报找不到lib3rdparty.so。

libmain.so是app库,但这app库依赖于lib3rdparty,为什么要把app代码分为在两个库,原因是android要支持开始C/C++代码的小程序,要强制开发这些小程序也依赖于lib3rdparty.so,这样发布新版lib3rdprty.so时,可以停止运行不兼容的小程序。关于哪些库分在了lib3rdparty.so,哪些分在了libmain.so,参考“Rose开源包”。

// 编译lib3rdparty.so 
cd %lib3rdpary%
%NDK%/ndk-build

// 把lib3rdaprty.so复制到NDK可查找到目录。
// 如果能确定lib3rdpary.so向外提供的api参数没发生改变,可以不执行这个复制。
cd %SCRIPTS%
android_2_ndk.bat

// 编译libmain.so
cd %launcher%
%NDK%/ndk-build

1.4 launcher.apk及运行

一旦以上全部编译成功,DOS命令行中执行以下命令。

cd %SCRIPTS%
android_2_app.bat %launcher%
图3 android_2_app.bat执行结果

以上命令把相关so复制到<projectfiles>/android/launcher/libs/arm64-v8a目录。下一步生成launcher.apk。

  1. 运行Android Studio前,先把grade替换为电脑上正使用的版本,源码包中grade是7.0.2,一旦不一致,可能会出现漫长下载过程。修改步骤:1)替换android/grade目录。2)编辑android/build.gradle,修改当中的版本号:classpath "com.android.tools.build:gradle:7.0.2"
  2. 运行Android Studio。
  3. “Welcome to Android Studio”选择“Open an existing Android Studio project”。
  4. “Open File or Project”中定位到<projectfiles>/android,打开。
  5. 连上Android设备,执行菜单命令“Run”——“Run ‘app’”。就会在<projectfiles>/android/app/build/outputs/apk生成launcher.apk,并可调试该apk。

1.5 编译、链接选项

1.5.1 Android.mk

添加额外私有库存放目录

LOCAL_LDFLAGS += -L$(LOCAL_PATH)/../linker/android/lib/arm64-v8a -L$(LOCAL_PATH)/../linker/android/lib-android-private/arm64-v8a

链接libmain.so时,需要lib3rdprty.so、libsdl2.so、libSDL2_image.so等私有库。为让链接时能找到这些私有库,一种简单方法是它们放在NDK库目录,但对开发者来说,NDK目录还是保持只读为好,于是选择把这些私有库放在<apps-src>/linker/android。

android_2_ndk.bat的功能就是把各私有库从编译出的目录复制到<apps-src>/linker/android。

1.5.2 Application.mk

Android Studio的JNI(NDK)开发主要有两种情况:一种是使用已经编译好的.so动态库(用自写的Android.mk);一种是使用c/c++源代码开发(由gradle自动生成Android.mk),Rose现阶段使用第一种方法。主要原因是第二种生成的“LOCAL_C_INCLUDES”会使编译变复杂并可能失败。

APP_ABI := arm64-v8a

指示要生成的CPU架构。当前Android支持的CPU架构包括armeabi、armeabi-v7a、arm64-v8a、x86、x86_64、mips、mips64。针对手机、平板市场,绝大多数基于arm,即前三种。armeabi针对早期arm v5 cpu,市场上基本已绝迹。arm64-v8a针对arm v8 cpu,NDK到APP_PLATFORM=android-21已开始支持64位。考虑到每一种架构会生成一组so,为减少安装包容量只设置了arm64-v8a。

生成的apk缺少架构可能严重影响效率,主要原因是缺少相应架构后只能采用低效渲染方式。举个例子,手机用的CPU是ARMv7-A Cortex,如果只编译armeabi,那它只会用软件渲染,渲染一窗口可能要花1.8秒,一旦增加armeabi-v7a,那运行时会选择到硬件泻染,时间会降低到0.8秒。

如果主so编译出了armeabi-v8a架构,那依赖的第三方库需要有armeabi-v8a相关的so文件,不然就会报错。

APP_PLATFORM := android-21

android-21换成通俗版本号就是Android-5.0。指示要基于运行的Android版本。

针对每一APP_FLATFORM,NDK提供了和之对应的API,它们是头文件形式的函数声明,类似Windows上的dll头文件部分。具体运行则要到真机,真机版本决定了它的最后执行效率。比如strchr,如果真机是android-21,那它最终运行的是android-21实现的strchr,换到android-23的真机,那就变成23版本。

一般来说,APP_PLATFORM值越大,会向外提供更多API。举个例子,一直到android-21都未提供stpcpy,但android-23有了。编译app用APP_PLATFORM := android-23,意味着由android系统提供stpcpy实现,可如果这app换在android-21运行,会由于该系统没有stpcpy导致loadLibrary加载该so时失败。从这个说,APP_PLATFORM值越小兼容性更好,但这个值越低意味着不能调用新版才有的函数。

综上所述,设置这值要考虑兼容性(要小)和功能(要大),当前选择android-21。考虑到三个原因,一是webrtc要求至少android-16,二是android-18开始支持蓝牙4.x,三是TensorFlow Lite要求至少android-21。

APP_STL := c++_shared

LLVM libc++共享运行库。rose需要c++11、异常处理,RTTI,此库完整支持了这些特色。

APP_CPPFLAGS += -fexceptions

默认情况下不使能exceptions,需手动打开。

APP_CPPFLAGS += -frtti

默认情况下不使能rtti,需手动打开。

1.6 编译其它app,像kdesktop

在同一个apps-src开发包目录,所有app共用SDL目录下的相关库、lib3rdparty.so,因而如果这些so都已最新的话,只要编译kdesktop的libmain.so就行了。但是,代码在lib3rdparty.so还是在libmain.so,好多时候是不容易记的,建议只要编译libmain.so,就强制编译lib3rdparty.so。

// 编译lib3rdparty.so 
cd %lib3rdpary%
%NDK%/ndk-build

// 把lib3rdaprty.so复制到NDK可查找到目录。
// 如果能确定lib3rdpary.so向外提供的api参数没发生改变,可以不执行这个复制。
cd %SCRIPTS%
android_2_ndk.bat

// 编译libmain.so
cd %kdesktop%
%NDK%/ndk-build
 
// 相关so复制到<projectfiles>/android/kdesktop/libs/arm64-v8a目录
cd %SCRIPTS%
android_2_app.bat %kdesktop%

相关so复制到<projectfiles>/android/kdesktop/libs/arm64-v8a目录后,就可用android studio生成kdesktop.apk。

1.7 权限策略

基本原则:app必须拥有需要权限,否则不让继续执行。

对Android 6.0或以上版本,支持运行时申请权限。在这些系统,app一开始会检查权限,是“询问”状态的让用户选择。一旦用户有选择了“拒绝”,弹出“缺少限权”警告框,给出两个出口,或跳到“设置”让手动选择为“允许”,或退出app。

app如何收集需要的限权?mPessionList是SDLActivity的成员变量,存储着需要的权限,它来自两部分。一是librose需要的权限,它们在onCreate时加入mPessionList。二是app私有,建议在app的构造函数中加入mPermissionList。举个例子,app需要发送短信,那在<java>/com/leagor/smsrobot/app.java加入以下代码。

public class app extends SDLActivity {
	public app() {
		mPermissionList.add(Manifest.permission.SEND_SMS);
	}
}

系统先执行构造app,然后onCreate,是第二部分先于第一部分加入mPessionList。

1.8 android_ndk-build.bat

为编译launcher这个app(libmain.so),上面在编译lib3rdparty.so、libmain.so时,用了三个“cd”、两个ndk-build,以及一个android_2_ndk.bat。6条命令,难免有时会落掉一条,那能一条命令就执行吗?

<apps-src>/scripts/android_ndk-build.bat,使用示例:android_ndk-build.bat %launcher%
------
@echo off
if '%1'=='' goto help
if '%1'=='help' goto help

@echo on

cd %lib3rdparty%
%NDK%/ndk-build
cd %scripts%
android_2_ndk.bat
cd %1
%NDK%/ndk-build

@echo off
goto exit

:help
echo Missing parameter, you must set app. for example: android_ndk-build %%studio%%

:exit

如果要编译launcher,这个bat的使用方法是“android_ndk-build.bat %launcher%”。但使用下来,编译出lib3rdparty.so就不再往下执行了,即使是成功编译出了lib3rdparty.so。为什么不继续,没找到原因,只能建议先不用android_ndk-build.bat。

全部评论: 0

    写评论: