Android面试考点

手机游戏开发者 2024-10-2 23:09:05 255 0
1、Activity、Dialog、PopupWindow、Toast 与Window的关系

简单的从创建方式的角度来说一说:
Activity。在Activity创建过程中所创建的PhoneWindow,是层级最小的Window,叫做应用Window,层级范围1-99。(层级范围大的Window可以覆盖层级小的Window)
Dialog。Dialog的表现过程和Activity根本雷同,也是创建了PhoneWindow,初始化DecorView,并将Dialog的视图添加到DecorView中,终极通过addView表现出来。
但是有一点差别的是,Dialog的Window并不是应用窗口,而是子窗口,层级范围1000-1999,子Window的表现必须依附于应用窗口,也会覆盖应用级Window。这也就是为什么Dialog传入的上下文必须为Activity的Context了。
PopupWindow。PopupWindow的表现就有所差别了,它没有创建PhoneWindow,而是直接创建了一个View(PopupDecorView),然后通过WindowManager的addView方法表现出来了。
没有创建PhoneWindow,是不是就跟Window不要紧了呢?
并不是,实在只要是调用了WindowManager的addView方法,那就是创建了Window,跟你有没有创建PhoneWindow无关。View就是Window的表现情势,只不外PhoneWindow的存在让Window形象更立体了一些。
以是PopupWindow也是通过Window展示出来的,而它的Window层级属于子Window,必须依附与应用窗口。
Toast。Toast和PopupWindow比力像,没有新建PhoneWindow,直接通过addView方法表现View即可。差别的是它属于系统级Window,层级范围2000-2999,以是无须依附于Activity。
四个比力下来,可以发现,只要想表现View,就会涉及到WindowManager的addView方法,也就用到了Window这个概念,然后会根据差别的分层依次表现覆盖到界面上。
差别的是,Activity和Dialog涉及到告终构比力复杂,还会有结构主题等元素,以是用到了PhoneWindow举行一个解耦,资助他们管理View。而PopupWindow和Toast结构比力简单,以是直接新建一个雷同DecorView的View,通过addView表现到界面。
2、onSaveInstanceState()什么时间会被调用呢?

概括的讲,onSaveInstanceState 这个方法会在activity 将要被kill之前被调用以生存每个实例的状态,以包管在未来的某个时候返来时可以规复到原来的状态,但和activity 的生命周期方法onStop 和 onPause 不一样,与两者并没有绝对的先后调用次序,大概说并非全部场景都会调用onSaveInstanceState 方法。
那么onSaveInstanceState 方法何时会被调用呢,大概这么问,什么时间activity 会被系统kill 掉呢?
有以下几种比力常见的场景:
(1)用户自动按下home 键,系统不能确认activity 是否会被烧毁,实际上现在系统也无法猜测未来的场景,好比说内存占用,应用运行情况等,以是系统会调用onSaveInstanceState生存activity状态 ;
(2)activity位于前台,按下电源键,直接锁屏;
(3)横竖屏切换;
(4)activity B启动后位于activity A之前,在某个时候activity A由于系统接纳资源的题目要被kill掉,A通过onSaveInstanceState生存状态。
换句话说,onSaveInstanceState()的调用依照一个告急原则,即当系统存在“未经你答应”时烧毁了我们的Activity,则onSaveInstanceState()会被系统调用,这是系统的职责,由于它必须要提供一个时机让用户生存数据。
3、Android 数据持久化之 SharedPreferences

