文章目录
- 1. App的启动流程
- 2. 五种布局方式
- 3. Activity的生命周期
- 4. Activity启动模式
- 5. Activity的缓存机制
- 6. Fragment的生命周期
- 7. 为什么用Service而不用Thread呢
- 8. Service的基础知识
- 9. Android中的动画类型
- 10. 事件分发机制
- 11. SurfaceView
- 12. 设计模式
- 13. LRUCache
- 14. MVP,MVC
- 15. IPC
1. App的启动流程
基础:
1. Zygote:翻译为“受精卵”。因为Android系统是基于Linux系统的,而Linux都是通过init来fork出来的。但是在Android中,当开机的时候init会先创建一个Zygote进程,它的作用和init是相似的。由于他的快速分裂的特性,所以系统启动之后所有的App进程都是基于Zygote的来fork出来的。所以可以说所有的App进程都是源于Zygote进程。
2. system_server: 他也是一个进程,所以也是Zygote来创建出来的。他主要负责创建系统里面重要的服务,其中包括:ActivityManagerService,PackageManagerService等。
3. ActivityManagerService: 当SystemService进程开启的时候就会自动调用ActivityManagerService,其作用就是负责系统中所有的Activity的生命周期。
4. Launcher:Launcher本质上也是一个应用程序,也继承自Activity
启动流程:
1. Launcher采用BinderIPC向system_service发起startActivity请求
2. system_service接受到请求之后向Zygote发送创建进程的请求
3. Zygote进程fork出子进程,并将其放入App进程中去
4. App进程采用BinderIPC向system_service发送一个attachApplication的请求
5. system_service收到请求后通过BinderIPC向APP进程发送scheduleLaunchActivity请求
6. App接受到请求之后,使用handler想住线程发送LAUNCH_ACTIVITY消息
7. 住线程收到消息之后创建目标Activity,并毁掉Activity.onCreate方法
2. 五种布局方式
- FrameLayout:最简单的布局方式,Android中这种方式会默认将所有的空间都放在视图的左上角,可以使用margin,gravity去控制位置
- LinearLayout:分为水平布局和垂直布局两种。
- AbsoluteLayout:通过空间的xy坐标来确定空间的位置
- RelativeLayout:以另一个控件作为参考系来确定位置
- TableLayout:将自元素的位置分配到行或者列汇总,一个TableLayout由许多的TableRow组成
3. Activity的生命周期
- 启动:onCreate() ->onStart() ->onResume()
- Activity移到后台:这种情况常见于:转到新的activity中或者按home键返回桌面
onPause() -> onStop()此时进入停滞状态 - Activity返回前台:onRestart() -> onStart() -> onResume()
- Activity移到后台,此时内存空间不足了,会被杀死,重新打开:onCreate() -> onStart() -> onResume()
- 锁屏:onPause() -> onStop()
- 解锁:onRestart() ->onStart() -> onResume()
- 切换横屏:onPause -> onStop() -> onDestroy -> onCreate() -> onStart() -> onResume()
- 配置了configChanges="orientation|screenSize"之后切换横屏:onConfigurationChanged()
4. Activity启动模式
- standard:标准模式,每次启动新的Activity都换创建一个新的Activity实例,并且将其押入任务栈中
- singleTop:栈顶复用模式,若新启动的Activity已经在栈顶了,那么直接回调onNewIntent()方法就可以了
- singleTask:栈内复用模式,在创建新的Activity之前系统会先在栈中找一下是否已经有了这个Activity的实例,若有的话直接调用onNewIntent()方法,并且把当前Activity上面所有的Activity全都清理掉
- singleInstance:这种模式的Activity只能单独存在于一个新的任务栈之中。
5. Activity的缓存机制
- onSaveInstanceState(Bundle outState):必须在onStop()前执行,但不能保证是在onPause()前还是后执行。用于保存当前Activity的相关的状态。onSaveInstanceState()遵循一个重要原则就是:当系统未经过用户的允许就销毁了Activity的时候就需要调用此方法。而正是因为他的不确定性,所以他一般只用于保存activity 的瞬态而不是进行存储持久化数据。
- onRestoreInstanceState(Bundle outState): 这两个方法并不一定是成对出现的!!!比如目前正在执行activityA, 然后按home键回到桌面,然后迅速返回,此时的activity并没有被杀,所以自然也不用调用onRestoreInstanceState方法。此方法在onStart()方法之后执行,他的参数也会传递到onCreate方法中去。
6. Fragment的生命周期
7. 为什么用Service而不用Thread呢
- Thread是线程,是程序执行的最小单元,是CPU分配任务的基本单位,Thread常用来执行一些异步的操作。
- Service:当Service是LocalService的时候,他是运行在主线程上的;当Service是RemoteService的时候,他是运行在另一个进程的主线程上的。
- 理由1:Thread的运行是和Activity独立的,也就是说即使Activity被finish掉了但是没有主动停止Thread那么Thread会继续去执行,那么此时Activity不在拥有对Thread的引用,也没有办法在不同的Activity中对同一个Thread进行控制。
8. Service的基础知识
- 启动方式1-startService():一个Service被某个Activity调用了Context.startService方法启动,即使多次调用startService也只会执行一次onCreate方法,而onStart将会被多次调用。该Service会一直在后台运行,不管调用他的Activity还活着没有。直到调用stopService才会结束服务。
- 启动方式2-bindService():一个Service被某个Activity嗲用了Context.bindService方法启动,即使多次调用bindService也只会执行一次onCreate方法,同时onStart方法不会被调用。连接建立之后除非断开连接,Service会一直在后台运行,
- 两种启动方式的区别:startService启动在调用者退出之后Service仍然会存在;bindService启动在调用者退出之后也会随即推出。
- 先start一个Service再bind:此时会调用onBind()方法,如果向destroy该Service应当调用onUnBind()方法。
- 通过bindService可以在Activity中实现一个ServiceConnection接口,并将接口传递个bindService()方法,在ServiceConnection接口的onServiceConnection()方法中执行相关操作。
- Service中不能进行耗时工作:不能!UI线程怎么能执行耗时的操作呢。要执行一些耗时操作可以新建一个线程或者使用IntentService。
- onStartCommand返回的类型值:
- START_NOT_STICKY:被系统杀掉之后不会被重建
- START_STICKY:在被系统杀掉之后其状态依然是started,但是获取不到之前传入的intent,之后进行重建Service
- START_REDELIVER_INTENT :在重建Service的时候会传入之前的intent
9. Android中的动画类型
- tween补间动画:指定View的初末状态和变化的时间、方式,来对图形变化实现动画效果
- frame动画:帧动画
- PropertyAnimation:属性动画
10. 事件分发机制
Android的事件分发机制较为复杂,参考资料:
事件分发其本质就是把点击事件传递给某个具体的View以及其处理的过程。
首先事件的分发分为三个部分,分别是Activity, ViewGroup, View这三个部分,其中有三个比较重要的方法,分别是:onTouchEvent(负责点击事件的处理), onInterceptTouchEvent(负责判断是否拦截,只有ViewGroup才有这个方法)和dispatchTouchEvent(负责事件的分发)。
Activity:
public boolean dispatchTouchEvent(MotionEvent ev) {
// 一般事件列开始都是DOWN事件 = 按下事件,故此处基本是true
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
// ->>分析1
}
// ->>分析2
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
// 若getWindow().superDispatchTouchEvent(ev)的返回true
// 则Activity.dispatchTouchEvent()就返回true,则方法结束。即 :该点击事件停止往下传递 & 事件传递过程结束
// 否则:继续往下调用Activity.onTouchEvent
}
// ->>分析4
return onTouchEvent(ev);
}
- 在事件发生后首先调用了Activity.dispatchTouchEvent方法
- 在其中先对Action进行判断是否为按下的动作,如果是的话则进入onUserInteration方法,该方法的作用是实现屏保的功能。
- 进入第二个if判断,进入getWindow.superDispatchTouchEvent方法。
- 该方法会接着调用mDecor.superDispatchTouchEvent方法,mDecor是顶层View的实例
- 4中的方法会接着调用super.dispatchTouchEvent方法进而去调用ViewGroup的dispatchTouch方法
ViewGroup:
public boolean dispatchTouchEvent(MotionEvent ev) {
... // 仅贴出关键代码
// 重点分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
// 判断值1:disallowIntercept = 是否禁用事件拦截的功能(默认是false),可通过调用requestDisallowInterceptTouchEvent()修改
// 判断值2: !onInterceptTouchEvent(ev) = 对onInterceptTouchEvent()返回值取反
// a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),就会让第二个值为true,从而进入到条件判断的内部
// b. 若在onInterceptTouchEvent()中返回true(即拦截事件),就会让第二个值为false,从而跳出了这个条件判断
// c. 关于onInterceptTouchEvent() ->>分析1
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
// 重点分析2
// 通过for循环,遍历了当前ViewGroup下的所有子View
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
// 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的View
// 若是,则进入条件判断内部
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
// 条件判断的内部调用了该View的dispatchTouchEvent()
// 即 实现了点击事件从ViewGroup到子View的传递(具体请看下面的View事件分发机制)
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
return true;
// 调用子View的dispatchTouchEvent后是有返回值的
// 若该控件可点击,那么点击时,dispatchTouchEvent的返回值必定是true,因此会导致条件判断成立
// 于是给ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出
// 即把ViewGroup的点击事件拦截掉
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
// 重点分析3
// 若点击的是空白处(即无任何View接收事件) / 拦截事件(手动复写onInterceptTouchEvent(),从而让其返回true)
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
// 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
// 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),即自己处理该事件,事件不会往下传递(具体请参考View事件的分发机制中的View.dispatchTouchEvent())
// 此处需与上面区别:子View的dispatchTouchEvent()
}
...
}
/**
* 分析1:ViewGroup.onInterceptTouchEvent()
* 作用:是否拦截事件
* 说明:
* a. 返回true = 拦截,即事件停止往下传递(需手动设置,即复写onInterceptTouchEvent(),从而让其返回true)
* b. 返回false = 不拦截(默认)
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
// 回到调用原处
- ViewGroup会先去调用onInterceptTouchEvent方法区判断是否对事件进行拦截,默认是false
- 若不拦截则会用for循环遍历这个ViewGroup去寻找被点击的子控件直到找到被点击的子控件
- 调用子控件的dispatchTouchEvent方法
View:
**
* 源码分析:View.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
// 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent()
// 1. mOnTouchListener != null
// 2. (mViewFlags & ENABLED_MASK) == ENABLED
// 3. mOnTouchListener.onTouch(this, event)
// 下面对这3个条件逐个分析
/**
* 条件1:mOnTouchListener != null
* 说明:mOnTouchListener变量在View.setOnTouchListener()方法里赋值
*/
public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener = l;
// 即只要我们给控件注册了Touch事件,mOnTouchListener就一定被赋值(不为空)
}
/**
* 条件2:(mViewFlags & ENABLED_MASK) == ENABLED
* 说明:
* a. 该条件是判断当前点击的控件是否enable
* b. 由于很多View默认enable,故该条件恒定为true
*/
/**
* 条件3:mOnTouchListener.onTouch(this, event)
* 说明:即 回调控件注册Touch事件时的onTouch();需手动复写设置,具体如下(以按钮Button为例)
*/
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
// 若在onTouch()返回true,就会让上述三个条件全部成立,从而使得View.dispatchTouchEvent()直接返回true,事件分发结束
// 若在onTouch()返回false,就会使得上述三个条件不全部成立,从而使得View.dispatchTouchEvent()中跳出If,执行onTouchEvent(event)
- 调用View.onTouch()方法, 若返回true则不调用onClick方法
- 若onTouch()方法返回false则会先调用onTouchEvent方法,然后进入performClick方法,最后调用onClick方法
11. SurfaceView
- 与View的不同之处:
1. View适合主动刷新(基于VSYSC信号,频率不可控),SurfaceView适合被动刷新(可控制刷新的频率)
2. View在主线程中进行画面更新;SurfaceView通常使用一个子线程来进行画面更新
3. View中绘图没有使用双缓冲机制;SurfaceView底层就实现了双缓冲。SurfaceView底层有两个独立的graphic buffer用于缓冲。
双缓冲的实现:
首先在BufferA中绘制,并让屏幕显示BufferA的内容,在线一个循环中再去让屏幕显示BufferB。由此往复。
由于这个双循环很可能会导致闪屏,如果遇到一个buffer为空而且被post到屏幕上了那就会黑屏,解决方法:post之前先检查是否为空; - 为什么会出现SurfaceView:
1. 在Android中使用View开进行绘图处理,大部分情况下View都能满载绘图需求。但是Android系统通过VSYSC信号来进行屏幕重绘,刷新间隔16ms,如果在16ms内View没有完成所需执行的操作那么会给用户造成卡顿的效果。
2. 在游戏上(60FPS以上)尤其需要频繁刷新界面,使用View的话会造成卡顿,Android提供SurfaceView来解决这个问题。 - 使用:
1. 两个接口:surfaceholder.CallBack, Runnable
2. 在SurfaceHolder中需要实现surfaceview的生命周期(创建,改变,销毁)
3. 在Runnable接口中实现run方法用于在子线程中去进行draw操作
12. 设计模式
- 单例模式:
public class Singleton{
private Singleton(){}
private volatile static Singleton instance;
public Singleton getInstance(){
if(singleton == null){
sychronized(Singleton.class){
if(singleton == null){
instance = new Singleton();
}
}
}
return instance;
}
}
- 建造者模式:
1. 定义一个静态内部类Builder,内部成员变量和外部类一样
2. Builder类通过一系列方法用于成员变量的赋值,并返回当前对象本身(this)
3. Builder类型提供一个build方法用于创建对应的外部类,内部调用了外部类的一个私有构造函数,该函数的参数是内部类Builder
Person.Builder builder = new Person.Builder();
Person person = builder.age(13)
.name("张三")
.weight(53.1)
.height(171.1)
.build();
- 观察者模式:
13. LRUCache
ruCache就是为了解决加载图片慢的问题。比如在一下的场景:
当用户有第三个滑动窗口,在滑动到第二个窗口的时候那么第一个窗口的图片就没用了,就有可能被GC所回收掉,那么如果此时再返回到第一个窗口那么此时就需要重新发送请求去申请窗口1的图片资源,这样的话不仅不会使整个系统的效率变低还很可能会导致用户需要等待较长的时间。
三级缓存机制:内存缓存,本地缓存,网络缓存
LRUCache的原理就是吧最近使用的对象使用强引用(new)存储到LinkedHashMap上,并且把最近最少使用的对象在缓存值达到预设值之前从内存中移除。
14. MVP,MVC
因为在Android中Activity既包括了业务逻辑也包括了界面处理,所以在MVC中很难去界定Activity到底应该属于哪一层。
MVP:
1. View : 是指显示数据和用户交互的层,在Android中View可以是Activity,可以是Fragment,可以是android.view.View, 也可以是Dialog
2. Model: 是数据源层,比如数据库的接口或者远程服务器的api
3. Presenter:是从Model层获取数据提供给View层,Presenter也负责处理后台任务
MVC:
1. Model, View, Controler
2. 在MVC中,View是能使街区访问Model的。 而一些业务逻辑在View中实现的因此要更改View的时候要一起修改很多内容
不同:
1. 在MVP中presenter将Model和View进行了分离,主要的程序逻辑在Presenter中去实现,而Presenter和View没有直接相关联,而是通过定义好的接口去进行交互,这样即使去变更View也不会对Presenter造成影响
2. 在进行测试的时候可以去编写测试的View来检查Presenter的逻辑是否正确
15. IPC
IPC的方式:
1. 使用Bundle:在Intent中传递Bundle数据。因为Bundle实现了Parcelable接口,所以方便于咋在不同的进程之间传递数据
2. 使用文件共享:两个进程通过读写同一个文件来交换数据
3. 使用Messager:Messager把装有Bundle的Message发送到别的进程
4. AIDL
5. ContentProvider:底层通过Binder来实现的