MS(3):Android之机制原理篇

五、重点机制原理

1、Handler机制

MS思考:Android面试一天一题(8 Day):Handler相关

分析篇:Android 异步消息处理机制源码解析

问题: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事件分发机制及整个流程

分析篇:Android View事件分发机制源码分析

MS(3):Android之机制原理篇_第1张图片

问题:介绍触摸事件的分发机制

(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绘制机制和加载过程

Android组件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机制

MS思考:Android面试一天一题(Day 35:神秘的Binder机制)

分析篇:Android Bander设计与实现 - 设计篇

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:学习AIDL,这一篇文章就够了(上)

Android:学习AIDL,这一篇文章就够了(下)

你真的理解AIDL中的in,out,inout么?

问题:Android 进程间的通信有哪几种方式

Bundle/Intent传递数据,Messenger(消息队列适合单线程,底层还是AIDL),AIDL,ContentProvider,Socket

问题:Bundle、文件共享、ContentProvider、AIDL的使用场景

7、异步任务机制之AsycTask

MS思考:Android面试一天一题(13 Day: AsyncTask)

分析篇:Android异步任务机制之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 Application启动流程分析

Android Activity.startActivity流程简介

问题:Android窗口设计

Android应用程序窗口设计框架介绍

问题:activity启动过程

链接: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机制

Android 深入理解Loader机制 让APP轻装上阵

AsyncTaskLoader设计原理大揭秘

10、安卓权限管理

11、Dalvik及ART虚拟机系列问题

MS思考:Android面试一天一题(Day 27:ART & Dalvik)

问题:什么是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)。这样的话,应用的启动(首次)和执行都会变得更加快速。

问题:虚拟机原理,

Dalvik虚拟机简要介绍和学习计划

问题:如何自己设计一个虚拟机(内存管理,类加载,双亲委派);

问题:JVM内存模型及类加载机制;

java内存模型与类加载机制

【深入理解JVM】:类加载机制

问题:内存对象的循环引用及避免

java垃圾回收之循环引用

在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。

你可能感兴趣的:(MS(3):Android之机制原理篇)