Android之SharedPreferences内部原理浅析
分析 SharedPreference apply 引起的 ANR 题目
总结:

  • sSharedPrefsCache 是一个 ArrayMap<String,ArrayMap<File,SharedPreferencesImpl>>,它会生存加载到内存中的 SharedPreferences 对象,ContextImpl 类中并没有界说将 SharedPreferences 对象移除 sSharedPrefsCache 的方法,以是一旦加载到内存中,就会存在直至进程烧毁。相对的,也就是说,SP 对象一旦加载到内存,背面任何时间使用,都是从内存中获取,不会再出现读取磁盘的情况
  • SharedPreferences 和 Editor 都只是接口,真正的实现在 SharedPreferencesImpl 和 EditorImpl ,SharedPreferences 只能读数据,它是在内存中举行的,Editor 则负责存数据和修改数据,分为内存利用和磁盘利用
  • 获取 SP 只能通过 ContextImpl#getSharedPerferences 来获取,它内里起首通过 mSharedPrefsPaths 根据传入的 name 拿到 File ,然后根据 File 从 ArrayMap<File, SharedPreferencesImpl> cache 里取出对应的 SharedPrederenceImpl 实例
  • SharedPreferencesImpl 实例化的时间会启动子线程来读取磁盘文件,但是在此之前假如通过 SharedPreferencesImpl#getXxx 大概 SharedPreferences.Editor 会壅闭 UI 线程,由于在从 SP 文件中读取数据大概往 SP 文件中写入数据的时间必须等候 SP 文件加载完
  • 在 EditorImpl 中 putXxx 的时间,是通过 HashMap 来存储数据,提交的时间分为 commit 和 apply,它们都会把修改先提交到内存中,然后在写入磁盘中。只不外 apply 是异步写磁盘,而 commit 可能是同步写磁盘也可能是异步写磁盘,在于前面是否尚有写磁盘任务。对于 apply 和 commit 的同步,是通过 CountDownLatch 来实现的,它是一个同步工具类,它答应一个线程或多个线程划一等候,直到其他线程的利用实行完之后才实行
  • SP 的读写利用是线程安全的,它对 mMap 的读写利用用的是同一把锁,考虑到 SP 对象的生命周期与进程划一,一旦加载到内存中就不会再去读取磁盘文件,以是只要包管内存中的状态是划一的,就可以包管读写的划一性
留意事项以及优化建议


  • 剧烈建议不要在 SP 内里存储特殊大的 key/value ,有助于淘汰卡顿 / ANR
  • 请不要高频的使用 apply,尽可能的批量提交;commit 直接在主线程利用,更要留意了
  • 不要使用 MODE_MULTI_PROCESS
  • 高频写利用的 key 与高频读利用的 key 可以适当的拆分文件,以淘汰同步锁竞争
  • 不要一连多次 edit,每次 edit 就是打开一次文件,应该获取一次 edit,然后多次实行 putXxx,淘汰内存颠簸,以是在封装方法的时间要留意了
  • apply 在 QueueWork 维护的单线程池调用,固然是异步的但是可能会壅闭 Service.onStop 和 Activity.onPause 方法,可能会导致 ANR
ANR 容易发生的地方:

  • sp.getXxx,起首会调用 awaitLoadedLocked 等候初次 sp 文件创建与读取利用完成
  • sp.apply 固然是异步的但是可能会在 Service Activity 等生命周期期间 mcr.writtenToDiskLatch.await() 等候过久
  • sp.commit 终极会调用 sp.writeToFile 方法,很耗时
  • ContextImpl.getSharedPreferences,主线程直接调用的话,假如 sp 文件很大处理惩罚时间也就会变成
4、Activity的启动过程

应用启动过程

  • Launcher通过Binder进程间通讯机制通知AMS,它要启动一个Activity
  • AMS通过Binder进程间通讯机制通知Launcher进入Paused状态
  • Launcher通过Binder进程间通讯机制通知AMS,它已经预备停当进入Paused状态,于是AMS就创建一个新的线程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行
  • ActivityThread通过Binder进程间通讯机制将一个ApplicationThread范例的Binder对象通报给AMS,以便以后AMS能够通过这个Binder对象和它举行通讯
  • AMS通过Binde进程间通讯机制通知ActivityThread,现在统统预备停当,它可以真正实行Activity的启动利用了
5、Service生命周期

