Android 开发艺术探索 读书笔记

来源:互联网 发布:火龙果软件培训 编辑:程序博客网 时间:2024/05/19 13:18

第二章 IPC机制

Android开发艺术探索
p50
IPC 是Inter-Process Communication的缩写,含义:进程间通信或跨进程通信,是指两个进程之间进行数据交换的过程。
   android中主线程就是UI线程,在UI线程中才能操作界面元素。
    android中开启多进程只有一种方法:给四大组件(Activity,Service,Receiver,ContentProvider)在AndroidMenifest中指定 android:process属性。“:”开头的含义是指要在当前的进程名前面附上当前包名。属于当前应用的私有进程,其他应用的组件不可以和他跑在同一个进程中,不以:开头是全局进程,其他进程通过ShareUID方式可以和他跑在同一进程中。
                                                                      还有一种不常用,通过JNI在native层 去fork一个新的进程。


使用多进程会造成如下问题:

1.静态成员和单例模式完全失效。

2.线程同步机制完全失效

3.sharePreferences的可靠性下降

4.Application会多次创建

P57 IPC基础概念介绍

 一 Serializable接口

    Serializable是java所提供的一个序列化接口,为对象提供标准的序列化和反序列化操作 

  使用方法:继承Serializable接口,在类的声明中指定  private static final long serialVersionUID=xxxxxxxxxxxxx

                  序列化:ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("cache.text"));

out.writeObject(user);

out.close();

反序列化:ObjectIutputStream in=new ObjectIutputStream(new FileIutputStream("cache.text"));

User newUer=(Uesr)in.readObject();

in.close();

二 Parcelable接口

   使用方法: 实现接口 Parcelable

  序列化:writeToParcel(Parcel out,int flags)方法来完成

 反序列化:CREATER         public static final  Parcelable.Creator<User> CREATOR =new  Parcelable.Creator<User>()

 功能描述 describeContents方法完成几乎在所有情况下都返回0,当且仅当当前对象存在文件描述符时,返回1.


Serializable接口和Parcelable接口对比

Serializable是java中的序列化接口 使用简单但是开销很大,需要大量I/O操作。对象序列化到存储设备或者通过网络传输使用。

Parcelable是android中的序列化机制,使用麻烦效率很高,首选。主要用在内存序列化

三 Binder   P69

1.Binder实现了IBinder接口,是android中一种跨进程通讯的方式,从androidFramework角度,他是serviceManager链接各种Manager和相应ManagerService的桥梁;从应用层,Binder是客户端和服务器端通信的媒介,通过Binder对象,客户端就可以获取服务器端提供的服务(普通服务和基于AIDL的服务)或者数据

 Binder主要用在Service中包括AIDL和Manager,Manager的底层就是AIDL

    注意,AIDL中两个类在同一包中 使用时仍要导入包名

 所有可以在Binder中传输的接口都需要继承IInterface接口

内部类stub就是一个Binder类,当客户端和服务器端都位于同一进程,方法调用不会跨进程的transact过程,而当两者位于不同进程时 ,方法需要走transact过程,这个逻辑由 stub的内部代理类proxy来完成。

 stub中的一些方法

        1.DESCRIPTOR Binder的唯一标识,一般用当前的Binder的类名表示

2.asInterface(android.os.IBinder obj)  将服务器端的Binder对象转换为客户端所需要的AIDL接口类型的对象, 如果客户端和服务器端位于同一进程,返回stub对象本身,否则返回系统封装后的Stub.proxy 对象。

3.asBinder   返回当前Binder对象

       4.onTansact  这个方法运行在服务器端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过底层封装后交由此方法来处理。public Boolean onTransact(int code ,android.os.Parcel data,android.os.Parcel reply,int flags)  返回false,客户端请求失败

5.Proxy#getBookList   这个方法运行在客户端,当客户端远程调用此方法时,先创建该方法所需要的输入型Parcel对象data ,输出型Parcel对象reply,和返回值对象List,;接着调用服务器端的onTransact方法发起RPC(远程过程调用)请求,同时当前线程挂起,服务器端的onTransact方法调用,知道RPC过程返回,当前线程继续执行。

6.Proxy#addBook 运行在客户端  执行过程同上

说明:首先,当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以 如果一个远程方法是很耗使的,不能在UI线程中发起此远程请求,其次 由于服务器端的Binder方法运行在BInder的线程池中,所以Binder方法不管是否耗时都应该采取同步的方式去实现,因为他已经运行在一个线程中了。

  AIDL文件并不是实现Binder的必需品,AIDL文件的本质是系统为我们提供了一种快速实现Binder的工具,仅此而已。

 2.Binder的两个重要的方法linkToDeath和unlinkToDeath 给Binder设置死亡代理

