Android 车企一年半技术总结

1、技术基础和多应用场景基础知识:

java基础:
final :
final来修饰对象时,不能改变对象的引用,但是可以修改对象的属性值。final类不能被继承,不能被覆盖,以及final类在执行速度方面比一般类快。
static:
https://www.cnblogs.com/paulwinflo/p/7920791.html
https://www.cnblogs.com/LiaHon/p/11075178.html
泛型:
https://juejin.cn/post/6978833860284907527
finalize()方法:
https://blog.csdn.net/qq_37823003/article/details/107333386
重写和重载:
重载是定义相同的方法名,参数不同,重写是子类重写父类的方法;重载是在一个类中,重写是子类与父类之间;重载是编译时的多态性,重写时运行时的多态性。
https://www.html.cn/qa/other/22009.html
https://www.cnblogs.com/wuhenzhidu/p/anonymous.html
https://juejin.cn/post/6844903655330545671

activity启动过程:
https://juejin.cn/post/6844903959581163528#heading-5
https://www.jianshu.com/p/160a53701ab6

是否支持其他应用调用:
android:exported=true/false
是Android中的四大组件 Activity,Service,Provider,Receiver 四大组件中都会有的一个属性。需要注意的是,如果两个程序的userid是一样的话,exported=”false“就没有用了。比如,两个程序都定义了android:sharedUserId=”android.uid.system",这时候两个程序的userid是一样的,两个程序之间还是可以互相访问的。

android:sharedUserId="android.uid.system"的作用和局限:
在manifest节点添加android:sharedUserId="android.uid.system作用是使应用所在进程和系统进程为同一个UID,也不需要再动态申请权限,只需要在manifest正常声明所需权限。来自Android官网(可以安排两个应用共享同一 Linux 用户 ID,在此情况下,二者便能访问彼此的文件。为节省系统资源,也可安排拥有相同用户 ID 的应用在同一 Linux 进程中运行,并共享同一 VM。应用还必须使用相同的证书进行签名。)局限性是比如系统内地图应用,语音应用,多媒体应用都用了media框架且声明了**android:sharedUserId=“android.uid.system”,我们系统的UID是1000,比如一个媒体源-抖音播放,这时候音频焦点在抖音,硬按键下发事件应该给抖音,这时候语音应用的一个tts播了,系统只知道音频焦点在UID是1000的应用那里,下一次硬按键播放就会发给地图、语音和多媒体,只有多媒体监听了硬按键事件,多媒体就会播放,显然是错误的。

android:enabled:
定义服务能否被系统实例化的标签,true表示可以实例化,false不能实例化,默认为true。

标签也有enabled标签,这个标签适用于application下所有组件。只有当和下enabled标签的属性都为true的时候,才可以将广播接受者启动(enabled),否则广播接受者不能开启(disabled),不能被实例化。

persistent属性:
https://blog.csdn.net/salmon_zhang/article/details/90741912

JobScheduler:
首先在一个实现了JobService的子类的onStartJob方法中执行这项任务,使用JobInfo的Builder方法来设定条件并和实现了JobService的子类的组件名绑定,然后调用系统服务JobScheduler的schedule方法。这样,即便在执行任务之前应用程序进程被杀,也不会导致任务不会执行,因为系统服务JobScheduler会使用bindServiceAsUser的方法把实现了JobService的子类服务启动起来,并执行它的onStartJob方法。

源码位置:/frameworks/base/services/core/java/com/android/server/job/JobServiceContext.java
客户端JobInfo封装的JobStatus可以拿到客户端的自定义jobservice的全限定名,bindServiceAsUser会把实现了JobService的子类服务启动起来。

   final Intent intent = new Intent().setComponent(job.getServiceComponent());
250              boolean binding = mContext.bindServiceAsUser(intent, this,
251                      Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
252                      new UserHandle(job.getUserId()));

理解JobScheduler机制
回顾之前经历的保活经历

一套代码多套ui
Flavor

2、Android源码:

handler
https://www.jianshu.com/p/ed9e15eff47a
https://juejin.cn/post/7020060105773154312#heading-0
https://www.jianshu.com/p/9c10beaa1c95
1、说一下Android 消息传递机制:
Android 消息传递机制就是handler。在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现对UI的更新处理,最终实现异步消息的处理。多个线程并发更新UI的同时 保证线程安全。Handler只是一个入口,核心的是Message、Message Queue、Looper(循环器)。handler添加消息到消息队列,处理循环器分派的消息。Message是线程间通讯的数据单元,存储需要操作的通信信息。message queue是一种先进先出的数据结构,底层是单链表结构。存储message。Looper消息循环,循环取出消息队列的消息,分发给对应的handler。
looper的构造方法是私有的,只能通过looper的prepare这个静态方法初始化。首先判断sThreadLocal.get() != null 的话会抛出异常,sThreadLocal存储的就是looper,ThreadLocal是线程中的。也就是说一个线程只能有一个looper。

 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

prepare方法调用sThreadLocal.set(new Looper(quitAllowed)),loop的构造方法中会初始化MessageQueue,也就是一个线程也保证了只有一个MessageQueue。消息的入队是enqueueeMessage根据时间放入消息队列。MessageQueue然后利用 Message 的 next 属性进行多个消息之间的链接,同时使用 when 属性对消息进行排序,when 的值越小,在链表中的排序越靠前。
取出消息就是Looper.loop(),拿到消息队列 queue 以后,进入死循环,通过MessageQueue.next获取消息,通过msg.target的dispatchMessage 分发出去消息。msg.target就是handler。阻塞和唤醒主要是nativePollOnce,nativeWake方法,这两个方法实际是实现了空队列阻塞,以及唤醒功能,底层使用epoll机制实现。

由于Looper中拥有当前线程的引用,所以有时候可以用Looper的这种特点来判断当前线程是不是主线程。

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
boolean isMainThread() {
    return Objects.requireNonNull(Looper.myLooper()).getThread() == Looper.getMainLooper().getThread();
}

handler调用链
MessageQueue -> Message -> Handler -> Activity 调用链。当activity关闭后,正常应该被GC回收,发现activity仍然被handler所引用,导致不能正常回收,依然占用内存,导致了内存泄漏。

handler内存泄漏:
1、在Handler消息队列 还有未处理的消息 / 正在处理消息时,消息队列中的Message持有Handler实例的引用。
2、Handler = 非静态内部类 / 匿名内部类(2种使用方式),故又默认持有外部类的引用。
解决:静态内部类+弱引用,外部类结束生命周期时,清空Handler内消息队列mHandler.removeCallbacksAndMessages(null);

如何理解 ThreadLocal 的作用?

首先要明确并非不是用来切换线程的,只是为了让每个线程方便程获取自己的 Looper 实例,见 Looper#myLooper();
后续可供 Handler 初始化时指定其所属的 Looper 线程;
也可用来线程判断自己是否是主线程。

同步屏障:
异步 Message:设置了 isAsync 属性的 Message 实例
同步屏障:在 MessageQueue 的某个位置放一个 target 属性为 null 的 Message,确保此后的非异步 Message 无法执行,只能执行异步 Message。当 MessageQueue 轮循 Message 时候发现建立了同步屏障的时候,会去跳过其他 Message,读取下个 async 的 Message 并执行,屏障移除之前同步 Message 都会被阻塞。比如屏幕刷新 Choreographer 就使用到了同步屏障,确保屏幕刷新事件不会因为队列负荷影响屏幕及时刷新。同步屏障的添加或移除 API 并未对外公开,App 需要使用的话需要依赖反射机制。

IdleHandler :
适用于期望空闲时候执行,但不影响主线程操作的任务。
系统应用:
Activity destroy 回调就放在了 IdleHandler 中
ActivityThread 中 GCHandler 使用了 IdleHandler,在空闲的时候执行 GC 操作。

2、binder
Binder 驱动 - 启动 ServiceManager 进程
分析Binder线程池的启动流程
https://www.cnblogs.com/SupperMary/p/14748261.html
https://www.cnblogs.com/SupperMary/p/14746477.html
https://blog.csdn.net/weixin_44021334/article/details/114933298

大致能总结出 Binder 通信过程:

首先,一个进程使用 BINDER_SET_CONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;

Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。

Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。