startService() --> onCreate() --> onStartCommand() --> Service running --> onDestory()
bindService() --> onCreate() --> onBind() --> Service running --> onUnbind() --> onDestory()
onCreate():
系统在Service第一次创建时实行此方法,来实行只运行一次的初始化工作,假如service已经运行,这个方法不会调用。
onStartCommand():
每次客户端调用startService()方法启动该Service都会回调该方法(多次调用),一旦这个方法实行,service就启动而且在配景恒久运行,通过调用stopSelf()或stopService()来克制服务。
onBind():
当组件调用bindService()想要绑定到service时,系统调用此方法(一次调用),一旦绑定后,下次在调用bindService()不会回调该方法。在你的实现中,你必须提供一个返回一个IBinder来使客户端能够使用它与service通讯,你必须总是实现这个方法,但是假如你不答应绑定,那么你应返回null
onUnbind():
当前组件调用unbindService(),想要排除与service的绑定时系统调用此方法(一次调用,一旦排除绑定后,下次再调用unbindService()会抛非常)
onDestory():
系统在service不在被使用而且要烧毁的时间调用此方法(一次调用)。service应在此方法中释放资源,好比线程,已注册的监听器、吸取器等等。
三种情况下Service的生命周期


  • startService / stopService
    生命周期:onCreate --> onStartCommand --> onDestory
    假如一个Service被某个Activity调用Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService排除绑定到该Service,该Service都在配景运行,直到被调用stopService,或自身的stopSelf方法。固然假如系统资源不敷,Android系统也可能竣事服务,尚有一种方法可以关闭服务,在设置中,通过应用 --> 找到本身应用 --> 克制。
    留意:
    第一次startService会触发onCreate和onStartCommand,以后在服务运行过程中,每次startService都只会触发onStartCommand
    岂论startService多少次,stopService一次就会克制服务
  • bindService / unbindService
    生命周期:onCreate --> onBind --> onUnbind --> onDestory
    假如一个Service在某个Activity中被调用bindService方法启动,岂论bindService被调用频频,Service的onCreate方法只会实行一次,同时onStartCommand方法始终不会调用。
    当创建连接后,Service会不停运行,除非调用unbindService来排除绑定、断开连接或调用该Service的Context不存在了(如Activity被finish --- 即通过bindService启动的Service的生命周期依附于启动它的Context),系统会在这时间自动克制该Service。
    留意:
    第一次bindService会触发onCreate和inBind,以后在服务运行过程中,每次bindService都不会触发任何回调
  • 混淆型
    当一个Service再被启动(startService)的同时又被绑定(bindService),该Service将会不停在配景运行,不管调用频频,onCreate方法始终只会调用一次,onStartCommand的调用次数与startService调用的次数划一(使用bindService方法不会调用onStartCommand)。同时,调用unBindService将不会克制Service,必须调用stopService或Service自身的stopSelf来克制服务。
三种情况下的应用场景

假如你只是想启动一个配景服务恒久举行某项任务,那么使用startService便可以了。
假如你想与正在运行的Service取的接洽,那么有两种方法,一种是使用broadcast,别的是使用bindService。前者的缺点是假如互换较为频仍,容易造成性能上的题目,而且BroadcastReceiver本身实行代码的时间是很短的(大概实行到一半,背面的代码便不会实行),而后者则没有这些题目,因此我们肯定选择使用bindService(这个时间便同时使用了startService和bindService了,这在Activity中更新Service的某些运行状态是相称有用的)
假如你的服务只是公开了一个长途接口,供连接上的客户端(Android的Service是C/S架构)长途调用实行方法。这个时间你可以不让服务一开始就运行,而只用bindService,如许在第一次bindService的时间才会创建服务的实例运行它,这会节省很多系统资源,特殊是假如你的服务是Remote Service,那么该效果会越明显。
6、BroadcastReceiver

应用场景:

  • 差别组件之间的通讯(包罗应用内 / 差别应用之间)
  • 与Android系统在特定情况下的通讯,如当电话呼入时,网络可用时
  • 多线程通讯
