深入学习Android

news/2024/11/29 3:38:56/
我通过阅读邓凡平前辈的《深入理解Android》,为了加深学习作此学习笔记。
虽然是邓老师2011著的书,但其中的安卓框架还是可以学习的。
另老师的csdn地址在:阿拉神农的博客_CSDN博客-Android开发系列,深入理解Android,移动万态领域博主
tips:阅读该知识应具有C++的基本知识,因为本书的大部分内容都集中在了Native层。
本书是在分析Android源码的基础上展开的,而源码文件所在的路径一般都很长,例如,
文件AndroidRuntime.cpp的真实路径就是framework/base/core/jni/AndroidRuntime.cpp
本书的编写顺序,是65478910231章,但出于逻辑连贯性的考虑,还是建议读者按本书的顺序阅读。其中,第256章分别讲述了JNIAndroid常用类 ,以及Binder系统,这些都是基础知识,我们有必要完全掌握。其他部分的内容都是针对单个模块的,例如ZygoteAudioSurfaceMediaScanner等,读者可各取所需,分别对其进行研究
该书是上册,全书一共10章,10章的主要内容是:
        第1章  介绍了阅读本书所需要做的一些准备工作,包括对Android整个系统架构的认识,以
Android开发环境和源码阅读环境的搭建等。注意,本书分析的源码是Android2.2
        第2章  通过Android源码中的一处实例深入地介绍了JNI技术
        第3章  围绕init进程,介绍了如何解析init.rc以启动Zygote和属性服务(property service)的工作原理。
        第4章  剖析了zygote和system_server进程的工作原理。本章的拓展思考部分讨论了Andorid启动速度、虚拟机heapsize的大小调整问题以及看门狗的工作原理。
        第5章  讲解了Android源码中常用的类,如sp、wp、RefBase、Thread类、同步类、Java中的 Handler类以及Looper类。这些类都是Android中最常用和最基本的,只有掌握这些类的知识, 才能在分析后续的代码时游刃有余。
        第6章   以MediaServer为切入点,对Binder进行了较为全面的分析。本章拓展思考部分讨论
了与Binder有关的三个问题,它们分别是Binder和线程的关系、死亡通知以及匿名Service
        第7章   阐述了Audio系统中的三位重要成员AudioTrack、AudioFlinger和AudioPolicyService 的工作原理。本章拓展思考部分分析了AudioFlingerDuplicatingThread的工作原理,并且和 读者一道探讨了单元测试、ALSADesktop check等问题。
        第8章   以Surface系统为主,分析了Activity和Surface的关系、Surface和SurfaceFlinger的关系 以及SurfaceFlinger的工作原理。本章的拓展思考部分分析了Surface系统中数据传输控制对象 的工作原理、有关ViewRoot的一些疑问,最后讲解了LayerBuffer的工作流程。这是全书中难度较大的一章,建议大家反复阅读和思考,这样才能进一步深入理解Surface系统。
        第9章   分析了Vold和Rild,其中Vold负责Android平台中外部存储设备的管理,而Rild负责
与射频通信有关的工作。本章的拓展思考部分介绍了嵌入式系统中与存储有关的知识,还探
讨了RildPhone设计优化方面的问题。
        第10章   分析了多媒体系统中MediaScanner的工作原理

1章 阅读前的准备工作

1.1 系统架构

1.1.1 Android系统架构

该平台本身是基于Linux内核的

Linux内核层:包含了Linux内核和一些驱动模块(比如USB驱动、Camera驱动、蓝牙驱动
等)。目前Android2.2(代号为Froyo)基于Linux内核2.6版本。
Libraries层:这一层提供动态库(也叫共享库)、Android运行时库、Dalvik虚拟机等。从
编程语言角度来说,这一层大部分都是用CC++写的,所以也可以简单地把它看成是Native
层。
Framework层:这一层大部分用Java语言编写,它是Android平台上Java世界的基石。
Applications层:与用户直接交互的就是这些应用程序,它们都是用Java开发的。

 Android系统搭建出了一个java世界,他的运转依赖于另一个被Google极力隐藏的Native世界。

