Android调用C程序的七荤八素

在安卓平台上开发应用,通用的语言是 Java ,而对于从其它平台迁移到安卓的项目、产品,或者对于惯用 C/C++ 编程的开发人员来讲,会希望复用已有的 C/C++ 代码。安卓平台提供了复用 Native 代码的途径,也提供了编译 C 代码的环境和工具链: NDK 。 NDK 是一套工具链,有了它,在安卓上使用 C 语言成为可能。其实安卓原本是在 Linux 上套了个 Java 环境,要说不能用C 那才是不可思议的事儿,只是 Google 没完全开放而已(话说我到现在都在腹黑,为么不能让 C 程序员在安卓上活得自在些呢,简直是人为制造障碍)。

安卓平台上服用 C 代码有两种方式:

  1. JNI
  2. 原生 C 可执行程序

    JNI 方式

JNI 原本是Java 提供的一种复用 C 代码的框架,安卓又对此进行了一些扩充,加了个 AIDL 用在服务框架中,搞了一套工具,在使用 Android.mk 编译时可以根据 AIDL 文件自动生成对应的 Java 代码并编译。

使用 JNI 主要是把 C 代码编译成动态库,在 Java 中调用。使用的步骤大概是这样的:

  1. 在 Java 代码中声明 native 方法
  2. 在 JNI(桥接 C 代码的这部分 C 代码称之为 JNI 层)层按照命名规则实现与 Java 层对应的本地方法
  3. 在 Java 层加载 C 动态库

JNI 方式的例子,如 Qt on Android ,Vitamio ,还有安卓框架本身中的一些例子,如 ServiceManager , android.util.Log 。

    原生 C 可执行程序

安卓本是 Linux ,调用 Native 可执行程序是自然而然的途径。 Java 也提供了语言层面的支持,Runtime.exec() 函数就是干这个的。通过 exec() 启动进程,可以读取 Native 进程的标准输出,可以向 Native 进程的标准输入写入数据。

调用原生可执行程序的方式的例子, WifiManager 在连接无线网络时会通过控制接口和 wpa_supplicant (用于无线连接的 wpa_suppliacant ,是原生C可执行程序)通信传递诸如扫瞄接入点、选择网络、连接网络等指令。

还有,有些安卓系统机顶盒上自带的宽带拨号(PPPoE)程序,也是 C 可执行程序, Java 层的 PPPoEService 最终通过调用pppd/ppppoe 这样一些程序来执行实际的拨号过程。

还有,我们常说的 root ,其实也是通过调用一个叫 su 的程序来实现的。

我们在实际开发中也可以这么用,比如想看某个目录下都有什么文件,可以直接调用 ls 命令,读取它的标准输出。

    Java 和 C 程序通信问题

我们在安卓上通过 Java 调用 C 代码时还会遇到进程或线程间通信的问题。

这里专门说下 Java 进程和 C 进程、Java 进程和 C 共享库的通信问题。

有时我们的需求很简单,阻塞式调用 C 代码,拿到计算结果就达到目的了。比如你通过 JNI 调用 C 共享库的一个 Hash 函数,又比如你通过 Runtime.exec() 调用一个 Native 可执行程序来计算 Hash 读取其标准输出获得结果。这些场景足够简单,你可以不考虑 Java 和 C 共享库或者 C Native 可执行程序间的通信,反正是一锤子买卖也没啥状态要维护的。

但是还有一些复杂的应用,我们必须在 Java 和 C 之间建立一种长期的通信机制。

还是举无线连接的例子好了,有兴趣的读者可以浏览 wpa_supplicant 的源代码,它提供了基于 dbus、unix domain socket 两种方式的控制接口。

其实 Linux 常用的进程间通信机制,如 管道pipe 、socket 、信号,也都可以用于 Java 层和 C 层的通信,而且安卓框架就这么用了。举个例子,我们都很熟悉的安卓世界的第一个进程 Zygote(实际是 app_process ,启动后更名为 Zygote ),有一个功能就是启动 Java 进程,当我们要启动一个 APK ,该 APK 的进程几经辗转最终是由 Zygote 启动的(可以进程间共享 Java 类库、C 共享库,大大节省资源也加快进程启动过程),而 Zygote 正是通过 socket 接受命令的。

再举个信号的例子,我们看到很多进程管理类的应用,其实都是使用 android.os.Process 来杀进程,而 Process.kill() ,实际上就是给目标进程发送了一个信号,非常标准的 Linux 机制。

还有一个常用的 Java 和 C Native 可执行程序进程间通信的机制:标准输入输出。我们可以向一个进程的标准输入写入数据,也可以从它的标准输出读取数据。那么我们就可以定义一套控制协议用来通信。

像管道 pipe、socket 既可以用于进程间通信(Java 和 C Native 可执行程序),也可以用于进程内通信( Java 和 C 共享库)。那么什么场景下我们会这么用呢?前面的无线连接服务程序 wpa_supplicant 可以给我们一些提示。

假如我们用 C 实现了一个服务,而 Java 层需要经常访问这个服务并且需要得到反馈,那么就可以这么干。

试着说一个场景,我们用 C 实现了一个可以支持并行下载的模块(单线程 select 模型),而 Java 层会频繁地下载图片(安卓上 Java 层貌似没有简单易用占用资源又少的http 下载库),比如一个云相册类的应用或视频类应用,我们就可以把下载动作委托给 C 进程。