(1).首先声明一个DeathRecipient对象,DeathRecipient是一个接口只有一个方法binderDied,实现这个方法,Binder死亡的时候,系统就会调用binderDied方法,然后我们就可以移出之前绑定的binder代理并重新绑定远程服务。

 private IBander.DeathRecipient mDeathRecipient=new IBinder.DeathRecipient()

(2).在客户端绑定远程服务成功时,给binder设置死亡代理

 mService=IMessageBoxManager.Stub.asInterface(binder);

binder.linkToDeath(mDeathDecipient,0);

Binder死亡的时候我们就可以接到通知了,通过Binder的方法isBinderAlive也可以判断Binder是否死亡。


P76 Android中的IPC方式

一,Bundle

Bundle实现了Parcelable接口,具体要看Bundle类才能判断支持的传输类型,这是最简单的进程间通信方式。

二,使用文件共享

  适合对数据同步要求不高的进程之间进行通信 并且要求妥善处理并发读写的问题。SharePreferences是个特例 不用于进程间通信

三,使用Messenger   P81

 他是一种轻量级的IPC方案,他的底层实现是AIDL,他对AIDL做了封装

实现步骤:

1,服务端进程

首先在服务端创建一个service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象然后咋Service的onBinder中返回这个Messenger对象底层的Binder即可。

2,客户端进程

首先绑定服务端的service绑定成功用服务端返回的IBinder对象创建一个Messenger,通过Messenger向服务端发送消息,如果 需要服务端能回应客户端 还需创建一个Handler病床一个新的Messenger,并把这个Messenger对象通过message的replyTo参数传递给服务端,服务端通过replyTo参数就可以回应客户端

(四。使用AIDL

实现:

1.服务端,首先创建一个service来监听客户端的连接请求,然后创建一个AIDL文件将暴露给客户端的接口在这个AIDL文件中声明,最后在service中实现这个AIDL接口即可。

2,客户端,首先绑定服务端的service 成功后将服务端返回的Binder对象转换为AIDL接口所属类型,接着就可以调用AIDL中的方法了。

3.AIDL接口的创建   eg:

//IBookManager.aidl  文件

package ........

import .......

interface IBookManager{

List<Book> getBookList();

void addBook(in Book book);   

}

AIDL 支持的数据类型

(1),基本数据类型(int long char,boolean,double等)

(2),String 和CharSequence

(3),List: 只支持ArrayList,且里面的每个元素都能够被AIDL支持

(4),Map 只支持HashMap 且里面的每个元素都能够被AIDL支持 包括key 和value

(5),Parcelable:实现了Parcelable的所有对象

(6),AIDL :AIDL接口本身也可以在AIDL文件中使用

其中Parcelable对象和AIDL对象  必须import ;如果AIDL问价中用到了自定义的Parcelable对象 必须重建一个和他同名的AIDL文件并在其中声明他为Parcelable类型;

AIDL接口中支支持方法,不支持声明的静态变量,建议把所有和AIDL相关的类和文件全部放入一个包中。

4.远程服务端Service的实现  实现之前定义的AIDL接口

5.客户端实现 绑定远程服务,成功后将服务器端返回的Binder对象转换为AIDL接口 然后通过这个接口去调用服务端的远程方法了。




P105
我们知道,客户端调用远程服务的方法,被调用的方法运行在服务端的Binder线程池中,同时客户端的线程会被挂起,这个时候如果服务器端方法执行的比较耗时,就会倒是客户端线程长时间
的阻塞在这里,而 如果这个客户端的线程是UI的话会导致ANR。避免这种ANR的方法,只需要把调用放在非UI线程即可。
当服务器端需要调用客户端的Listener中的方法时,被调用的方法也运行在Binder线程池中,只不过是客户端的线程池。


为了程序的健壮性,Binder是有可能意外死亡的,这往往是由于服务端的进程意外停止了,这时我们需要重新连接服务,1,给Binder设置DeathRecipient监听,当Binder死亡时,我们就会收到binderDied方法的回调,
在binderDied方法中我们可以重连远程服务。2,在onServiceDisconnected中重连远程服务。区别:onServiceDisconnected在客户端的UI线程中被回调,
而binderDied在客户端的Binder线程池中被回调。 也就是说,在binderDied方法中我们不能访问UI。


AIDL权限验证
1.在onBind中验证,使用permission验证,需要在androidMenifest中声明所需权限。(自己要先定义一些权限)
2.在服务器端的onTransact方法中验证 可以使用permission验证,方法同上,也可以采用Uid和Pid来做验证,通过getCallingUid和getCallingPid可以拿到客户端所属的Uid和Pid,可以验证包名


0 0