? 对Android底层有一定的认识,研究过相关的Android源码
我将从以下几方面简单说明: {{{{{{----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一、系统架构:
一)、系统分层:(由下向上)【如图】
1、安卓系统分为四层,分别是Linux内核层、Libraries层、FrameWork层,以及Applications层;
其中Linux内核层包含了Linux内核和各种驱动;
Libraries层包含各种类库(动态库(也叫共享库)、android运行时库、Dalvik虚拟机),编程语言主要为C或C++ FrameWork层大部分使用java语言编写,是android平台上Java世界的基石
Applications层是应用层,我们在这一层进行开发,使用java语音编写
2、Dalvik VM和传统JVM的区别:
传统的JVM:编写.java文件 ? 编译为.class文件 ? 打包成.jar文件
Dalvik VM: 编写.java文件 ? 编译为.class文件 ? 打包成.dex文件 ? 打包成.apk文件(通过dx工具) 将所有的类整合到一个文件中,提高了效率。更适合在手机上运行
1、Linux内核层[LINUX KERNEL]:
包含Linux内核和驱动模块(比如USB、Camera、蓝牙等)。
Android2.2(代号Froyo)基于Linux内核2.6版本。
2、Libraries层[LIBRARIES]:
这一层提供动态库(也叫共享库)、android运行时库、Dalvik虚拟机等。
编程语言主要为C或C++,所以可以简单的看成Native层。
3、FrameWork层[APPLICATION FRAMEWORK]:
这一层大部分用java语言编写,它是android平台上Java世界的基石。
4、Applications层[APPLICATION]:应用层
如图所示:
系统分层的图整体简化为下面的一张图,对应如下:
FrameWork层 --------? Java世界
Libraries层 --------? Native世界
Linux内核层 --------? Linux OS
Java世界和Native世界间的通信是通过JNI层 JNI层和Native世界都可以直接调用系统底层
}}}}}}----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二)、系统编译:
1、主要步骤:系统环境的准备,下载源码、编译源码、输出结果:
目前系统的编译环境只支持Ubuntu 以及 Mac OS 两种操作系统,磁盘的控件要足够大
在下载源码的时候,由于Android源码使用Git进行管理,需要下载一些工具,如apt-get install git-core curl
源码下载好后,进行编译:首先搭建环境,部署JDK(不同的源码编译时需要的JDK版本不同,如2.2需要JDK5,
2.3需要1.6),
脚本;选择编译目标(可以根据自己需要的版本进行不同的搭配)
最后通过make –j4的命令进行编译。(make是编译的函数即命令,j4指的是cpu处理器的核数:单核的是j4 x i;双核的是j8)
最后将编译好的结果进行输出:所有的编译产物都位于 /out 目录下
2、编译流程图
二、系统的启动: {{{{{{----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
通过Linux内核将Linux系统中用户空间的第一个进程init启动起来,这是安卓世界第一个被启动的进程;
然后在init中会加载init.rc的配置文件,并开启系统的守护进程(守护media(多媒体的装载)和孵化器zygote(Java世界的开启)),其实此时调试桥的守护进程也被开启起来了;
然后会处理一些动作执行,在app_main.cpp中会将Zygote孵化器(Zygote是整个java世界的基础,整个安卓世界中(包括framework和app等apk)都是由孵化器启动的)启动起来:
在app_main中,会调用AppRuntime的start方法开启AppRuntime,其实开启的是其父类AndroidRuntime的start方法被调用,zygote由此就被调用了,此时Native层的右上角有一块区域即ANDROID RUNTIME就启动起来了;
与此同时,AppRuntime会调用ZygoteInit的main方法启动ZygoteInit(整个的APPLICATION 和FRAMEWORK都会由ZygoteInit带起来的,JNI也被启动起来):
在ZygoteInit中会调用SystemServer这个类,在SystemServer的main方法中启动init1()方法,将system_init.cpp开启起来,在init1()方法中,将整个Native世界(即LIBRARIES层)开启起来了
然后在system_init.cpp会调用SystemServer的init2()方法开启ServerThread,通过ServerThread将framework层开启起来(所有的就全部开启起来了),即java世界(APPLICATION FRAMEWORK)就被启动了;此时ActivityManager,WindowManager,PackageManager(最主要,所有的清单文件及apk都有它管理)等等framework层全部开启起来
一)安卓系统的总体启动顺序:
1、通过LINUX内核,将init进程启动起来(是Linux系统中用户空间的第一个进程)
2、将ANDROID RUNTIME这一块的内容启动完毕
3、分为两步分别启动LIBRARIES(即Native世界)和APPLICATION FRAMEWORK(即java世界)
1)先启动LIBRARIES(即Native世界)
2)后启动APPLICATION FRAMEWORK(即java世界)【ActivityManager,WindowManager,电源管理等等】
二)具体启动流程
一)、启动流程:
1、init进程:——安卓世界第一个被启动的进程
加载一堆配置文件,核心加载的init.rc配置文件,其中包含了孵化器和守护进程都被开启了
1)、启动服务:开启ServerManager
守护进程启动(Daemon Process):/system/bin/servicemanager
守护的是:
①、Java世界的开启:onrestart restart zygote
②、多媒体的装载:onrestart restart media
@、adbd的守护也被开启起来了,即调试桥的守护进程也被开启起来了
2)、启动孵化器Zygote
在app_main中启动孵化器Zygote,整个安卓世界中(包括framework和app等apk)都是由孵化器启动的
【此时虚拟机还没开启起来,只是配置了一些vm的参数】
3、app_main:——开启孵化器
app_main中,调用AppRuntime的start方法,将Native层的右上角有一块区域,即ANDROID RUNTIME启动起来
其中的start方法实际是其父类AndroidRuntime的start方法
【此时VM虚拟机被开启起来了,通过start方法开启,在AndroidRuntime中并设置了默认的内存大小16M】
【注册JNI,并启动孵化器Zygote】
4、ZygoteInit开启
AppRuntime被启动后,会调用ZygoteInit的main方法,启动ZygoteInit;
然后,整个的APPLICATION 和FRAMEWORK都会由ZygoteInit带起来的
5、SystemServer启动:
ZygoteInit调用SystemServer这个类,在SystemServer的main方法中启动init1()方法,将system_init.cpp开启起来
在init1()方法中,将整个Native世界开启起来了
6、ServerThread启动(开启framework层)
调用SystemServer的init2()方法开启ServerThread,通过ServerThread将framework层开启起来(所有的就全部开启起来了)
此时ActivityManager,WindowManager,PackageManager(最主要,所有的清单文件及apk都有它管理)等等framework层全部开启起来
二)、具体介绍:
1、启动入口:init进程
@、源码位置:/system/core/init/init.c
@、进程入口:main方法
1)创建文件夹,挂载设备【通过mkdir的命令创建,挂载一些系统设备后】
2)重定向输入输出,如错误信息输出【设置了一些输入输出的处理】
3)设置日志输出【一些系统的日志】
4)init.rc系统启动的配置文件【加载了相关的信息,不同版本的手机所特有的配置信息】
①、文件位置:/system/core/rootdir
②、守护进程启动(Daemon Process):/system/bin/servicemanager
守护的是
Java世界的开启:onrestart restart zygote
多媒体的装载:onrestart restart media
adbd守护也被开启起来了,即调试桥(adb[Android Debug Bridge])的守护进程(adbd[Android Debug Bridge Daemon])也被开启起来了
③、启动Zygote——app_main.cpp【Zygote是整个java世界的基础】
当编译之后,在system/bin/app_process下会有孵化器的启动Xzygote 守护进程被开启之后,紧接着Zygote也被启动起来了
5)解析和当前设备相关的配置信息(/init.%s.rc)
Tips:
当解析完init.rc和设备配置信息后会获取到一系列Action
Init将动作的执行划分为四个阶段(优先级由大到小):
early-init :初期
Init :初始化阶段
early-boot :系统启动的初期
boot :系统启动
6)处理动作执行:这个阶段Zygote将被启动
7)无限循环阶段,等待一些事情发生
2、Zygote简介:
@、Zygote启动:app_main.cpp
1)Zygote简介:
①、本身为Native的应用程序
②、由init进程通过init.rc加载
2)功能分析:
①、Main方法中AppRuntime.start(),工作由父类AndroidRuntime来完成
②、在AndroidRuntime中开启了如下内容:
@startVM——开启虚拟机(查看堆内存设置):默认16M【】
@注册JNI函数【此时还在Native层,需要将连接java和c的桥(即JNI)搭建好】 @启动―com.android.internal.os.ZygoteInit‖的main方法
【系统级别的包(由runtime的start方法开启的这个包)】
start方法实际是其父类AndroidRuntime的
@进入java世界的入口
3、ServiceThread的简介:(java世界所做的事情)
1)preloadClasses();:预加载class
读取一个preloaded-classes的配置文件
此文件的内容非常多,这就是安卓系统启动慢的原因之一
此时会有一个垃圾回收的操作gc(),将无用的回收掉
2)ZygoteInit在main方法中利用JNI开启com.android.server.SystemServer
3)启动system_init.cpp处理Native层的服务
4)然后调用SystemServer的init2()
5)启动ServiceThread,启动android服务
6)Launcher启动
三、开机时的时间消耗:
1、ZygoteInit.main()中会预加载类
目录:framework/base/preload-class
ZygoteInit.main()会加载很多的类,将近1800多个(安卓2.3的)
2、开机时会对系统所有的apk进行扫描
需要将所有的应用展现给用户,就需要对apk进行扫描,扫描所有的包 data目录下有个apk的包
system目录下有个apk的包
framework目录下也有相关的包
3、SystemServer创建的那些Service
系统启动 类图===============
系统启动时序图===============
四、安卓工程的启动过程
1、Eclipse将.java源文件编译成.class
2、使用dx工具将所有.class文件转换为.dex文件
3、再将.dex文件和所有资源打包成.apk文件
4、将.apk文件安装到虚拟机完成程序安装
5、启动程序 – 开启进程 – 开启主线程
6、创建Activity对象 – 执行OnCreate()方法
7、按照main.xml文件初始化界面
=================================
应用程序启动:
一、解析清单文件并加载
应用程序的启动需要从PackageManagerService说起,由于应用程序是有PackageManager管理的,可以简单认为PackageManagerService是为应用程序启动的做了一些准备工作,才能将应用程序开启起来。
1、PackageManagerService(资料)读取所有应用程序的Mainfest信息,并且建立信息库存储在系统级共享内存中
1)解析:
PackageManagerService在启动后,会进行解析的工作,它会重点监控一些文件:system/framework、system/app、data/app、data/app_private;一旦将数据存入到这些文件中,就会去解析
2)权限分配:
PackageManagerService会建立底层userids和groupids同上层permissions之间的映射,就会给一些底层用户分配权限,
进行权限的映射,UID和GroupID,都会分配相应的权限
3)保存数据:
PackageManagerService还有重要的一个操作就是将解析的每个apk的信息保存到packages.xml和packages.list文件里,
在packages.list记录了如下数据:pkgName,userId,debugFlag,dataPath(包的数据路径)
【下次再开机的时候,不会再扫描每个apk了,只需要读取packages.xml和packages.list文件即可】
除了这两个主要的工作外,还会进行一些其他的操作,如检测文件等
2、Launcher就将PackageManagerService已经解析并处理好的数据都加载到内存中,从内存中就能获取到相应的数据,
并展示到手机上【之所以可以展示在手机桌面上,就是因为在清单文件中配置了如下的内容:】
<action android:name="android.intent.action.MAIN" />:应用程序的入口
<category android:name="android.intent.category.LAUNCHER" />:配置了这个属性就可以显示在列表中 点击图标,应用就被开启起来了:
二、Activity的启动与生命周期的监控
应用程序被开启后,是需要开启并创建Activity,加载相应的view,从而展示出应用程序
1、Activity是通过startActivity开启起来的,startActivity是由有Context调用的,其具体的实现类是ContextImpl 在ContextImpl中的startActivity方法中,会调用ActivityThread的相关方法
【mMainThread.getInstrumentation().execStartActivity()】;可以追溯到Instrumentation这个类,其中的execStartActivity()的方法中实现了startActivity的调用:ActivityManagerNative.getDefault().startActivity,由此可以看出是底层进行处理。
2、ActivityMonitor监控Activity
当Activity实例创建的时候,就会给Activity配置一个监视器ActivityMonitor,监控Activity的声明周期: 在Instrumentation的execStartActivity()的方法中,上来先判断ActivityMonitor是否为null:在第一次开启Activity的时候,ActivityMonitor还是null的,就会调用ActivityManagerNative.getDefault().startActivity(…..),是在操作native底层的信息,从而执行startActivity,再去开启一个Activity。
简单来说,就是通过调用JNI,调用startActivity方法,开启Activity;创建好了之后,随即也创建好了Activity的监视器ActivityMonitor
3、在ActivityMonitor中就有Activity各种生命周期的监控
①、在newActivity方法中:
可以通过拿到Activity的字节码,创建一个Activity,并将这个Activity返回
还会调用attach方法,传入ActivityThread的线程
②、在各种生命周期的方法中,调用activity的各自的生命周期的方法
总结:
1、通过PackageManagerService将所有用到的资源加载进内存中
2、在Launcher中,将view等控件加载到ViewGroup中,点击每个item会有相应的操作
3、在公开的文档中是找不到具体调用startActivity的类的,而是由系统完成调用的,实现了Activity的启动
实际就是通过Context的实现类ContextImpl进行调用的,一步步转到底层(ActivityManagerNative)实现调用
4、另一个重要的类就是ActivityMonitor,监控Activity生命周期的;在其newActivity方法中创建了Activity,并调用了attach方法;
也就是说当一个Activity被创建的时候,就会绑定一个ActivityMonitor,用来监控Activity的生命周期
三、应用程序启动的时序图:
应用程序启动时序图===============
? 对Activity、Window和View三者间的关系有一定的见解
一、简述如何将Activity展现在手机上
Tips:
Activity本身是没办法处理显示什么控件(view)的,是通过PhoneWindow进行显示的
换句话说:activity就是在造PhoneWindow,显示的那些view都交给了PhoneWindow处理显示
1、在Activity创建时调用attach方法:
2、attach方法中会调用PolicyManager.makeNewWindow()
实际工作的是IPolicy接口的makeNewWindow方法
①、其中创建了一个window(可以比喻为一个房子上造了一个窗户):mWindow = PolicyManager.makeNewWindow(this);
②、在window这个类中,才调用了setContentView(),这是最终的调用
在Activity的setContentView方法中,实际上是调用:getWindow().setContentView(view, params);
这里的getWindow()就是获取到一个Window对象
Tips:
为啥attch优先于onCreate调用,就是由于在attch方法中,会创建window,有了window才能调用setContentView
3、在IPolicy的实现类中创建了PhoneWindow:
①、由mWindow = PolicyManager.makeNewWindow(this);,
②、这里的makeNewWindow(this);方法中,返回的是:return sPolicy.makeNewWindow(context);
③、这个sPolicy实际是一个接口,其实现类是Policy,其中只是创建了一个PhoneWindow
4、在PhoneWindow的setContentView中向ViewGroup(root)中添加了需要显示的内容
①、PhoneWindow是继承Window的
②、setContentView这个方法中,需要先判断一个mContentParent是否为空,因为在默认进来的时候,什么都没创建呢
此时需要创建:installDecor(),DecorView是最根上的显示的
可以通过adt中的的tools中有个hierarchyviewer.bat的工具,可以查看手机的结构
③、DecorView:是继承与FrameLayout的,作为parent存在,最初显示的
④、下次再加载的时候,mContentParent就不为空了,会将其中的所有的view移除掉,然后在通过布局填充器加载布局
二、三者关系:
1、在Activity中调用attach,创建了一个Window
2、创建的window是其子类PhoneWindow,在attach中创建PhoneWindow
3、在Activity中调用setContentView(R.layout.xxx)
4、其中实际上是调用的getWindow().setContentView()
5、调用PhoneWindow中的setContentView方法
6、创建ParentView:
作为ViewGroup的子类,实际是创建的DecorView(作为FramLayout的子类)
7、将指定的R.layout.xxx进行填充
通过布局填充器进行填充【其中的parent指的就是DecorView】
8、调用到ViewGroup
9、调用ViewGroup的removeAllView(),先将所有的view移除掉
10、添加新的view:addView()
Tips:
①、Activity就是在造“窗户”,即创建PhoneWindow
②、PhoneWindow才是进行显示view的操作,主要就是setContentView()
Activity 启动时序图=====================
? 对OAuth2认证有一定的了解
转到分享界面后,进行OAuth2认证:
以新浪为例:
第一步、WebView加载界面,传递参数
使用WebView加载登陆网页,通过Get方法传递三个参数:应用的appkey、回调地址和展示方式display(如手机设备为mobile);
如:https://.cn/oauth2/authorize?client_id=1750636396&redirect_uri=/&display=mobile 第二步、回调地址获取code
当点击登陆(或授权)的时候,会将自定义的回调地址发送到相应的服务器端,这个回调地址只是为了从相应的服务器端(如新浪)获取到一个code;可以在WebViewClient的shouldOverrideUrlLoading方法中捕获到,然后获取到这个跳转的URL后,截取其中的code,如:/?code=3ea97ac6d5c1016a70d1c16e98b6f9ca
第三步、获取token
通过这个code到相应的服务器获取到token【当然不仅仅是获取到token这个认证令牌,还有令牌有效期、uid,如果有权限的话,有的还会返回刷新令牌的token】,这些数据需要加密后保存在本地。
然后下次再登陆的时候,就可以直接登陆,然后通过发送给服务器端token等数据,获取到相应的数据
××年,我在分局党组及大队班子的正确领导下,与全队上下齐心协力,勇敢拼搏,迎难而上,无论在政治思想、工作上都取得了一定的成绩。一、…
一、思想上积极要求进步,抓好自身理论学习在日常工作中我力争多参与、多学习,开拓视野、拓宽思路,使自身具有一定的马列主义理论水平,并…
德育工作总结一学期来,我们坚持以人为本,把学生看成德育的主体,充分发挥学校在未成年人思想道德建设中的主渠道、主阵地和主课堂的作用,…
官池食品药品监督管理所二0一三年度工作总结20xx年,我所在县局的正确领导下,按照十八大报告提出的食品药品监管工作新要求,坚持以科…
9月1号我们都参与迎接了学弟学妹,这让我想到了我们当时报道的情形,转眼之间,一年的大学就这样结束了,我们也成了学姐和学长,不由自主…
官池食品药品监督管理所二0一三年度工作总结20xx年,我所在县局的正确领导下,按照十八大报告提出的食品药品监管工作新要求,坚持以科…
德育工作总结一学期来,我们坚持以人为本,把学生看成德育的主体,充分发挥学校在未成年人思想道德建设中的主渠道、主阵地和主课堂的作用,…
一、思想上积极要求进步,抓好自身理论学习在日常工作中我力争多参与、多学习,开拓视野、拓宽思路,使自身具有一定的马列主义理论水平,并…
××年,我在分局党组及大队班子的正确领导下,与全队上下齐心协力,勇敢拼搏,迎难而上,无论在政治思想、工作上都取得了一定的成绩。一、…