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为每个应用分配了一个独立的虚拟机,或者说为每个进程都分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同虚拟机中访问同一个类的对象会产生多份副本,互不干扰。 所以运行在不同进程中的四大组件,想通过内存来共享数据,都是行不通的。

  • 使用多进程的问题
  1. 静态成员和单例模式完全失效
  2. 线程同步完全失效
  3.  SharedPreferences可靠性下降
  4. Application会多次创建 (运行在同一个进程的组件属于同一个虚拟机和同一个Application, 同理,运行在不同进程的组件是属于不同的虚拟机和Application的)
  •  基础知识
  1. 序列化Parcelable 和 Serializable   前者更高效,后者使用方便,将对象序列化到存储设备或将对象序列化后通过网络传输,这两种情况建议用后者。
  2. Binde

 

  手动实现Binder的步骤:

  1. 声明一个AIDL性质的接口,只需继承IInterface即可
  2. 实现Stub类和Stub类的Proxy代理类
  • 几种方式
  1. 使用Bundle
  2. 文件共享

   3、Messager

 

  4、AIDL , 注意使用RemoteCallbackList,解决释放注册对象的问题

  使用流程:首先创建一个Service和一个AIDL接口,接着创建一个类继承AIDL接口中的Stub类并实现Stub中的抽象方法,在Service的onBind方法中返回这个类的对象,然后客客户端就能绑定服务端的Service,建立链接后就可以反问服务端的方法了。

  支持的类型:

  1. 基本数据类型(int、long、char、boolean、double等)
  2. String 和 CharSequence
  3. List(ArrayList)
  4. Map(HashMap)
  5. Parcelable
  6. 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 匹配规则

  1. action区分大小写
  2. intent必须包含action,如果没有,匹配失败,且必须和过滤规则中的其中一个action相同,才匹配成功action
  • category 匹配规则

  1. 可以没有,若intent中含有category,那么所有的category都必须和过滤规则中其中一个category相同
  2. category  在移动activity时会默认添加
  • data 匹配规则 

  1. 和action类似,如果过滤规则中定义了data, 那么intent中必须也要定义可匹配的data
  2. data由两部分组成,mimeType和URI,URI默认值为content和file(值schema)

 

8、抽象类和接口

接口是对动作的抽象,抽象类是对根源的抽象。

1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。

2、抽象类要被子类继承,接口要被类实现。

3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现

4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。

5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。

6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果

7、抽象类里可以没有抽象方法

8、如果一个类里有抽象方法,那么这个类只能是抽象类

9、抽象方法要被实现,所以不能是静态的,也不能是私有的。

10、接口可继承接口,并可多继承接口,但类只能单根继承。

 

9、APK编译打包过程

aapt->aidl -> javac-> dx(dex)-> apkbuilder-> jarsigner-> zipalign
1、打包资源文件,生成R.java文件
2、处理AIDL文件,生成对应的.java文件
3、编译Java文件,生成对应的.class文件
4、把.class文件转化成Davik VM支持的.dex文件
5、打包生成未签名的.apk文件
6、对未签名.apk文件进行签名 
7、对签名后的.apk文件进行对齐处理(不进行对齐处理是不能发布到Google Market的)
 
所谓的字节对齐,就是各种类型的数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这个就是对齐。
需要字节对齐的根本原因在于CPU访问数据的效率问题,数据字节对齐后可以减少CPU访问内存的次数,但相应的,字节对齐也增加了内存空间的消耗(存在某些空内存没被使用)。
zipalign优化的最根本目的是帮助操作系统更高效率的根据请求索引资源,通过将apk中的未压缩数据进行字节对齐(一般为4字节对齐),允许系统使用mmap方法直接映射文件至内存空间,降低内存的消耗。
 

 

 

posted @ 2018-04-24 14:27  fangFXQ  阅读(262)  评论(0)    收藏  举报