android 基础
1.触发过程:Activity->Window->DocerView->ViewGroup->View,View不触发再返回由父级处理依次向上推。 2.在每个阶段都要经过三个方法 dispatchTouchEvent(分发)、onInterceptTouchEvent(拦截)、onTouch(处理)
大体流程: Activity中走了Window 的 dispatch,Window 的 dispatch 方法直接走了 DocerView 的 dispatch 方法,DocerView 又直接分发给了 ViewGroup,ViewGroup 中走的是 onInterce 判断是否拦截,拦截的话会走 onTouch 来处理,不拦截则继续下发给 View。到 View 这里已经是最底层了,View 若继续不处理,那就调用上层的 onTouch 处理,上层不处理继续往上推。
(1) standard:标准的启动模式。
(2) singleTop:单一顶部模式
如果 Activity 已经被开启,并且处于任务栈的栈顶,就不会创建新的 Activity,而是复用这个已经开启的 Activity。为了防止出现一些奇怪的用户体验,推荐使用单一顶部模式,整个任务栈可以有多个实例存在.应用场景:短信发送界面.
(3)singletask:单一任务栈
在整个任务栈里面只允许有一个当前 Activity 的实例存在如果要开启的 Activity 在任务栈中已经存在,直接复用这个已经存在的 Activity,并且把这个 Activity 上面的所有的其他 Activity 给清空应用场景:如果一个 Activity 非常消耗内存和 cpu 资源,建议把这个 Activity 做成 singletask 的模式。浏览器的 browserActivity
(4)singleinstance:单一实例.
整个手机操作系统只有一个实例存在,并且是运行在自己单独的任务栈里面.应用场景:通话界面的 Activity
参考答案:
参考答案:
1,onAttach:fragment 和 activity 关联时调用,且调用一次。在回调中可以将参数 content 转换为 Activity保存下来,避免后期频繁获取 activity。
2,onCreate:和 activity 的 onCreate 类似
3,onCreateView:准备绘制 fragment 界面时调用,返回值为根视图,注意使用 inflater 构建 View时 一定要将 attachToRoot 指明为 false。
4,onActivityCreated:activity 的onCreated 执行完时调用
5,onStart:可见时调用,前提是 activity 已经 started
6,onResume:交互式调用,前提是 activity 已经 resumed
7,onPause:不可交互时调用
8,onStop:不可见时调用
9,onDestroyView:移除 fragment 相关视图时调用
10,onDestroy:清除 fragmetn 状态是调用
11,onDetach:和 activity 解除关联时调用
从生命周期可以看出,他们是两两对应的,如 onAttach 和 onDetach ,
onCreate 和 onDestory ,onCreateView 和 onDestroyView等
参考答案:
(1) MVC,Model View Controller,是软件架构中最常见的一种框架,简单来说就是通过 controller 的控制去操作 model 层的数据,并且返回给 view 层展示。当用户发出事件的时候,view 层会发送指令到 controller 层,接着 controller 去通知 model 层更新数据,model 层更新完数据以后直接显示在 view 层上,这就是 MVC 的工作原理。
(2) MVP 是 MVC 的演化。MVP 的 model 层相对于 MVC 是一样的,而 activity 和 fragment 不再是 controller 层,而是纯粹的 view 层,所有关于用户事件的转发全部交由 presenter 层处理。presenter 层充当了桥梁的作用,用于操作 view 层发出的事件传递到 presenter 层中,presenter 层去操作 model 层,并且将数据返回给 view 层。
(3) MVVM 和 MVP 的区别貌似不大,只不过是 presenter 层换成了 viewmodel 层,还有一点就是 view 层和 viewmodel 层是相互绑定的关系,这意味着当你更新 viewmodel 层的数据的时候,view 层会相应的变动 ui。
(1) IntentService 继承自 Service。由于 Service 运行在主线程,无法进行耗时操作。所以你需要在 Service 中开启一个子线程,并且在子线程中运行。为了简化这一操作,Android 中提供了 IntentService 来进行这一处理。通过查看 IntentService 的源码可以看到,在 onCreate 中,我们开启了一个 HandlerThread 线程,之后获取 HandlerThread 线程中的 Looper,并通过这个 Looper 创建了一个 Handler。然后在 onStart 方法中通过这个 Handler 将 intent 与 startId 作为 Message 的参数进行发送到消息队列中,然后交由 Handler 中的 handleMessage 中进行处理。由于在 onStart 方法是在主线程内运行的,而 Handler 是通过工作者线程 HandlerThread 中的 Looper 创建的。所以也就是在主线程中发送消息,在工作者接收到消息后便可以进行一些耗时的操作。
(2) 进程间通信
参考答案:
首先先说下 Window:
Window 表示窗口,是个抽象的概念,每个 Window 都对应着一个 View 和一个 ViewRootImpl,Window 通过 ViewRootImpl 和 View 建立联系,因此 Window 并不是实际存在的,他是以 View 的形式存在的。也就是说 View 才是 Window 存在的实体。实际使用中无法直接访问 Window,对 Window 的访问必须通过 WindowManager。
再说 View:
View 是 Android 中视图的呈现方式,但是 View 不能单独存在,它必须附着在 Window 这个抽象的概念上,因此有视图的地方就有 Window。
最后 Activity:
Android 中的四大组件之一 Activity 就是用来显示视图的,因此一个 Activity 就对应着一个 Window,不止 Activity,其实 Dialog,Toast,PopUpWindow,菜单等都对应着一个 Window。
Activity,View,Window 相关连的具体实现:
在 Activity 启动时 通过 attach 方法,创建 Window 的实例即 PhoneWindow 然后绑定 Activity,Activity 实现了 Window 的 Callback 等接口,当 Window 接收到外界的状态改变时就会回调给 Activity,比如 onAttachToWindow、onDetacheFromWindow、dispatchTouchEvent 等。 PhoneWindow 初始化时会创建 DecorView (也就是顶级 View),然后初始化 DecorView 的结构,加载布局文件到 DecorView,之后再将 Activity 的视图添加到 DecorView 的 mContentParent 中。
总结:
Activity 通过 Window 完成对视图 View 的管理,一个 Activity 对应一个 Window,每个 Window 又对应一个 View。
单例模式
常见应用场景:网络请求的工具类、sp存储的工具类、弹窗的工具类等
工厂模式
常见应用场景:activity的基类等
责任链模式
常见应用场景:OKhttp的拦截器封装
观察者模式
常见应用场景:Rxjava的运用
代理模式
常见应用场景:AIDL的使用
建造者模式
常见应用场景:Dialog的创建
参考答案:
首先在主线程创建一个 Handler 对象 ,并重写 handleMessage() 方法。然后当在子线程中需要进行更新UI的操作,我们就创建一个 Message 对象,并通过 Handler 发送这条消息出去。
之后这条消息被加入到 MessageQueue 队列中等待被处理,通过 Looper 对象会一直尝试从 Message Queue 中取出待处理的消息,最后分发回 Handler 的 handleMessage() 方法中。
参考答案:
Window 是 WindowManager 最顶层的视图,它负责背景(窗口背景)、Title之类的标准的UI元素,Window 是一个抽象类,整个Android系统中, PhoneWindow是 Window 的唯一实现类。至于 DecorView,它是一个顶级 View,内部会包含一个竖直方向的LinearLayout,这个 LinearLayout 有上下两部分,分为 titlebar 和 contentParent 两个子元素,contentParent 的 id 是 content,而我们自定义的 Activity 的布局就是 contentParent 里面的一个子元素。View 层的所有事件都要先经过 DecorView 后才传递给我们的 View。 DecorView 是 Window 的一个变量,即 DecorView 作为一切视图的根布局,被 Window 所持有,我们自定义的 View 会被添加到 DecorView ,而DecorView 又会被添加到 Window 中加载和渲染显示
参考答案:
AIDL:AIDL是用于进程间的通迅 能够实现进程间的一对多实时并发通迅 通过定义AIDL接口 创建一个服务将接口暴露给客服端 客户端把定义的ALDL文件复制到项目中 通过bindService绑定服务端服务 并通过 定义AIDL.Stub.asInterface(service)将服务端返回的Binder代理对象转换成AIDL接口所属的类型 调用方法即可
参考答案:
一、svg格式 svg是矢量图,这意味着svg图片由直线和曲线以及绘制它们的方法组成。当你放大一个svg图片的时候,你看到的还是线和曲线,而不会出现像素点。svg图片在放大时,不会失真,所以它非常适合用来绘制企业Logo、Icon SVG格式特点:
1、SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
2、SVG 用来定义用于网络的基于矢量的图形
3、SVG 使用 XML 格式定义图形
4、SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失
5、SVG 是万维网联盟的标准
6、SVG 与诸如 DOM和 XSL 之类的W3C标准是一个整体
二、webp格式 webp是谷歌开发的一种新图片格式,是同时支持有损和无损压缩的、使用直接色的、点阵图。
参考答案:
内存泄漏简单地说就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。由程序申请的一块内存,且没有任何一个指针指向它,那么这块内存就泄露了。
参考答案:
界面刷新的本质流程
通过ViewRootImpl的scheduleTraversals()进行界面的三大流程。调用到scheduleTraversals()时不会立即执行,而是将该操作保存到待执行队列中。并给底层的刷新信号注册监听。当VSYNC信号到来时,会从待执行队列中取出对应的scheduleTraversals()操作,并将其加入到主线程的消息队列中。主线程从消息队列中取出并执行三大流程: onMeasure()-onLayout()-onDraw()
同步屏障的作用
同步屏障用于阻塞住所有的同步消息(底层VSYNC的回调onVsync方法提交的消息是异步消息)用于保证界面刷新功能的performTraversals()的优先执行。
同步屏障的原理?
主线程的Looper会一直循环调用MessageQueue的next方法并且取出队列头部的Message执行,遇到同步屏障(一种特殊消息)后会去寻找异步消息执行。如果没有找到异步消息就会一直阻塞下去,除非将同步屏障取出,否则永远不会执行同步消息。界面刷新操作是异步消息,具有最高优先级我们发送的消息是同步消息,再多耗时操作也不会影响UI的刷新操作
参考答案:
handler.getLooper().getQueue().postSyncBarrier()加入同步屏障后,Message.obtain()获取一个target为null的msg,并根据当前时间将该msg插入到链表中。 在Looper.loop()循环取消息中 Message msg = queue.next(); target为空时,取链表中的异步消息。 通过setAsynchronous(true)来指定为异步消息
应用场景:ViewRootImpl scheduleTraversals中加入同步屏障 并在view的绘制流程中post异步消息,保证view的绘制消息优先执行
参考答案:
场景: 1、触摸无响应5s 2、BroadCastReciver 前台处理超过10s 后台超过60s 3、Server 前台处理超过20s 后台超过200s
ANR出现的类型有两种 1、主线程耗时导致 2、CPU、内存、IO 占用过高资源耗尽(其他进程也可以导致)
如何避免: 1、不要在主线程中做耗时的操作 2、避免CPU占用过高,简化方法,减少执行时间 3、避免内存占用过高,防止内存泄漏
参考答案:
代码混淆的目的: 1、代码混淆之后会随机生成难易理解的类名、方法名以及属性名,加大反编译的难度; 2、可以对apk进行优化,缩小包的体积;
代码混淆的步骤: 1、在gradle文件里面打开配置:minifyEnabled true; 2、在proguard-rules.pro 文件里面配置混淆规则; 在配置混淆的时候,需要注意:1、四大组件以及view可以不进行混淆;2、实体类不进行混淆;3、第三方库根据各自的要求配置混淆规则;
通信方式 |
优点 |
缺点 |
使用场景 |
Bundle |
简单易用 |
只能传输Bundle支持的数据类型 |
四大组件间的进程间通信 |
文件共享 |
简单易用 |
不适合高并发场景,并且无法做到即时通信 |
无并发访问的情况,交换实时性不高的简单数据 |
Messenger |
功能一般,支持一对多串行通信,支持实时通信 |
不能很好的处理高并发情况,不支持RPC,只能传输Bundle支持的数据类型 |
低并发的一对多即时通信,无RPC需求,或无须返回结果的RPC需求 |
AIDL |
功能强大,支持一对多并发和实时通信 |
使用复杂,需要处理好线程同步问题 |
一对多通信且有RPC需求 |
ContentProvider |
在数据源访问方面功能强大,支持一对多并发数据共享,可通过call方法扩展其他操作 |
可理解为受约束的AIDL,主要提供数据源的CRUD操作 |
一对多的进程间数据共享 |
Socket |
功能强大,可通过网络传输字节流,支持一对多并发实时通信 |
实现细节较为烦琐,不支持直接RPC |
网络数据交换 |
参考答案:
参考答案:
双重反射,即利用反射调用反射API,这个时候系统进行栈回溯,发现直接调用者是反射API,反射API也是系统API,就直接通过了
垃圾回收机制: 当堆内存中的某块区域没有对象引用时,这个内存区域就会变成垃圾,等待垃圾回收器的回收,
强制垃圾回收: 程序只能控制一个对象不被任何引用变量引用,绝对不能控制它的回收。 System.gc(); Runntime.getTuntime().gc(); 上面两个方法会建议系统进行垃圾回收,但是系统也有可能不进行回收。 对象的复活可以通过 finalize()方法来实现
Java语言系统自带有三个类加载器:
我们需要知道这三个加载器的加载顺序
然后需要知道这个加载顺序具体执行策略 双亲委托机制
一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象。这种机制就叫做双亲委托。
1、JVM指Java虚拟机,能够运行Java字节码的虚拟机器,有多种实现
2、Dalvik虚拟机是Google设计用于Android平台的虚拟机,支持已转换为.dex格式的Java程序的运行。与JVM的区别:
3、ART虚拟机是现行的Android虚拟机,与Dalvik的区别:
所以,ART虚拟机提高了程序的运行效率;首次安装需要预编译,所以安装时间比Dalvik中略长,机器码占用空间大,应用占用空间也会略大。
App 启动流程(基于Android8.0):
补充:
参考答案:
View.post方法调用时,如果在 View 还没开始绘制时( Activity 的 onResume方法还没回调之前 或者onResume方法执行了,但是 ViewRootImpl 的 performTraversals 还没开始执行)就会用一个初始长度为 4 的数组缓存起来(Runnable 数量大于4时会进行扩容),ViewRootImpl 在初始化时创建了一个 View.AttchInfo 对象并绑定 ViewRootImpl 的 Handler ,该 Handler 也用于发送 View 绘制相关的 msg ;
等到 ViewRootImpl执行 performTraversals方法时(此时 Activity 已经回调了onResume),会配置 View 的 AttchInfo 对象并且通过 View 的 dispatchAttachedToWindow 方法传入到 View 里面完成绑定,在该方法中会取出 View 缓存的 Runnable 并用 View.AttchInfo 的 Handler 来进行 post 方法,这样子就会加入到 MessageQueue 里面进行排队,等到这些缓存 Runnable 执行时,主线程里面 View的绘制流程也就结束了,所以这时候 Looper 取出这些缓存的Runnable 执行时就可以拿到 View 的宽高
那么,什么情况下View.post 方法不会执行呢?如果 Activity 因为某些原因没有执行到 onResume 的话,无法顺利调用 ViewRootImpl 的 performTraversals 的话,View.post 方法就不会执行
提醒:如果 View 是 new 出来的,并且没有通过 addView 等方法依赖到 DecorView 上面,它的 post 方法也是不会执行的,因为它没有机会和 ViewRootImpl 进行互动了
1.为了保证进程空间不被其他进程破坏或干扰,Linux中的进程是相互独立或相互隔离的。
2.进程空间分为用户空间和内核空间。用户空间不可以进行数据交互;内核空间可以进行数据交互,所有进程共用一个内核空间。
3.Binder机制相对于Linux内传统的进程间通信方式:
4.Binder跨进程通信机制:基于C/S架构,由Client、Server、Server Manager和Binder驱动组成。
5.Binder驱动实现的原理:通过内存映射,即系统调用了mmap()函数。
6.Server Manager的作用:管理Service的注册和查询。
7.Binder驱动的作用:
(1)传递进程间的数据,通过系统调用mmap()函数;
(2)实现线程的控制,通过Binder驱动的线程池,并由Binder驱动自身进行管理。
8.Server进程会创建很多线程处理Binder请求,这些线程采用Binder驱动的线程池,由Binder驱动自身进行管理。一个进程的Binder线程池默认最大是16个,超过的请求会阻塞等待空闲的线程。
9.Android中进行进程间通信主要通过Binder类(已经实现了IBinder接口),即具备了跨进程通信的能力。
参考答案:
1) aapt 为res 目录下资源生成R.java 文件,同时为AndroidMainfest.xml 生成Mainfeset.java文件
2)aidl 将项目中自定义的aidl 生成相应的Java文件
3) javac 将项目中所有java 代码编译成class 文件,包括 业务逻辑代码, aapt 生成 java文件,aidl 生成java 文件
4) proguard , 混淆同时生成mapping.txt ,这步可选
5) 将所有class 文件(包括第三方class 文件) 转换为dex 文件
6)aapt 打包, 将res资源,assets资源,打包成一个.ap的文件
7)apkbuilder ,将所有dex 文件,.ap文件, AndroidMainfest.xml打包为.apk 文件,这是一个未签名的apk包 8)jarsigner 对apk 进行签名
9)ziplign 对apk 进行对齐操作,以便运行时节约内存。
参考答案: 热修复分为三个部分,分别是Java代码部分热修复,Native代码部分热修复,还有资源热修复。
资源部分热更新直接反射更改所有保存的AssetManager和Resources对象就行(可能需要重启应用)
Native代码部分也很简单,系统找到一个so文件的路径是根据ClassLoader找的,修改ClassLoader里保存的路径就行(可能需要重启应用)
Java部分的话目前主流有两种方式,一种是Java派,一种是Native派。