
so库指的是Android中调用通过JNI的方式高用低层的函数
Android JNI作用及其详解
Java Native Interface (JNI)标准是Java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互 *** 作。
1从如何载入so档案谈起
由于Android的应用层的类都是以Java写的,这些Java类编译为Dex型式的Bytecode之后,必须靠Dalvik虚拟机(VM: Virtual Machine)来执行。VM在Android平台里,扮演很重要的角色。
此外,在执行Java类的过程中,如果Java类需要与C组件沟通时,VM就会去载入C组件,然后让Java的函数顺利地调用到C组件的函数。此时,VM扮演着桥梁的角色,让Java与C组件能通过标准的JNI介面而相互沟通。
应用层的Java类是在虚拟机(VM: Vitual Machine)上执行的,而C件不是在VM上执行,那么Java程式又如何要求VM去载入(Load)所指定的C组件呢 可使用下述指令:
SystemloadLibrary(so的档案名);
例如,Android框架里所提供的MediaPlayerjava类,含指令:
public class MediaPlayer{
static {
SystemloadLibrary("media_jni");
}
}
这要求VM去载入Android的/system/lib/libmedia_jniso档案。载入so之后,Java类与so档案就汇合起来,一起执行了。
2如何撰写so的入口函数
---- JNI_OnLoad()与JNI_OnUnload()函数的用途
当Android的VM(Virtual Machine)执行到SystemloadLibrary()函数时,首先会去执行C组件里的JNI_OnLoad()函数。它的用途有二:
(1)告诉VM此C组件使用那一个JNI版本。如果你的so档没有提供JNI_OnLoad()函数,VM会默认该so档是使用最老的 JNI 11版本。由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 14的javanioByteBuffer,就必须藉由JNI_OnLoad()函数来告知VM。
(2)由于VM执行到SystemloadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),所以C组件的开发者可以藉由JNI_OnLoad()来进行C组件内的初期值之设定(Initialization) 。
在Android中启动JAVA程序其实有很多种方式,现总结如下
一、在Android应用程序中发送Intent启动Android应用程序
这个方式最简单,最常用。在此不在累述。关于Intent的更多内容请阅读《Intent技术简介》
二、在shell控制台通过am命令发送Intent来启动Android应用程序
在Android的shell控制台通过am命令发送Intent来启动Android应用程序
关于此的详细内容请参考《Android命令am详解》
三、在shell控制台直接通过davlikvm命令启动一个JAVA程序。
该方式有个天生的缺点,即在其中,很多Android的JNI无法调用。因为Android的很多JNI其实是需要手动注册的。
关于请参考《基本Dalvik VM调用》
四、在shell控制台直接通过运行app_process程序启动一个JAVA程序
在app_process程序中,他会对Android的JNI进行手动注册的,能很好的使用Android的API,因此通过运行app_process程序启动一个JAVA程序,是一个比较完美的方式。app_process程序是一个C程序,它的源码位于frameworks\base\cmds\app_process。
关于它的使用请参考《Android命令am详解》以及shell脚本frameworks\base\cmds\am\am和frameworks\base\cmds\pm\pm
am脚本文件如下:
# Script to start "am" on the device, which has a very rudimentary# shell#base=/systemexport CLASSPATH=$base/framework/amjarexec app_process $base/bin comandroidcommandsamAm "$@"pm脚本文件如下:
# Script to start "pm" on the device, which has a very rudimentary# shell#base=/systemexport CLASSPATH=$base/framework/pmjarexec app_process $base/bin comandroidcommandspmPm "$@"CLASSPATH指定了你的程序的位置,comandroidcommandspmPm则说明了程序的入口为comandroidcommandspmPm,即入口函数main()所在的类,"$@"就是传递给main()函数的参数,只是这里"$@"本身又是个shell传入的参数而已
需要注意的是CLASSPATH中的文件必须是dalvik文件格式的,关于此的转换请参考《基本Dalvik VM调用》当然CLASSPATH中的文件可以是apk文件,只是你的apk中至少应该有个拥有main()入口函数的类。
转载
//总结:后面代码不太相同;
//补充:
另外一个函数queue_builtin_action来向init进程中的一个待执行action队列增加了一个名称等于“console_init”的action。这个action对应的执行函数为console_init_action,它就是用来显示第二个开机画面的。
queue_builtin_action中也会执行action_add_queue_tail;和接下来调用的action_for_each_trigger一样;
action_list列表用来保存从启动脚本/initrc解析得到的一系列action,以及一系列内建的action。当这些action需要执行的时候,它们就会被添加到action_queue列表中去,以便init进程可以执行它们。
回到init进程的入口函数main中,最后init进程会进入到一个无限循环中去。在这个无限循环中,init进程会做以下五个事情:
A 调用函数execute_one_command来检查action_queue列表是否为空。如果不为空的话,那么init进程就会将保存在列表头中的action移除,并且执行这个被移除的action。由于前面我们将一个名称为“console_init”的action添加到了action_queue列表中,因此,在这个无限循环中,这个action就会被执行,即函数console_init_action会被调用。
B 调用函数restart_processes来检查系统中是否有进程需要重启。在启动脚本/initrc中,我们可以指定一个进程在退出之后会自动重新启动。在这种情况下,函数restart_processes就会检查是否存在需要重新启动的进程,如果存在的话,那么就会将它重新启动起来。
C 处理系统属性变化事件。当我们调用函数property_set来改变一个系统属性值时,系统就会通过一个socket(通过调用函数get_property_set_fd可以获得它的文件描述符)来向init进程发送一个属性值改变事件通知。init进程接收到这个属性值改变事件之后,就会调用函数handle_property_set_fd来进行相应的处理。后面在分析第三个开机画面的显示过程时,我们就会看到,SurfaceFlinger服务就是通过修改“ctlstart”和“ctlstop”属性值来启动和停止第三个开机画面的。
D 处理一种称为“chorded keyboard”的键盘输入事件。这种类型为chorded keyboard”的键盘设备通过不同的铵键组合来描述不同的命令或者 *** 作,它对应的设备文件为/dev/keychord。我们可以通过调用函数get_keychord_fd来获得这个设备的文件描述符,以便可以监控它的输入事件,并且调用函数handle_keychord来对这些输入事件进行处理。
E 回收僵尸进程。我们知道,在Linux内核中,如果父进程不等待子进程结束就退出,那么当子进程结束的时候,就会变成一个僵尸进程,从而占用系统的资源。为了回收这些僵尸进程,init进程会安装一个SIGCHLD信号接收器。当那些父进程已经退出了的子进程退出的时候,内核就会发出一个SIGCHLD信号给init进程。init进程可以通过一个socket(通过调用函数get_signal_fd可以获得它的文件描述符)来将接收到的SIGCHLD信号读取回来,并且调用函数handle_signal来对接收到的SIGCHLD信号进行处理,即回收那些已经变成了僵尸的子进程。
注意,由于后面三个事件都是可以通过文件描述符来描述的,因此,init进程的入口函数main使用poll机制来同时轮询它们,以便可以提高效率。
启动第二个activity,然后返回数据给第一个数据 。
MainActivity:每个种语言都有一个程序入库(如:C#main函数),而Android程序的入口就是Main Actiivty函数。
Activity是Android的核心类(androidappActivity),在Activity类有onCreate事件方法,一般用于对Activity进行初始化,并且通过setContentView方法将View放到Activity上,绑定后,Activity会显示View上的控件。
碰到的问题是控制台程序安装到手机上后,没一个图标可以用来启动,以下转载的是加入后,在手机的"安装"下可以找到对应的图标打开程序需要改的地方我加了背景色
我们使用Carbide的向导,可以方便的建立一个控制台应用程序,步骤如下:
1、菜单File->New->Symbian OS C++ Project
2、在d出的对话框选择Generic Symbian OS->Basic consle application(EXE)
3、完成向导就成功创建了一个控制台应用程序
此引用程序能正确的在模拟器中运行。但是,传入真机后,安装成功,在程序项里却找不到此程序。
解决方案:
1、新建一个文件夹data,添加资源文件TestConsoleApp_regrss,内容如下:
#include <appinforh>
UID2 KUidAppRegistrationResourceFile
UID3 0x0E0C8D49 // 改为与mmp文件相匹配的UID3
RESOURCE APP_REGISTRATION_INFO
{
app_file="TestConsoleApp"; // 改为工程包含入口函数的文件名
embeddability = KAppNotEmbeddable;
}
2、在mmp文件中添加一下几行:
SOURCEPATH /data
START RESOURCE TestConsoleApp_regrss
LANG SC
TARGETPATH /private/10003a3f/apps
END
3、在pkg文件中加入对资源文件编译后的文件的引用:
"$(EPOCROOT)Epoc32/data/z/private/10003a3f/apps/TestConsoleApp_regrsc"-"!:/private/10003a3f/import/apps/TestConsoleApp_regrsc"
4、添加自签名,编译完成。
注:1、以上控制台程序名为TestConsoleApp,请自行更改为对应的程序名;
2、以上程序仅针对S60 3rd有效。
本文源码基于 Android 10 ,涉及相关源码如下。
ServiceManagaer 是 Binder 的守护进程,在 Binder 机制中起着重要的作用。本文将从源码的角度对其进行分析,整体流程如下:
时序图如下。
先来看看 ServiceManager 是如何启动的:
在 Zygote 一文中说过, init 进程启动的第二阶段会解析 initrc 文件。
在这之后会触发 trigger init 。
结合 initrc 看看 action init 做了什么。
当触发 trigger init 后,会启动 servicemanager 服务,其声明如下。
对应的执行文件为 /system/bin/servicemanager ,在编译前位于 frameworks/native/cmds/servicemanager 下,来看看 Androidbp 。
其对应的源码为 service_managerc 和 binderc ,入口函数 main() 位于 servicemanagerc 。
启动完 ServiceManager 后会打开 Binder 驱动。
在 main() 中首先调用 binder_open() 。
binder_open() 主要做了如下事情:
给结构体 binder_state 分配内存。
系统调用 open() 打开 /dev/binder ,如果打开驱动失败,则执行 fail_open 释放内存。
简单的解释一下什么是系统调用?
由于需要限制不同的程序之间的访问能力,防止程序获取别的程序的内存数据, CPU 划分出两个权限等级, 用户态 和 内核态 。
所有的用户程序都是运行在用户态,但有时需要做一些内核态的事情,而唯一可以做这些事情的就是 *** 作系统,所以程序需要向 *** 作系统发起请求,以程序的名字来执行这些 *** 作。这时就需要一个从用户态切换到内核态但不能控制内核态中执行的机制,这种机制就是 系统调用 。
系统调用 ioctl() 传入 BINDER_VERSION 命令获取 Binder 驱动版本,对比版本是否一致,不一致则执行 fail_open 释放内存。
系统调用 mmap() 映射 128kb 的内存空间,即把 Binder 驱动文件的 128kb 映射到内存空间供 ServiceManager 使用,内存映射失败则执行 fail_map ,关闭 fd 并释放内存。
ServiceManager 进程 mmap 的内存大小可以通过 adb shell 命令查看。
可以看到内存映射地址为 0xf10f8000 ~ 0xf1118000 ,差为 0x20000 即十进制的 128kb 。
打开 Binder 驱动后会将 ServiceManager 设置为上下文管理者。
调用 binder_become_context_manager() 。
android 10 新增 BINDER_SET_CONTEXT_MGR_EXT 命令来设置安全的上下文管理者,如果设置失败,则使用原有的 BINDER_SET_CONTEXT_MGR 命令来设置上下文管理者,两者区别在于是否携带参数。
最后会进入循环,从 Binder 驱动读取和解析数据。
调用 binder_loop() 进入循环,不断地通过系统调用 ioctl() 从 Binder 驱动读取数据,并通过 binder_parse() 进行数据解析。
注意这里调用 binder_loop() 传入的 svcmgr_handler() ,后面会使用到。
binder_write() 会封装 struct binder_write_read ,并通过系统调用 ioctl() 将对应的命令传递给 Binder 驱动。
binder_parse() 用来解析从 Binder 驱动读取到的数据,然后根据不同的命令执行对应的 *** 作。
因为 cmd 命令可能有多个,所以通过 while 循环每次处理一个 cmd 命令,多 cmd 的结构大致如下图所示。
这里重点看下 BR_TRANSACTION 命令。
BR_TRANSACTION 是 Binder 驱动向 Server 端发送请求数据。
binder_transaction_data 的结构如下,其表明了 transcation 传输的具体语义,语义码记录在 code 中,不同语义码携带的数据是不同的,这些数据由 data 指定。
在解析完 binder_transaction_data 的具体语义后,会调用前面传给 binder_loop() 的 svcmgr_handler() ,其实就是 switch case 语义码做不同的事情。
ServiceManager 的功能其实很简单:
至此 ServiceManager 就分析完了。
第1章 阅读前的准备工作 / 1
11 系统架构 / 2
111 Android系统架构 / 2
112 本书的架构 / 3
12 搭建开发环境 / 4
121 下载源码 / 4
122 编译源码 / 6
13 工具介绍 / 8
131 Source Insight介绍 / 8
133 Busybox的使用 / 11
14 本章小结 / 12
第2章 深入理解JNI / 13
21 JNI概述 / 14
22 学习JNI的实例:MediaScanner / 15
23 Java层的MediaScanner分析 / 16
231 加载JNI库 / 16
232 Java的native函数和总结 / 17
24 JNI层MediaScanner的分析 / 17
241 注册JNI函数 / 18
242 数据类型转换 / 22
243 JNIEnv介绍 / 24
244 通过JNIEnv *** 作jobject / 25
245 jstring介绍 / 27
246 JNI类型签名介绍 / 28
247 垃圾回收 / 29
248 JNI中的异常处理 / 32
25 本章小结 / 32
第3章 深入理解init / 33
31 概述 / 34
32 init分析 / 34
321 解析配置文件 / 38
322 解析service / 42
323 init控制service / 48
324 属性服务 / 52
33 本章小结 / 60
第4章 深入理解zygote / 61
41 概述 / 62
42 zygote分析 / 62
421 AppRuntime分析 / 63
422 Welcome to Java World / 68
423 关于zygote的总结 / 74
43 SystemServer分析 / 74
431 SystemServer的诞生 / 74
432 SystemServer的重要使命 / 77
433 关于 SystemServer的总结 / 83
44 zygote的分裂 / 84
441 ActivityManagerService发送请求 / 84
442 有求必应之响应请求 / 86
443 关于zygote分裂的总结 / 88
45 拓展思考 / 88
451 虚拟机heapsize的限制 / 88
452 开机速度优化 / 89
453 Watchdog分析 / 90
46 本章小结 / 93
第5章 深入理解常见类 / 95
51 概述 / 96
52 以“三板斧”揭秘RefBase、sp和wp / 96
521 第一板斧——初识影子对象 / 96
522 第二板斧——由弱生强 / 103
523 第三板斧——破解生死魔咒 / 106
524 轻量级的引用计数控制类LightRefBase / 108
525 题外话—三板斧的来历 / 109
53 Thread类及常用同步类分析 / 109
531 一个变量引发的思考 / 109
532 常用同步类 / 114
54 Looper和Handler类分析 / 121
541 Looper类分析 / 122
542 Handler分析 / 124
543 Looper和Handler的同步关系 / 127
544 HandlerThread介绍 / 129
55 本章小结 / 129
第6章 深入理解Binder / 130
61 概述 / 131
62 庖丁解MediaServer / 132
621 MediaServer的入口函数 / 132
622 独一无二的ProcessState / 133
623 时空穿越魔术—defaultServiceManager / 134
624 注册MediaPlayerService / 142
625 秋风扫落叶—StartThread Pool和join Thread Pool分析 / 149
626 你彻底明白了吗 / 152
63 服务总管ServiceManager / 152
631 ServiceManager的原理 / 152
632 服务的注册 / 155
633 ServiceManager存在的意义 / 158
64 MediaPlayerService和它的Client / 158
641 查询ServiceManager / 158
642 子承父业 / 159
65 拓展思考 / 162
651 Binder和线程的关系 / 162
652 有人情味的讣告 / 163
653 匿名Service / 165
66 学以致用 / 166
661 纯Native的Service / 166
662 扶得起的“阿斗”(aidl) / 169
67 本章小结 / 172
第7章 深入理解Audio系统 / 173
71 概述 / 174
72 AudioTrack的破解 / 174
721 用例介绍 / 174
722 AudioTrack(Java空间)分析 / 179
723 AudioTrack(Native空间)分析 / 188
724 关于AudioTrack的总结 / 200
73 AudioFlinger的破解 / 200
731 AudioFlinger的诞生 / 200
732 通过流程分析AudioFlinger / 204
733 audio_track_cblk_t分析 / 230
734 关于AudioFlinger的总结 / 234
74 AudioPolicyService的破解 / 234
741 AudioPolicyService的创建 / 235
742 重回AudioTrack / 245
743 声音路由切换实例分析 / 251
744 关于AudioPolicy的总结 / 262
75 拓展思考 / 262
751 DuplicatingThread破解 / 262
752 题外话 / 270
76 本章小结 / 272
第8章 深入理解Surface系统 / 273
81 概述 / 275
82 一个Activity的显示 / 275
821 Activity的创建 / 275
822 Activity的UI绘制 / 294
823 关于Activity的总结 / 296
83 初识Surface / 297
831 和Surface有关的流程总结 / 297
832 Surface之乾坤大挪移 / 298
833 乾坤大挪移的JNI层分析 / 303
834 Surface和画图 / 307
835 初识Surface小结 / 309
84 深入分析Surface / 310
841 与Surface相关的基础知识介绍 / 310
842 SurfaceComposerClient分析 / 315
843 SurfaceControl分析 / 320
844 writeToParcel和Surface对象的创建 / 331
845 lockCanvas和unlockCanvasAndPost分析 / 335
846 GraphicBuffer介绍 / 344
847 深入分析Surface的总结 / 353
85 SurfaceFlinger分析 / 353
851 SurfaceFlinger的诞生 / 354
852 SF工作线程分析 / 359
853 Transaction分析 / 368
854 关于SurfaceFlinger的总结 / 376
86 拓展思考 / 377
861 Surface系统的CB对象分析 / 377
862 ViewRoot的你问我答 / 384
863 LayerBuffer分析 / 385
87 本章小结 / 394
第9章 深入理解Vold和Rild / 395
91 概述 / 396
92 Vold的原理与机制分析 / 396
921 Netlink和Uevent介绍 / 397
922 初识Vold / 399
923 NetlinkManager模块分析 / 400
924 VolumeManager模块分析 / 408
925 CommandListener模块分析 / 414
926 Vold实例分析 / 417
927 关于Vold的总结 / 428
93 Rild的原理与机制分析 / 428
931 初识Rild / 430
932 RIL_startEventLoop分析 / 432
933 RIL_Init分析 / 437
934 RIL_register分析 / 444
935 关于Rild main函数的总结 / 447
936 Rild实例分析 / 447
937 关于Rild的总结 / 459
94 拓展思考 / 459
941 嵌入式系统的存储知识介绍 / 459
942 Rild和Phone的改进探讨 / 462
95 本章小结 / 463
第10章 深入理解MediaScanner / 464
101 概述 / 465
102 androidprocessmedia分析 / 465
1021 MSR模块分析 / 466
1022 MSS模块分析 / 467
1023 androidprocessmedia媒体扫描工作的流程总结 / 471
103 MediaScanner分析 / 472
1031 Java层分析 / 472
1032 JNI层分析 / 476
1033 PVMediaScanner分析 / 479
1034 关于MediaScanner的总结 / 485
104 拓展思考 / 486
1041 MediaScannerConnection介绍 / 486
1042 我问你答 / 487
105 本章小结 / 488
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)