Java虽具有与平台无关的特性,但Java和具体平台之间的隔离却是由JNI层来实现的。Java
是通过JNI层调用Linux OS中的系统调用来完成对应的功能的,例如创建一个文件或一个
Socket等。
除了Java世界外,还有一个核心的Native世界,它为整个系统高效和平稳地运行提供了强
有力的支持。一般而言,Java世界经由JNI层通过IPC方式与Native世界交互,而Android平台
上最为神秘的IPC方法就是Binder了,第6章将详细分析Binder。除此之外,Socket也是常用的
IPC方式。

1.1.2 本书的架构

本书所分析的模块也将遵循Android系统架构

该书籍所分析的各个模块除未涉及Kernel外,其他三层均有所涉及,它们分别是:
Native层包括init、Audio系统(包括AudioTrack、AudioFlinger和AudioPolicyService)、Surface系统(包括Surface和SurfaceFlinger)、常用类(包括RefBase、sp、wp等)、Vold和 Rild
Java Framework层包括zygote、System_server以及Java中的常用类(包括HandlerLooper
等)。
Java Application层包括MediaProvider和Phone

1.2.1 下载源码

下面将详细介绍如何下载Android的源码。
1.设置软件源

 将软件源地址设置成了http//mirror.bjtu.edu.cn/ubuntu

2.下载Android源码
下面开始下载Android源码,工序比较简单,可一气呵成。
apt-get install git-core curl#先下载这两个工具
mkdir-p/develop/download-froyo#在根目录下建立developdownload-froyo两个目录
cd~/develop/download-froyo#进入这个目录
curl http://Android.git.kernel.org/repo>./repo#从源码网站下载repo脚本,该脚本是Google
为了方便源码下载而提供的,通过该脚本可下载整套源码。
chmod a+x repo#设置该脚本为可执行
./repo init-u git://Android.git.kernel.org/platform/manifest.git-b froyo#初始化git
./repo sync#下载源码,大小约为2GB,如果网速快,估计也得要2个多小时。

注意 Kernel的代码必须单独下载,下载方法如下:
git clone git//android.git.kernel.org/kernel/common.git kernel

1.2.2 编译源码

1.部署JDK

Froyo的编译依赖JDK 1.5,所以首先要做的就是下载JDK 1.5。下载网址是
http://www.oracle.com/technetwork/java/javase/downloads/index-jdk5-jsp-142662.html
下载得 到的文件为jdk-1_5_0_22-linux-i586.bin,把它放到任意一个目录中,笔者将它放在了/develop 中,然后在这个目录中执行如下命令:
./jdk-1_5_0_22-linux-i586.bin#执行这个文件 
这个命令的功能其实就是解压,解压后的结果在/develop/jdk1.5.0_22目录中。现在有了
JDK,再按照下面的步骤部署它即可:
1)在~/.bashrc文件的末尾添加以下几句话:
export JAVA_HOME=/develop/jdk1.5.0_22#设置为刚才解压的目录
export JRE_HOME=JAVA_HOME/jre
export CLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH 
(2)重新登录系统,这样JDK资源就能被正确找到了。

2.编译源码

