Activity的启动流程
- 点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;
- system_server进程接收到请求后,向zygote进程发送创建进程的请求;
- Zygote进程fork出新的子进程,即App进程;
- App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;
- system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进 程发送scheduleLaunchActivity请求;
- App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;
- 主线程在收到Message后,通过发射机制创建目标Activity,并回调Activity.onCreate()等方法。
Activity四种启动模式
- 标准模式(默认模式)android:launchMode="standard"
每次启动一个Activity就会创建一个新的实例。- 栈顶复用模式:android:launchMode="singleTop"
如果新Activity已经位于任务栈的栈顶,就不会重新创建,并回调onNewIntent()方法。- 栈内复用模式:android:launchMode="singleTask"
只要该Activity在一个任务栈中存在,都不会重新创建,并回调onNewIntent()方法- 单实例模式: android:launchMode="singleInstance"
具有此模式的Activity只能单独位于一个任务栈中,且此任务栈中只有唯一一个实例。
Android中有哪几种类型的动画
- View动画(View Animation)/补间动画(Tween animation):对View进行平移、缩放、旋转和透明度变化的动画,不能真正的改变view的位置。应用如布局动画、Activity切换动画
- 逐帧动画(Drawable Animation):是View动画的一种,它会按照顺序播放一组预先定义好的图片
- 属性动画(Property Animation):对该类对象进行动画操作,真正改变了对象的属性
谈谈消息机制Hander
- 作用:跨线程通信。当子线程中进行耗时操作后需要更新UI时,通过Handler将有关UI的操作切换到主线程中执行。
- Message(消息):需要被传递的消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。
- MessageQueue(消息队列):用来存放Handler发送过来的消息,内部通过单链表的数据结构来维护消息列表,等待Looper的抽取。
- Handler(处理者):负责Message的发送及处理。通过 Handler.sendMessage() 向消息池发送各种消息事件;通过 Handler.handleMessage() 处理相应的消息事件。
- Looper(轮询器):通过Looper.loop()不断地从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者。
Handler导致的内存泄漏
- 泄漏原因:Message持有对Handler的引用,而非静态内部类的Handler又隐式持有对外部类Activity的引用,使得引用关系会保持至消息得到处理,从而阻止了Activity的回收。
- 防止泄漏:
1、当外部类结束生命周期时清空消息队列removeCallbacksAndMessages(null)
2、使用静态内部类+WeakReference弱引用
在项目中用到哪些设计模式,以及应用场景。
- 单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
场景:一些工具类,RetrofitHelper、EventBus等。
public class Singleton {
// 注意要加 volatile,保证可见性、有序性
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
- 建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的展示。
场景:Dialog、Retrofit、OkHttp等。
new MaterialDialog.Builder(activity)
.title("标题")
.positiveText(R.string.cancel)
.negativeText(call)
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
}
}).show();
- 观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都能得到通知并被自动更新。
场景:EventBus、RxJava等。
- 适配器模式:把一个类的接口转换为客户端所期待的另一种接口,从而使原本因接口不匹配而无法再一起工作的两个类能够在一起工作。
场景:ListView与Adapter的应用就是典型的适配器模式
谈谈几个常用的框架的实现原理
Glide框架
使用: Glide.with(content).load(url).into(imageView);
with绑定生命周期,load指定加载资源,into指明加载目标。
生命周期绑定原理:
1. Glide绑定Activity时,生成一个无UI的Fragment。
2. 在RequestManager的构造方法中,将RequestManager存入到之前传入的Fragment的LifeCycle
3. 通过Lifecycle在Fragment关键生命周期通知RequestManger进行相关的操作。
// RequestManager的onDestroy方法。页面销毁,会自动中断所有的图片加载
缓存机制:内存缓存、磁盘缓存(DiskLruCache)
内存缓存分为HashMap和LruCache
# 弱应用 活动缓存 HashMap
Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
# LruCache LinkedHasMap
Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);
Retrofit框架
Retrofit通过动态代理,用MethodHandler完成接口方法。
Retrofit的MethodHandler通过RequestFactoryParser.parse解析,获得接口方法的参数和注解的值,传入到OkHttpCall,OkHttpCall生成okhttp3.Call完成Http请求并使用Converter解析数据回调。
Retrofit通过工厂设置CallAdapter和Converter,CallAdapter包装转换Call,Converter转换(解析)服务器返回的数据、接口方法的注解参数。
OkHttp 是一个高效的 HTTP 客户端,具有非常多的优势:
1、能够高效的执行 http,数据加载速度更快,更省流量
2、支持 GZIP 压缩,提升速度,节省流量
3、缓存响应数据,避免了重复的网络请求
4、使用简单,支持同步阻塞调用和带回调的异步调用
OkHttp流程:
1、采用责任链方式的拦截器,实现分成处理网络请求,可更好的扩展自定义拦截器(采用GZIP压缩,支持http缓存)
2、采用线程池(thread pool)和连接池(Socket pool)解决多并发问题,同时连接池支持多路复用(http2才支持,可以让一个Socket同时发送多个网络请求,内部自动维持顺序.相比http只能一个一个发送,更能减少创建开销))
3、底层采用socket和服务器进行连接.采用okio实现高效的io流读写
分化器 dispatcher:内部维护队列与线程池,完成请求调配。
// 双端队列,支持首尾两端 双向开口可进可出
// 准备运行的异步队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
// 正在运行的异步
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
// 正在执行的同步队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
拦截器:5个拦截器,完成整个请求过程
1、Interceptors:用户自定义拦截器
2、BridgeInterceptor:请求时,对必要的Header进行一些添加,接收响应时,移除必要的Header
3、RetryAndFollowUpInterceptor:负责失败重试以及重定向
4、CacheInterceptor:负责读取缓存直接返回(根据请求的信息和缓存的响应的信息来判断是否存在缓存可用)、更新缓存
5、ConnectInterceptor:负责和服务器建立连接
LeakCanary 原理
1、通过 registerActivityLifecycleCallbacks 监听Activity或者Fragment 销毁时候的生命周期
(如果不想那个对象被监控则通过 AndroidExcludedRefs 枚举,避免被检测)
2、通过弱引用和引用队列监控对象是否被回收(弱引用和引用队列ReferenceQueue联合使用时,
如果弱引用持有的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
即 KeyedWeakReference持有的Activity对象如果被垃圾回收,该对象就会加入到引用队列queue)。
Android 创建多个进程,Android中的进程通信
通过在配置清单中给四大组件设置android:process属性值,这样我们就可以轻易开启多进程模式。
Android中支持的多进程通信方式主要有以下几种,它们之间各有优缺点,可根据使用场景选择选择:
1、AIDL:功能强大,支持进程间一对多的实时并发通信,并可实现 RPC (远程过程调用)。
2、Messenger:支持一对多的串行实时通信, AIDL 的简化版本。
3、Bundle:四大组件的进程通信方式,只能传输 Bundle 支持的数据类型。
4、ContentProvider:强大的数据源访问支持,主要支持 CRUD 操作,一对多的进程间数据共享,例如我们的应用访问系统的通讯录数据。
5、BroadcastReceiver:即广播,但只能单向通信,接收者只能被动的接收消息。
6、文件共享:在非高并发情况下共享简单的数据。
7、Socket:通过网络传输数据。
谈谈 MVC、MVP、MVVM
MVC
视图层(View) 对应于xml布局文件和java代码动态view部分
控制层(Controller) MVC中Android的控制层是由Activity来承担的,Activity本来主要是作为初始化页面,展示数据的操作,但是因为XML视图功能太弱,所以Activity既要负责视图的显示又要加入控制逻辑,承担的功能过多。
模型层(Model) 针对业务模型,建立数据结构和相关的类,它主要负责网络请求,数据库处理,I/O的操作。
总结:
具有一定的分层,model彻底解耦,controller和view并没有解耦层与层之间的交互尽量使用回调或者去使用消息机制去完成,尽量避免直接持有 controller和view在android中无法做到彻底分离,但在代码逻辑层面一定要分清业务逻辑被放置在model层,能够更好的复用和修改增加业务。
MVP
通过引入接口BaseView,让相应的视图组件如Activity,Fragment去实现BaseView,实现了视图层的独立,通过中间层Preseter实现了Model和View的完全解耦。MVP彻底解决了MVC中View和Controller傻傻分不清楚的问题,但是随着业务逻辑的增加,一个页面可能会非常复杂,UI的改变是非常多,会有非常多的case,这样就会造成View的接口会很庞大。
MVVM
MVP中我们说过随着业务逻辑的增加,UI的改变多的情况下,会有非常多的跟UI相关的case,这样就会造成View的接口会很庞大。而MVVM就解决了这个问题,通过双向绑定的机制,实现数据和UI内容,只要想改其中一方,另一方都能够及时更新的一种设计理念,这样就省去了很多在View层中写很多case的情况,只需要改变数据就行。
ListView跟RecyclerView的区别
动画区别:
(1)在RecyclerView中,内置有许多动画API,例如:notifyItemChanged(), notifyDataInserted(), notifyItemMoved()等等;如果需要自定义动画效果,可以通过实现(RecyclerView.ItemAnimator类)完成自定义动画效果,然后调用RecyclerView.setItemAnimator();
(2)但是ListView并没有实现动画效果,但我们可以在Adapter自己实现item的动画效果;
刷新区别:
ListView中通常刷新数据是用全局刷新notifyDataSetChanged(),这样一来就会非常消耗资源;本身无法实现局部刷新,但是如果要在ListView实现局部刷新,依然是可以实现的,当一个item数据刷新时,我们可以在Adapter中,实现一个onItemChanged()方法,在方法里面获取到这个item的position(可以通过getFirstVisiblePosition()),然后调用getView()方法来刷新这个item的数据;
RecyclerView中可以实现局部刷新,例如:notifyItemChanged();
缓存区别:
RecyclerView比ListView多两级缓存,支持多个离ItemView缓存,支持开发者自定义缓存处理逻辑,支持所有RecyclerView共用同一个RecyclerViewPool(缓存池)。
ListView和RecyclerView缓存机制基本一致,但缓存使用不同。
AIDL简单讲解一下。底层实现的是什么机制?
AIDL是进程间通信的一种方式,底层实现是通过Binder机制来实现的。Binder机制底层又是通过mmap内存映射原理来实现的,内存分为用户空间和内核空间,Binder机制通过一次一次的数据拷贝来传递数据。
Binder机制了解
每个进程的内存空间都分为用户空间和内核空间,用户空间和内核空间之间的通信需要native层的方法来实现,方法叫copy_form_user()。进程A的内核空间和进程B的用户空间通过Linux的一个mmap方法来创建出一个共享的物理内存地址,然后进程A的用户空间和进程B的用户空间进行通信的话,就只需要进程A的用户空间往进程A的内核空间进行一次数据拷贝(copy_from_user),进程B的用户空间与进程A的内核空间通过mmap内存映射的方式,就能在共享的物理内存中获取到进程A拷贝的数据,就实现了通信。
AsyncTask原理
1 、AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正地执行任务,InternalHandler用于将执行环境从线程池切换到主线程。
2、InternalHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程创建。由于静态成员会在加载类的时候进行初始化,因此这就变相要求AsyncTask的类必须在主线程中加载,否则同一个进程中的AsyncTask都将无法正常工作。
谈谈对RxJava的理解
RxJava是基于响应式编程,基于事件流、实现异步操(类似于Android中的AsyncTask、Handler作用)作的库,基于事件流的链式调用,使得RxJava逻辑简洁、使用简单。RxJava原理是基于一种扩展的观察者模式,有四种角色:被观察者Observable 观察者Observer 订阅subscribe 事件Event。RxJava原理可总结为:被观察者Observable通过订阅(subscribe)按顺序发送事件(Emitter)给观察者(Observer), 观察者按顺序接收事件、作出相应的响应动作。
Android各版本新特性
1、Android5.0新特性:MaterialDesign设计风格、支持64位ART虚拟机、通知详情可以用户自己设计。
2、Android6.0新特性:动态权限管理、支持快速充电的切换、支持文件夹拖拽应用、相机新增专业模式。
3、Android7.0新特性:多窗口支持、V2签名、增强的Java8语言模式、夜间模式。
4、Android8.0(O)新特性:优化通知、画中画模式、自动填充框架、系统优化。
5、Android9.0(P)新特性:室内WIFI定位、“刘海”屏幕支持、安全增强。
6、Android10.0(Q)新特性:用户存储权限的变更、用户的定位权限的变更。
谈一下一次完整的http请求
首先进行DNS域名解析
- 三次握手建立TCP连接
- 客户端向服务器发送请求命令
- 客户端发送请求头信息
- 服务器应答 Http/1.1 200 OK
- 服务器返回相应头信息
- 服务器向客户端发送数据
- 服务器关闭TCP连接
谈一下一次完整的https请求
- 客户端发起https请求
- 服务器必须要有一套数字证书,可以自己制作,也可以向权威机构申请。这套证书其实就是一对公私钥。
- 服务器将自己的数字证书(含有公钥、证书的颁发机构等)发送给客户端。
- 客户端收到服务器端的数字证书之后,会对其进行验证,主要验证公钥是否有效,比如颁发机构,过期时间等等。如果不通过,则弹出警告框。如果证书没问题,则生成一个密钥(对称加密算法的密钥,其实是一个随机值),并且用证书的公钥对这个随机值加密。
- 客户端会发起https中的第二个请求,将加密之后的客户端密钥(随机值)发送给服务器。
- 服务器接收到客户端发来的密钥之后,会用自己的私钥对其进行非对称解密,解密之后得到客户端密钥,然后用客户端密钥对返回数据进行对称加密,这样数据就变成了密文。
- 服务器将加密后的密文返回给客户端。
- 客户端收到服务器发返回的密文,用自己的密钥(客户端密钥)对其进行对称解密,得到服务器返回的数据。
布局性能优化之Include、ViewStub、Merge
- include标签常用于将布局中的公共部分提取出来供其他layout共用,以实现布局模块化,也是平常我们设计布局时用的最多的。
- ViewStub就是一个宽高都为0的一个View,它默认是不可见的,只有通过调用setVisibility函数或者Inflate函数才会将其要装载的目标布局给加载出来,从而达到延迟加载的效果,这个要被加载的布局通过android:layout属性来设置。
- 在一个布局中包含一个布局时,merge标签有助于消除视图层次结构中的冗余视图组。如果是merge标签,那么直接将其中的子元素添加到merge标签parent中,这样就保证了不会引入额外的层级。
网友评论