在Android中,所有的组件都是在主线程上构造的。因此,Service.onStartCommand函数的执行会阻塞主线程。如果涉及数据库读写、网络通信、复杂运算等耗时操作,那么就需要将相关操作放入独立的进程或线程中去执行。
将组件放入独立的进程中,可以通过配置文件的process参数来实现,代码如下:
- // 在service配置信息中,增加process
- <service android:name="SimpleService"
- android:process=":a_unique_process_name"/>
但这样的方式会增加进程开销。因此,另一种可行的策略是在服务组件中另起一个独立的线程,将那些耗时又费力的操作交给它来打理。
最简单的实现策略是通过派生android.app.IntentService来执行服务组件中的处理逻辑。如图3-8所示,IntentService的实现原理非常简单。它本身是android.app.Service的子类,当启动时会在Service.onCreate函数中建立一个后台线程,该线程通过其独立的消息循环在后台等待事件。当Service.onStartCommand函数接到相关的Intent参数时,主线程会将其中的内容打包发送到后台线程中,在Service.onHandleIntent函数中进行处理。从实现来看,只需把本应放在Service.onStartCommand中执行的逻辑内容挪到Service.onHandleIntent中即可。
Parcel序列化后的数据是齐位的二进制流,如此设计是为了效率而不是为了灵活性。这就意味着,写入和读取数据的顺序与类型必须完全一致,稍有偏差,就会触发异常。如果需要传输的数据随着场景的不同有很大的变动,可以选择使用android.os.Bundle对象,它会按照键值对的模式将信息写入二进制数据流,以提高灵活性。
进程间通信机制是Android的重要基础,被广泛地应用在各类核心机制中,因此Parcel类型的实现主要通过C++来完成,上层Parcel对象通过JNI接口进行调用,从而提高了序列化相关操作的执行效率,确保Android系统可以高效运行。
在实际开发中,通常会在界面组件Activity.onResume函数中进行触发器组件的注册,而在Activity.onPause函数中注销对应的触发器组件。
数据源组件和其他组件一样,都构造于应用主线程中。直接对它进行数据的读写操作可能会阻塞主线程,从而影响应用与用户的交互。因此,当涉及大量的读写和查询时,调用者可以通过android.content.AsyncQueryHandler对象来实现对数据源组件的异步访问。
调用时,可以通过AsyncQueryHandler.startXXX系列方法将请求打包发送到后台线程,当相关处理完成后,会将结果异步回传给主线程并调用AsyncQueryHandler.onXXXComplete方法通知调用者。调用者在每次请求时,需要传入一个整型值token作为这次请求的标识,当该请求完成后进行回调时,会将token传回,帮助调用者确定这是哪一次请求。
ContentResolver相当于数据源组件的DNS和本地代理,它负责将各个URI定位到具体的数据源组件,并经由它对数据源进行增、删、改、查等操作。
当ContentResolver收到数据操作请求时,会先根据URI的信息去查找对应的数据源组件。ContentResovler对象中,有一个数据源组件的缓存对象ProviderMap,它是一个哈希表,存储各个URI对应到数据源组件对象。如果ContentResolver在缓存中未能查到对应的数据源组件,则会将查找请求发送给组件管理服务,该服务会负责查找到对应数据源组件的信息,并在ContentResolver中构造对象缓存。
Java虽有运行时二次编译的机制,但并非是解释型的语言,它的运行时加载单元是类。因此,在编写SDK版本相关代码时,要用类而不是函数为隔离单位。比如SDK版本的实现,可以按照如下格式进行划分:
- class MethodInLowSdk {
- public static void aMethod();
- };
- class MethodInHighSdk {
- public static void aMethod();
- };
在使用时通过android.os.Build.VERSION.SDK_INT判断SDK版本,从而决定使用哪个类中的方法,比如:
- void CallAMethodByVersion() {
- if (android.os.Build.VERSION.SDK_INT > low_sdk) {
- MethodInHighSdk.aMethod();
- } else {
- MethodInLowSdk.aMethod();
- }
- }