实现原理


  • 使用了观察者模式:基于消息的发布/订阅变乱模子。
  • 模子中有三个脚色:消息订阅者(广播吸取者)、消息发布者(广播发布者)和消息中心(AMS,即Activity Manager Service)
    [图片上传中...(image-100ae0-1663300893576-0)]
  • 原理形貌

    • 广播吸取者通过Binder机制在AMS注册
    • 广播发送者通过Binder机制向AMS发送广播
    • AMS根据广播发送者要求,在已注册列表中,寻找符合的广播吸取者,寻找依据:IntentFilter / Permission
    • AMS将广播发送到符合的广播吸取者相应的消息循环队列
    • 广播吸取者通过消息循环拿到此广播,并回调onReceive()
    留意:广播发送者和广播吸取者的实行是异步的,发出去的广播不会关心有没有吸取者吸取,也不确定吸取者何时能担当到。
    广播的范例告急分为5类:

    • 平常广播(Normal Broadcast)
    • 系统广播(System Broadcast)
    • 有序广播(Ordered Broadcast)
    • 粘性广播(Sticky Broadcast)Android 5.0 & API 21中已经失效
    • App应用内广播(Local Broadcast)

动态广播最好在Activity的onResume()注册,onPause()注销,否则会导致内存走漏,固然,重复注册和重复注销也不答应。
7、ContentProvider

作用

进程间举行数据交互&共享,即跨进程通讯
2.png 原理

ContentProvider的底层接纳Android中的Binder机制
同一资源标识符(RUI)

作用:唯一标识ContentProvider & 其中的数据,外界进程通过URI找到对应的ContentProvider & 其中的数据,再举行数据利用
详细使用:
URI分为系统预置 & 自界说,分别对应系统内置的数据(如通讯录、日程表等等)和自界说数据库。
8、Context

Android应用模子是基于组件的应用筹划模式,组件的运行要有一个完备的Android工程情况。在这个工程情况下,Activity、Service等系统组件才气够正常工作,而这些组件并不能接纳平常的Java对象创建方式,new一下就能创建实例了,而是要有它们各自的上下文情况,也就是Context,Context是维持Android步调中各组件能够正常工作的一个焦点功能类。
怎样生动形象的明白Context?
一个Android步调可以明白为一部影戏,Activity、Service、BroadcastReceiver和ContentProvider这四大组件就好比戏了的四个主角,它们是剧组(系统)一开始定好的,主角并不是大街上恣意拉个人(new 一个对象)都能演的。有了演员固然也得有摄像机拍摄啊,它们必须通过镜头(Context)才气将戏传给观众,这也就正对应说四大组件必须工作在Context情况下。那么Button、TextView等等控件就相称于群演,显然没那么重用,恣意一个路人甲都能演(可以new一个对象),但是它们也必须在面对镜头(工作在Context情况下),以是Button mButtom = new Button(context) 是可以的。
[图片上传中...(image-df38f2-1663300893577-4)]
它有两个详细实现类:ContextImpl和ContextWrapper。
其中ContextWrapper类,是一个包装类而已,ContextWrapper构造函数中必须包罗一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象指定真正的Context对象,调用ContextWrapper的方法都会被转向其包罗的真正的Context对象。ContextThemeWrapper类,其内部包罗了与主题Theme相干的接口,这里所说的主题就是指在AndroidManifest,xml中通过android:theme为Application元素大概Activity元素指定的主题。固然,只有Activity才必要主题,Service是不必要主题的,以是Service直接继承与ContextWrapper,Application同理。而ContextImpl类则真正实现了Context中的全部函数,应用步调中所调用的各种Context类的方法,实在现均泉源于该类。Context得两个子类分工明白,其中ContextImpl是Context的详细实现类,ContextWrapper是Context的包装类。 Activity、Application、Service虽都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们初始化的过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法。
一个应用步调有几个Context?
在应用步调中Context的详细实现子类就是:Activity、Service和Application。那么Context数目=Activity数目+Service数目+1。那么为什么四大组件中只有Activity和Service持有Context呢?BroadcastReceiver和ContextPrivider并不是Context的子类,它们所持有的Context都是其他地方传已往的,以是并不计入Context总数。
Context的作用域
固然Context神通广大,但并不是恣意拿到一个Context实例就可以为所欲为,它的使用还是有一些规则限定的。由于Context的详细实例是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种范例的Context都是可以通用的。不外有几种场景比力特殊,好比启动Activity,尚有弹出Dialog。出于安全缘故原由的考虑,Android是不答应Activity或Dialog凭空出现的,一个Activity的启动必须要创建在另一个Activity的根本之上,也就是以此形成返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert范例的Dialog),因此在这种场景下,我们只能使用Activity范例的Context,否则将会报错。
[图片上传中...(image-d795d0-1663300893576-2)]
从上图我们可以发现Activity所持有的Context的作用域最广,无所不能,因此Activity继承至ContextThemeWrapper,而Application和Service继承至ContextWrapper,很显然ContextThemeWrapper在ContextWrapper的根本上又做了一些利用使得Activity变得更强盛。侧重讲一下不保举使用的两种情况:

  • 假如我们用ApplicationContext去启动一个LaunchMode为standard的Activity的时间会报错:
    android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
    这是由于非Activity范例的Context并没有所谓的任务栈,以是待启动的Activity就找不到栈了。解决这个题目的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,如许启动的时间就为它创建一个新的任务栈,而此时Activity是以singleTask模式启动的。全部这种用Application启动Activity的方式都不保举,Service同Application。
  • 在Application和Service中去LayoutInflate也是合法的,但是会使用系统默认的主题样式,假如你自界说了某些样式可能不会被使用,这种方式也不保举使用。
