第三篇 功能实现(1)
第8章 Android应用程序组成
●Android的一些中、底层基础知识
※ Android Framework 启动过程 Android手机系统本质上是一个基于Linux的应用程序,它以Linux系统为内核。 系统的启动过程包括两个阶段: ① Linux内核的启动; ②Android框架的启动 一、Linux内核启动包括: 1、加载 引导程序bootloader Linux内核启动时首先装载执行bootloader引导程序,装载完成后进入内核程序。 2、加载 Linux内核 Linux内核加载主要包括初始化kernel核心(内存初始化,打开中断,初始化进程表等)、初始化驱动、启动内核后台(daemons)线程、安装根(root)文件系统等。 Linux加载的最后阶段启动执行第一个用户级进程init(内核引导参数上一般都会设置"init=/init",由kernel自动执行,PID为1,是所有进程的父进程)。 ※ PID(rocess Identifier,进程控制符) PID就是各进程的身份标识,程序一运行系统就会自动分配给进程一个独一无二的PID。进程中止后PID被系统回收,可能会被继续分配给新运行的程序。 3、Android框架的启动阶段 二、Android框架启动 Android框架启动过程可以分为以下几个主要的阶段: 1、init进程启动 2、init.rc脚本启动 3、zygote服务启动(下文有详解) 4、System Server进程启动(下文有详解) 5、Home应用启动 ※ BOOT: "启动"、"引导"之意。 ※ mediaserver是android中一个非常重要的进程,它是系统诸多重要Native Services的栖息地,包括: AudioFlinger:音频系统中的核心服务。 AudioPolicyService:音频系统中关于音频策略的重要服务。 MediaPlayerService:多媒体系统中的重要服务。 CameraService:有关摄像/照相的重要服务。 ※ "服务"是每次开机会按照服务启动模式(startup mode for services)启动的进程的集合,服务可能包括很多进程。 |
systemserver进程启动过程中最核心的一步是"启动Android系统的各种系统服务(System Services)",这些系统服务构成了整个Android框架的基础,通过Binder IPC为上层应用提供各种功能。介绍下几个重要系统服务的功能。 1)ActivityManagerService (AmS) Activity管理服务,主要功能包括: ---统一管理和调度各应用程序的Activity,维护系统中运行的所有应用Task和Activity ---内存管理:应用程序关闭时对应进程还在运行,当系统内存不足时根据策略kill掉优先级较低进程 ---进程管理:维护和管理系统中运行的所有进程,并提供了查询进程信息的API ---Provider、Service和Broadcast管理和调度 2)WindowManagerService 窗口管理服务,主要功能包括为应用程序分配窗口,并管理这些窗口。包括分配窗口的大小、调节各窗口的叠放次序、隐藏或者显示窗口,程序退出时删除窗口。 3)PackageManagerService 程序包管理服务,主要功能为: ---根据intent查找匹配的Activity、Provider以及Service ---进行权限检查,即当应用程序调用某个需要一定权限的函数时,系统能够判断调用者是否具备该权限 ---提供安装、删除应用程序的API 4)NotificationManagerService 通知管理服务,负责管理和通知后台事件的发生等,这个和statusbar服务结合在一起,一般会在statusbar上添加响应图标。用户可以通过这知道系统后台发生了什么事情。 5)AudioService 音频管理服务,AudioFlinger的上层管理封装,主要是音量、音效、声道及铃声等的管理。 6)TelephonyRegistry 电话服务管理,用于监听和上报电话状态,包括来电、通话、信号变化等。 到这里,Android Framework的启动已经完成,框架中提供的各种服务也已经就绪,可以正常运行并响应处理应用的各种操作请求。 |
Android专家Jollen提到三种服务: 1)SDK Service——是应用层(Application Layer)的Android应用程序的四大组件(Component)之一; 2)Android Service——又称为System Service, 由框架层(Framework Layer)的systemserver提供, 它包括ActivityManagerService、InputMethodService等; 3)Native(原生) Service——由运行时环境层(Runtim Layer)的mediaserver(Native Servers)提供. 它们的关系图如下: ※ HAL, Hardware Abstraction Layer, 硬件抽象层。 |
Zygote是一个守护进程(Daemon), 它在Android中主要有两个作用: Android的zygote主要涵盖两个方面的功能: ① 一个是C/C++编写的zygote进程(一个守护进程, 即daemon),主要用来为App应用和SystemService创建(fork)进程。 ※ 建立运行时环境并启动虚拟机,执行com.android.internal.os.ZygoteInit的main函数,从而创建(fork)SystemService; ② 一个是Java编写的zygote接口,负责为App应用和service调用C/C++ zygote的接口执行fork,从而创建DalvikVM进程。 下面的内容作参考: Zygote is a daemon which only mission is to launch applications. This means that Zygote is the parent of all App process. The Zygote process starts at boot time (开机时间) and initializes a Dalvik Virtual Machine. Zygote packages the function fork( ) owned by Linux system, which is used to create a new instance of virtual machine process and run Android application. So, for different operating system, we only need to modify Zygote, rewrite fork( ) function and interface class In the Dalvik virtual machine, Zygote supplies zygote interface to access to the Dalvik virtual machine. In general, each Android application is encapsulated in a separate Linux process, running inside a dedicated Dalvik VM. The process called Zygote plays a crucial role in Android. Zygote is started during system boot time, where it preloads and preinitializes core library classes. During the system runtime Zygote is responsible for spawning new DVM instances by using the Linux fork() command. With this concept Zygote provides a shared memory area for all running applications (e.g. for shared libraries), which reduces the memory footprint and startup time of new DVMs. Android 运行环境主要指的虚拟机技术——Dalvik。Android中的所有Java程序都是运行在Dalvik VM上的。 每个Android应用在底层都对应有一个独立的DalvikVM实例并在其解释下执行。 Each Dalvik virtual machine process is a Linux process, and each Dalvik virtual machine thread is a Linux thread. 汉:每个Dalvik虚拟机的进程是一个Linux进程,每个Dalvik虚拟机的线程是一个Linux线程。 ※ fork()函数原型: pid_t fork(void); //pit_t被定义为int型 在Linux中创建一个新进程的惟一方法是使用fork函数。它执行一次却返回两个值。 新进程称为子进程,而原进程称为父进程。其中父进程的返回值是子进程的进程号,而子进程则返回0。 |
※ IPC, Inter-Process Communication, 进程间通信 Binder机制(Mechanism)的目的是实现IPC(Inter-Process Communication), 具体实现方式是建立Binder Thread; 注意, 每个应用都包含Binder Thread. Android采用Binder机制,而不是其它的IPC通信方式. ※ △比较Intent, Binder和AIDL 相对于Intent这样低效的消息处理方式,Binder机制带来的是更高效的进程间通信,由此带来更高性能的功能模块整合。因此,Intent大多用在应用层的功能整合,而Binder则大多用在系统层的功能整合。而从应用层看,系统功能的访问通过API完成,绝大多数API的实现,都是通过封装Binder的代理类来实现对系统进程中系统服务的访问的。 AIDL(Android Interface Definition Language)属于IDL(接口描述语言),它可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信(IPC)的目的。 图中最里层是Android系统匿名共享内存Ashmem(Anonymous Shared Memory),其作用之一即通过Binder进程间通信机制来实现进程间的内存共享。 注意: 进程间发送消息或者Broadcast,并不是直接把intent发过去,而是把intent打包到Parcel中,通过Binder机制传递消息或者Broadcast. Binder就是一段物理内存在不同进程中都映射了的虚拟地址(虚拟地址可能不同),一个对这一段物理内存写,另一个直接对这块内存读,然后解析、处理。 |
●如何阅读Android系统源码
比较简便的方法是直接在线阅读: http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/ |
●AmS的介绍
ActivityManagerService 简称(AMS),顾名思义:Activity总管家 AmS主要功能: Activity及其它三大组件的运行状态管理、进程管理、内存管理 (主要) 查询组件当前的运行情况,如getServices等 提供了系统运行时的查询,如getMemoryInfo等 AMS的启动流程: Zygote进程启动SystemServer进程 -> SystemService进程创建一个线程来启动ActivityManagerService -> ActivityManagerService内部创建一个ActivityStack来维护系统的Activity组件堆栈。 然后,ActivityManagerServer再请求Zygote进程创建一个应用程序进程 -> 应用程序进程加载一个ActivityThread类的实例(ActivityThread所在的线程即为UI线程或主线程) -> ActivityThread实例在内部创建一个ApplicationThread实例,用来和ActivityManagerService通信。 详细代码见: http://anany.me/2015/05/11/ams/
|
●Android应用程序资源的编译和打包过程
Android 应用程序的基本开发流程: 1) 构思布局,用xml构建基本的布局和控件 2) 写java程序,实体化xml的布局、控件,并实现业务逻辑。 菜鸟提示:很多控件既可以在xml文件中设定各种属性,也可以在java文件中设定属性;通常如果需要动态的改变某些属性,比如setText(),则需要在java中实现,这也是非常常用的,比如按下按键,按键颜色变深等等。 3) 运行,调试程序。 既可以使用Android模拟器调试,也可以通过数据线,直接接手机调试。 提示:如果是用模拟器调试,调试后需要修改程序再次调试时,不需要关闭模拟器,直接再次运行就可以。 4) 美化、找图片。 菜鸟提示:Android标准图标根据手机分辨率不同,分别有36*36/48*48/72*72这三种尺寸,你会看到drawable-hdpi、drawable-ldpi、drawable-mdpi这些不同的目录用来存储不同尺寸的图标,在AndroidManifest.xml中只需要写@drawable/icon就可以,它会根据屏幕分辨率去找不同目录下的图标。(参考:http://zl4393753.iteye.com/blog/1056785) 5) 关于应用中的文字等引用资源:重点说说文字吧,应用中比如菜单名字,标题等,既可以直接写在java文件中,也可先写在res/values/strings.xml文件里。不过呢,还是强烈建议,尽量都写在strings.xml文件里,这样以后要修改文字,直接改这个文件就行,否则在java程序里找可费了大劲了,尤其是要开发多语言版本的话,所有文字全部要改成外语,当然还是在strings.xml里写方便的多。养成个好习惯,不要嫌麻烦,Android系统里专门弄这么个文件是有道理的。 6) 最后,切记切记,修改AndroidManifest.xml文件,修改版本信息,加上需要的各种权限,如发送短信,访问网络等等,否则程序发布了,很多相关功能没法用。 |
整个过程的第一步,资源打包的工作,就是aapt这个工具完成的。 1. 在最后打包的apk中,所有的xml文件已经不是原来的文本文件了,是被aapt parser后,直接保存下来的xml数据结构,这样做的一大好处就是:到手机中无需再次parser xml文件,直接读到定义好的数据结构中就可以了。 2. drawable中的png图片也被aapt给优化过了。 3. 所有的资源文件都被自动生成一个索引,并生成到R.java中。为什么这么做?我想一个是效率,另一个好处就是最大限度的在编译过程中由编译器给你找错(通过string来索引很难做到)。 |
●组件、插件、控件的区别
控件(controls):是编程中用到的, 按钮就算是一个控件, 窗口也是等等; 组件(components):是软件的一部分, 软件的组成部分; 插件(plug-ins):网页中用到的,flash插件,没有它浏览器不能播放flash. |
组件、插件和控件本质上都是功能模块,都是已经做好的,控件是与视图展示有关的,可以改里面的除了视图以外的东西,组件是底层用的,插件是采用通用接口编写的,多用于制作好的东西功能扩展。 |
控件一般是为了完成特定的展示或特定页面/窗体的技术功能,而组件一般指对一些小功能点的封装,封装后的集合(组件)一般具有较独立的功能,可以完成某一项任务 所以控件是为了页面/窗体级复用而出现的,而组件是为了项目级复用而出现的,从这个意义上来说,组件的意思更接近于模块。 |
●Android应用框架的四个核心要点
四个核心要点: Activity, Task, Intent, View 一个Android应用→海洋 每个Activity→岛屿 若干个岛屿建一个国家→这个大的Task(有的资料是Event)→包含一系列Activity的堆栈 连接岛屿(Activity)的桥梁→Intent, 意图, Activity的跳转(例如,按下一个Button按钮后,可能会跳转到其他的Activity)与传值, 主要是通过Intent类来连接多个Activity, Intent带有用来传递的数据。 (准确的说, Intent不是Android的组件,而是Activity、Service、BroadcastReceiver和ContentProvider组件之间进行数据交换的载体。) 建筑公司→View, View类是所有UI控件、容器控件的基类. ※ 详解Task Task是包含一系列Activity的堆栈, 遵循先进后出原则, 一个Task栈可以有多个Activity,同一个Activity也可以在不同的Task 栈中。 栈顶的Activity是用户当前正在进行交互的Activity。 用户不断返回的时候,栈顶的Activity会不断弹出. |
●AndroidManifest.xml和四大组件
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"//第一层次 2 package="cn.edu.siso.hello" 3 android:versionCode="1" 4 android:versionName="1.0" > 5 <uses-sdk // 第二层次,描述android sdk 的版本信息 6 android:minSdkVersion="9" 7 android:targetSdkVersion="15" /> 8 <application // 第二层次 ,声明描述应用程序的相关特征 9 android:icon="@drawable/ic_launcher" 10 android:label="@string/app_name" 11 android:theme="@style/AppTheme" > 12 <activity // 第三层次,声明应用程序中的组件,如Activity 13 android:name=".MainActivity" 14 android:label="@string/title_activity_main" > 15 <intent-filter> // 第四层次,声明此activity的filter特性 16 <action android:name="android.intent.action.MAIN" /> 17 <category android:name="android.intent.category.LAUNCHER" /> 18 </intent-filter> // 第四层次声明结束 19 </activity> // 第三层次Activity的声明结束 20 </application> // 第二层次Application的声明结束 21 </manifest> //第一层次声明结束 |
注: 1. 对于: <manifest xmlns:android="http://schemas.android.com/apk/res/android" 其中xmlns:android定义了android的命名空间,一般是http://schemas.android.com/apk/res/android 2. 第2行代码package="cn.edu.siso.hello"指定本应用程序中java主程序包的包名,它也是一个应用进程的默认名称。 3. 对于属性(attribute): android:layout_width="fill_parent" 可见,属性包括两个部分:android和layout_width——android是命名空间,layout_width是属性名,"fill_parent"是属性的值。 |
有时xmlns:android=http://schemas.android.com/apk/res/android下面一行是: xmlns:tools=http://schemas.android.com/tools 这是xml的命名空间是新版ADT加入的, 例如tools:ignore="HardcodedText",这个属性是让ADT忽略硬编码文字的检查 |
●Android四大组件
组件的使用步骤: 先声明, 再调用 |
① 活动(Activity): 用于前台表现功能, 通常是一个单独的屏幕; Activity只是一个容器, 包括单选项、多选项、编辑框等视图(View). 大部份的应用都会包含多个的屏幕,。例如,一个短消息应用程序将会有一个屏幕用于显示联系人列表,第二个屏幕用于写短消息,同时还会有用于浏览旧短消息及进行系统设置的屏幕。每一个这样的屏幕就是一个activity。当一个新的屏幕打开后,前一个屏幕将会暂停,并保存在历史堆栈中。用户可以返回到历史堆栈中的前一个屏幕。当屏幕不再使用时,还可以从历史堆栈中删除。默认情况下,Android将会保留从主屏幕到每一个应用的运行屏幕。 Android使用了Intent这个特殊类,实现在屏幕与屏幕之间移动。 Intent类用于描述一个应用将会做什么事。在Intent的描述结构中,有两个最重要的部分:动作和动作对应的数据。典型的动作类型有:MAIN(activity的门户)、VIEW、PICK、EDIT等。而动作对应的数据则以URI(统一资源标识符, Uniform Resource Identifier)的形式进行表示。 ② 服务(Service):用于后台运行服务。例如, 通过Service我们可以一边听音乐, 一边看电子书. ③ 广播接收器(BroadcastReceiver):用于接收广播, 没有用户界面, 但是可以启动Activity或Service来响应它们收到的消息, 或者用NotificationManager来通知用户(震动, 播放声音等方式). 常见的与广播有关的Intent有开机启动、电池电量变化、时间改变、短信/电话到达通知. ④ 内容提供者(ContentProvider):通过它将应用的数据与其它的应用共享. 应用程序能够将它们的数据保存到文件中、SQL数据库中,甚至是任何有效的设备中。 |
Android应用程序会在一个清单(AndroidManifest.xml)文件中声明它的组件,这个文件会被打包到Android包中。这个.apk文件还将包括应用程序的代码、文件以及其它资源。 |
●Android应用程序的入口点
Android应用程序框架层 创建的 应用程序进程的 入口点是ActivityThread.main()函数,即进程创建完成之后,Android应用程序框架层就会在这个进程中将ActivityThread类加载进来,然后执行它的main函数,这个main函数就是进程执行消息循环的地方了。 具体来说(仔细揣摩): 刚接触Android的开发者可能会因为找不到Java程序的执行入口main()方法而觉得疑惑,其实Android中当然是也有main()方法的,它被包装在源码中的ActivityThread类里。 ActivityThread类是应用程序的主线程类(创建子线程用Thread类或Runnable接口),所有的apk程序都有且仅有一个ActivityThread类,程序的入口为该类中的static main()方法,ActivityThread类的实例即为UI线程或主线程。 每启动一个应用进程,都会创建ActivityThread与之对应的实例,它是应用程序的UI线程,Android进程启动时会建立消息循环。 Activity从main()方法开始执行,调用prepareMain()为UI线程创建一个消息队列(MessageQueue)。 然后创建一个ActivityThread对象,在ActivityThread的初始化代码中会创建一个H(Handler)对象和一个ApplicationThread(Binder)对象。 其中Binder负责接收远程AmS的IPC调用,接收到调用后,则通过Hander把消息发送到消息队列,UI主线程会异步地从消息队列中取出消息并执行相应操作,比如start,pause,stop等。 接着UI主线程调用Looper.loop()方法进入消息循环体,进入后就会不断地从消息队列中读取并处理消息。 ActivityThread: This manages the execution of the main thread in an application process, scheduling and executing activities, broadcasts, and other operations on it as the activity manager requests. 一个apk程序的进程的示意图: ※ 每个应用程序都以ActivityThread.main()为入口进入到消息循环处理。对于一个进程来讲,我们需要这个闭合的处理框架。
这个意思是: 一个Activity的生命周期的第一阶段, 或者说Android中类似Java的Main函数是什么. |
●Activity
Activity栈可以由一个或多个Task组成。 当用户在Application中,从一个Activity跳到另一个时,Android系统会 保存一个用户访问Activity的线性导航历史。 这就是activity栈,也被称为返回栈。 一般来说,当用户运行一个新的Activity,这个Activity就会被加到Activity栈里。 Task是一个具有栈结构的容器,可以放置多个Activity实例。 启动一个应用,系统就会为之创建一个Task,来放置根Activity;默认情况下,一个Activity启动另一个Activity时,两个Activity是放置在同一个Task中的,后者被压入前者所在的Task栈,当用户按下后退键,后者从task被弹出,前者又显示在幕前,特别是启动其他应用中的Activity时,两个Activity对用户来说就好像是属于同一个应用;系统task和task之间是互相独立的,当我们运行一个应用时,按下Home键回到主屏,启动另一个应用,这个过程中,之前的task被转移到后台,新的task被转移到前台,其根Activity也会显示到幕前,过了一会之后,在此按下Home键回到主屏,再选择之前的应用,之前的task会被转移到前台,系统仍然保留着task内的所有Activity实例,而那个新的task会被转移到后台,如果这时用户再做后退等动作,就是针对该task内部进行操作了。 |
●Activity生命周期
An activity has essentially four states: If an activity in the foreground of the screen (at the top of the stack), it is active or running. If an activity has lost focus but is still visible (that is, a new non-full-sized or transparent activity has focus on top of your activity), it is paused. A paused activity is completely alive (it maintains all state and member information and remains attached to the window manager), but can be killed by the system in extreme low memory situations. If an activity is completely obscured by another activity, it is stopped. It still retains all state and member information, however, it is no longer visible to the user so its window is hidden and it will often be killed by the system when memory is needed elsewhere. If an activity is paused or stopped, the system can drop the activity from memory by either asking it to finish, or simply killing its process. When it is displayed again to the user, it must be completely restarted and restored to its previous state. |
●Android Activity life circle brief
|
|
●横竖屏切换时的生命周期
当手机横竖屏切换时,Activity会销毁重建(模拟器中横竖屏切换可以使用ctrl+F11)。 如果不希望在横竖屏切换时Activity被销毁重建,可以在AndroidManifest.xml文件中设置Activity的android:configChanges的属性,具体代码如下所示:
如果希望某一个界面一直处于竖屏或者横屏状态,可以在清单文件中通过设置Activity的属性来完成,具体代码如下所示:
|