android 基础问题随笔
1.关于LayoutInflater类inflate(int resource, ViewGroup root, boolean attachToRoot)方法三个参数的含义
resource:需要加载布局文件的id,意思是需要将这个布局文件中加载到Activity中来操作。
root:需要附加到resource资源文件的根控件,什么意思呢,就是inflate()会返回一个View对象,如果第三个参数attachToRoot为true,就将这个root作为根对象返回,否则仅仅将这个root对象的LayoutParams属性附加到resource对象的根布局对象上,也就是布局文件resource的最外层的View上,比如是一个LinearLayout或者其它的Layout对象。
attachToRoot:是否将root附加到布局文件的根视图上
2、 ArrayMap和HashMap,SparseArray区别
HashMap内部存储结构是使用哈希表的拉链结构(数组+链表),这种存储数据的方法叫做拉链法 。如图:
讲到这里,重点来了,我们知道HashMap中默认的存储大小就是一个容量为16的数组,所以当我们创建出一个HashMap对象时,即使里面没有任何元素,也要分别一块内存空间给它,而且,我们再不断的向HashMap里put数据时,当达到一定的容量限制时(这个容量满足这样的一个关系时候将会扩容:HashMap中的数据量>容量*加载因子,而HashMap中默认的加载因子是0.75),HashMap的空间将会扩大,而且扩大后新的空间一定是原来的2倍,我们可以看put()方法中有这样的一行代码:
int newCapacity = oldCapacity * 2;
所以,只要一满足扩容条件,HashMap的空间将会以2倍的规律进行增大。假如我们有几十万、几百万条数据,那么HashMap要存储完这些数据将要不断的扩容,而且在此过程中也需要不断的做hash运算,这将对我们的内存空间造成很大消耗和浪费,而且HashMap获取数据是通过遍历Entry[]数组来得到对应的元素,在数据量很大时候会比较慢,所以在Android中,HashMap是比较费内存的。
所以我们在一些情况下可以使用SparseArray和ArrayMap来代替HashMap
ArrayMap是一个<key,value>映射的数据结构,它设计上更多的是考虑内存的优化,内部使用两个数组进行工作,其中一个数组记录 key hash 过后的顺序列表,另外一个数组按 key 的顺序记录 Key - Value 的值。如下图所示:

当你想获取某个 Value 的时候,ArrayMap 会计算输入 key 转换过后的 hash 值,然后对 hash 数组使用二分查找法寻找到对应的 index,然后我们可以通过这个 index 在另外一个数组中直接访问到需要的键值对。如果在第二个数组键值对中的 key 和前面输入的查询 key 不一致,那么就认为是发生了碰撞冲突。为了解决这个问题,我们会以该 key 为中心点,分别上下展开,逐个去对比查找,直到找到匹配的值。如下图所示:

随着数组中的对象越来越多,查找访问单个对象的花费也会跟着增长,这是在内存占用与访问时间之间做权衡交换。
ArrayMap的构成原理
1、arrayMap的存储结构。
arrayMap中主要存储的数据的是两个数据,
int[] mHashes; Object[] mArray;
mHashs中存储出的是每个key的hash值,并且在这些key的hash值在数组当中是从小到大排序的。
mArray的数组长度是mHashs的两倍,每两个元素分别是key和value,这两元素对应mHashs中的hash值。mArray的结构如下图所示
For Example
key2和value2分别位于数组的第2位和第3位(从0开始计算),对应的是hash值就是hash的2/2=1位,也就是mHashes[1];
2、arrayMap的get方法。
@Override
public V get(Object key) {
final int index = indexOfKey(key);
return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}
get方法其实就是一个计算index的过程,计算出来之后如果index大于0就代表存在,直接乘以2就是对应的key的值,乘以2加1就是对应的value的值。
ArrayMap应用场景
- 1.数据量不大,最好在千级以内
- 2.数据结构类型为Map类型
3、SparseArray
SparseArray是android v4包里提供的工具类,在某些场景下可以用来替代Hashmap进行对象的存储,其内部实现了一个矩阵压缩算法,可以进行矩阵压缩,大大减少了存储空间,节约内存。此外它的查找算法是二分法,提高了查找的效率。
SparseArray的value可以是任意类型,但key只能是Integer、Long类型
比较
HashMap、ArrayMap、SparseArray比较如下:
| 类 | cpu | 内存 | 适用场景 |
|---|---|---|---|
| HashMap | 增、删、查找速度较快 | 双倍扩容、不做空间整理,内存使用效率低 | 数据量较大或内存空间相对宽裕 |
| ArrayMap | 增、删、查速度较慢 | size大于8扩容时,只增大当前数组大小的一半,做空间收缩整理 | 数据量小于1000时,速度相对差别不大,可替代HashMap |
| SparseArray | 增、查速度较慢,由于延迟删除机制,删速度比ArrayMap快,比HashMap慢 | 矩阵压缩,大大减少了存储空间,节约内存 | 避免了key的自动装箱,空间压缩等机制,使得其在key是Integer、Long,且数据量较小场景下性能最优 |
3、IPC 进程间通信
- 开启多进程模式的运行机制
android中使用多进程只有一种方法(除了,用JNI在native层fork一个新的进程),就是给四大组件在AndroidManifest中指定Android:process属性。
anroid为每个应用分配了一个独立的虚拟机,或者说为每个进程都分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同虚拟机中访问同一个类的对象会产生多份副本,互不干扰。 所以运行在不同进程中的四大组件,想通过内存来共享数据,都是行不通的。
- 使用多进程的问题
- 静态成员和单例模式完全失效
- 线程同步完全失效
- SharedPreferences可靠性下降
- Application会多次创建 (运行在同一个进程的组件属于同一个虚拟机和同一个Application, 同理,运行在不同进程的组件是属于不同的虚拟机和Application的)
- 基础知识
- 序列化Parcelable 和 Serializable 前者更高效,后者使用方便,将对象序列化到存储设备或将对象序列化后通过网络传输,这两种情况建议用后者。
- Binde