一句话总结:凡是跟UI相干的,都应该使用Activity作为Context来处理惩罚;其他的一些利用,Service、Activity、Application等实例都可以,固然了留意Context引用的持有,防止内存走漏。
怎样获取Context?
有四种方法:

  • View.getContext 返回当前View对象的Context对象,通常是当前正在展示的Activity对象。
  • Activity.getApplicationContext 获取当前Activity所在的进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。
  • ContextWrapper.getBaseContext() 用来获取一个ContextWrapper举行装饰之前的Context,可以使用这个方法,这个方法在实际开辟中使用的不多,也不建议使用。
  • Activity.this 返回当前Activity实例,假如是UI控件必要使用Activity作为Context对象,但是默认的Toast实际上使用ApplicationContext也可以。
getApplication()和getApplicationContext()的区别?
其内存地址是一样的。Application本身就是一个Context,这里获取getApplicationContext得到的效果就是Application本身的实例。getApplication方法的语义性很强,就是用来获取Application实例的,但是这个方法只有在Activity和Service中才气调用的到。那么大概在绝大多数情况下我们都是在Activity大概Service中使用Application,但是假如在一些其他的场景,好比BroadcastReceiver中也想获取Application实例,这时就可以借助getApplicationContext方法了。
9、Android APK编译打包流程

[图片上传中...(image-e1bab6-1663300893576-1)]

  • AAPT(Android Asset Packaging Tools)工具会打包应用中的资源文件,如AndroidManifest.xml、layout结构中的xml等,并将xml文件编译成二进制情势,固然assets文件夹中的文件不会被编译,图片以及raw文件夹中的资源也会保持原有的形态,必要留意的是raw文件夹中的资源也会天生资源ID。AAPT编译完成后会天生R.java文件。
  • AIDL工会将全部的aidl接口转换为java接口。
  • 全部的Java源代码、R文件、接口都会编译器编译成.class文件。
  • Dex工具会将上述产生的.class文件以及第三方库和其他class文件转化为dex(Dalvik假造机可实行文件)文件,dex文件终极会被打包进APK文件。
  • apkbuilder会把编译后的资源和其他资源文件同dex文件一起打入APK中。
  • 天生APK文件之后,,必要对其签名才气安装到装备上,平常测试都会使用debug keystore,当发布应用时必须使用release版的keystore对应用举行签名。
  • 假如对APK正式签名,还必要使用zipalign工具对APK举行对齐利用,如许做的利益是当应用运行时能进步速率,但是会相应的增长内存开销。
总结:编译 --> DEX --> 打包 --> 签名和对齐
10、Window、Activity、DecorView以及ViewRoot之间的关系

