微信截图_20230512153226.png
1:int、Integer有什么区别?
Integer是int的包装类,默认是null;int是基本数据类型,默认值是0;
2:equals与==区别,stringBuffer和stringbuilder区别
equals:比较的是两个对象是否相等
==:比较的是栈内存中的值
StringBuffer:线程安全
StringBuilder:线程不安全
既然 StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder 的性能要远大于 StringBuffer。
3:A启动B时activity的两个activity生命周期的流程。
A启动:
Aactivity oncreate onstart onResume
A进入B:
Activity onPause BActivity oncrete onstart onResume Activity onStop
B返回:
Bctivity onPause Activity restart start onResume Bactivity onStop ondestroy
A返回:
Activity onPase onstop ondestroy
4:Activity旋转会调用哪些方法?
没有任何配置的情况下:
onPause()--onStop()--onDestroy()--onCreate()--onStart()--onResume();
5:android四种启动模式
1:standard标准模式:
每次启动都会重新创建一个新的实例入栈,不管之前是否存在。
2:SingleTop:
栈顶复用模式,如果Activity处于栈顶,则不再创建新的activity,如果不存在栈顶则重新创建实例。
3:SingleTask:
栈内复用模式,当需要创建这个activity时,如果activity已经存在则将所有的在它之上的activity销毁,让它处于栈顶。
4:singleInstance:
单实例模式,具有此模式的Activity单独位于一个任务栈中。只能有一个实例。
6:startService和bindService区别,多次启动调用哪些方法?
1:生命周期上的区别:
startService:onCreate-->onStartCommand-->onStop-->onDestory
bindService:onCreate-->onBind-->unBindService-->onDestroy。
多次调用startService时service只能被创建一次,每次调用startService,onStartCommand都会执行;
第一次执行bindService时,onCreate-->onBind都会被调用,多次执行时候,onCreate-->onBind不会多次调用。
7:IO操作(断点续传原理)
RandomAccessFile:
支持对随即访问文件的读写。
8:跨进程通信方式
Content Provider
广播
Binder机制
Intent
文件读写
9:Android动画
逐帧动画(放电影,一帧一帧)
补间动画(控制平移,旋转,缩放等)
属性动画(升级版的补间动画,支持任意属性的变化)
SurfaceView实现动画。
10:invalidate()和postInvalidate()区别
invalidate会刷新整个View,只能在UI线程中调用。
postInvalidate,可以在非UI线程中去调用刷新UI,postInvalidate是通过handler将刷新事件通知到handlerMessage中执行invalidate的。
11:广播注册后不注销有什么问题?(内存泄漏)
广播在onReceiver方法中的参数Context,所以BroadCastReveiver中持有Activity,
而且广播不仅被Activity持有,可能还被ActivityManagerService等系统服务持有。
当Activity调用onDestroy方法时候,Activity并不能被回收,从而导致内存泄漏。
所以需要结束时候调用unregisterReceiver()。
onReceive()在10s内没有执行完会ANR。
12:屏幕适配方案
传统方案:
在res下建不同的values文件夹,创建不同的dimens。
今日头条:
通过反射适配系统的density(密度)值
密度=屏幕中1dp所占的多少像素点
限定符适配:
smallestwidth,通过宽高限定符,配置在manifast.xml中,程序启动时候读取xml设值。
13:如何监听activity是否后台切换到前台?不是在onReusme()中处理。
1:ActivityManager中处理:
getRunningTasks返回一个list集合,通过getClassName获取在前台的Activity。
2:OnResume中和onPause中记录一个变量。
14:FragmentPageAdater和FragmentStateAdapter区别?
FragmentPageAdater:fragment对象会一直在内存中,适用于少数的page情况。
FargmentStateAdapter:默认保存三个fragment,上一个当前的和下一个,一起对象的fragment会被销毁,但是在执行ondestroy时先调用onSaveInstenceState()来保存Fragment的状态,当fragment执行oncreate时候再把Bundle中的值取出来,比较适合多页面。
15:Servie&IntentService
Servie不能做耗时操作
IntentService继承自service,可以做后台下载等耗时操作。
16:Android常用性能调优
1:内存优化
GC释放对象包含(引用点):java栈中引用的对象;静态方法引用的对象;静态常量引用的对象;Native中JNI引用的对象,Thread。
内存溢出原因:瞬间申请了大量内存导致OOM;长期不能释放导致超过阈值导致OOM;小范围累积不能释放导致卡顿的OOM。
优化方式:利用profiler查看堆栈快照,具体查看波动内存是哪里导致的。
利用leakCanary工具查看生成的dump文件。
2:UI布局优化
UI卡顿的情况:View过渡绘制;Layout过于复杂,无法在16ms内完成渲染;View频繁出发measure,layout。
优化方式:开启GPU过渡绘制工具查看当前布局的绘制情况。
a:用RealtiveLayout代替LinearLayout
b:include,ViewStub,merge标签的使用
3:代码质量优化
a:使用as自带的unused resource清理无用资源
b:代码混淆
4:网络优化
a:用as自带的profiler进行网络监听。
ANR产生的原因:
Activity5s内无响应;广播10s无法处理完;服务service20s无法处理完。
网络请求中的图片可以用webp格式,质量相同的情况下减少流量。
优化方式:
一般多线程可以通过Asynctask处理,可以控制线程的顺序,执行完A后根据A返回的参数执行B线程。
5:耗电优化
a:需要进行网络请求时先判断当前的网络状态。
b:当有wifi和移动网络时候,优先选择wifi比移动网络耗电低。
c:减少后台任务的唤醒操作。
6:启动优化
a:冷启动,杀死进程后启动或程序第一次启动,耗时最久,因为要重新经历application初始化,启动ui线程,创建Activity,导入视图绘制视图等。
b:暖启动,当activity被销毁,但在内存中常驻时,启动减少了对象的初始化,布局加载等,启动时间短。
c:热启动,启动时间最短,比如按home键重新回到应用。
优化方式:Application创建过程尽量少,减少布局层次,启动页预加载等。
17:Java常用设计模式
1.单一职责原则:不要存在多于一个导致类变更的原因。
通俗的说:即一个类只负责一项职责。
2.里氏替换原则:所有引用基类的地方必须能透明地使用其子类
通俗的说:当使用继承时。类 B 继承类 A 时,除添加新的方法完成新增功能外,尽量不要重写父类 A 的方法,也尽量不要重载父类 A 的方法。如果子类
对这些非抽象方法任意修改,就会对整个继承体系造成破坏。子类可以扩展父类
的功能,但不能改变父类原有的功能。
3.依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依
赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
通俗的说:在 java 中,抽象指的是接口或者抽象类,细节就是具体的实现类,
使用接口或者抽象类的目的 ,是制定好规范和契约,而不去涉及任何具体的操作,
把展现细节的任务交给他们的实现类去完成。依赖倒置原则的核心思想是面向接
口编程.
4.接口隔离原则:客户端不应该依赖它不需要的接口;一个类对
另一个类的依赖应该建立在最小的接口上。
通俗的说:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的
方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一
个很庞大的接口供所有依赖它的类去调用。
5.迪米特法则:一个对象应该对其他对象保持最少的了解
通俗的说:尽量降低类与类之间的耦合。
6.开闭原则:一个软件实体如类、模块和函数应该对扩展开放,
对修改关闭。
通俗的说:用抽象构建框架,用实现扩展细节。因为抽象灵活性好,适应性广,
只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节,我们用
从抽象派生的实现类来进行扩展,当软件需要发生变化时,我们只需要根据需求
重新派生一个实现类来扩展就可以了。
单例模式:某个类只能有一个实例,提供一个全局的访问点。避免对象的重复创建,提高资源的有效利用。(饿汉式)
代理模式:为其他对象提供一个代理,以便控制这个对象的访问。
工厂模式:定义一个创建对象的接口,让子类决定实例化哪个类。
观察者模式:对象间的一对多的依赖关系。
18:Android常用开发架构
1.mvc:数据、View、Activity,View 将操作反馈给 Activity,Activitiy 去获取数据,数
据通过观察者模式刷新给 View。循环依赖
1.Activity 重,很难单元测试
2.View 和 Model 耦合严重
2.mvp:数据、View、Presenter,View 将操作给 Presenter,Presenter 去获取数据,数
据获取好了返回给 Presenter,Presenter 去刷新 View。PV,PM 双向依赖
1.接口爆炸
2.Presenter 很重
3.mvvm:数据、View、ViewModel,View 将操作给 ViewModel,ViewModel 去获取数
据,数据和界面绑定了,数据更新界面更新。
1.viewModel 的业务逻辑可以单独拿来测试
2.一个 view 对应一个 viewModel 业务逻辑可以分离,不会出现全能类
3.数据和界面绑定了,不用写垃圾代码,但是复用起来不舒服viewmodel不能持有上下文。
19:自定义控件
自定义View:继承View
基于现有组件:继承View的派生类
组合方式:自定义控件中包含了其他的组件
onMeasure:测量View的大小宽高
根据父View和MeasureSpec加上子view自己的LayoutParams,通过相应的转化规则而得到大小。
MeasureSpec代表宽度或高度的要求,每个MeasureSpec都包含了size和mode,MeasureSpec的模式主要分为:
(1)exactly:父容器已经测量出子view的大小,对应view的layoutparams的match_parent。
(2)at_most:父容器已经限制了view的大小,对应view的layoutParams的wrap_content。
onLayout:计算显示位置。
对View进行排版布局,要看父容器,也就是viewGroup。
onDraw:绘制背景,内容,绘制子view等
20:事件分发机制?
Activity/View处理事件分发的方法:
dispatchTouchEvent()
onTouchEvent()
ViewGroup事件分发处理:
dispatchTouchEvent()
onInterceptTouchEvent()
onTouchEvent()
在activity中当用户屏幕点击了后会先触发dispatchTouchEvent()如果view处理该事件则返回true,事件传递结束。如果返回false则调用onTouchEvent()处理该事件。
在ViewGroup中ViewGroup的dispatchTouchEvent先判断ViewGroup是否拦截Touch事件,如果拦截了则不向下传递直接调用TouchEvent处理事件,如果没有拦截则遍历所有子view找到点击的那个View把touch事件传递给它。viewgroup的onInterceptTouchEvent方法默认false,viewgroup事件分发都会调用它,一旦onInterceptTouchEvent返回true则表示拦截了事件,后续进行事件分发不再调用onInterceptTouchEvent方法。
总体优先级 setTouchListener > onTouchEvent > onClick > setClickListener
注意:DOWN或UP就是一个事件,不是DOWM+MOVE+UP才是一个事件,加起来是我们称作一系列事件
重写onTouchEvent时千万别删了super.onTouchEvent(event)——本人手贱,为此付出过惨痛代价。
getParent().requestDisallowInterceptTouchEvent(true):父控件不会拦截事件
getParent().requestDisallowInterceptTouchEvent(false):父控件会根据自身的判断来决定是否拦截
21:如何计算View大小&如何对子控件进行布局
计算View大小:
view在测量过程和activity生命周期不是同步的,所以在onCreate/onStart/onResume获取view宽高是0。
在onWindowFocusChanged里面获取。(getMeasuredWidth(),getMesureHeight())
子View布局:
可以通过addView方式,infalte布局
22:RecyclerView优缺点
RecyclerView卡顿情况:布局复杂,多层嵌套,设置setNestedScrollingEnable(false);含视频,滑动到item时加载,item滑出界面释放资源;含图片,图片懒加载(默认图占位)
RecyclerView4级缓存:
第一级ChangedScrap匹配position或者id获取holder缓存。可见缓存 x(无限制) ArrayList 用于屏幕中可见View的回收和复用
第二级从CachedViews 缓存列表 2 ArrayList 用于移除屏幕的View的回收和复用,不会清空数据
第三级ViewCacheExtension自定义缓存 x 一般不使用这个
第四级通过RecyclerViewPool 缓存池 5 SparseArray 用于移除屏幕的View的回收和复用,会将ViewHolder的数据重置
23:Http&Socket编程,TCP/IP原理
TCP 建立连接要进行 3 次握手,而断开连接要进行 4 次
1.基于连接与无连接;
2.对系统资源的要求(TCP 较多,UDP 少);
3.UDP 程序结构较简单;
4.流模式与数据报模式 ;
5.TCP 保证数据正确性,UDP 可能丢包,TCP 保证数据顺序,UDP 不
保证。
三次握手:
A:你好我是A。
B:你好A,我是B。
A:你好B
四次挥手:
A:B啊,我不想玩了。
B:哦,你不想玩了,我知道了。(这个时候,只是A不想玩了,A不再发送数据,但B可能有未完成的数据,所以需要等待)
B:A啊,好吧,我也不玩了,拜拜。
A:好的,拜拜。
Http和Https区别:
https是具有安全性的ssl加密身份认证的传输协议,需要到ca申请证书。端口443。
http是超文本传输协议,信息是明文传输。端口是80,是无状态连接。
24:图片优化
bitmap占用内存=图片长*图片宽*一个像素点占用的字节数。
bitmap.config中argb_4444和argb_8888代表一个像素点占用多少位。
图片压缩方式:
a:质量压缩,bitmap.compress不会改变图片所在内存大小,改变的是图片所占磁盘大小。
b:设置图片格式,png无损,jpeg有损,webp有损和无损,体积更小。
c:采样率压缩inSampleSize宽高压缩。
d:缩放压缩,减少图片的像素来降低磁盘和内存大小,可用于缩略图。
e:JNI调用JPEG库。(图片引擎采用skia,去掉了压缩算法导致)
Glide:
用法是with().load().into()。用法和pissco一致,是pissco的升级版。
SupportRequestManagerFragment是一致存在于栈中的fragment。不管上下文是用activity还是application的基本不会造成OOM,LruCache通过键值对形式存储bitmap,通过linkedHashmap保证插入顺序,Gilde可以从LruCache获取图片,从弱引用中获取缓存,从网络请求中获取图片。
25:Handle机制(postDelay),postDelay执行时候是什么时候把message添加Queue中的?
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 定义了一系列消息类型,包含四大组件的启动停止。
5.MessageQueue 和 Looper 是一对一关系,Handler 和 Looper 是多对一
https://www.jianshu.com/p/c7b87f8ed0c7(问题1)
https://blog.csdn.net/cmyperson/article/details/98970586 (问题2)
总的来说,一个线程中可以创建许多 Handler,然而这些 Handler 对象持有的 Looer、MessageQueue 引用指向的都是相同的对象(线程中唯一的的);这样一来,在其他线程中用这些 Handler 对象去发送消息(发出的消息持有发消息的 Handler 对象的引用),发出去的消息最终都是被放到了创建 Handler 线程中对应那个 MessageQueue 中
而创建 Handler 的线程中通过 Looper.loop 死循环不断地从消息队列中取出消息,这样便实现了线程之间的通信;由于对消息的入队和出队操作都是加了锁的,因此便保证了通信的安全性
为什么loop死循环不会卡死:
线程是一段可执行的代码,当执行完后线程的生命周期便终止,线程退出。而主线程我们希望一直运行下去,死循环便能保证不会被退出。真正导致卡死主线程的操作是在回调onCreate/onStart/onResume等操作时间过长导致ANR,looper.loop本身不会导致应用卡死。
26:EventBus原理
register注册,post发送消息。
EventBus解耦其实是使用了反射,在register时候会通过反射将信息记录下来。
观察者模式:通过subscrible订阅消息,Observer观察者。
源码解析:
EventBus发送事件原理是,subscriptionsByEventType通过事件类型EventType.class获取到订阅方法包装类List集合。
然后通过postToSubscription方法,在方法中匹配发送事件的线程模式threadMode,
例如匹配到主线程时,会直接通过反射调用订阅方法subscription.subscriberMethod.method.invoke(subscription.subscriber, event)
实现事件的发布-订阅执行。
EventBus事件的解注册
EventBus解注册原理则是清空typesBySubscriber和subscriptionsByEventType。
EventBus发送粘性事件
EventBus在处理粘性事件的原理,先通过postSticky方法将键值对(key=事件类型class,value=事件实例)存入字典stickyEvents中。
而真实的执行粘性事件的订阅方法,则是通过EventBus.register()来实现的。
发布事件的订阅类在注册时如果判断得知该事件属于粘性事件,那么就会通过循环遍历字典stickyEvents,
以执行订阅方法的执行~ 【在执行订阅方法进入方法postToSubscription时,跟非粘性事件处理就一致了】
27:jvm模型,java内存模型,垃圾回收机制,垃圾回收哪个区域,对象在内存哪个区域。
jvm内存模型:方法区,堆,程序计数器,本地方法栈。
垃圾回收机制执行:
1:年老代被写满
2:持久带被写满
3:System.gc()被显示调用
4:上一次gc之后heap的分配策略动态变化。
对象在内存中的堆里面。JVM所有对象都是在堆中分配内存空间,栈只是保存局部变量和临时变量,如果是对象只保存引用。
28:Parcelable和Serializable区别
Parcelable:
Android提供的,代码多,速度高
Parcelable中的三大过程介绍(序列化,反序列化,描述)
什么是序列化
序列化,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。
Parcelable和Serializable的区别和比较
Parcelable和Serializable都是实现序列化并且都可以用于Intent间传递数据,Serializable是Java的实现方式,可能会频繁的IO操作,所以消耗比较大,但是实现方式简单 Parcelable是Android提供的方式,效率比较高,但是实现起来复杂一些 , 二者的选取规则是:内存序列化上选择Parcelable, 存储到设备或者网络传输上选择Serializable(当然Parcelable也可以但是稍显复杂)
选择序列化方法的原则
1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。
Serializable:
java自带的,代码量少,速度低
29:ContentProvider实现原理,如何进行批量操作?
ContentProvider可以作为数据访问接口之外,还可以在不同应用程序之间进行数据共享。因为Binder传递数据有大小限制,所以用ContentProvider来处理比较高效。
ContentProvider必须在Manifest中声明。访问provider需要知道它的URI,所以把provider的URI作为公共常量出来。
ContentProviderOperation:
可以批量更新,插入,删除数据。
ContentProviderOperation.Builder:
newInsert 插入操作
newUpdate 更新操作
newDelete 删除操作
newAssertQuery 查询没有符合条件的数据
30:Android动态权限?各版本差异性?
5.0
沉浸式状态栏
6.0
动态权限,httpclient删除。
7.0 8.0 9.0
更注重耗电和后台优化。将bitmap中的像素点放在native层等。
10.0
不再需要任何权限即可在外部存储设备中访问和保存自己的文件,谷歌官方推荐应用在沙盒内存储文件的地址为Context.getExternalFilesDir()下的文件夹
11
安装app动态权限
12
隐私,存储,权限优化
13
优化蓝牙、音频,为大屏设备和平板做优化
31:SurfaceView和View
SurfaceView是从View基类中派生出来的显示类,他和View的区别有:
View需要在UI线程对画面进行刷新,而SurfaceView可在子线程进行页面的刷新,View适用于主动更新的情况,View频繁刷新会阻塞主线程,导致界面卡顿
SurfaceView在底层已实现双缓冲机制,而View没有,因此SurfaceView更适用于被动更新,需要频繁刷新、刷新时数据处理量很大的页面
32:AsyncTask异步任务
更新UI操作必须在主线程进行,下载图片,文件,网络请求需要在子线程进行,可以用handler机制。
Asynctask:
对线程和Handler进行了封装,可以直接对UI进行更新操作。
1:onPreExecute():执行在后台下载操作之前,与现行在主线程中。
2:doInBackground:核心方法,运行在子线程中
3:onPostExecute():后台下载完成后回调,运行在主线程中。
4:onProgressUpdate():在下载操作中调用publicProgress用于更新下载进度,运行在主线程中。
别再傻傻得认为AsyncTask只可以在主线程中创建实例和调用execute方法
总结一下就是,可以在子线程中创建AsyncTask实例,并执行execute. 注意在哪个线程执行AsyncTask的execute(),onPreExecute会运行在执行在哪个线程中,onPostExecute方法还是会执行在主线程中,doInBackground方法会执行在AsyncTask创建的线程中。
这个问题的核心是, Handler实例化需要用到线程的Looper,而我自己新建的Thread并没有调用Looper.prepare(),所以新开的线程Looper肯定为空,怎么会运行正常呢?
因为AsyncTask源码里Handler持有的主线程的Looper
sHandler = new InternalHandler(Looper.getMainLooper());
33:Android强软弱虚引用的应用场景
private String a="test";
1:强引用,定义的常量属于强引用,内存不足OOM时候也不会去回收该对象。
2:软引用,内存足够就不回收,不足是进行回收
3:弱引用,比软引用生命周期更短,只要被垃圾回收器线程发现,就会被回收。
4:虚引用,任何时候都可能会回收。
34:混淆的优点和使用场景
nimifyEnabled true开启混淆
优点:
1:增加对apk反编译的困难性
2:减少apk体积
注意:
1:避免混淆Android基本组件,避免混淆get/set方法,避免混淆枚举类,序列化类等。
35:java反射
反射:
对于任意一个类,都能得到它的属性和方法。通过setAccessible(true),可以得到私有属性
36:android安全性
安全性,可以通过混淆代码形式。
防止抓包,可以通过指纹码或者公钥证书,配合okhttp里面的sslFactory做防止中间人攻击。
AES
1、AES加密是一种对称式加密,即加密和解密所需秘钥是相同的,你可以生成一组秘钥,然后利用该秘钥加密数据,然后发给合作伙伴,同时也需要把秘钥发送给合作伙伴,这样你的合作伙伴才能解密。
相比RSA加密来说,好处是不会限制加密字符串的长度
RES
2、RSA加密,是一种非对称式加密.
加壳原理(爱加密):
20190506114334.png
37:SharedPrefrence原理,能否夸进程,如何实现?
不能跨进程,默认模式mode_private,其他支持跨境成模式已经被废弃,可以用contentProvider跨进程通信。
SharedPrefrence是基于xml实现的一种数据持久化手段。
SharedPrefrence不支持多进程。
SharedPrefrence的commit与apply一个是同步一个是异步。
SharedPrefrence不要存储过大数据。
SharedPreferences 也有其自身缺陷,比如其职能存储 boolean,int,float,long 和 String 五种简单的数据类
38:webView加载h5的优化,webView内存泄漏是否了解。
页面加载速度优化:
1:选择合适的浏览器缓存机制
2:常用资源预加载,常用JS本地化及延迟加载,放在本地加载。
导致内存泄漏:
系统在attachTowindow和detachFromWindow处进行注册和反注册component callback导致。
封装自己的webView,不再xml中声明,在代码中直接new WebView,传入Application的上下文防止acitivty被滥用。
WebView webView = new WebView(getContext().getApplicationContext());
webFrameLayout.addView(webView, 0);
39:插件化
腾讯的tinker和阿里的andfix。
本地热修复
1:修改好代码后编译后生成.class文件
2:用sdk中提供的dx工具将需要替换的.class文件转化成dex。
3:将.dex文件放入磁盘,创建一个DexClassLoader,通过PathClassLoader遍历出需要修改的dexElements,设值新的参数。
4:启动时候重新loadFixdex()
问题:
模块间耦合度过大沟通成本高,app方法数超过65535等问题
解决:
将一个apk拆分成多个小apk,每个小apk能单独运行。业务模块基本完全解耦。
1:dex加载原理
dexClassLoader:可以加载文件系统上的jar、dex、apk。
PathClassLoader:可以加载/data/app目录下的apk,只能加载已安装的apk。
PathClassLoader和DexClassLoader都是Android提供给我们的ClassLoader,都能加载dex
网上很多文章都说PathClassLoader只能加载已经被系统安装过的apk,DexClassLoader无此限制,然而我自己测试是都可以
根据art源码来看,两者都继承自BaseDexClassLoader,最终都会创建一个DexFile,不同点是一个关键的参数:optimizedDirectory,PathClassLoader为null,DexClassLoader则使用传递进来的
然后会根据optimizedDirectory判断对应的oat文件是否已经生成(null则使用/data/dalvik-cache/),如果有且该oat对应的dex正确则直接加载,否则触发dex2oat(就是这家伙耗了我们宝贵的时间!!),成功则用生成的oat,失败则走解释执行
ps:貌似Q版做了优化,不会再卡死在dex2oat里了
根据加载外部dex的实验,DexClassLoader会触发dex2oat,而PathClassLoader不会
40:线程池
wait:线程睡眠时,释放对象锁,其他线程可以访问。
sleep:线程睡眠时,仍然占有该锁,其他线程无法访问。
线程池是用来存放线程的池子,里面的线程可以被重复利用,不浪费。
创建线程池:用Executors 。
singleThreadPool单一线程
fixedThreadPool固定数量的线程池
scheduleThreadPool周期任务
cacheThreadPool想建几个就建几个
41:Glide源码分析
42:okhttp源码分析
43:LeakCanary源码分析
44:RxJava操作符
45:HashMap原理
46:framework层AMS、PMS、WMS
activity启动过程:
1:通过Launcher启动Activity或者startActivity来启动,都是通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity接口;
2:ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动activity的相关信息;
3:ActivityStack通知Application Thread要进行Activity启动调度了。
4:Application Thread不执行真正的启动操作,它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity;
5:如果是通过Launcher的情况,ActivityManagerService会调用startProcessLocked来创建新的进程,对于startActivity来启动的情况,这一步不需要执行,直接在原来Activity所在的进程中启动。
6:ActivityManagerService调用Application Thread.scheduleLaunchActivity接口,通知相应的进程启动Activity;
7:Application Thread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后启动起来了。
安装过程:
复制APK安装包到/data/app目录下,
解压并扫描安装包,
向资源管理器注入APK资源,
解析manifest文件,
在data/data目录下创建应用数据目录,
针对dalvik环境优化dex文件,
保存到dalvik-cache目录,
将manifest文件解析出的组件权限注册到packgeManagerService并发送广播。
47:binder机制原理
48:flutter原理优势,kotlin协程
flutter都是write once,run everywhere。
Flutter,基于Dart语言,基于独有的图形引擎Skia,不需要要桥接,不基于webkit,解决比较彻底。
摒弃JSBridge,Flutter是直接编译成本地代码,用skia渲染展示。热重载hot reload,jsBridge需要build打包。flutter不支持热更新,适合整体app开发。
49:冒泡排序&手写单例
冒泡:
public static void main(String[] arg) {
int arry[] = {2, 3, 1, 4};
for (int i = 0; i < arry.length; i++) {
boolean isOver = false;
for (int j = 1; j < arry.length - i; j++) {
if (arry[j - 1] > arry[j]) {
int temp = arry[j - 1];
arry[j - 1] = arry[j];
arry[j] = temp;
isOver = true;
}
}
if (!isOver) {
break;
}
}
for (int num : arry) {
System.out.println(num);
}
}
单例:(饿汉式)
public class Singleton {
// 直接创建对象
public static Singleton instance = new Singleton();
// 私有化构造函数
private Singleton() {
}
// 返回对象实例
public static Singleton getInstance() {
return instance;
}
}