Android的编译有自己的一套规则,主要利用的是mk文件,在此简单地介绍其编译工序:
  1. 进入源码目录(以笔者的开发环境为例,也就是cd/develop/download_froyo):
  2. 执行.build/envsetup.sh,这个脚本用来设置Android的编译环境。
  3. 执行choosecombo命令,这个命令用来选择编译目标(如目标硬件平台、eng还是user等)。一般而言,手机厂商会设置自己特有的编译选项。
  4. 执行完上面几个步骤后,就可以编译系统了。
        Android平台提供了三个命令用于编译,它 们分别是make、mmm和mm,这三个命令的使用方法及其优劣如下:
        make:不带任何参数,它用于编译整个系统,时间较长,笔者不推荐这种做法,除非读者想编译整个系统。
        make MediaProvider:下面几个例子都以编译MediaProvider为例。这种方式对应于单个模块编译。它的优点是,会把该模块依赖的其他模块也一起编译。例如make libmedia,就会把libmedia依赖的库全部编译好。其缺点也很明显,它需要搜索整个源码来定位MediaProvider块所使用的Android.mk文件,并且还要判断该模块所依赖的其他模块是否有修改。整体编译时间较长
        mmm packages/providers/MediaProvider:该命令将编译指定目录下的目标模块,而不编译 它所依赖的模块。所以,如果读者是初次编译,采用这种方式编译一个模块往往会报错。错
误的原因是因为它依赖的模块没有被编译
        mm:这种方式需要先用cd命令进入packages/providers/MediaProvider目录,然后执行mm令。该命令会编译当前目录下的模块。它和mmm一样,只编译目标模块,mmmmm命令编
译的速度都很快。
        如果只知道目标模块的名称,则应使用make模块名的方式来编译目标模块。例如,如果要 编译libmedia,则直接使用make libmedia即可。另外,初次编译时也要采用这种方法。
        如果不知道目标模块的名称,但知道目标模块所处的目录,则可使用mmmmm命令来编 译。当然,初次编译还必须使用make命令,以后的编译就可使用mmmmm了,这样会节约 不少时间。
        注意 一般的编译方式都使用增量编译,即只编译发生变化的目标文件,但有时则需重新
编译所有目标文件,那么就可使用make命令的-B选项。例如make-B模块名,或者mm-B
mmm-B。在mmmmm内部,也是调用make命令的,而make-B选项将强制编译所有目标文        件

3.本书各模块的编译目标
        本书各模块的编译目标如下所示,这里仅列出几个有代表性的模块:

目标模块        make命令        mmm命令

1.init         make init         mmm system/core/init

2.zygote         make app_process         mmm frameworks/base/cmds/app_process

3.system_server         make services         mmm frameworks/base/services/java

4.RefBase等        make libutils         mmm frameworks/base/libs/utils

5.Looper等        make framework         mmm frameworks/base

6.AudioTrack         make libmedia         mmm frameworks/base/media/libmedi 

7.AudioFlinger         make libaudioflinger         mmm frameworks/base/libs/audioflinger

8.AudioPolicyService         make libaudiopolicy         mmm hardware/msm7 k/libaudio-qsd8k(示例)

 假设make framework,那么编译完的结果则如图1-6所示

        从上图可看出,make命令编译了framework-res.apkframework.jar两个模块。它们编译的
结果在out/target/product/generic/system/framework下。利用adb命令把这两个文件push到手机的
system/framework目录即可替换旧的文件。
        如果想测试这个新模块,则需要先杀掉所有使用该模块的进程,进程重启后会重新加载模块,这时就能使用新的文件了。
        例如,想测试刚修改的libaudioflinger模块,通过adb命令push上去后,要先杀掉mediaserver进程,因为 libaudioflinger库目前只有该进程在使用。当mediaserver重启后,就会加载新push上来的libaudioflinger库了。
注意 系统服务被杀掉后一般都会自动重启

1.3 工具介绍

1.3.1 Source Insight介绍

 关于使用工具source insight的解释可以参考我另外一篇文档:

【精华】Source Insight从入门到精通

1.3.3 Busybox的使用

        Busybox号称Linux平台上的瑞士军刀,它提供了很多常用的工具,例如grepfind等。这
些工具在标准Linux上都有,但Android系统却去掉了其中的大多数工具。
        这导致我们在调试 程序和研究Android系统时十分不便,所以我们需要在手机上安Busybox
