Activity和Fragment的区别
一、Activity的启动流程
启动流程:L:Lock(L代表是锁) I:Install(代表是安装)
1.Activity.java : startActivity()->startActivityForResult()
2.Instrumentation.java : startActivity()
3.ActivityManagerProxy.java : startActivity()
4.通过IBinder传递到APM.java : startActivity()->startActivityAsUser()
5.ActivityStarter.java : startActivityMayWait()->startActivityLocked()
6.APM.java : startProcessLock : 判断是否已经开启该进程,如果开启就不再创建
7.Process.java : Start("android.app.ActivityThread") : 这里就是开启一个ActvityThread入口
8.Process.java : startZygote() : 真正的孵化出一个进程
9.ActivityThread.java : main() -> attach()----->通过调用IActivityManager来和AMS进行通行,完整Activity的生命周期
10.主线程中调用Loop,那么为什么没有造成阻塞呢
ActivityThread的Main方法里面有个Loop的消息队列,ActivityThread 有个 getHandler 方法,得到这个 handler 就可以发送消息,然后 loop 里就分发消息,然后就发给 handler,然后就执行到 H(Handler )里的对应代码。所以说ActivityThread的main方法主要就是做Activity相关动作的消息循环,一旦退出消息循环,那么你的程序也就可以退出了,所以这些代码就不会卡死。
1.ActivityThread : 该类是应用程序对应的进程的主线程类,即UI线程
2.ActiityRecord : activity的记录对象
3.ActivityStack 、ActivityStackSupervisor : 管理activity的启动,生命周期以及activity的堆栈
4.TaskRecord : 应用activity记录任务栈
5.ProcessRecord : 该类用于记录每个进程的全部信息。主要信息包括该进程包含的activity、provider、service等信息、进程文件信息、进程状态信息
二、本地广播和全局广播的区别
1、接受情况:全局广播任何程序都可以接收到,本地广播只能本应用可以接收到
2、安全性:全局广播不安全,本地广播安全
3、注册情况:全局广播可以静态和动态注册,本地广播只能动态注册
三、BindSerce和Service的区别
1、生命周期不同
2、使用startService()方法启用服务,调用者与服务之间没有关连,属于静态绑定,服务会一直存在。使用bindService()方法启用服务,调用者与服务绑定在了一起,属于动态绑定调用者一旦退出,服务也就终止。
四、view显示的过程
Window及其实现类是PhoneWindow, 页面都是依附在窗口之上的,DecorView即是窗口最顶层的视图
1.MainActivity.java : setContentView
2.AppCompatDelegateImpl.java: setContentView,这里面会有LayoutInfalter去解析xml文件成view,然后会调用mWindow.setContentView方法
3.PhoneWindow.java : setContentView里面去创建DecorView,然后解析view添加到DecorView当中。WindowManager继承ViewManager并实现里面的addView、deleteView、updateView三个方法
4.ActivityThread.java : handleResumeActivity 方法里面,有个WindowManager.addView(decorView) , 这个时候就开始view真正的绘制流程,WindowManager是继承ViewManager的类,ViewManager里面有对view的增删更新三个方法
5.WindowManagerImpl.java : WindowManagerImpl是继承WindowManger的类,去调用addView的方法
6.WindowManagerGlobal.java : 去调用addView的方法,这里面会创建ViewRootImpl的对象,然后去root.setView你要显示的view,在这里之前会有个子线程是否可以更新UI问题,调用完setView这个方法就开始执行view的绘制、测量等操作,在这里之前会有一个是否为自线程更新UI的判断
7.屏幕刷新机制:
在viewRootImpl哪里通过单例模式创建Choreographer(拷牙骨缝儿)的对象,Choreographer作用是可以接受系统的 VSYNC 信号,以及统一管理应用的输入、动画和绘制等任务的执行时机,16.6ms是在Choreographer的构造方法里面创建的。
requestLayout() ——> scheduleTraversals() ———> Choreographer会执行posSyncBarrier(),通过同步屏障消息机制,把消息插入到handler最前面,通过执行postCalllBack等待vsync的信号到来
8.vsync的监听以及处理:
在FrameDisplayEventReceiver里面的onReceiver,通过JNI的方式去注册Vsync的监听者,通过有onVsync去接受并对Vsync信号的处理, doFrame执行每一帧情况,超过16.6ms就用上一帧,没有超过就及时刷新,然后去执行callBack的函数,然后执行performTraversals——>再去执行viewRootImpl的绘制流程
两篇屏幕刷新机制:
https://cloud.tencent.com/developer/article/1685247
https://www.jianshu.com/p/86d00bbdaf60
如何优先去执行view的测绘呢? 有同步屏障消息和异步消息去执行
ViewRootImpl的作用
1.它是关联Window和View相关联的桥梁
2.它执行view 的onMeasure、onLayout、onDraw三大流程
3.它可以接受屏幕输入事件和分发手势
在子线程view.requestLayout和view.postInvalidate更新Ui就不会生效了,因为ViewRootImpl的setView方法时候会有线程的检查
onMeasure里面的参数使用:
1、MeasureSpec.UNSPECIFIED ->未指定尺寸
2、MeasureSpec.EXACTLA ->精确尺寸,控件的宽高指定大小或者为FILL_PARENT
3、MeasureSpec.AT_MOST ->最大尺寸,控件的宽高为WRAP_CONTENT,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸
五、自定义view的流程
1.LayoutParams是什么,与MeasureSpec有什么关系
LayoutParams代表布局中的宽和高
MeasureSpec里面的三个参数
EXACTLY : 确切的控件大小
AT_MOST : 不会超过某个数值,比如父布局大小
UNSPECIFIED : 控件大小不确定
2.如何把LayoutParams转换为MeasureSpec的具体参数值
getChildMeasureSpec() : 这个需要自己看看里面算法
面试考点:getMeasuredWidth和getWidth的区别
六、view的事件分发流程
rootViewImpl->DecorView->Activity->Window->DecorView->ViewGroup
dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
View事件的总结:
dispatchTouchEvent的总结:
ViewGrop:
在down改为true : ViewGrop down、move、up三个事件都可以收到,从Activity传递给ViewGrop的,view什么事件都不能接收到
在move改为true: ViewGrop可以接收到down事件,view也可以接收到down事件,其余什么事件都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理
在up改为true :ViewGroup可以接收到down事件,view也可以接收到down事件,其余什么都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理
onInterceptTouchEvent的总结:
ViewGroup: 只能接受down的事件,其余什么都收不到
在down改为true : ViewGrop可以接收到down事件,view什么事件都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理
在up改为true : ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理
在move改为true : ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理
onTouchEvent的总结:
ViewGrop:
在down改为true : ViewGrop down、move、up三个事件都可以收到,从Activity传递给ViewGrop的,view可以收到down事件
在up改为true : ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理
在move改为true : ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理
dispatchTouchEvent的总结:
View:
在down改为true : ViewGrop down、move、up三个事件都可以收到,view这个三个事件也都可以收到
在move改为true: ViewGrop可以接收到down事件,view也可以接收到down事件,其余什么事件都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理
在up改为true: ViewGroup可以接收到down事件,view也可以接收到down事件,其余什么都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理
onTouchEvent的总结:
View:
在down改为true : ViewGrop down、move、up三个事件都可以收到,view这个三个事件也都可以收到
在up改为true : ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理
在move改为true : ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理
滑动冲突解决的办法
1.外部拦截法:在父布局这里加onInterceptTouchEvent方法处理
2.内部拦截法:在子布局的dispatchTouchEvent里面的调用getParent().requestDisallowInterceptTouchEvent(true)
七、多进程通信的方式
1.intent/bundle
2.ContentProvider
3. 广播
4.AIDL
5.FileObserver : 使用startWatching和stopWatching两个方法进行加锁,通过监听它的onEvent事件来做处理,原理是底层inotify原理机制,采用是对文件变化做监听
6.IPC通信
7.socket : 两次拷贝、C/S架构(复用性差)、不安全
8.binder : 一次拷贝、C/S架构(复用性强)、安全
9.管道 : 两次拷贝、C/S架构(复用性差)、不安全
10. 共享内存 : 两次拷贝、复用性差、不安全
八、Binder的通信使用和原理
1. aidl如何生成java的文件,客户端和服务端建立联系
主要是三个文件:Stub(服务端接受数据)、Proxy(封装客户端处理数据是调用stub.asInterface())、继承IInterface接口
sub的实现在继承service的onBind里面去实现的
2. 客户端于服务数据连通
service端的使用: 继承service然后再onBind里面把stub给new出去
client端的使用:通过bindService去serviceConnection里面去调用Stub.asInterface方法来获得远端Service的对象
总结: sub -----> 是给服务端创建的
proxy -----> 是给客户端创建的
IIterface ------> 公共的抽象接口,给sub和procy来使用
3.通信实现过程:
1.首先会用Pacel这个数据结构定义data、reply两个对象,data是用来存客户端准备发给服务端的数据,reply是服务端返回给客户端的数据,用writeToParcel方法,把你们数据转换为Parcel数据
2.IBinder.transact(变量定义int a= 1,。。。。。)去发送,这个时候客户端等待服务端给返回数据
3.Stub 的onTrancation()方法,通过定义的a来确定去执行
4.Binder对象的获取 : Stub.asInterface去拿到Proxy的对象
https://www.bilibili.com/video/BV13A411J7i4?p=7 讲解的地址
4.Binder的原理
它是基于C/S架构实现,binder的驱动、Client端、Service 端、ServiceManager构成的
ServiceManager里面常见的四种方法:
addService、getService、listService、checkService
addService案例举例:
1. 先获得ServiceManager的对象
sp < IServiceManager > sm = defaultServiceManager();
2.调用ServiceManager的addService方法
3.IPCThreadState.cpp(又可以理解为ProcessState) :
transact() ---> 通过Parcel传递数据,同时传递ADD_SERVICE_TRANSACTION这个参数
writeTransactionData() ---> 把Parcel数据转为binder_transaction_data
waitForResponse() ---->传递一个BC_transaction变量,这个时候就等待驱动层返回给数据,再返回给上层
4.binder.c
binder_open():在驱动底层开辟空间,主要是为用户态传递数据,这里也是数据拷贝一次
binder_ioctl() ---> binder_transaction()
把binder_transation_data数据转化为binder_transaction,这里收到传入BC_transaction参数传递到下面,驱动层等待创建好被唤起
binder_thread_write()
binder_transaction的数据转换为binder_write_read数据
把binder_write_read存在binder_transaction_data结构中,它里面有个binder_node节点数据结构-->binder_proc(它是存进程的)-->binder_ref(它是存线程的),里面有个红黑树,专门存放线程id和名字作为一一对应的,这样就能从节点--->进程---->再找到对应的线程,数据存储好之后会调IPCThreadState.cpp的waitForResponse方法,并且返回给BR_TRANSACTION_COMPLETE参数,告诉它存储完毕
IPCThreadState.cpp ------>BpBinder端,Binder.cpp -------->BnBinder端,他们两个都是基于IBinder去写的
九、进程保活的实现
1.Android的进程优先级:前台进程、可见进程、服务进程、后台进程、空进程(主要为了做缓存、缩短下次启动时间)
2.进程保活方案
a.利用系统广播拉活
缺点:当只有在特定场景下才可以拉起保活,下次被杀死后,无法得到控制
b.利用系统Service机制拉活 : onStartCommand方法,返回START_STICKY
缺点:在短时间内被多次杀死就不会被拉活、被root或者其他工具被stop也是无法拉活的
c.通过native进程拉活(通过AMS的进程杀死)
d.进程相互唤醒(打开一个app同时唤醒另一个app)
e.提升Service进程优先级,比如改为前台进程 startForeground(1,notification)
f.JobScheduler用来检测你自己的服务是否被杀掉,如果被杀掉了,你重启自己的服务 缺点:需要有自启动权限
g.采用SyncAdapters机制,等待系统去更新同步信息,就是在账号和同步添加账户
十、Handler,Looper,MessageQueue的工作原理
主线程的Looper对象的生命周期 = 该应用程序的生命周期,它是导致于内存泄漏的原因
Message消息的种类:
同步(Barrier)屏障消息、异步消息(在handler初始化可以设置)、普通消息
同步屏障消息作用:一般该消息都为null,只是起到flag的作用。目的是忽略同步消息让异步消息先执行,一般同步屏障消息是和异步消息一起使用,只是给系统级别使用,当发送异步消息处理完之后就把同步消息给移除
存消息是按照发送时间去存储的,取出消息的顺序是:是否为同步屏障消息作为判断,遍历单向链表,优先取出异步消息,如果没有,再按照时间顺序去取出普通消息
Handler常见几个面试题:
1.Looper.loop里死循环为什么没有卡死
主线程会进入循环状态,它循环的是线程的生命周期,它结束了这个功能也就结束了,等待其他消息再进来,没有消息时候它会进入休眠,会释放cpu并不会占用很大的消耗
1.1. handler是如何阻塞的?
MessageQueue.next()里面的nativePollOnce这个方法里面,里面有个nextPollTimeoutMillis整形变量,如果它!= 0就会进入阻塞状态,如果为-1,它就会进入休眠状态,并且释放cpu。
2.post和sendMessage的区别
post发送是Runnable对象,然后封装成Message对象,主要区别是dispatchMessage这个函数时候,handleCallBack会比handleMessage的对象的优先级高,也就是post比sendMessage的使用优先级高
3.message.obtain和new Message的区别
Message.obtain()是一种享元设计模式,采用单向链表数据结构存储消息,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后系统放入缓存池,会占用很多内存,造成内存浪费
4.handler发送延迟原因:
通过updateTime方法,获取当前时间戳,时间戳顺序来存储到MessageQueue里面
5.SyncBarrier是什么以及作用:
它是同步屏障消息,作用是跳过所有的同步消息,然后尽快处理异步消息,提高优先级
6.如何让子线程弹出Toast的方法:
需要调用looper.prepare以及Looper.loop函数,需要最后手动调用Looper.quitSafely方法才能退出,否则线程不会结束
7. 发送一个异步消息
Message msg = handler.obtainMessage();
msg.setAsynchronous(true) //设置异步消息
msg.sendMessage() ......
十一、HandlerThread、ThreadLocal的工作原理
ThreadLocal的工作原理
ThreadLocal的作用是:简化了参数的传递和数据的获取,线程是不安全的,它是对ThreadLocalMap数据模型进行封装
数据模型
四种引用:强引用、弱引用、软应用、虚引用
弱引用回收的机制:当强引用删除时候,GC才对弱引用进行回收,不在GC内存不足才对它回收
key是弱引用,value是强引用
十二、SurfaceView和TextureView的区别
https://www.zhihu.com/question/30922650 :有篇文章进行讲解
1.SurfaceView的理解:
普通的View共享一个Window,它根据DecorView找到对应的Windows,Window找到对应的WindowState,WindowState又对应一个Surface,所以所有的view的DecorView会共享一个Surface。SurfaceView就自己会单独有一个属于自己的Surface,然后又可以拿到Canvas对象,这样就可以绘制画面,这也就是为什么它可以在子线程自己单独绘制原因
2.SurfaceView双缓冲机制
双缓冲:在运用时可以理解为:SurfaceView在更新视图时用到了两张Canvas,一张frontCanvas和一张backCanvas,每次实际显示的是frontCanvas,backCanvas存储的是即将显示的视图。前端缓冲区是正在渲染的图形缓冲区,而后端缓冲区是接下来要渲染的图形缓冲区。把要画的东西先画到一个内存区域里,然后整体的一次性画出来,好处是反复局部刷屏带来的闪烁
3.TextureView的理解:
它可以将内容流直接投影到View,和SurfaceView不同,它不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化。值得注意的是TextureView必须在硬件加速的窗口中,它显示的内容流数据可以来自App进程或是远端进程
4.SurfaceView、TextureView的区别
SurfaceView的优缺点:
优点:可以在一个独立的线程中进行绘制,不会影响主线程,使用双缓冲机制,播放视频时画面更流畅
缺点:Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中。SurfaceView 不能嵌套使用
TextureView的优缺点
优点:支持移动、旋转、缩放等动画,支持截图
缺点:必须在硬件加速的窗口中使用,占用内存比SurfaceView高,在5.0以前在主线程渲染,5.0以后有单独的渲染线程。
十三、onNewIntent 的使用以及注意事项
解释 : 如果一个Activity已经启动过,并且存在当前应用的Activity任务栈中,为了防止它再次启动创建新的实例,就会走onNewInetnt这个方法
1.当ActivityA的LaunchMode为SingleTop时:
如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent()方法 ,生命周期顺序为:onCreate--->onStart--->onResume---onPause>onNewIntent--->onResume
2.当ActivityA的LaunchMode为SingleInstance,SingleTask:如果ActivityA已经在堆栈中,那么此时会调用onNewIntent()方法,生命周期调用顺序为:
onCreate--->onStart--->onResume---按下Home键>onPause--->onstop--->onNewIntent--->onRestart--->onstart--->onResume
3.这里面有个注意事项
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);//设置新的intent
int data = getIntent().getIntExtra("tanksu", 0);//此时的到的数据就是正确的了
}
在这里,如果你没有调用setIntent()方法的话,则getIntent()获取的intent都只会是最初那个intent,这里很重要
十四、Android动画几种方式
帧动画、补间动画、属性动画
帧动画可以通过顺序播放资源来实现动画的,补间动画可以实现控件的渐入渐出、移动、旋转和缩放效果,以上两种只是针对于view上,且不能实现复杂动画,而属性动画既可以在作用在view上也可以作用在对象上,还可以制作很复杂的动画显示效果
十五、application和activity的context区别
Application的context的生命周期是整个应用结束后
activity的context的生命周期是当前页面的生命周期
十六、Intent 为什么无法传递大数据
因为它底层是是由Binder实现,Binder底层开辟内存大部分是1024*1024
Intent和Bundle区别
Bundle可对对象进行操作,而Intent是不可以,Bundle相对于Intent拥有更多的接口,用起来比较灵活,但是使用Bundle也还是需要借助Intent才可以完成数据传递总之,Bundle旨在存储数据,而Intent旨在传值。
十七、Android的类加载机制
1.什么是双亲委派机制
首先判断parent的loadClass是否加载过,如果没有,一直找到最顶级parent去处理;如果到最顶级的parent都没有找到,则去交换给子的class去调用findClass去处理
它的作用 : 防止.class文件被重复加载、保证一个类在JVM虚拟机中具有唯一性、保证系统的.class文件不能被修改
2.Android主要几个加载器
PathClassLoader、DexClassLoader、BaseClassLoader、BootClassLoader
3.PathClassLoader和DexClassLoader的区别
PathClassLoader : 负责app的应用程序的class的加载和资源文件的加载
DexClassLoader :
他们在8.0以后没有区别,8.0以前的区别是:DexClassLoader不为空,PathClassLoader为空,optimizedDirectory作用是存放优化后的dex,DexClassLoader存放优化后的dex位置可以自己定义,而PathClassLoader为空,只能默认存储系统的位置
冷启动优化的点:BaseClassLoader里面有个Element的数组,里面就是所有的.dex的文件,它需要通过dexElement.findClass遍历整个数组,找到对应的.class才能启动,把我们要先启动的.class放在最前面,这样就可以加快启动速度。
4.Class文件不同的加载方式
为什么静态变量不能加载非静态变量呢?
类加载到内存步骤:加载、链接、初始化
加载:查找并加载类的二进制数据
链接:验证(是否为java文件)、准备(包含初始化静态变量和函数-->static)、解析
初始化:给静态变量赋值
5.Class.forName和ClassLoader.loaderClass的区别
class.forName会初始化静态变量和初始化过程,ClassLoader.loaderClass不会有,只是把.class文件加载到jvm内存中
十八、ActivityLifecycleCallBack去监听生命周期函数的回调,有时间去学习
DecorView->Activity->Window->DecorView->ViewGroup
主线程的Looper对象的生命周期 = 该应用程序的生命周期
十九、System.currentTimeMillis和SystemClock.elapsedRealtime区别
System.currentTimeMillis : 获取当前系统时间戳,可以修改
SystemClock.elapsedRealtime : 从设备boot后经历的时间值,不可以修改
网友评论