Activity
Activity并不负者视图控制,它只是控制生命周期和处理惩罚变乱。真正控制视图的是Window。一个Activity包罗了一个Window,Window才是真正代表一个窗口。Activity就像一个控制器,统筹视图的添加与表现,以及通过其他回调方法,来与Window以及View举行交互。
Window
Window是视图的承载器,内部持有一个DecorView,而这个DecorView才是view的跟结构。Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的结构。Window通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,举行视图绘制以及其他交互。
DecorView
DecorView是FrameLayout的子类,它可以被以为是Android视图树的根节点视图。
DecorView作为顶级View,一样平常情况下它内部包罗一个竖直方向的LinearLayout,在这个LinearLayout内里有上下三个部门,上面是个ViewStub,延伸加载的视图(应该是设置ActionBar,根据Theme设置),中心的是标题栏(根据Theme设置,有的结构没有),下面是内容栏。在Activity中通过setContentView所设置的结构文件实在就是被加到内容栏之中的,成为其唯一子View。
ViewRoot
ViewRoot可能比力陌生,但是其作用非常重大。全部View的绘制以及变乱分发等交互都是通过它来实行或通报的。
ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(丈量、结构、绘制)均通过ViewRoot来完成。
ViewRoot并不属于View树的一份子。从源码实现上来看,它既优劣View的子类,也优劣View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。RootView继承了Handler类,可以吸取变乱并分发,Android的全部触屏变乱,按键变乱、界面革新等变乱都是通过ViewRoot来举行分发的。
[图片上传中...(image-6f8e3a-1663300893576-3)]
总结

Activity就像个控制器,不负责视图部门。Window像个承载器,装着内部视图。DecorView就是个顶级视图,是全部View的最外层结构。ViewRoot像个连接器,负者沟通,通过硬件感知来通知视图,举行用户之间的交互。
11、Assets目次与res目次的区别

assets目次与res下的raw、drawable目次一样,也可用来存放资源文件,但它们三者区别如下:

3.png res/raw和assets的区别:
res/raw中的文件会被映射到R.java文件中,访问的时间直接使用资源ID即可,assets文件夹下的文件不会被映射到R文件中,
访问的时间必要AssetManager类。
res/raw不可以有目次结构,而assets则可以有目次结构,也就是assets目次下可以再创建文件夹。
读取res/raw下的文件资源,通过以下方式获取输入流来举行写利用:
InputStream is = getResources().openRawResource(R.id.filename)
留意:
AssertManager中不能处理惩罚单个高出1M的文件,而raw没有这个限定
assets文件夹是存放不举行编译加工的原生文件,即该文件夹内里的文件不会像xml、java文件被预编译,可以存放一些图片、html、js等等
112、View视图绘制过程原理