1.下载Busybox
        我们可从下面这个网站中下载已编译好的Busybox
        http://www.busybox.net/downloas/binaries/1.18.4/

2章 深入理解JNI

本章涉及源代码主要在以下路径:

  1. MediaScanner.javaframework/base/media/java/src/android/media/MediaScanner.java
  2. android_media_MediaScanner.cppframework/base/media/jni/MediaScanner.cpp
  3. android_media_MediaPlayer.cppframework/base/media/jni/android_media_MediaPlayer.cpp
  4. AndroidRunTime.cppframework/base/core/jni/AndroidRunTime.cpp
  5. JNIHelp.cdalvik/libnativehelper/JNIHelp.c

2.1 JNI概述

JNIJava Native Interface的缩写,中文译为Java本地调用
通俗地说,JNI是一种技术,
通过这种技术可以做到以下两点:
  1. Java程序中的函数可以调用Native语言写的函数,Native一般指的是C/C++编写的函数。
  2. Native程序中的函数可以调用Java层的函数,也就是说在C/C++程序中可以调用Java的函数。
JNI技术的推出有以下几个方面的考虑:
        承载Java世界的虚拟机是用Native语言写的,而虚拟机又运行在具体的平台上,所以虚拟
机本身无法做到平台无关。然而,有了JNI技术后就可以对Java层屏蔽不同操作系统平台(如
WindowsLinux)之间的差异了(例如同样是打开一个文件,Windows上的API使用OpenFile
函数,而Linux上的APIopen函数)。这样,就能实现Java本身的平台无关特性。其实Java
直在使用JNI技术,只是我们平时较少用到罢了。
        早在Java语言诞生前,很多程序都是用Native语言写的,它们遍布在软件世界的各个角
落。Java出世后,它受到了追捧,并迅速得到发展,但仍无法将软件世界彻底改朝换代,于
是才有了折中的办法。既然已经有Native模块实现了相关功能,那么在Java中通过JNI技术直
接使用它们就行了,免得落下重复制造轮子的坏名声。另外,在一些要求效率和速度的场合
还是需要Native语言参与的。
        在Android平台上,JNI就是一座将Native世界和Java世界间的天堑变成通途的桥。如下图,它展示了Android平台上JNI所处的位置:

        在Android平台上尽情使用Java的程序员们不要忘了,如果没有JNI的支持,我们将寸步难行
注意! 虽然JNI层的代码是用Native语言写的,但本书还是把与JNI相关的模块单独归类到JNI层了。

2.2 学习JNI的实例:MediaScanner

首先看第一个例子,是和MediaScanner相关的,如下图是:MediaScanner和它的JNI

        Java世界对应的是MediaScanner,而这个MediaScanner类有一些函数需要由Native层来实
现。
        JNI层对应的是libmedia_jni.somedia_jniJNI库的名字,其中,下划线前的“media”
Native层库的名字,这里就是libmedia库。下划线后的“jni”表示它是一个JNI库。注意,JNI
的名字可以随便取,不过Android平台基本上都采用lib模块名_jni.so的命名方式。
Native层对应的是libmedia.so,这个库完成了实际的功能。
        MediaScanner将通过JNIlibmedia_jni.soNative层的libmedia.so交互。
        从上面的分析中还可知道:JNI层必须实现为动态库的形式,这样Java虚拟机才能加载它并
调用它的函数
下面来看MediaScanner
提示 :MediaScanner是Android平台中多媒体系统的重要组成部分,它的功能是扫描媒体文件,得到诸如歌曲时长、歌曲作者等媒体信息,并将它们存入到媒体数据库中,供其他应用程序使用。

 2.3 Java层的MediaScanner分析