进程间通信,安卓框架中还有一个在 Linux IPC 框架之上扩展出来的新框架 Binder ,很多 Native 系统服务使用 Binder 框架,比如 AudioFlinger ,我们在 Java 层调用 AudioManager 设置音量的功能时,最终就是通过 Binder 框架使用了 Native 系统服务 AudioFlinger 的功能,是典型的跨进程调用,但是作为 Java 程序,完全不用关心这个。

想详细了解 Binder 的读者,可以进一步学习。这里提一下 Binder 的限制:不是所有 C 程序都可以使用 Binder 来注册服务,只有被授权的服务如 media.player ,media.camera 之类的 Native 系统服务才可以,如果你是系统开发,可以通过修改授权列表(通过 UID 控制)来允许你的 C 程序注册服务,而作为应用开发者,就别妄想了,还是考虑使用管道pipe、socket 或者标准输入输出稳妥些。

编译 C 可执行程序

为安卓平台编译 C 可执行程序,有几个方法:

在 APK 中集成和使用 C 可执行程序

怎样在 APK 中集成和使用一个 C 可执行程序呢?遵循下列步骤即可:

  1. 可以把可执行程序放在 assets 目录下,这样打包成 APK 时会自动打包
  2. APK 运行时访问 assets 文件夹内的资源,释放可执行程序,添加可执行权限
  3. 使用 Runtime.exec() 启动可执行程序
时间: 2024-12-28 02:11:17

Android调用C程序的七荤八素的相关文章

得知Android小遴选程序第七头(他们定义对话框、Gallery、ImageSwitcher)

效果如下面的:            一共一个activity和两个xml. ******当我们须要使用的组件不在setContentView()设置的布局文件里,那我们就须要使用inflate()方法来获取.使用view对象调用findViewByid(),作者一开直接调用findViewByid.走了好多弯路,于是写此博文来帮助各位博友,知道了就非常easy了. ******* MainActivity.java package com.example.head; import androi

Android:调用其他程序中的activity和Permission Denial: starting Intent 错误解决办法

今天想调试多个task中栈的情况,在测试程序中调用另一个程序的activity, 代码片段如下: [java] view plaincopy btnStartX=(Button)findViewById(R.id.btnStartX); btnStartX.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stu

android native HAL程序 java程序 linux kernel打印调用栈的方法

android native HAL程序 java程序 linux kernel打印调用栈的方法 关于android java打出调用栈的方法 1)方法一:refs:frameworks/base/services/java/com/android/server/ActivityManagerService.javastartProcessLocked(){Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "amProcessStart&quo

android 调用拨打电话,程序崩溃

FATAL EXCEPTION: main03-27 17:43:08.012: E/AndroidRuntime(18523): Process: com.mtq.freighthelper, PID: 1852303-27 17:43:08.012: E/AndroidRuntime(18523): java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.CALL

Android开发艺术探索——第七章:Android动画深入分析

Android开发艺术探索--第七章:Android动画深入分析 Android的动画可以分成三种,view动画,帧动画,还有属性动画,其实帧动画也是属于view动画的一种,,只不过他和传统的平移之类的动画不太一样的是表现形式上有点不一样,view动画是通过对场景的不断图像交换而产生的动画效果,而帧动画就是播放一大段图片,很显然,图片多了会OOM,属性动画通过动态的改变对象的属性达到动画效果,也是api11的新特性,在低版本无法使用属性动画,但是我们依旧有一些兼容库,OK,我们还是继续来看下详细

疯狂Android讲义 - 学习笔记(七)

第8章 Android数据存储与IO  Java IO的数据存储可以移植到Android应用开发上来,Android系统还提供了一些专门的IO API. Android系统内置了SQLite数据库,SQLite是轻量级的,没有后台进程,整个数据库对应一个文件,这样可以非常方便的在不同设备之间移植.Android为访问SQLite提供了大量便捷的API. 8.1 使用SharedPreferences 适用于保存简单格式的数据. 8.1.1 SharedPreferences 与 Editor S

Android群英传笔记——第七章:Android动画机制和使用技巧

Android群英传笔记--第七章:Android动画机制和使用技巧 想来,最近忙的不可开交,都把看书给冷落了,还有好几本没有看完呢,速度得加快了 今天看了第七章,Android动画效果一直是人家中十分重要的一部分,从早期的Android版本中,由于动画机制和绘图机制的不健全,Android的人机交互备受诟病,Android从4.X开始,特别是5.X,动画越来越完善了,Google也开始重视这一方面了,我们本章学习的主要内容有 Android视图动画' Android属性动画 Android动画

Android调用系统相机、自定义相机、处理大图片

Android调用系统相机和自定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理(避免OOM),还有简要提一下有些人SurfaceView出现黑屏的原因. Android应用拍照的两种方式,下面为两种形式的Demo展示出来的效果.    知识点: 一.调用系统自带的相机应用 二.自定义我们自己的拍照界面 三.关于计算机解析图片原理(如何正确加载图片到Android应用中) 所需

调试 Android* x86 应用程序的方法以及要使用的工具

作者:Xiaodong Wang 1.简介 众所周知,Android* 开发人员头顶许多称呼:设计员.程序猿等,并且通常会不可避免地被称为故障检修工.代码中的错误无法避免,因此无论您是否一开始就造成错误,了解调试工具以及如何迅速而有效地跟踪并解决错误都很重要.鉴于此,如今的 Android 开发人员必须掌握有效的调试技巧.本文提供了 Android 应用程序调试工具的简单教学,用于帮助 Android SDK 以及相关工具的新手迅速入门,并在 Android x86 平台上更有效地解决故障. 2