Android进程内通信


什么是IPC?
IPC(Inter-Process Communication,进程间通信)
IPC ( Instruction per Clock 及CPU每一时钟周期内所执行的指令多少) IPC代表了一款处理器的设计架构,一旦该处理器设计完成之后,IPC值就不会再改变了。

1、Android进程内通信
Android为了屏蔽进程的概念,利用不同的组件[Activity、Service 或者其它组件]来表示进程之间的通信。
组件间通信的核心机制是Intent ,通过Intent可以开启一个Activity或Service,

Intent包含两部分:action和category(区分性数据)、data(内容性数据)
Intent类型:
1)显示Intent---直接指定消息的目的地,只适合同一进程内的不同组件之间通信new Intent(this,Target.class)
2)隐式Intent---AndroidMainifest.xml中注册,一般用于跨进程通信new Intent(String action)

3)实现Intent简单进程间通信
重点在于隐式Intent:
a、一个<activity>包括:
零个或多个<intent-filter>
它主要是作为匹配的标准,能否匹配成功由<action>、<category>、<data>三个tag共同决定的。
b、一个<intent-filter>包括:
一个或多个 <action>
零个或多个 <category>
指定<activity>的分类特征
例如:
<category android :name="android.intent.category.LAUNCHER" />
--说明该<activity>是该project运行 的第一个界面
<category android:name="android.intent.category.HOME" />
--说明该<activity>可以作为Launcher的,即系统 操作界面
c、零个或一个 <data>
指定携带的数据的类型,使用MIME类型描述方式来描述
例如:<data android:mimeType="video/mpeg" />
video/mpeg表示编码格式为mpeg的视频 ,
也可以使用通配符video/*表示任意格式的视频文件类型;

4)一个Intent对应多种匹配结果的处理说明
一个intent有多个可匹配的处理组件,系统如何处理?
分响应消息的组件类型:
1)如果是service那么这些service都可以启动并处理消息。
2)如果是Activity则会弹出一个对话框让用户进行选择。
5)安全问题----权限permission


2、IPC机制
Android 对进程间通信实现了一套轻量级的IPC机制 --- Binder机制,基于此基础之上提供了整体的封装,从而实现对象代理机制。
A、什么是Binder工作模式?
1)客户端通过某种方式得到服务器端的代理对象。
2)客户端通过调用服务器代理对象的方法向服务器端发送请求。
3)代理对象把用户请求通过Android内核(Linux内核)的Binder驱动发送到服务器进程。
4)服务器进程处理用户请求,并通过Android内核(Linux内核)的Binder驱动返回处理结果给客户端的服务器代理对象。
5)客户端收到服务器端的返回结果。
B、binder机制的组成
1)Binder驱动---/dev/binder 是Android内核的一个字符驱动设备,它是IPC的核心部分。
客户端发送请求最终就是通过它来传递到服务端,而服务端的返回结果也是通过它来传给客户端。内核源码:binder.c
2)Service Manager---服务端有服务的话就得向它注册,而客户端需要向它查询、获得服务。
3)提供服务的Server (Service)
提供服务的Server, 对普通的应用开发来讲,咱们用到的就是Service, 具体的工作Android都帮忙做了封装,所以开发变得很容易。
4)调用对象代理的Client (Activity)
普通的应用开发来讲就是 Activity 通过代理对象去请求调用服务。
注意:这个过程是同步的,所以如果估计这个服务调用很耗时,那么需要考虑启新线程来调用,而不能用UI主线程。
5)代理对象 (底层:BpBinder)
客户端进程通过服务代理BpBinder对象,调用transact函数,该函数作用就是把客户端的请求写入binder设备另一端的Service进程。
从JAVA层面来讲,Android已经为我们封装定义了IBinder接口.

3、AIDL--远程服务
1)在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java中是不支持跨进程内存共享的。
因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的。在JavaEE中,采用RMI通过序列化传递对象。
在Android中, 则采用AIDL(Android InterfaceDefinition Language:接口定义语言)方式实现。

2)什么是AIDL?
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。
原理:进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。
由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的。

3)使用AIDL实现进程间通信的步骤:
a、远程服务应用中:
远程服务端,也就是RemoteService应用中,在src下某个包中创建一个处理业务的接口类
[Java] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
9
package.cn.itcast.bh.service;
 public interface IStudentService{
       public String query(int no);
           }
 然后,根据后面注意事项中的规则,将接口的代码做一些处理,变成如下:
 package.cn.itcast.bh.service;
 interface IStudentService{
        String query(int no);
           }


然后,将相应的接口文件的后缀名改为“ .aidl”.
然后,刷新,则系统会在gen目录下生成一个Java文件,会用到这个Java文件中的类及其内部类。
(注意:如果项目中还有有错误的地方,系统则无法根据AIDL生成Java代码。

远程服务的类文件如下:
其中,(1)部分,是来自于系统根据AIDL生成的Java类的一个内部类,远程服务的onBind方法返回的IBinder对象,必须是该类(IstudentService.Stub)的子类的对象。
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
pcakage cn.itcast.bh.remoteService;
  
import cn.itcast.bh.service.IStudentService;
  
public class StudentRemoteService extends Service{
      private String[] students = new String[]{"张""郭","李""lihao"};
          private MyBinder myBinder = new MyBinder();
          @override
          public IBinder onBind(Intent intent) {
                         return myBinder;
                 }
          
        private final class MyBinder extends IStudentService.Stub(1){
                 public String query(int no) throws RemoteException{
                  
                        return students[no];
                         }
                 }
         }


b、访问者的应用中:
将远程服务端的aidl文件拷贝到访问者的应用中(src目录下),然后,访问者端代码如下:
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import android.view.View;
 import android.widget.EditText;
 import android.widget.TextView;
 import cn.itcast.bh.service.IStudentService;
  
public class MainActivity extends Activity{
  
        private EditText noText;
         private IStudentService studentService;
         private TextView showView;
         public void onCreate(Bundle savedInstanceState){
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         noText = (EditText)findViewById(R.id.no);
         showView = (TextView)findViewById(R.id.showView);
         Intent intent = new Intent("cn.itcast.remoteService");
         this.bindService(intent,new MyConnection(),Context.BIND_AUTO_CREATE);
         }
          
        private final class MyConnection implenments ServiceConnection{
                 @override
                 public void onServiceConnected(ComponentName name, IBinder service){
                         srudentService=IStudentService.Stub.asInterface(service);
                 }
                  
                @override
                 public void onSericeDisconnected(ComponentName name) {
                         studentService = null;
                 }
         }
          
        public void query(View v){
                 try{
                         int no = Integer.parseInt(noText.getText().toString());
                         String name = studentService.query(no);
                         showView.setText(name);
                 }catch(Exception e){
                                 e.printStackTrace();
                                 }
                 }
         }
 }


访问者代码中,访问者端拿到的IBinder对象不能(像访问者与本地服务通信那样)强制类型转换,需要通过如下方法进行类型转换。
binder = StudentService.Stub.asInterface(service);
进程间通信的时候,访问者获得的对象,也并不是远程服务onBind方法中返回的原始的对象,
而是那个对象的代理对象,操作系统在代理中做了协议转换的事情,对开发者是透明的。
进程间通过AIDL通信的注意事项:
AIDL文件的书写规则:
1)接口名和aidl文件名相同。
2)接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static。
3)aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),
使用这些类型时不需要import声明。对于List和Map中的元素类型必须是Aidl支持的类型。
如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口。
4)自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。
5)在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。
6)Java原始类型默认的标记为in,不能为其它标记。       
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章