先来看MediaScanner(简称MS)的源码,这里将提取出与JNI有关的部分,其代码如下所
示:
[-->MediaScanner.java]
public class MediaScanner
{
static{static语句
/*①加载对应的JNI库,media_jni是JNI库的名字。在实际加载动态库的时候会将其拓展成
libmedia_jni.so,在Windows平台上则拓展为media_jni.dll
*/
System.loadLibrary("media_jni");
native_init();//调用native_init函数
}
……
//非native函数
public void scanDirectories(String[]directories,String volumeName){
……
}
//②声明一个native函数。native为Java的关键字,表示它将由JNI层完成。
private static native final void native_init();
……
private native void processFile(String path,String mimeType,
MediaScannerClient client);
……
}
上面代码中列出了两个比较重要的要点:一个是加载JNI库;另一个是Javanative函数。

2.3.1 加载JNI

        如果Java要调用native函数,就必须通过一个位于JNI层的动态库来实现。顾名思义,动态库就是运行时加载的库,那么在什么时候以及什么地方加载这个库呢?
        这个问题没有标准答案,原则上是:在调用native函数前,任何时候、任何地方加载都可
以。通行的做法是在类的static语句中加载,调用System.loadLibrary方法就可以了。这一点在
上面的代码中也见到了,我们以后就按这种方法编写代码即可。
        另外,System.loadLibrary数的参数是动态库的名字,即media_jni。系统会自动根据不同的平台拓展成真实的动态库文 件名,例如在Linux系统上会拓展成libmedia_jni.so,而在Windows平台上则会拓展成media_jni.dll


http://www.ppmy.cn/news/1967.html

相关文章

python简单实现网络爬虫

前言 在这一篇博客中,我会用python来实现一个简单的网络爬虫。简单的爬取一下一些音乐网站、小说网站的标题、关键字还有摘要!所以这个爬虫并不是万能爬,只针对符合特定规则的网站使用。(只使用于爬标题、关键字和摘要的&#xff…

【OpenCV学习】第5课:图像模糊(均值滤波,高斯滤波)

参考文章链接:https://blog.csdn.net/qq_30460949/article/details/121990114 仅自学做笔记用,后续有错误会更改 理论 1.Smooth/blur是图像处理中最简单和常用的操作之一 2.使用该操作的原因之一就是为了给图像预处理的时候减低噪声 3.使用Smooth/Blur操作其背后是数学的卷积…

小红书和达人合作步骤是什么?对接达人合作流程分享

现在小红书作为不错的内容分享媒体,小红书内容分享的核心便是达人。许多商家也想知道该如何与达人合作。今天,就来和大家一起分享一下这个问题,带领大家了解并解析小红书和达人合作步骤是什么?并给大家解析一下期间有哪些注意事项。 其实商家…

使用setuptools构建python包

python包分发方式 源码包分发: 源码包安装过程是先解压,再编译。最后才安装,所以其是跨平台的,由于每次安装都需要进行编译,相对于二进制包安装方式来说安装速度较慢。 解压——编译——安装 源码包本质上是一个压缩…

LINUX下看门狗的使用

0、基本原理 使能看门狗,并配置看门狗,周期性的给看门狗设备写入数据即为喂狗。 1、使能硬看门狗 内核和设备树使能看门狗,具体的需要参考对应的cpu文档对看门狗的描述。 2、应用程序喂狗 参考应用程序源码如下: #include &…

【入门】初识深度学习

文档背景 机器学习和深度学习的概念十分火热。听上去也很难,不慌,有时候就需要行动在前脑子在后。不管,干就完啦。 前言 人工智能(ArtificialIntelligence,AI)是最宽泛的概念,是研发用于模拟、延…

【Pytorch】第 1 章 :强化学习和 PyTorch 入门

🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎 📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃 🎁欢迎各位→点赞…

Unity简单基础

Unity文件夹的作用 Resources存放资源的特殊文件夹,Resources中全部资源会被打包Pluging存放第三方包、工具代码、sdk文件Editor为Unity编辑器扩展程序的目录,可以在根目录下,也可以在子目录下StreamingAssets该文件夹也会在打包的时候全部打…