五、重点机制原理
1、Handler机制
问题:handler机制介绍
1.MessageQueue:读取会自动删除消息,单链表维护,在插入和删除上有优势。在其next()中会无限循环,不断判断是否有消息,有就返回这条消息并移除。
2.Looper:Looper创建的时候会创建一个MessageQueue,调用loop()方法的时候消息循环开始,loop()也是一个死循环,会不断调用messageQueue的next(),当有消息就处理,否则阻塞在messageQueue的next()中。当Looper的quit()被调用的时候会调用messageQueue的quit(),此时next()会返回null,然后loop()方法也跟着退出。
3.Handler:在主线程构造一个Handler,然后在其他线程调用sendMessage(),此时主线程的MessageQueue中会插入一条message,然后被Looper使用。
4.系统的主线程在ActivityThread的main()为入口开启主线程,其中定义了内部类Activity.H定义了一系列消息类型,包含四大组件的启动停止。
Handler通过调用sendmessage方法把消息放在消息队列MessageQueue中,Looper负责把消息从消息队列中取出来,重新再交给Handler进行处理,三者形成一个循环
通过构建一个消息队列,把所有的Message进行统一的管理,当Message不用了,并不作为垃圾回收,而是放入消息队列中,供下次handler创建消息时候使用,提高了消息对象的复用,减少系统垃圾回收的次数
每一个线程,都会单独对应的一个looper,这个looper通过ThreadLocal来创建,保证每个线程只创建一个looper,looper初始化后就会调用looper.loop创建一个MessageQueue,这个方法在UI线程初始化的时候就会完成,我们不需要手动创建
问题:Android为什么只能在主进程中更新UI?主线程有Looper的死循环为什么不会卡死。
关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。
2、HandlerThread理解
3、View事件分发机制及整个流程
问题:介绍触摸事件的分发机制
(1) 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。
(2) 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。
(3) 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。
(4) 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来。
(5) OnTouchListener优先于onTouchEvent()对事件进行消费。
上面的消费即表示相应函数返回值为true。
问题:View中 setOnTouchListener的onTouch,onTouchEvent,onClick的执行顺序
追溯到View的dispatchTouchEvent源码查看,有这么一段代码
public boolean dispatchTouchEvent(MotionEvent event) {
if (!onFilterTouchEventForSecurity(event)) {
return false;
}
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
当以下三个条件任意一个不成立时,
mOnTouchListener不为null
view是enable的状态
mOnTouchListener.onTouch(this, event)返回true,
函数会执行到onTouchEvent。在这里我们可以看到,首先执行的是mOnTouchListener.onTouch的方法,然后是onTouchEvent方法
继续追溯源码,到onTouchEvent()观察,发现在处理ACTION_UP事件里有这么一段代码
if (!post(mPerformClick)) {
performClick();
}
此时可知,onClick方法也在最后得到了执行
所以三者的顺序是:
setOnTouchListener() 的onTouch
onTouchEvent()
onClick()
4、View绘制机制和加载过程
measure()方法,layout(),draw()三个方法主要存放了一些标识符,来判断每个View是否需要再重新测量,布局或者绘制,主要的绘制过程还是在onMeasure,onLayout,onDraw这个三个方法中
1.onMesarue()为整个View树计算实际的大小,即设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性: mMeasureWidth),每个View的控件的实际宽高都是由父视图和本身视图决定的。
2.onLayout()为将整个根据子视图的大小以及布局参数将View树放到合适的位置上。
3. onDraw()开始绘制图像,绘制的流程如下
首先绘制该View的背景
调用onDraw()方法绘制视图本身 (每个View都需要重载该方法,ViewGroup不需要实现该方法)
如果该View是ViewGroup,调用dispatchDraw ()方法绘制子视图
绘制滚动条
5、Binder机制
1.在Activity和Service进行通讯的时候,用到了Binder。
1.当属于同个进程我们可以继承Binder然后在Activity中对Service进行操作
2.当不属于同个进程,那么要用到AIDL让系统给我们创建一个Binder,然后在Activity中对远端的Service进行操作。
2.系统给我们生成的Binder:
1.Stub类中有:接口方法的id,有该Binder的标识,有asInterface(IBinder)(让我们在Activity中获取实现了Binder的接口,接口的实现在Service里,同进程时候返回Stub否则返回Proxy),有onTransact()这个方法是在不同进程的时候让Proxy在Activity进行远端调用实现Activity操作Service
2.Proxy类是代理,在Activity端,其中有:IBinder mRemote(这就是远端的Binder),两个接口的实现方法不过是代理最终还是要在远端的onTransact()中进行实际操作。
3.哪一端的Binder是副本,该端就可以被另一端进行操作,因为Binder本体在定义的时候可以操作本端的东西。所以可以在Activity端传入本端的Binder,让Service端对其进行操作称为Listener,可以用RemoteCallbackList这个容器来装Listener,防止Listener因为经历过序列化而产生的问题。
4.当Activity端向远端进行调用的时候,当前线程会挂起,当方法处理完毕才会唤醒。
5.如果一个AIDL就用一个Service太奢侈,所以可以使用Binder池的方式,建立一个AIDL其中的方法是返回IBinder,然后根据方法中传入的参数返回具体的AIDL。
6.IPC的方式有:Bundle(在Intent启动的时候传入,不过是一次性的),文件共享(对于SharedPreference是特例,因为其在内存中会有缓存),使用Messenger(其底层用的也是AIDL,同理要操作哪端,就在哪端定义Messenger),AIDL,ContentProvider(在本进程中继承实现一个ContentProvider,在增删改查方法中调用本进程的SQLite,在其他进程中查询),Socket
6、跨进程通信(AIDL及其它) --1
MS思考:Android面试一天一题(Day 36:AIDL)
分析篇:AIDL的使用情况和实例介绍
问题:Android 进程间的通信有哪几种方式
Bundle/Intent传递数据,Messenger(消息队列适合单线程,底层还是AIDL),AIDL,ContentProvider,Socket
问题:Bundle、文件共享、ContentProvider、AIDL的使用场景
7、异步任务机制之AsycTask
问题:AsyncTask原理及不足
问题:AsynTask为什么要设计为只能够一次任务?
最核心的还是线程安全问题,多个子线程同时运行,会产生状态不一致的问题。所以要务必保证只能够执行一次
问题:AsynTask造成的内存泄露的问题怎么解决,比如非静态内部类AsynTask会隐式地持有外部类的引用,如果其生命周期大于外部activity的生命周期,就会出现内存泄漏
注意要复写AsynTask的onCancel方法,把里面的socket,file等,该关掉的要及时关掉
在 Activity 的onDestory()方法中调用Asyntask.cancal方法
Asyntask内部使用弱引用的方式来持有Activity
问题:若Activity已经销毁,此时AsynTask执行完并且返回结果,会报异常吗?
当一个App旋转时,整个Activity会被销毁和重建。当Activity重启时,AsyncTask中对该Activity的引用是无效的,因此onPostExecute()就不会起作用,若AsynTask正在执行,折会报 view not attached to window manager 异常
同样也是生命周期的问题,在 Activity 的onDestory()方法中调用Asyntask.cancal方法,让二者的生命周期同步
问题:Activity销毁但Task如果没有销毁掉,当Activity重启时这个AsyncTask该如何解决?
还是屏幕旋转这个例子,在重建Activity的时候,会回掉Activity.onRetainNonConfigurationInstance()重新传递一个新的对象给AsyncTask,完成引用的更新
问题:AstncTask+HttpClient与AsyncHttpClient有什么区别
8、Android启动过程及应用启动过程--1
分析篇:Android启动过程图解
问题:Android窗口设计
问题:activity启动过程
1.Activity中最终到startActivityForResult()(mMainThread.getApplicationThread()传入了一个ApplicationThread检查APT)
->Instrumentation#execStartActivity()和checkStartActivityResult()(这是在启动了Activity之后判断Activity是否启动成功,例如没有在AM中注册那么就会报错)
->ActivityManagerNative.getDefault().startActivity()(类似AIDL,实现了IAM,实际是由远端的AMS实现startActivity())
->ActivityStackSupervisor#startActivityMayWait()
->ActivityStack#resumeTopActivityInnerLocked
->ActivityStackSupervisor#realStartActivityLocked()(在这里调用APT的scheduleLaunchActivity,也是AIDL,不过是在远端调起了本进程Application线程)
->ApplicationThread#scheduleLaunchActivity()(这是本进程的一个线程,用于作为Service端来接受AMS client端的调起)
->ActivityThread#handleLaunchActivity()(接收内部类H的消息,ApplicationThread线程发送LAUNCH_ACTIVITY消息给H)
->最终在ActivityThread#performLaunchActivity()中实现Activity的启动完成了以下几件事:
2.从传入的ActivityClientRecord中获取待启动的Activity的组件信息
3.创建类加载器,使用Instrumentation#newActivity()加载Activity对象
4.调用LoadedApk.makeApplication方法尝试创建Application,由于单例所以不会重复创建。
5.创建Context的实现类ContextImpl对象,并通过Activity#attach()完成数据初始化和Context建立联系,因为Activity是Context的桥接类,
最后就是创建和关联window,让Window接收的事件传给Activity,在Window的创建过程中会调用ViewRootImpl的performTraversals()初始化View。
6.Instrumentation#callActivityOnCreate()->Activity#performCreate()->Activity#onCreate().onCreate()中会通过Activity#setContentView()调用PhoneWindow的setContentView()
更新界面。
术语:
1.ActivityManagerServices,简称AMS,服务端对象,负责系统中所有Activity的生命周期
2.ActivityThread,App的真正入口。当开启App之后,会调用main()开始运行,开启消息循环队列,这就是传说中的UI线程或者叫主线程。与ActivityManagerServices配合,一起完成Activity的管理工作
3.ApplicationThread,用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通讯。
4.ApplicationThreadProxy,是ApplicationThread在服务器端的代理,负责和客户端的ApplicationThread通讯。AMS就是通过该代理与ActivityThread进行通信的。
5.Instrumentation,每一个应用程序只有一个Instrumentation对象,每个Activity内都有一个对该对象的引用。Instrumentation可以理解为应用进程的管家,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作。
6.ActivityStack,Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系,状态信息等。通过ActivityStack决定是否需要启动新的进程。
7.ActivityRecord,ActivityStack的管理对象,每个Activity在AMS对应一个ActivityRecord,来记录Activity的状态以及其他的管理信息。其实就是服务器端的Activity对象的映像。
8.TaskRecord,AMS抽象出来的一个“任务”的概念,是记录ActivityRecord的栈,一个“Task”包含若干个ActivityRecord。AMS用TaskRecord确保Activity启动和退出的顺序。如果你清楚Activity的4种launchMode,那么对这个概念应该不陌生。
9、Loader机制
10、安卓权限管理
11、Dalvik及ART虚拟机系列问题
问题:什么是Dalvik虚拟机?
Dalvik虚拟机是Android平台的核心。它可以支持.dex格式的程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,可以减少整体文件尺寸,提高I/O操作的速度,适合内存和处理器速度有限的系统。
问题:Dalvik虚拟机的作用是什么?
Dalvik虚拟机主要是完成对象生命周期管理,内存回收,堆栈管理,线程管理,安全和异常管理等等重要功能。
问题:Dalvik虚拟机与JVM有什么区别
主要区别:
Dalvik是基于寄存器的,而JVM是基于栈的。
Dalvik运行dex文件,而JVM运行java字节码
自Android 2.2开始,Dalvik支持JIT(just-in-time,即时编译技术)。
优化后的Dalvik较其他标准虚拟机存在一些不同特性:
1.占用更少空间
2.为简化翻译,常量池只使用32位索引
3.标准Java字节码实行8位堆栈指令,Dalvik使用16位指令集直接作用于局部变量。局部变量通常来自4位的“虚拟寄存器”区。这样减少了Dalvik的指令计数,提高了翻译速度。
当Android启动时,Dalvik VM 监视所有的程序(APK),并且创建依存关系树,为每个程序优化代码并存储在Dalvik缓存中。Dalvik第一次加载后会生成Cache文件,以提供下次快速加载,所以第一次会很慢。
Dalvik解释器采用预先算好的Goto地址,每个指令对内存的访问都在64字节边界上对齐。这样可以节省一个指令后进行查表的时间。为了强化功能, Dalvik还提供了快速翻译器(Fast Interpreter)。
一般来说,基于堆栈的机器必须使用指令才能从堆栈上的加载和操作数据,因此,相对基于寄存器的机器,它们需要更多的指令才能实现相同的性能。但是基于寄存器机器上的指令必须经过编码,因此,它们的指令往往更大。
Dalvik虚拟机既不支持Java SE 也不支持Java ME类库(如:Java类,AWT和Swing都不支持)。 相反,它使用自己建立的类库(Apache Harmony Java的一个子集)。
问题:每个应用程序对应多少个Dalvik虚拟机
每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行 ,而所有的Android应用的线程都对应一个Linux线程
问题:Art和Dalvik对比;大致工作流程。两者有什么区别?
ART 的机制与 Dalvik 不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。
问题:虚拟机原理,
问题:如何自己设计一个虚拟机(内存管理,类加载,双亲委派);
问题:JVM内存模型及类加载机制;
问题:内存对象的循环引用及避免
在Ios中比较常见,用assign而不是retain声明属性可以避免循环引用,ARC下用弱引用也可以解决
12、Window和WindowManager机制
1.Window用于显示View和接收各种事件,Window有三种类型:应用Window(每个Activity对应一个Window)、子Window(不能单独存在,附属于特定Window)、系统window(Toast和状态栏)
2.Window分层级,应用Window在1-99、子Window在1000-1999、系统Window在2000-2999.WindowManager提供了增删改View三个功能。
3.Window是个抽象概念:每一个Window对应着一个View和ViewRootImpl,Window通过ViewRootImpl来和View建立联系,View是Window存在的实体,只能通过WindowManager来访问Window。
4.WindowManager的实现是WindowManagerImpl其再委托给WindowManagerGlobal来对Window进行操作,其中有四个List分别储存对应的View、ViewRootImpl、WindowManger.LayoutParams和正在被删除的View
5.Window的实体是存在于远端的WindowMangerService中,所以增删改Window在本端是修改上面的几个List然后通过ViewRootImpl重绘View,通过WindowSession(每个应用一个)在远端修改Window。
6.Activity创建Window:Activity会在attach()中创建Window并设置其回调(onAttachedToWindow()、dispatchTouchEvent()),Activity的Window是由Policy类创建PhoneWindow实现的。然后通过Activity#setContentView()调用PhoneWindow的setContentView。