Android是移动设备的主流操作系统,近年来越来越多的工业领域的客户开始关注基于Android操作系统的设备在工控领域的应用。鉴于Android是基于Linux内核的事实,我们发展了一种以双应用进程为特色的Android工控应用方案,并在ESM6802工控主板上加以实现。具体说来,就是在Linux平台上运行一个直接操作硬件接口的控制通讯管理进程,为保证运行效率,该进程采用C/C++语言编写(以下简称C进程或控制进程);另一方面在Android平台采用标准Java语言编写一个人机界面进程(以下简称Java进程)。底层的控制进程并不依赖与上层的Java进程而独立运行,两个进程之间通过本地IP进行通讯,控制进程处于服务器侦听模式,Java进程则为客户端模式。本方案的主要优点是客户可以直接继承已有的现成应用程序作为底层控制进程的基础,仅仅增加标准的Socket侦听功能,即可快速完成新的底层应用程序的设计。而界面的Java程序,由于不再涉及具体的工控硬件接口,属于单纯的Android程序,编程难度也大大降低。
我们将通过多篇技术报告来具体介绍双进程方案在ESM6802主板上实现的相关技术。本文是《Android双应用进程工控方案》的第一篇,主要介绍在Android环境中,如何编译C/C++应用程序,下载并配置为开机启动程序。
1、重新编译C/C++应用程序
如图1所示,由于传统的Linux程序依赖的是glibc库,而Android程序需要的是谷歌公司在AOSP(Android Open Source Project)中提供的Bionic库(比glibc小,提供了Android特定的函数)。所以,原来Linux上的C/C++程序要运行在Android系统上,必须要在Android的编译环境中重新编译。英创推荐使用Android官方开发工具Android Studio,下载CMake和NDK工具,进行C/C++程序的重新编译。
图1 Android和Linux依赖库区别
下面开始介绍使用Android Studio的NDK编译工具重新编译C/C++程序的过程。
1.1 搭建Android Studio NDK编译环境
Android Studio的安装具体过程请参考文档《Android Studio 应用开发简介》的第一章,在SDK Tools页面中一定要勾选NDK和CMake。
1.2 在Android Studio中新建C++项目
图2 新建C++项目
首先新建一个Android Studio项目,并勾选Include C++ support选项,此处的Application name是Android app的名字,与最终需要的C++程序无关,用户可随意设定。然后一直点击下一步“Next”,直到图3页面,使用默认的工具链,点击Finish。
图3 默认工具链
点击finish后会进入项目编辑页面,进入到图4所示的项目视图,可以看到所有的目录结构,其中app/src/main/cpp目录、app/build.gradle和app/CMakeLists.txt是用户需要编辑修改的。然后,点击左上角File >> Project Structure进入图5的页面,检查NDK环境路径是否正确设置。
图4 项目目录结构及要修改的文件
图5 环境路径设置检查
1.3 复制C/C++应用程序源码
将原C/C++应用程序的所有源文件拷贝到app/src/main/cpp目录。
1.4 修改CMakeLists.txt
新的Android Studio已经支持使用cmake编译c++项目,这里提供对于简单项目使用的CMakeLists.txt,对于更复杂的需求,用户可以参考cmake官网文档https://cmake.org/cmake/help/v3.4/自行修改。
app/CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
# Android 5.0 以上需要在此处设置PIE
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie")
# 头文件目录
include_directories(
src/main/cpp
)
# 源文件目录
aux_source_directory(src/main/cpp DIR_SRCS)
# 添加要编译的可执行文件
add_executable(serialControlDaemon ${DIR_SRCS})
其中,add_executable表明要生成的是可执行文件,名字为SerialControlDaemon,源文件为DIR_SRCS变量代表的文件,而aux_source_directory将src/main/cpp目录下的所有文件赋给了变量DIR_SRCS。
1.5 修改build.gradle
app/build.gradle文件主要是设置构建app的一些参数,这里主要往android>> defaultConfig>>externalNativeBuild>>cmake添加targets和abiFilters两个参数。其中,targets表示生产目标文件的名字,与CmakeLists.txt中的相同;abiFilters表示要生产哪种cpu架构下的目标文件,这里使用armeabi-v7a。修改之后会在右上角提示需要同步项目,点击即可。
图6 修改build.gradle
1.6 编译cpp项目
在Android Studio中直接使用Build>>Make Project即可编译整个项目,包括cpp和java。生成的目标文件在目录app/.externalNativeBuild/cmake/debug/armeabi-v7a目录下,名字为serialControlDaemon。
1.7 下载目标文件到Android
Android Studio集成了Android开发的所有工具,在Android Studio中使用adb push命令可以将编译得到的目标文件下载到Android目标板上。首先,要使用usb otg的调试线连接PC和目标板;然后点击左下角的Terminal窗口会弹出所在项目的命令行窗口;输入命令:
adb push app\.externalNativeBuild\cmake\debug\armeabi-v7a\serialControlDaemo /data/local
这样,serialControlDaemon便下载到了目标板的/data/local目录下。这时,使用adb shell登陆到Android目标板的命令行,修改目标文件的运行权限并运行,整个过程如图7所示。程序正常运行起来后,表明整个编译过程没有问题,用户可以在命令行中按Ctrl+c停止运行应用程序,并输入exit命令退出adb shell登陆,然后进行下一步的开机自启动配置。
图7 下载目标文件到Android
2、开机自启动配置
ESM6802上电后通过uboot引导进入linux内核,内核完成一系列系统配置后会启动第一个用户进程:init进程。Android相关的启动过程也是从init开始的。在init进程中会挂载Android的文件系统,运行init.rc脚本。init进程启动过后,会fork出子进程去开启init.rc文件中配置的service。
为了满足用户运行不同名字的应用程序,英创在init.rc中配置了一个usersh服务。usersh服务开机自动运行,具体过程用户不用关心。 要想开机自启动C/C++程序,用户只需要做两件事:
● 编辑userinfo.txt文件
● 复制userinfo.txt以及C/C++程序的目标文件到指定目录/sdcard/Download
2.1 编辑userinfo.txt
Android启动后,usersh服务会自动检测/sdcard/Download/userinfo.txt文件。如果userinfo.txt文件存在,usersh会去解析并启动userinfo.txt文件中指定的应用;如果userinfo.txt不存在,则结束usersh服务。userinfo.txt起到一个配置文件的作用,其格式如下:
Name=serialControlDaemon
Param=2
其中,Name指定程序名字,Param指定要带的参数,没有可以不写。用户可以直接在Android Studio中创建并编辑userinfo.txt文件。
图8 Android Studio中新建userinfo.txt
2.2 复制userinfo.txt以及C/C++程序到指定目录/sdcard/Download
Android系统中,不是每个目录都有读写以及可执行的权限,这里我们选择/sdcard/Download作为存储userinfo.txt和C/C++程序的指定目录。复制userinfo.txt以及C/C++程序到指定目录有两种方法:通过usb_otg接口使用Android Studio的adb push命令下载到ESM6802,或者通过U盘从PC端拷贝到ESM6802。用户按其中一种方法下载文件到指定目录后,重启ESM6802即可以开机启动userinfo.txt中指定的C/C++程序。
1、Android Studio命令行下载userinfo.txt和C/C++程序到ESM6802
使用Android Studio命令行下载文件到ESM6802,首先需要使用调试线连接PC和目标板的usb_otg接口。然后,在Android Studio的Terminal窗口输入:
adb push app\.externalNativeBuild\cmake\debug\armeabi-v7a\serialControlDaemo /sdcard/Download
adb push app\userinfo.txt /sdcard/Download
重启设备即可实现开机自启动serialControlDaemon。
2、U盘拷贝userinfo.txt和C/C++程序到ESM6802
使用U盘拷贝userinfo.txt和C/C++程序到ESM6802,只需要将userinfo.txt和目标文件(serialControlDaemon)拷贝到U盘,插到ESM6802的USB接口上,打开Android的文件管理应用ES File Explorer,将userinfo.txt和serialControlDaemon拷贝到/sdcard/Download目录,重新启动即可。
2.3 查看程序是否开机运行
通过以上设置之后,Android开机boot_completed=1之后会启动应用程序serialControlDaemon,用户可以通过命令adb shell登陆consolo控制台,输入命令getprop | grep init.svc | grep usersh来查看usersh服务的运行状态;当然usersh实际运行的应用程序serialControlDaemon的进程状态可以通过ps | grep serialControlDaemon查看。
图9 检测usersh服务运行状态
3、Q & A
Q1:查看C/C++程序输出
在Android控制台上看不到开机启动的C/C++程序输出信息,开发中如何在Android上调试C/C++程序?
A1:使用kill命令终止掉已经启动的C/C++程序;然后,在Android命令行中执行命令:user.sh,即可手动启动C/C++应用程序,并且C/C++应用程序的输出信息将打印到Android控制台。
Q2:关于userinfo.txt和C/C++程序指定目录的说明
A2:userinfo.txt和C/C++程序指定目录要具有读写可执行权限,在2.2节中,adb push命令将C/C++应用程序(serialControlDaemon)下载到了/sdcard/Download目录,其实下载到/data/local也是可以的,而U盘却只能拷贝到/sdcard/Download/。这是因为usersh服务会比较/sdcard/Download/serialControlDaemon是否比/data/local/serialControlDaemon更新,如果是,则先用新文件覆盖旧文件,再运行/data/local/serialControlDaemon。因此,使用adb push命令的指定目录用/sdcard/Download/或者/data/local都是可以的;而使用U盘,则受限于ES File Manager应用不能访问/data/local目录,只能拷贝到/sdcard/Download。
Q3:关于Android Studio的Terminal窗口
A3:Android Studio的Terminal窗口在进入的时候,工作在PC的文件系统上,操作的文件都是PC上的;当使用adb shell登陆Android目标板之后,工作在Android目标板的文件系统上,操作的文件、执行的命令都是Android目标板上的;在使用adb shell登陆之后,可以使用exit命令退出登陆状态,返回到PC端的工作目录。
Q4:adb连接不上设备
使用adb devices查看一下是否有已连接的设备;检查usb_otg和PC端的物理连接;重新插拔一下调试线或者重启系统。
如果ethernet正常工作,可以使用ethernet代替usb_otg,在Terminal中输入一下命令:
$ adb usb
restarting in USB mode
$ adb devices
List of devices attached
???????????? device
$ adb tcpip 5555
restarting in TCP mode port: 5555
$ adb connect YOUR_IP_ADDRESS
connected to YOUR_IP_ADDRESS:5555
$ adb devices
List of devices attached
???????????? device
YOUR_IP_ADDRESS:5555 device
退出:
adb disconnect YOUR_IP_ADDRESS
Q5:常用命令
查看所有service运行状态:getprop | grep init.svc
adb相关:
adb devices 查看usb_otg已连接的设备
adb push localfile remotepath 将PC端的localfile下载到Android端的remotepath目录下。
adb pull remotefile 复制Android端的remotefile文件到PC端的当前目录
成都英创信息技术有限公司 http://www.emtronix.com
原文下载:http://www.emtronix.com/download/android_c_cpp.pdf