View视图绘制必要搞清楚两个题目,一个是从那边开始绘制,一个是怎么绘制?
从那边开始绘制?我们寻常使用Activity的时间,都会调用setContentView来设置结构文件,没错,视图绘制就是从这个方法开始。
怎么绘制?
在我们的Activity中调用了setContentView之后,会转而实行PhoneWindow的setContentView,在这个方法内里会判定我们存放内容的ViewGroup(这个ViewGroup可以是DecorView也可以是DecorView的子View)是否存在。不存在的话,则会创建一个DecorView处理惩罚,而且会创建出相应的窗体风格,存在的话则会删除原先的ViewGroup上面已有的View,接着会调用LayoutInflater的inflate方法以pull分析的方式将当前结构文件中存在的View通过addView的方式添加到ViewGroup上面来,接着在addView方法内里就会实行我们常见的invalidate方法了,这个方法不但是在View视图绘制的过程中经常用到,实在动画的实现原理也是不停的调用这个方法来实现视图不停重绘的,实行这个方法的时间会调用父View的invalidateChild方法,这个方法是属于ViewParent的,ViewGroup以及ViewRootImpl中都会他举行了实现,invalidateChild内里告急做的是就是通过do while循环一层一层盘算出当前View的四个点所对应的矩阵在ViewRoot中所对应的位置,那么有了这个矩阵的位置之后终极都会实行ViewRootImpl的invalidateChildInParent方法,实行这个方法的时间起首会查抄当火线程是不是主线程,由于我们要开始预备更新UI了,不是主线程的话是不答应更新UI的,接着就会实行scheduleTraversals方法了,这个方法会通过handler来实行doTraversal方法,在这个方法内里就见到了我们寻常所认识的View视图绘制的出发点方法performTraversals了。
那么接下来就是真正的视图绘制流程了,大要上讲View的绘制流程履历了Measure丈量、Layout结构以及Draw绘制的三个过程,详细来讲是从ViewRootImpl的performTraversals方法开始,起首实行的将是performMeasure方法,这个方法内里会传入两个MeasureSpec范例的参数,它在很大水平上决定了View的尺寸规格,对于DecorView来说宽高的MeasureSpec值的获取与窗口尺寸以及自身的LayoutParams有关,对于平常View来说其宽高的MeasureSpec值获取由父容器以及自身的LayoutParams属性共同决定,在performMeasure内里会实行measure方法,在measure方法内里会实行onMeasure方法,到这里Measure丈量过程对View与ViewGroup来说是没有区别的,但是从onMeasure开始两者有差别了,由于View本身已经不存在子View了,以是他onMeasure方法将实行setMeasuredDimension方法,该方法会设置View的丈量值,但是对于ViewGroup来说,由于它内里还存在着子View,那么我们就必要继承丈量它内里的子View了,调用的方法是measureChild方法,该方法内部又会实行measure方法,而measure方法转而又会实行onMeasure方法,如许不停的递归举行下去,直到整个View树丈量竣事,如许performMeasure方法实行竣事了。接着便是实行performLayout方法了,performMeasure只是丈量出了View树中View的巨细了,但是还不知道View的位置,以是也就出现了performLayout方法了,performLayout方法起首会实行layout方法,以确定View自身的位置,假如当前View是ViewGroup的话,则会实行onLayout方法。在onLayout方法内里又会递归的实行layout方法,直到当前遍历到的View不再是ViewGroup为止,如许整个layout结构过程就竣事了。在View树中View的巨细以及位置都确定之后,接下来就是真正的绘制View表现在界面的过程了,该过程起首从performDraw方法开始,performDraw起首会实行draw方法,在draw方法中起首绘制配景,接着调用onDraw方法绘制本身,假如当前View是ViewGroup的话,还要调用dispatchDraw方法绘制当前ViewGroup的子View,而dispatchDraw方法内里实际上是通过drawChild方法间接调用draw方法形成递归绘制整个View树,直到当前View不再是ViewGroup为止,如许整个View的绘制过程就竣事了。
总结:

  • ViewRootImpl会调用performTraversals(),其内部会调用performMeasure()、performLayout、performDraw
  • performMeasure会调用最外层的ViewGroup的measure() --> onMeasure() ,ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),这之中会遍历子View然后循环调用measureChild(),传入MeasureSpec参数,然后调用子View的measure()到View的onMeasure() -->setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()缄默返回measureSpec的丈量数值,以是继承View举行自界说的wrap_content必要重写。
  • performLayout()会调用最外层的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()设置本View的四个极点位置。在onLayout(抽象方法)中确定子View的位置,如LinearLayout会遍历子View,循环调用setChildFrame() --> 子View.layout()
  • performDraw()会调用最外层的ViewGroup的draw()方法,其中会先后调用background.draw()绘制配景,onDraw(绘制本身),dispatchDraw(绘制子View)、onDrawScrollBars(绘制装饰)
  • MeasureSpec由两位SpecMode(UNSPECIFIED、EXACTLY(对于准确值和match_parent)、AL_MOST(对应warp_content))和三十位SpecSize组成一个int,DecorView的MeasureSpec由窗口巨细和其LayoutParams决定,其他View有父View的MeasureSpec和本View的LayoutParams决定。ViewGroup中有getChildMeasureSpec()来获取子View的MeasureSpec。
113、IntentService

