利用服务实现进程之间的通信,aidl 安卓接口定义语言的使用 与 Messenger信使的使用

AIDL: android interface definition language  安卓接口定义语言  作用:可以调用另一个工程中的方法

在Service总结里面使用绑定服务的方法暴露了MyBind里面的方法

为了不暴露服务类中发方法,就需要一个中间人去调用服务里的方法,而我们就只需要这个中间人的对象,代码如下:

public class MyService extends Service {
    private class MyBind extends Binder implements InServiceBinder{
        private void method(){
            System.out.println("我是服务里面的方法");
        }

        @Override
        public void inMethod() {
            method();
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBind();
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }    
}

接口的定义:

public interface InServiceBinder {
    public void inMethod();
}

调用服务:

public class MainActivity extends Activity implements OnClickListener {
    public InServiceBinder binder;//调用服务方法的对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button bind=(Button) findViewById(R.id.bind);
        Button unbind=(Button) findViewById(R.id.unbind);
        Button method=(Button) findViewById(R.id.method);
        bind.setOnClickListener(this);
        unbind.setOnClickListener(this);
        method.setOnClickListener(this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);//避免了退出程序时因为没有取消绑定而程序报错
    }
    public ServiceConnection conn=new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            System.out.println("杀死进程时调用");
        }
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            System.out.println("连接了服务");
            binder = (InServiceBinder) service;//转换成接口对象,去调用该接口的方法,service对象就是MyBind返回的对象
        }
    };
    @Override
    public void onClick(View v) {
        Intent intent=new Intent(this,MyService.class);
        switch (v.getId()) {
            case R.id.bind://绑定服务
                bindService(intent, conn, BIND_AUTO_CREATE);
                break;
            case R.id.unbind://解除绑定
                unbindService(conn);
                break;
            case R.id.method://调用服务里的方法
                binder.inMethod();//这样就没有暴露MyService里的方法,而是通过接口来调用MyService里的方法
                break;
        }
    }
}

如果把MyBind类改为public,那么在Activity里面可以通MyBind对象调用该类的所有方法,可是通常我们不想把所有方法都暴露出来,或者不想暴露具体实现的步骤,就可以通过定义接口来实现这样的功能,该接口也就相当于中间人的作用,这样使得MyService类中的方法没有暴露出来,在上面的代码中还有一个问题,就是在解除绑定后还能调用服务中的方法,所以可以在解除绑定的时候把bind对象赋值为null并在使用时进行判断就可以了.

前面说的为本地服务,下面介绍远程服务

远程服务:调用者和服务在不同的工程代码里面。
本地服务:调用者和服务在同一个工程代码里面。

进程间进行通讯都是需要一块公共的空间的

绑定本地服务调用方法的步骤:
1.在服务的内部创建一个内部类 提供一个方法,可以间接调用服务的方法
private class MiddlePerson extends Binder implements IMiddlePerson{}
2.实现服务的onbind方法,返回的就是中间人 MiddlePerson
3.在activity 绑定服务。bindService();
4.在服务成功绑定的时候 会执行一个方法 onServiceConnected 传递过来一个 IBinder对象
5.强制类型转化 调用接口里面的方法。


绑定远程服务调用方法的步骤:
1.在服务的内部创建一个内部类 提供一个方法,可以间接调用服务的方法
2.把暴露的接口文件的扩展名改为aidl文件 去掉访问修饰符 public
private class MiddlePerson extends IMiddlePerson.Stub{} IPC的子类
3.实现服务的onbind方法,返回的就是中间人 IMiddlePerson
4.在activity 绑定服务。bindService();
5.在服务成功绑定的时候 会执行一个方法 onServiceConnected 传递过来一个 IBinder对象
6.IMiddlePerson.Stub.asInterface(binder) 调用接口里面的方法。

 

aidl文件都是公有的,没有访问权限修饰符

IPC: inter process communication 进程间通讯,在生成的类中里面就是利用了IPC实现两个工程的通讯

上面的那个例子是在自己的工程中创建一个服务,称为本地服务

具体实现两个工程通讯的代码:

1.先创建远程服务工程:在远程工程中创建一个服务

/*
定义远程服务的关键:
1.创建一个接口作为中间人,然后修改该接口的类型为.aidl文件,系统并会在gen目录中自动生成这个类,该类就是实现远程通信的关键
2.在另一个程序中需要创建一个和这个服务同包名,同一个aidl文件
3.在注册服务的时候需要定义一个action,这样才可以在不同的工程中绑定该服务
 */
public class RemoteService extends Service {
    
    private class RemoteBind extends InRemote.Stub{
        private void method(){
            System.out.println("远程服务中的方法");
        }
        @Override
        public void InRemotemethod() {
            method();
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        return new RemoteBind();
    }
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }    
}

说明:InRemote.Stub的Stub为自定生成的类,那么怎么生成的?按照第一个例子创建接口,然后修改扩展名为.aidl文件

InRemote.aidl文件:

package com.example.ts.yuanc;

interface InRemote {
    /**
     * 远程服务的接口方法
     */
    void InRemotemethod();
}

AndroidManifest.xml文件中注册服务

 <service android:name=".RemoteService">
            <intent-filter >
                <action android:name="com.ts.remoteService"/>  //为了别的工程也能绑定该服务
            </intent-filter>
 </service>

2.创建一个工程去绑定上面这个工程中的服务:

1.首先需要在该工程中创建一个与上面.aidl同名同包名同内容的文件  (复制上面的就是)

2.绑定远程服务:

public class MainActivity extends Activity implements OnClickListener {
    public InRemote binder;//远程服务通讯的对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button bind=(Button) findViewById(R.id.bind);
        Button unbind=(Button) findViewById(R.id.unbind);
        Button method=(Button) findViewById(R.id.method);
        bind.setOnClickListener(this);
        unbind.setOnClickListener(this);
        method.setOnClickListener(this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);//避免了退出程序时因为没有取消绑定而程序报错
    }
    public ServiceConnection conn=new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            System.out.println("杀死进程时调用");
        }
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            System.out.println("连接了服务");
            binder=InRemote.Stub.asInterface(service);//得到远程服务的中间人对象
        }
    };
    @Override
    public void onClick(View v) {
        Intent intent=new Intent();
        intent.setAction("com.ts.remoteService");//指定开启远程服务
        switch (v.getId()) {
            case R.id.bind://绑定服务
                bindService(intent, conn, BIND_AUTO_CREATE);
                break;
            case R.id.unbind://解除绑定
                unbindService(conn);
                break;
            case R.id.method://调用远程服务里的方法
                try {
                    binder.InRemotemethod();//调用远程服务中的方法
                    break;
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
        }
    }
}

 如果是使用studio实现:可以在module项目上直接右键添加aidl文件

 

跨进程不同应用之间的通信2-Messenger(信使)实现的:

服务端(注册了服务的)代码如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

上面只是用来显示个界面而已,下面看服务类:MyService

public class MyService extends Service {
    private final int service_message=0;
    private final int client_message=1;
    /**
     * messenger(信使)处理进程的通讯,handler处理线程的通信
     * 声名一个服务端的Meesnger用来接收客服端的跨进程消息,通过  handler去处理消息
     */
    public Messenger serviceMessenger=new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case client_message://代表接收到的为客服端消息
                    int arg1 = msg.arg1;
                    Log.i("tag","获取到客服端的数据:"+arg1);
                    Messenger clientMessenger = msg.replyTo;//获取客服端对象
                    Message message = Message.obtain();
                    message.what=service_message;//代表这是服务端发过去的
                    message.arg1=100;//发送的数据
                    try {
                        clientMessenger.send(message);//利用客服端对象发送消息到客服端的handler
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    });
    @Override
    public IBinder onBind(Intent intent) {
        return serviceMessenger.getBinder();
    }
}

注册信息:

<service
     android:name=".MyService" android:enabled="true" android:exported="true">
     <intent-filter>
                <action android:name="com.messenger.service"/>
     </intent-filter>
</service>

客服端类:用来绑定上面的服务来实现数据之间的传递:

/**
 * 利用Messenger(信使)在进程中通信的原理:
 * 在客服端绑定服务,连接服务成功就能得到服务端的Messenger对象,就能利用这个对象发送数据给服务端
 * 注意:在发送数据给服务端的时候需要携带自己客服端的Messenger对象,这样服务端才能拿到这个对象发送数据给客服端
 * 也就是Messenger处理进程的通信,各自的handler再进行处理
 */
public class MainActivity extends AppCompatActivity {
    private final int service_message=0;
    private final int client_message=1;
    private Messenger clientMessenger=new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case service_message://接收到服务端的消息
                    Log.i("tag","接收到服务端的消息:"+msg.arg1);
                    break;
            }
        }
    });

    private ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //service为服务端的serviceMessenger.getBinder()
            Messenger serviceMessenger = new Messenger(service);//获取服务端的Messenger对象
            Message message = Message.obtain();
            message.what=client_message;//代表是客服端发过去的
            message.arg1=1;//向服务端发送数据1
            //需要携带客户端的 Messenger,这样服务端才能通过msg获取客服端对象发送消息
            message.replyTo = clientMessenger;//切记要给个回应,服务那边才能收到这个对象
            try {
                serviceMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent=new Intent();
        intent.setAction("com.messenger.service");
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);//绑定另一个程序的服务
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);//取消绑定,不然退出时会有异常
    }
}

总结:Messenger其实是封装了一下AIDL

1. Messenger本质也是AIDL,只是进行了封装,开发的时候不用再写.aidl文件。

结合我自身的使用,因为不用去写.aidl文件,相比起来,Messenger使用起来十分简单。但前面也说了,Messenger本质上也是AIDL,故在底层进程间通信这一块,两者的效率应该是一样的。

2. 在service端,Messenger处理client端的请求是单线程的,而AIDL是多线程的。

使用AIDL的时候,service端每收到一个client端的请求时,就会启动一个线程(非主线程)去执行相应的操作。而Messenger,service收到的请求是放在Handler的MessageQueue里面,Handler大家都用过,它需要绑定一个Thread,然后不断poll message执行相关操作,这个过程是同步执行的。

3. client的方法,使用AIDL获取返回值是同步的,而Messenger是异步的。

Messenger只提供了一个方法进行进程间通信,就是send(Message msg)方法,发送的是一个Message,没有返回值,要拿到返回值,需要把client的Messenger作为msg.replyTo参数传递过去,service端处理完之后,在调用客户端的Messenger的send(Message msg)方法把返回值传递回client,这个过程是异步的,而AIDL你可以自己指定方法,指定返回值,它获取返回值是同步的。

 

posted @ 2016-05-30 15:00  ts-android  阅读(273)  评论(0编辑  收藏  举报