手动实现Binder的步骤:
- 声明一个AIDL性质的接口,只需继承IInterface即可
- 实现Stub类和Stub类的Proxy代理类
- 几种方式
- 使用Bundle
- 文件共享
3、Messager

4、AIDL , 注意使用RemoteCallbackList,解决释放注册对象的问题
使用流程:首先创建一个Service和一个AIDL接口,接着创建一个类继承AIDL接口中的Stub类并实现Stub中的抽象方法,在Service的onBind方法中返回这个类的对象,然后客客户端就能绑定服务端的Service,建立链接后就可以反问服务端的方法了。
支持的类型:
- 基本数据类型(int、long、char、boolean、double等)
- String 和 CharSequence
- List(ArrayList)
- Map(HashMap)
- Parcelable
- AIDL
注意:多进程中, Binder会把客户端传递过来的对象重新转化并生成新的对象。RemoteCallbackList内部有一个Map结构专门保存所有AIDL的回调,这个Map的key是IBinder类型,value是Callback类型。
如果AIDL中用了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并声明为Parcelable类型。
5、ContentProvider
CURD四个方法存在多线程并发,注意线程同步,SQLiteDatabase内部对数据库的操作有同步处理,但多个SQLiteDatabase对象来操作数据库就无法保证数据同步。如果contentProvider的底层数据集是一块内存,比如List,这种情况就需要进行线程同步。
6、Socket
- Binder连接池
将每个业务模块的Binder请求统一转发到远程Service中去执行 ,从而避免重复创建Service的过程。

- IPC方式的特点

4、synchronized
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
5、App的启动过程
本文所指的优化针对冷启动。简单解释一下App的启动过程:
1.点击Launcher,启动程序,通知ActivityManagerService
2.ActivityManagerService通知zygote进程孵化出应用进程,分配内存空间等
3.执行该应用ActivityThread的main()方法
4.应用程序通知ActivityManagerService它已经启动,ActivityManagerService保存一个该应用的代理对象,ActivityManagerService通过它可以控制应用进程
5.ActivityManagerService通知应用进程创建入口的Activity实例,执行它的生命周期
启动过程中Application和入口Activity的生命周期方法按如下顺序调用:
1.Application 构造方法
2.attachBaseContext()
3.onCreate()
4.入口Activity的对象构造
5.setTheme() 设置主题等信息
6.入口Activity的onCreate()
7.入口Activity的onStart()
8.入口Activity的onResume()
9.入口Activity的onAttachToWindow()
10.入口Activity的onWindowFocusChanged()
6、Scroller实现弹性滑动
流程:invalidate()让view重绘 -> draw()-> computeView()(我们重写这个方法)

scroller并不能直接实现view的滑动,它需要配合view的computeScroll()方法。在computeScroll()方法中不断让view重绘(invalidate()方法),每次重绘都会计算滑动持续的时间(computeScrollOfset()方法),根据这个时间就能算出view滑动的位置,我们根据每次滑动的位置调用scrollTo ()方法惊醒滑动,这让不断重复就形成了弹性滑动。
7、Intent-filter 匹配规则 (activity隐试调用)
-
action 匹配规则
- action区分大小写
- intent必须包含action,如果没有,匹配失败,且必须和过滤规则中的其中一个action相同,才匹配成功action
-
category 匹配规则
- 可以没有,若intent中含有category,那么所有的category都必须和过滤规则中其中一个category相同
- category 在移动activity时会默认添加
-
data 匹配规则
- 和action类似,如果过滤规则中定义了data, 那么intent中必须也要定义可匹配的data
- data由两部分组成,mimeType和URI,URI默认值为content和file(值schema)
8、抽象类和接口
接口是对动作的抽象,抽象类是对根源的抽象。
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
9、APK编译打包过程
浙公网安备 33010602011771号