Potato
Potato copied to clipboard
Read the fucking source code for the Android interview
# Android plug-in and hotfix summary [TOC] ## 插件化和热修复总述 首先明确一点,两者不是同一个概念。虽然在技术实现的角度来说,他们都是从系统加载起的角度出发,无论是 hook ,或者代理还是其他底层方式实现,都是通过 欺骗 Android 系统的方式,让宿主正常的加载和运行插件(补丁)中的内容; 但是两者的出发点不同。插件化,更多是想把需要实现的模块或者功能当作一个独立模块提取出来,较少宿主的大小,当需要使用到相应的功能时再去加载相应的模块。热修复则是从修复 bug 出发,强调的是在不需要二次安装应用的前提下修复已知的 bug。 * 宿主:当前运行的 APP * 插件:相对于插件化技术来说,就是要加载运行的 apk类文件 * 补丁:对于热修复技术来说,就是要加载运行的 .patch, .dex 等一系列包含...
# SparseArray and ArrayMap [TOC] Android 设备为了较少内存的使用和装拆箱损耗的性能,提供一些特有的数据结构代替 java 中原来的数据结构,适当采用这些数据结构可以优化应用的内存。 ## ArrayMap 用于代替 HashMap 使用,有几个特点: * 内部实现基于两个数组,可以避免在数据插入 Map 时额外的空间消耗 * 有扩容和收缩功能 * 不适合存储大容量数据;数据量大时,性能退化至少 50 %。 ```java /** {@hide} */ public ArrayMap(int...
# Kotlin Coroutine [TOC] ## 科普解释进程,线程,协程 链接:https://www.zhihu.com/question/20511233/answer/24260355 没有啥复杂的东西,考虑清楚需求,就可以很自然的衍生出这些解决方案。 - 一开始大家想要同一时间执行那么三五个程序,大家能一块跑一跑。特别是UI什么的,别一上计算量比较大的玩意就跟死机一样。于是就有了**并发**,从程序员的角度可以看成是多个独立的逻辑流。内部可以是多cpu并行,也可以是单cpu时间分片,能快速的切换逻辑流,看起来像是大家一块跑的就行。 - 但是一块跑就有问题了。我计算到一半,刚把多次方程解到最后一步,你突然插进来,我的中间状态咋办,我用来储存的内存被你覆盖了咋办?所以跑在一个cpu里面的并发都需要处理上下文切换的问题。**进程**就是这样抽象出来个一个概念,搭配虚拟内存、进程表之类的东西,用来管理独立的程序运行、切换。 - 后来一电脑上有了好几个cpu,好咧,大家都别闲着,一人跑一进程。就是所谓的**并行**。 - 因为程序的使用涉及大量的计算机资源配置,把这活随意的交给用户程序,非常容易让整个系统分分钟被搞跪,资源分配也很难做到相对的公平。所以核心的操作需要陷入内核(kernel),切换到操作系统,让老大帮你来做。 - 有的时候碰着I/O访问,阻塞了后面所有的计算。空着也是空着,老大就直接把CPU切换到其他进程,让人家先用着。当然除了I\O阻塞,还有时钟阻塞等等。一开始大家都这样弄,后来发现不成,太慢了。为啥呀,一切换进程得反复进入内核,置换掉一大堆状态。进程数一高,大部分系统资源就被进程切换给吃掉了。后来搞出**线程**的概念,大致意思就是,这个地方阻塞了,但我还有其他地方的逻辑流可以计算,这些逻辑流是共享一个地址空间的,不用特别麻烦的切换页表、刷新TLB,只要把寄存器刷新一遍就行,能比切换进程开销少点。 - 如果连时钟阻塞、 线程切换这些功能我们都不需要了,自己在进程里面写一个逻辑流调度的东西。那么我们即可以利用到并发优势,又可以避免反复系统调用,还有进程切换造成的开销,分分钟给你上几千个逻辑流不费力。这就是**用户态线程**。 - 从上面可以看到,实现一个用户态线程有两个必须要处理的问题:一是碰着阻塞式I\O会导致整个进程被挂起;二是由于缺乏时钟阻塞,进程需要自己拥有调度线程的能力。如果一种实现使得每个线程需要自己通过调用某个方法,主动交出控制权。那么我们就称这种用户态线程是协作式的,即是**协程**。 **本质上协程就是用户空间下的线程。**
# Java common collections [TOC] ## Collection 最基本的集合类型,所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个共的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。 若要检查Collection中的元素,可以使用foreach进行遍历,也可以使用迭代器,Collection支持iterator()方法,通过该方法可以访问Collection中的每一个元素. ```java Iterator it=collection.iterator(); while(it.hasNext()){ Object obj=it.next(); } ``` Set和List是由Collection派生的两个接口 ### List List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引的位置来访问List中的元素,类似于Java数组。 List允许有相同的元素存在。 除了具有Collection接口必备的的iterator()方法外,还提供了listIterator()方法,放回一个 ListIterator接口。 实现List接口的常用类有LinkedList、ArrayList、Vector和Stack #### LinkedList类 LinkedList实现了List类接口,允许null元素。此外LinkedList提供额外的get、remove、insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。LinkedList没有同步方法 #### AyyayList类...
# LeakCanary Principle [TOC] 这篇文章介绍 LeakCanary 的原理,基于版本 2.0,kotlin。 ## 基本使用 加入依赖即可,以使用 ContentProvider 进行自动初始化。LifeCycle 库类似, 可用于初始化sdk,三方库等。 ```java dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-alpha-2' } ``` ```kotlin internal class LeakSentryInstaller : ContentProvider() { override...
# Android Invalidate() And RequestLayout() 这篇文章探讨能够触发 performTraversals() 执行的 invalidata(), requestLayout() 方法的流程。 在调用这几个方法到最后执行 performTravelsals() 方法,涉及到通过 Choroegrapher 请求 VSYNC 信号,实现按帧绘制的流程。 ## requestLayout invalidate() 和 postInvalidate() 都能够触发 View 的重画,这两个方法最终会调用 performTraversals() 中的performDraw() 来完成重绘制,是否执行 onMeasure...
# Android Root Check [TOC] ## Android 安全机制 Android 是基于 Linux 多用户机制的访问控制。应用程序在默认的情况下不可以执行其他应用程序,包括读写用户的私有数据。一个应用程序的进程就是一个安全的沙盒(在受限的安全环境中运行应用程序,在沙盒中的所有改动对操作系统不会造成任何危害)。 每一个Android应用程序都会在安装时就分配一个独有的Linux用户ID,这就为它建立了一个沙盒,使其不能与其他应用程序进行接触。这个用户ID会在安装时分配给它,并在该设备上一直保持同一个数值。 所有的Android应用程序必须用证书进行签名认证,而这个证书的私钥是由开发者保有的。该证书可以用以识别应用程序的作者。签名影响安全性的最重要的方式是通过决定谁可以进入基于签名的permisssions,以及谁可以share 用户IDs。通过这样的机制,在不考虑root用户的情况下,每个应用都是相互隔离的,实现了一定的安全。 在Linux操作系统中,root的权限是最高的,也被称为超级权限的拥有者。 在系统中,每个文件、目录和进程,都归属于某一个用户,没有用户许可其它普通用户是无法操作的,但对root除外。 root用户的特权性还表现在:root可以超越任何用户和用户组来对文件或目录进行读取、修改或删除(在系统正常的许可范围内);对可执行程序的执行、终止;对硬件设备的添加、创建和移除等;也可以对文件和目录进行属主和权限进行修改,以适合系统管理的需要(因为root是系统中权限最高的特权用户);root是超越任何用户和用户组的,基于用户ID的权限机制的沙盒是隔离不了它的。 ## root的方式 * 不完全 root * 完全 root 目前获取Android root 权限常用方法是通过各种系统漏洞,替换或添加SU程序到设备,获取Root权限,而在获取root权限以后,会装一个程序用以提醒用户是否给予程序最高权限,可以一定程度上防止恶意软件,通常会使用Superuser或者 SuperSU...
# Android Fragment 这篇文章讲解 Fragment 的基本概念使用。 ## 基础概念 > 因为support库是不断更新的,因此建议使用support库中的android.support.v4.app.Fragment,而不要用系统自带的android.app.Fragment。`而如果要使用support库的Fragment,Activity必须要继承FragmentActivity(AppCompatActivity是FragmentActivity的子类)`。 根据官方的定义: * framgent 依赖于 Activity,不能独立存在 * 一个 Activity 中可以有多个 Fragment * 一个 Fragment 可以被多个 Activity 重用。 * Fragment 有自己的生命周期,并能接收输入事件 *...
# Android Bitmap Efficient Load [TOC] 文章转载:https://juejin.im/post/5b0e6e6a5188251570336972 将大图加载到内存中总是令人痛苦,经常会造成 OOM。Android系统的内存有限 > **建议使用Picasso或Glide来加载图片。没有必要重新发明轮子。** ## 将图片加载到内存中 只需要使用BitmapFactory来解码你的图片. ```java Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.hqimage); imageView.setImageBitmap(bitmap); ``` 看一下这张解码过的图片在内存中实际占据的空间大小。 **bitmap.getByteCount**()方法将返回bitmap的大小。 这张图片在内存中的大小为12262248字节,相当于12.3 MB。是的,你可能会感到困惑。因为这张图片在磁盘上的实际大小约为3.5 MB,而getByteCount()方法返回的值远大于它。原因如下: > 存储在磁盘上的图片是被压缩过的(以JPG,PNG或类似的格式存储)。 一旦将图片加载到内存中,它就不再被压缩,并占用尽可能多的图片的所有像素所需的内存空间。...
# Android Service [TOC] Service 分为两种工作状态: * 启动状态:主要用于执行后台计算 * 绑定状态:主要用于其他组件和 Service 的交互 两种状态可以并存。 ## 生命周期 * onCreate:首次创建服务时,系统调用次方法;如果服务正在运行,则不会调用,只会执行一次。 * onStartCommand:但另一个组件调用 startService() 请求启动服务时,系统调用 * onBind:另一个组件通过调用 bindService() 与服务绑定时,系统将调用此方法 * onUnBind:另一个组件调用 unBindService() 与服务解绑,系统将调用此方法。 *...