1.线程进程
默认情况下,一个应用分配一个进程,同一应用的所有组件都在相同的进程和线程中运行。
四大组件可以通过设置 android:proccess 指定组件运行的线程,application 也支持 android:proccess 属性,以设置所有组件的默认值。
通过设置 android:process 可以使不同应用的组件在同一进程中运行,这些应用享有相同的Linux UserID( android:sharedUserId属性)和证书签名。
Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。
系统内存不足时,系统会选择相对不重要的进程杀死,其优先级如下:
前台进程
正在交互的Activity(已调用Activity的onResume)
正在交互的Activity绑定的Service
前台运行的Sercive(Service调用startForeground)
正在执行生命周期方法的Service(onCreate,onStart或onDestory)
正在执行onReceive()的BroadcastReceiver
可见进程 没有任何前台组件,但仍然能被用户看到内容的进程。
调用 onPause 方法的Activity(例如前台组件部分透明,允许显示下一层的Activity)。
调用 onPause 方法的Activity绑定的Service。
服务进程
正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程(例如,在后台播放音乐或从网络下载数据)。
后台进程 已经执行onStop的Activity。这些进程会被放在LRU列表中,以确保用户最近查看最后一个被终止。
空进程 不含任何应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。
因此耗时操作的Activity最好为该耗时操作启动服务而不是创建线程。例如:正在上传图片的Activity启动服务来上传,这样即使用户推出Activity,操作仍然可以在后台进行。
线程安全
当某些方法可能会从多个线程调用时,编写时必须考虑线程安全。
Binder方法的调用方如果在同一进程,则该方法在调用方的线程执行。
Binder方法的调用方如果不在同一进程,方法将在线程池(Binder所在的进程维护)中被调用,即使调用方是在UI线程中执行,Binder方法依然会被在线程池中执行后返回。
可能会有多个池线程在同一时间使用Binder方法。因此,Binder 方法必须实现为线程安全方法。
同理ContentProvider的增删改查方法也是在进程的线程池中调用,而不是在UI线程。由于这些方法可能会同时从任意数量的线程调用,因此它们也必须实现为线程安全方法。
2.IPC
多进程模式
进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法。
通过给四大组件指定 android:proccess 属性,只有这一种方法可以正常开启多进程模式。
多进程模式中,不同进程的组件会拥有独立的虚拟机,Application,内存。
通过JNI在native层中fork一个新的进程,是不常用的创建多线程的方法。
入口Activity如果不指定process属性,那么它运行在默认进程中,默认进程的进程名是包名,例如android:process=":"。
:的含义是指要在当前的进程名前附加上当前的包名。com.xxx.text:remote == :remote。
进程名以:开头的属于私有进程。而进程名不以:开头的属于全局进程,其他应用可以通过ShareUID和她跑在统一进程中。
Android为每个进程都分配了一个虚拟机,不同的虚拟机访问同一个对象会产生多份副本,导致数据不一致,所以需要IPC。
3.序列化
Serializable 和 Pracelable 接口可以完成对象的序列化。
Serializable是JAVA提供的序列化接口,只需要在类中声明serialVersionUID,即可自动实现默认的序列化过程。
对象序列化只要使用ObjectOutputStream和ObjectInputStream即可。
如果不手动指定serialVersionUID,反序列化时类有所改变,将导致crash。
静态成员变量不属于对象,所以不参与序列化。
用transient关键字编辑的成员变量不参与序列化过程,用来修饰无意义的不用序列化的变量。
Serializable 系统默认的序列化过程也是可以改变的,重写writeObject和readObject即可。
系统已经为我们提供了许多实现Parcelable接口的类,是可以直接序列化的,Intent,Bundle,Bitmap等。List和Map也可以序列化,前提是他们里面的类都是可序列化的。
使用Parcelable需要重写指定方法。
Serializable是JAVA中的序列化接口,使用简单但开销巨大,序列化个反序列化需要进行大量I/O操作;Parcelable是Android中的序列化方式,使用麻烦但是效率高,Android推荐。
Parcelable主要用在内存序列化上(intent传递)。将对象序列化到存储设备或序列化后网络传输通过Parcelable会稍显复杂,这两种情况建议使用Serializable。
4.Binder连接池
多个不同的业务模块需要使用AIDL进行进程间通讯,为了减少Service的数量,将所有的AIDL放在同一个Service中进行管理。
各个模块像服务端提供自己的唯一标识和与其对应的Binder对象;服务端至于要一个Service,然后提供一个queryBinder接口,这个接口根据相应的模块来返回对应的Binder。
5.Socket
Socket也称套接字,是网络通信中的概念。
分为流式套接字和用户数据报套接字,对应网络传输控制层中的TCP和UDP协议。
TCP面向连接的协议,提供稳定的双向通信功能。经过三次握手才能完成,自身提供超时重传机制,稳定性强。
UDP是面向过程,无连接的,提供不稳定的单向通讯,当然也可以实现双向。UDP有较好的性能,缺点是稳定性不强。
Socket的使用:
在配置文件中声明网络访问的权限(不能在主线程中访问网络)。
远程Service建立一个TCP服务(通过ServerSocket对象),然后在主界面中连接TCP服务。
重连机制:每次连接失败后尝试重新建立连接,为了降低重试机制的开销,加入休眠机制,即线程休眠1000毫秒。
Activity退出时(OnDestory),需要关闭Socket。
Socket可以实现进程间通讯,也可以实现设备间通信,前提是设备之间的IP地址互相可见。
6.ContentProvider
ContentProvider底层实现也是Binder。
ContentProvider进程中,除了onCreat由系统回调并运行在主线程里,其他五个方法均有外界回调发生在Binder线程池中。
虽然ContentProvider的底层数据看起来像SQLLite数据库,其实它对底层数据的数据没有任何要求。
ContentProvider还支持文件数据,比如图片视频表格等,处理这类数据时可以在ContentProvider返回文件的句柄给外界从而让文件来访问ContentProvider中的文件信息。
Android系统所提供的MediaStore功能就是文件类型的ContentProvider。
ContentProvider的使用:
声明时,android:authorities 是唯一标识,建议命名时加上包名前缀。而且可以通过控制permission来控制外界访问的权限。
ContentProvider的三次query方法可能运行在三个不同的线程中,一个Binder线程池中。
除了query方法,update,delete,insert方法会引起数据源的改变,可以通过注册registerContentResever来注册观察者,unregister解除观察者。通过ContentResever的notifyChange通知外界数据已经改变。
增删改查是存在多线程并发的,因此方法内部需要做好线程同步。比如只使用一个SQLite对象连接,因为SQLiteDatabase内部对数据库的操作是有同步处理的。
ContentProvider除了支持对数据源的增删改查之外,还支持自定义调用,通过ContentResever和ContentProvider的Call方法来完成。