3、页面刷新和事件传递机制
获取Vsync信号回调后,ViewRootImpl如何开始页面展示?

4、关键View
ViewPager2中的缓存和复用机制

5、Activity和fragment
App处于前台,Activity就不会被回收了?

6、IO相关
Java IO流详解
Java中怎么快速把InputStream转化为String

7、多线程
cas
https://www.jianshu.com/p/465417fbba45
从ReentrantLock的实现看AQS的原理及应用
【基本功】不可不说的Java“锁”事
https://www.jianshu.com/p/a6c3df1f12c4
https://www.jianshu.com/p/0446ad7d92bb
https://juejin.cn/post/7065960131866918926
https://www.jianshu.com/p/43e8e3a8b688
https://blog.csdn.net/suyimin2010/article/details/89278519
10个线程依次执行的方法:
[1] 使用线程的join方法
[2] 使用主线程的join方法
[3] 使用线程的wait方法
[4] 使用线程的线程池方法
[5] 使用线程的Condition(条件变量)方法
[6] 使用线程的CountDownLatch(倒计数)方法
[7] 使用线程的CyclicBarrier(回环栅栏)方法
[8] 使用线程的Semaphore(信号量)方法

3、常见技术问题点

OOM如何监控的?
“Out Of Memory”,指的是内存超出了限制,抛出的一个异常。运行时数据区除了程序计数器都可能oom。

堆。 通常发生的 OOM 都会发生在堆中,最常见的可能导致 OOM 的原因就是内存泄漏。

JVM虚拟机栈和本地方法栈。 当我们写一个递归方法,这个递归方法没有循环终止条件,最终会导致 StackOverflow 的错误。当然,如果栈空间扩展失败,也是会发生 OOM 的。

方法区。方法区现在基本上不太会发生 OOM,但在早期内存中加载的类信息过多的情况下也是会发生 OOM 的。

一般的解决方法:
Java抛出来的OutOfMemoryError,和其他崩溃一样也会有其堆栈信息。通过堆栈信息能确认到崩溃那一刻调用的方法,可以看到是何时出错。但是并不是真正的问题所在,因为这个时候内存已经达到了接近崩溃的边缘,做任何分配的操作都会导致OOM。
最常用的办法就是heapdump,这是java虚拟机上的应用解决OOM的通用办法。根据oom时候的demp下来内存分析。

可以debug的时候,调用
adb shell am dumpheap
对于release的应用,只能Java代码中使用
Android.os.Debug.dumpHprofData(filePath)Android.os.Debug.dunpHprofData()

我通过Android studio的Profile分析.hprof文件。

更进一步,要解决端上内存自监控,端上内存主动释放,端上dump时机,端上问题自动发现。

自己监控内存是因为在全局异常捕获中收到OOM error信息时再去做内存dump,会有一定的失败率。dump还未完成,程序就已经崩掉,所以如果能在抛出OOM error,及早发现可能OOM,这个时候去做一些处理,能有更多的操作空间。

端上内存自监控:
具体方案为在监控开始时,起一个定时线程,每隔3秒进行一个内存监测。

