当前位置:操作系统 > 安卓/Android >>

Android组件间通信机制对面向对象特性的影响

组件的特点
对于Android的四大组件Activity, Service, ContentProvider和Service,不能有Setter和Getter,也不能给组件添加接口。原因是组件都是给系统框架调用的,开发者只能实现其规定的回调接口,组件的创建与销毁都是由系统框架控制的,开发者不能强行干预,更没有办法获取组件的对象。比如Activity,Service,BroadcastReceiver,你没有办法去创建一个Activity,Service或BroadcastReceiver,然后像使用其他类那样的调用其上的接口与其通信,用Setters和Getters改变属性等等。这也决定了,组件之间通信只能用系统支持的Intent。而Intent只能传递基本数据类型和Uri等一些常见的数据类型。Intent只支持传递内置类型和一些限制类型,这就导致了组件之间的数据传递必须都是基本类型,所以枚举类型无法使用。
多态无法实现
比如你有一个Service用于在后台执行UI中发来的请求,这些请求有些是做数据请求,有些是做数据分析,等等。这里可以用多态,定义一个统一的Transaction类,然后再为每种特定的Transaction类型,Transaction中统一接口process()用于实际的处理,理想的情况是,Service接收一个Transaction对象,然后调用其process(),没有必要知道具体的类型,UI创建具体的一个类型对象然后交由Service来处理。但是这在Android当中是无法实现的,因为Intent通信机制所限,因为它不能直接传递Transaction对象。所以,Service必须要知道具体的类型。原生应用Mms中就有如此的现象,在transaction包中TransactionService是处理服务,UI发送到Service的只是区别不同Transaction的Id(一个整数),Service查看不同的Id创建不同的Transaction对象,然后调用process()对其处理。
建议:自己实现一个类似Service的服务类,在其内用Handler,Thread和Looper让其长时间运行。这样就没有组件间通信的限制,你可以像正常使用Java对象那样来使用这个服务类,向其传递自定义的处理请求:
[java]
1. public class TransactionServer extends HandlerThread { 
2.     public TransactionServer() { 
3.         start(); 
4.     } 
5.     public void onLooperPrepared() { 
6.         mHandler = new Handler(getLooper(), new Handler.Callback() { 
7.              @Override 
8.               public void handleMessage(Message msg) { 
9.                     Transaction request = (Transaction) msg.obj; 
10.                      request.process(); 
11.                } 
12.          } 
13.     } 
14.      
15.    public void execute(Transaction request) { 
16.        if (mHandler == null) { 
17.             return; 
18.         } 
19.        Message msg = Message.obtain(); 
20.        msg.obj = request; 
21.        mHandler.sendMessage(msg); 
22.    } 
23. } 


在Activity中就可以创建此Server的对象,然后使用它:
[java]
1. TransactionServer server = new TransactionServer(); 
2. Transaction updateRequest = new UpdateTransaction(); 
3. server.execute(updateRequest); 


另外,用AIDL与Service通信,虽可以获取Service的对象引用,可以直接调用Service的方法,但这个也有限制,对于AIDL的接口,所有的参数和返回类型都必须是基本数据
据类型,不能有对象。原因也好理解,因为AIDL也是要通过IPC的,即便Service与Activity在同一个进程内,所以本质上它与Intent通信机制无区别。
封装性被破坏
组件间的通信机制决定了Android的封装性,先来看一些实例:
[java]
1. Intent i = new Intent(Intent.ACTION_VIEW); 
2. i.setDataAndType(uri, "text/html"); 
3. startActivity(i); 

这在Android当中是再常见不过的了。
Intent和IntentFilter的使用让封装性受到大大的破坏,因为你必须把字串,参数等直接写入到Intent或IntentFilter当中。例如:
[java]
1. Intent i = new Intent("android.contacts.action.MULTIPLECONTACTSLISTS"); 
2. i.setExtra("request_type", 3); 
3.  
4. <intent-filter> 
5.      <action android:name="android.contacts.action.MULTIPLECONTACTSLISTS" /> 
6. </intent-filter> 


当然,可以再好一点,就是:
Intent i = new Intent(Contacts.ACTION_GET_CONTACTS);
但是在AndroidManifest中的IntentFilter还是要写字串常量(Literal Strings),这样就有一个问题,就是即使你写错了,编译器不会提醒你,直到你运行的时候才会发现程序不正常工作,你调试啊,调试,最终发现是字串写错了。或者,activity的name写错了,编译器同样不会提醒你,但你运行时却因找不到类而报出RuntimeException。
建议:尽可能在所有作用域内定义常量,以让组件间接口保持一致性,特别是对于字串常量,一定要在二个组件都可见的作用域内定义常量,否则将会有维护的麻烦。
例子:intent.putExtra("request_type", 3); --> intent.putExtra(TargetActivity.REQUEST_TYPE, TargetActivity.NO_BACKGROUND);否则,特别是当目标组件不在同一个包内,或距离很远时,如果另一方改了,编译时不会有错,但程序不会正常工作,从而引发难以发觉的Bug。
无法在组件间传递自定义的数据结构
如前面所述,因为无法获取组件的对象的引用,所以你无法向其设置数据,当然,可以用静态方法,但是不优雅且难以维护(对于Service倒是可以通过AIDL方式获取Service对象的引用,然后调用其方法来添加数据)。又因为Intent只能携带基本的数据类型,所以对于自定义的数据结构想要在组件间传递就特别的麻烦。当然你可以以让数据结构实现Parcleable接口,但是用起来也相当的麻烦。
建议:
1. 尽可能的避免使用自定义数据结构,特别是除了Setters和Getters以外不具有其他行为的数据结构
对于结构化的数据,为其定义ContentProvider,把数据写入SQLite数据库,这样数据库表中的每行数据都相当于是一个数据对象,每一列都是其属性。因为Android的组件与SQLite数据库的粘性很大,每个组件都可以很方便的从数据库中获取数据,再通过Cursor等工具来操作数据。最最重要的是这很方便在组件间传递数据,因数ContentProvider的访问都是通过Uri来实现的,而Uri又可以与Intent无缝接合,Uri可以方便的放入和从Intent中取出,每个组件又都可以直接访问ContentProvider用Uri读取数据,从而就可以实现组件间的无缝数据传递。
2. 尽可能的不要在组件之间传递数据
不要用太多的Activity,Service也能免则免,Activity+线程可能解决大部分问题,当然了,线程也不是那么好用的,特别是在Android里面。
3. 避免在组件之间传递自定义数据结构
如前所述,组件之间最好直接传递基本数据和Intent支持的数据类型。对于自定义的数据结构,要么不定义数据结构,要么不要在组件间传递,否则会很麻烦,虽然可能以实现Parcelable接口,但是效率和操作的方便性上都会大打折扣。
关于枚举和整数集
先前一篇博客曾说要尽量使用枚举(enum)代替整数集(ints),而且很多编程书籍(Effective Java)也建议用枚举代替整数集,这其中的好处就是降低出错率,把运行时的检查可以放到编译时,因为整数的范围较大,你可传递任意的整数,直到运行时才会检测或产生问题,但是枚举会在编译时检查类型,如果不是合法的枚举,编译器会报怨。
但是我们可以看到,在Android中的情况却很差,Android中大量的使用了整数集,系统定义了大量的整数集,很多参数也都是整数,虽然正确的方法都是向这些API传递其所定义的整数常量,但是你如果传个Integer.MAX_VALUE或Integer.MIN_VALUE,起码在编译时不会出问题。
既然这不是一个好的编程规范,为什么Android中还要大量的使用整数集呢?原因就在于组件间通信,组件之间要传递参数,但是Intent又只能放入基本数据类型,也就是说如果使用枚举,那么将无法用Intent传递给其他的组件,因

补充:移动开发 , Android ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,