IntentService是继承并处理惩罚异步哀求的一个类,其本质上是一个Service,由于它是继承至Service,以是开启IntentService宁静常的Service划一。但是他宁静常的Service差别之处在于它可以处理惩罚异步任务,在任务处理惩罚完之后会自动竣事。别的,我们可以启动多次IntentService,而每一个耗时任务会以工作队列的方式在IntentService的onHandleIntent回调方法中实行,而且是串行实行。实在IntentService的内部是通过HandleThread和Handle来实现异步利用的。
14、requestLayout、invalidate、postInvalidate 的区别


  • requestLayout 会回掉 onMeasure、onLayout、onDraw(ViewGroup.setWillNotDraw(fasle)情况下)方法
  • invalidate 只会回掉 onDraw 方法
  • postInvalidate 只会回掉 onDraw 方法(可以在非 UI 线程中调用)
15、深入明白Android插件化技能

16、美团外卖Android Crash管理之路

17、对 Activity.runOnUiThread 的明白

当火线程不是ui线程,即发送post消息切换到ui线程(这个和sendMessage是有区别的,sendMessage是在非ui线程发送消息,这个在当火线程发送消息,然后由于activity初始化的时间就有looper、和MessageQueue,就能直接处理惩罚消息,从而将mUiThread切换到当火线程,再次实行就直接举行action.run())
是ui线程,即直接实现方法
18、什么是 RemoteViews?使用场景有哪些?

RemoteViews
RemoteViews翻译过来就是长途视图.顾名思义,RemoteViews不是当进步程的View,是属于SystemServer进程.应用步调与RemoteViews之间依靠Binder实现了进程间通讯.
用法
通常是在通知栏
//1.创建RemoteViews实例
    RemoteViews mRemoteViews=new RemoteViews("com.example.remoteviewdemo", R.layout.remoteview_layout);    //2.构建一个打开Activity的PendingIntent    Intent intent=new Intent(MainActivity.this,MainActivity.class);    PendingIntent mPendingIntent=PendingIntent.getActivity(MainActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);    //3.创建一个Notification    mNotification = new Notification.Builder(this)    .setSmallIcon(R.drawable.ic_launcher)    .setContentIntent(mPendingIntent)    .setContent(mRemoteViews)    .build();    //4.获取NotificationManager    manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);    Button button1 = (Button) findViewById(R.id.button1);    button1.setOnClickListener(new OnClickListener() {        @Override        public void onClick(View v) {            //弹出通知            manager.notify(1, mNotification);        }    });19、谈谈 AIDL

AIDL 是一种辅助工具,不消AIDL ,一样可以实现跨进程通讯
AIDL 的原理是binder,真正有跨进程通讯本事的也是 Binder,以是 AIDL 只是一个能帮你少写代码,少堕落的辅助工具,由于筹划的太好,使用太方便,以是非经常用
就像 retrofit 和okhttp 关系一样, retrofit 提供 更加友好的api,真正的网络哀求还是由 okhttp发起的
20、Android进程间的通讯方式?

21、Binder机制:

1.为了包管进程空间不被其他进程破坏或干扰,Linux中的进程是相互独立或相互隔离的。
2.进程空间分为用户空间和内核空间。用户空间不可以举行数据交互;内核空间可以举行数据交互,全部进程共用一个内核空间。
3.Binder机制相对于Linux内传统的进程间通讯方式:(1)性能更好;Binder机制只必要拷贝数据一次,管道、消息队列、Socket等都必要拷贝数据两次;而共享内存固然不必要拷贝,但实现复杂度高。(2)安全性更高;Binder机制通过UID/PID在内核空间添加了身份标识,安全性更高。
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接口),即具备了跨进程通讯的本事。
起首 全部的server都会在 serviceManager 中注册
client 访问 server时 要向 serviceManager 发起哀求,
serviceManager 找到 这个 server 并通过 binder驱动 天生一个 server署理 返回给client
client得到了 署理之后 访问 署理server
署理server 相应方法被调用后 会向 binder驱动中去调用真正的server方法。
评论
登录 后才能评论

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2025-8-23 20:12, Processed in 0.137493 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表