内存占用计算:(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/Runtime.getRuntime().maxMemory()

端上内存主动释放:
当能监控到应用运行过程中的占用比时,能做的事情就比较多了,可以选择在高内存占比时,先做一些释放操作,在基线里面目前做了释放了两个模块,图片和Card,因为这两个模块是使用内存的大头。
现在的策略是在不同的占比做不同程度的内存释放。系统也会监控内存,提供了内存的回调OnLowMemory在系统内存不足并且所有后台程序已经被杀死时,会调用该方法。OnTrimMemory系统会根据不同的内存状态来回调。系统的这两个回调也应该做一些释放,这个和我们的监控不冲突,或者说是互补,因为一个是系统内存,一个是应用内存。

端上dump时机:
因为dump是非常影响用户体验的,最好要在真正需要dump的时候进行

当内存达到97%(这个是目前经验值)时,我们判断其已经接近OOM,是不是dump时机,还不能保证。因为可能有20%的内存在下次gc就能被回收掉

为了在gc之后再看看情况,流程中会主动调用System.gc(),熟悉的同学都知道gc()并不是你调用就马上执行,这是由虚拟机决定的。

通过查找资料,并没有直接的方式去判断虚拟机是否执行了我们的GC,我们也必须在GC后看看内存是否还在97%,这表示内存已达到了无可释放的程度。

现在是通过间接的方式来观察GC情况,Java引用类型分为四种强引用,软引用,弱引用,虚引用,每个的描述我就不介绍了,用到的就是弱引用,弱引用特点是当遇到gc时就会立马回收掉。

在首次内存达到97%的时候,创建一个弱引用对象作为哨兵,然后调用gc。当下次查看内存,发现哨兵被回收且内存继续保持97%的时候,我们就可以做dump操作了。

dump的性能问题
dump会导致应用进程冻结一段时间,会给用户带来比较明显的卡,经过测试在android 7.0以后的系统能降低到5s以内,而7.0以前的系统会卡10s及10s以上,所以在线上运行是我们是采样并且过滤版本的方式,样本控制的尽量少,以发现问题为主要目的。

内存泄漏加成:

利用LeakCanary的原理,首先registerActivityLifecycleCallbacks注册Activity的生命周期回调;然后在onActivityCreated,onActivityDestroyed记录每个Activity的开始和中止。

处理阶段:
端上问题自动发现主要是依赖的二次启动来处理,在dump完之后应用大概率会崩溃,而分析那些逻辑主要在第二次启动。第二次启动后发现有dump的文件,便开始进入处理分析流程,这里是另外起了一个:memery进行执行。

1、30s查一次内存情况 2、在内存超过一定时,在切到后台或者在Application的onLowMemory时候,就去dump内存 3、用profile就行,查看占用内存最大的对象;并分析Leak点。

内存泄漏:
指程序在申请内存后,当该内存不需再使用但却无法被释放或归还给程序的现象,即 Memory Leak(ml)。容易使得应用程序发生内存溢出,即 OOM。Android系统为每个应用程序分配的内存有限。
持有引用者的生命周期>被引用者的生命周期

常见内存泄漏:
https://www.jianshu.com/p/97fb764f2669
1、集合类
2、static关键字修饰的成员变量
3、非静态内部类/匿名类
4、资源对象使用后未关闭
5、未取消注册或回调导致内存泄露
6、Handler
7、属性动画造成内存泄露
8、WebView造成内存泄露
9、内存泄漏和监控
https://www.jianshu.com/p/2608f556a2e4
https://mp.weixin.qq.com/s/UfxG41HInNfv9nkDvKpcZQ
https://blog.csdn.net/u012165769/article/details/106843679/

性能优化:
https://www.jianshu.com/p/9745a9375191
应用程序App 更快、更稳定 & 更省
1、更快:启动速度,页面显示速度,响应速度
启动速度:异步加载(多线程)、分步加载、延期加载的策略,减少启动应用时加载的任务,从而提高启动速度。
减少onCreate的时间,精简onCreate中的代码,部分放到onResume,多线程细分显示的view,尽可能减少onCreate,onReume()。设置一个boolean
2、优化布局文件,减少嵌套,include,merge、Viewstub
观察布局工具 Hierachy Viewer。移除默认的 Window 背景,移除 控件中不必要的背景。
3、耗时操作放到后台线程执行,需要修改ui时通知主线程。

包体积优化:
1、减少不必要的图片和xml资源。Android Lint删除冗余资源。利用分辨路选择合适图片。压缩处理图片,降低图片彩色数为,使用占用内存小的图片webP。
2、减少引入不必要的库。
3、代码混淆。
4、插件化。主应用保留基础核心共饿呢过。其余模块化放到服务器上。

内存管理:
内存的分配和回收。
内存分配策略:由 ActivityManagerService 集中管理 所有进程的内存分配。
内存回收策略:Application Framework 决定回收的进程类型。
Android中的进程 是托管的;当进程空间紧张时,会 按进程优先级低->>高的顺序 自动回收进程。前台进程、可见进程、服务进程、后台进程、空进程。Linux内核真正回收具体进程。ActivityManagerService 对 所有进程进行评分(评分存放在变量adj中),更新评分到Linux 内核,由Linux 内核完成真正的内存回收。

Android的对于对象、变量的内存策略同 Java。对象 / 变量的内存分配 由程序自动 负责。静态分配、栈式分配 & 堆式分配,分别面向静态变量、局部变量 & 对象实例。静态分配在方法区,存储已被虚拟机加载的类信息,常量,静态变量,在程序编译期就已经分配好,存在于程序整个运行期间,不需要回收。栈式分配在栈区,存储方法执行时的局部变量(数据类型,对象的引用等),以栈帧的形式,,,,

https://juejin.cn/post/6975876569990447134
内存优化的目的是减少内存占用,降低OOM率、减少卡顿,也要根据具体情况具体分析。
Java内存回收可达性分析算法。可以作为GCroots的有四种。
内存泄漏:内存泄漏指的是一块内存没有被使用且无法被GC回收,从而造成了内存的浪费。内存泄漏的直接原因是长生命周期的对象引用了短生命周期的对象,导致短生命周期对象无法回收。
内存抖动:
当我们在短时间内频繁创建大量临时对象时,就会引起内存抖动,比如在一个for循环中创建临时对象实例。内存抖动意味着频繁的创建对象与回收,容易触发GC,而当GC时所有线程都会停止,因此可能导致卡顿。
防止内存抖动:
1、尽量避免在循环体中创建对象。
2、尽量不要在自定义View的 onDraw 方法中创建对象,因为这个方法会被频繁调用。
3、对于能够复用的对象,可以考虑使用对象池把它们缓存起来。
内存溢出:运行时数据区除了程序计数器都可能会内存溢出。
Android 车企一年半技术总结_第1张图片
卡顿:
内存问题之所以会影响到界面流畅度,是因为垃圾回收。在GC时,所有线程都要停止,包括主线程。当GC和绘制界面的操作同时触发时,绘制的执行就会被搁置,导致掉帧,也就是界面卡顿。

内存优化的手段:
1、SparseArray 替换掉 HashMap,其根本原因就在于 SparseArray 相比较 HashMap 会更省内存。
https://juejin.cn/post/6897892195483779080
2、在App可用内存过低时主动释放内存。onTrimMemory/onLowMemory 方法去释放掉图片缓存、静态缓存来自保;
http://t.zoukankan.com/CharlesGrant-p-5112438.html
3、使用 ViewStub 进行占位。对那些没有马上用到的资源去做延迟加载,并且还有很多大概率不会出现的 View 更要去做懒加载。
https://zhuanlan.zhihu.com/p/436597784

启动优化
https://juejin.cn/post/7068248984330240014

事件分发:
https://www.jianshu.com/p/e243838fb3ae

浅析Fragment为什么需要空的构造方法
https://blog.csdn.net/ruancoder/article/details/52001801

4、三方源码:

okhttp:
https://zhuanlan.zhihu.com/p/116777864
https://blog.csdn.net/qq_26498311/article/details/121680507
ARouter:
https://juejin.cn/post/6943206672898719780
ARouter原理与缺陷解析
ARouter原理与缺陷解析
ARouter疑难杂症解析
注解解析器(arouter-compiler源码)
看Arouter源码,想实现 通过指定包名,扫描包下面包含的所有的ClassName 这个小功能的话,Arouter源码的ClassUtils类的getFileNameByPackageName方法可以拿过来借鉴,不仅可以看三方源码的设计思路,一些代码也可以借鉴,毕竟三方框架经过大量实用测试过了。

注解:
https://www.jianshu.com/p/9ca78aa4ab4d
Nullable和NotNull
Glide:
手撕glide源码
为了性能,Glide 做了哪些优化?

LeakCanary:
为弱引用指定一个引用队列,当弱引用指向的对象被回收时,此弱引用就会被添加到这个队列中,我们可以通过判断这个队列中有没有这个弱引用,来判断该弱引用指向的对象是否被回收了。
精简流程如下所示:

  1. LeakCanary.install(application);此时使用application进行registerActivityLifecycleCallbacks,从而来监听Activity的何时被destroy。

  2. 在onActivityDestroyed(Activity activity)的回调中,去检测Activity是否被回收,检测方式如以下步骤。

  3. 使用一个弱引用WeakReference指向这个activity,并且给这个弱引用指定一个引用队列queue,同时创建一个key来标识该activity。

  4. 然后将检测的方法ensureGone()投递到空闲消息队列。

  5. 当空闲消息执行的时候,去检测queue里面是否存在刚刚的弱引用,如果存在,则说明此activity已经被回收,就移除对应的key,没有内存泄漏发生。

  6. 如果queue里不存在刚刚的弱引用,则手动进行一次gc。

  7. gc之后再次检测queue里面是否存在刚刚的弱引用,如果不存在,则说明此activity还没有被回收,此时已经发生了内存泄漏,直接dump堆栈信息并打印日志,否则没有发生内存泄漏,流程结束。
    https://blog.csdn.net/cpcpcp123/article/details/121655510
    https://www.jianshu.com/p/ed9e15eff47a
    https://www.jianshu.com/p/9fe944ee02f7

5、各种集合和数据结构

集合:
SparseArray:https://mp.weixin.qq.com/s/zAfUBW5kZ-X_oucfonQ6Yg
1、SparseArray 内部使用双数组,分别存储 Key 和 Value,Key 是 int[],用于查找 Value 对应的 Index,来定位数据在 Value 中的位置。
2、使用二分查找来定位 Key 数组中对应值的位置,所以 Key 数组是有序的。
3、使用数组就要面临删除数据时数据搬移的问题,所以引入了 DELETE 标记。
https://juejin.cn/post/6902793228026642446
https://www.jb51.net/article/196836.htm
https://www.cnblogs.com/aspirant/p/8657801.html
https://www.jianshu.com/p/cc2281b1a6bc
https://www.jianshu.com/p/9a652250e0d1
https://yangyu.blog.csdn.net/article/details/89043270
https://www.jianshu.com/p/4f455f2095e7
Java集合List的细节
ArrayList源码
LinkedHashMap中accessOrder属性的理解
https://www.jianshu.com/p/95a9a82d7a1c
https://mp.weixin.qq.com/s/zAfUBW5kZ-X_oucfonQ6Yg
https://www.cnblogs.com/JavaArchitect/p/10474448.html
https://www.cnblogs.com/wudb/p/12980680.html
https://www.jianshu.com/p/4176c0e929f1
https://mp.weixin.qq.com/s/Jy3WbV7G4RL-v0yZDYTfCg
https://juejin.cn/post/6902793228026642446
https://juejin.cn/post/6902793228026642446#heading-0
https://www.jianshu.com/p/9a652250e0d1
https://www.jianshu.com/p/bf5fa1557798
https://cloud.tencent.com/developer/article/1629155
https://blog.csdn.net/ThinkWon/article/details/102469889

红黑树:
https://www.jianshu.com/p/eb6e3fa41c66
https://mp.weixin.qq.com/s/Z4joQJMUph2Kwsl_6N319w

6、相关技术

VM基础知识:
Android专属JVM讲解—【运行时数据区详解】
一文看懂JVM运行时内存分布
JVM学习指南
五大JVM知识点

死锁:
https://www.cnblogs.com/godtrue/p/6341473.html
https://mp.weixin.qq.com/s/qSkRtwH_jUjXHPxu0KDQxA

AQS
https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247484202&idx=1&sn=dbf9e94d2486ee0baa43e043a2363231&chksm=ebd7422bdca0cb3dc0451e09d139b72558b1cfa3593a6bcc1716ae9d1bd443804d194a303985#rd

字节码:
https://www.csdn.net/tags/NtjaAg1sMTUzMS1ibG9n.html#11_3
装箱和拆箱:
https://droidyue.com/blog/2015/04/07/autoboxing-and-autounboxing-in-java/

aop:
https://www.jianshu.com/p/e66e8926c01d

Android Studio导出 hprof 到 MAT
https://www.jianshu.com/p/5c00ac166747

版本管理

view的绘制流程
https://mp.weixin.qq.com/s/jNdy0ol-oB2nQugptEK5wQ
https://mp.weixin.qq.com/s/klrev1rFQp74knlZjs2UgA
https://www.jianshu.com/p/cede93e863f0
https://juejin.cn/post/6939540905581887502
https://www.jianshu.com/p/5a71014e7b1b

wms
https://www.jianshu.com/p/aba442cdfe01

fragment
https://juejin.cn/post/7006970844542926855#heading-15

屏幕适配
https://juejin.cn/post/6999445137491230728

hashmap
https://juejin.cn/post/6904476052546912264

生产者消费者
https://juejin.cn/post/6952490038211018783
https://www.cnblogs.com/cxhfuujust/p/10772908.html
https://www.sohu.com/a/433643043_611601
https://zhuanlan.zhihu.com/p/143353788
https://blog.csdn.net/ThinkWon/article/details/102557126

转场 overridePendingTransition

元注解:
http://www.javashuo.com/article/p-qqyznzfk-ho.html
dex优化对Arouter查找路径的影响
https://segmentfault.com/a/1190000040098076

livedata:
https://www.cnblogs.com/button123/p/14871526.html

线上:
https://www.cnblogs.com/button123/p/14885566.html

工厂模式:
https://www.jianshu.com/p/4653b20be1ec

tcp
https://juejin.cn/post/6844904067601268744
https://juejin.cn/post/7001839784289108005#heading-1
https://mp.weixin.qq.com/s/hkAS6kBY-mtN0WSNytA2gg

handler同步屏障:
https://juejin.cn/post/6940607471153053704

崩溃和卡顿的定位和解决:
Native层崩溃
https://mp.weixin.qq.com/s/WoixQ_7bSGl6S6FGNOqsZg
java常见的异常有:NPE、OOM、ArrayIndexOutOfBoundsException、IllegalStateException、ConcurrentModificationException等

应用的安全性:
https://mp.weixin.qq.com/s/XAM9WscBvFqZJkvERZc2dw

serviceManager:
https://juejin.cn/post/6916846296678531085

什么是binder:
https://mp.weixin.qq.com/s/pLVgIQEjvxacZ__QtvT2GQ

https://blog.csdn.net/h_025/article/details/82632439

oom:
https://blog.csdn.net/cpcpcp123/article/details/121655510

换肤:
Android App换肤大体分为两种:
1、内置主题的切换(白天/黑夜)
2、插件式换肤

第一种的实现基本上使用设置本地Theme来操作,即将所有的资源打包到APP中,根据主题进行切换。(缺乏灵活性,不利于更新)
第二种,支持线上下载动态更新,动态加载资源包,替换原始包中的资源

Android Overlay是一种资源替换机制,能在不重新打包apk的情况下,实现资源文件的替换,Overlay分为静态Overlay(Static Resource Overlay)与运行时Overlay(Runtime Resource Overlay)
Android 车企一年半技术总结_第2张图片
Android 车企一年半技术总结_第3张图片

Android 车企一年半技术总结_第4张图片
Android 车企一年半技术总结_第5张图片

setcontentView:
https://www.jianshu.com/p/82b467be18b7

四代签名:
V1、V2、V3、V4
https://juejin.cn/post/7068079232290652197

链表:
https://www.cnblogs.com/easyidea/p/13371863.html

启动模式:
https://mp.weixin.qq.com/s/IDRVldIn5-tofbr9ihwrUA
https://blog.csdn.net/wq6ylg08/article/details/105911410

queries:
https://www.jianshu.com/p/d1ccd425c4ce

View和SurfaceView的区别:
View适用主动更新,SurfaceView 适用被动更新,如频繁的刷新
View在UI线程更新,在非UI线程更新会报错,当在主线程更新view时如果耗时过长也会出错, SurfaceView在子线程刷新不会阻塞主线程,适用于界面频繁更新、对帧率要求较高的情况。
SurfaceView可以控制刷新频率。
SurfaceView底层利用双缓存机制,绘图时不会出现闪烁问题。

双缓冲技术是游戏开发中的一个重要的技术,主要是为了解决 反复局部刷屏带来的闪烁。游戏,视频等画面变化较频繁,前面还没有显示完,程序又请求重新绘制,这样屏幕就会不停地闪烁。双缓冲技术会把要处理的图片在内存中处理好之后,把要画的东西先画到一个内存区域里,然后整体的一次性画出来,将其显示在屏幕上。

https://www.jianshu.com/p/b037249e6d31

你可能感兴趣的:(android知识回顾,android,android,studio,java)