美文网首页
IPC的用法的学习 一

IPC的用法的学习 一

作者: 嗯哼嗯哼嗯哼嗯哼 | 来源:发表于2017-07-07 14:28 被阅读0次

IPC的用法的学习

这篇文章来小结一下自己学的IPC的知识,包括Messenger和AIDL,然后我在总结一下自己最近学的关于Binder的知识。

在了解IPC前先说明一下什么是进程什么是线程,按照操作系统中的描述,线程是CPU调度的最小单元,同时线程是一种有限的系统资源,而进程一般指一个执行单元,在android指一个应用。一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系,在最简单的情况下,一个进程中可以只有一个线程即主线程,在android中主线程也叫UI线程,在UI线程里面才能操作界面元素。IPC(进程间通信)是主要用于两个进程,有时候我们需要在应用间(即两个进程间)交换数据,这时候就需要IPC。实现进程间通信有很多方式,我这里就只介绍Messenger和AIDL,至于其它的方式我就不介绍了。

Messenger

Messenger就是对Binder做了一个简单的封装,我们在做一些简单的跨进程通信的时候可以使用Messenger,比AILDL简单。首先看一下构造方法

Messenger(Handler target);

//用一个Handler创造一个Messenger实例,当有一个新的Message发送到这个Messenger的时候,这个Messenger就回调用它的handler.sendMessage(),这个时候这个Message就会被这个Handler所处理。里面具体的怎么跨进程的就留到总结Binder的时候再细说,这里就需要理解到这个时候就会调用远程进程的那个Handler。

Messenger(IBinder target);
//用一个IBinder对象来构建一个Messenger实例,这个Messnegr就会关联到这个IBinder对象的Handler,向这个Messenger发送 Message的时候就会让对应的Handler来处理,这个主要用来在客户端构造服务端的Messenger对象时调用。

下面来个简单的例子:
上代码:

//服务端
public class MessengerService extends Service {
    public static final int  SAY_HELLO =1;
    class MessengerHandler  extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case SAY_HELLO:
                    Bundle data = msg.getData();
                    String str = data.getString("data");
                    Log.e("TAG","MessengerService Received say_hello: "+str);

                    break;
            }
        }
    }
    private Messenger mMessenger = new Messenger(new MessengerHandler());

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mMessenger.getBinder();
    }
    
}
//客户端
 private ServiceConnection mConn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mMessenger = new Messenger(service);
        Message msg = Message.obtain(null,MessengerService.SAY_HELLO);
        Bundle data = new Bundle();
        data.putString("data","Hello from Client");
        msg.setData(data);
        try {
            mMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        mBond = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mBond = false;
    }
};
看一下服务端的打印
ipc_1.png

上面只是客户端向服务端传递数据,那么服务端向客户端传递数据的原理是一样的,首先需要创建一个客户端的Messenger的实例,然后丢给发送到服务端的Message的replyTo字段,服务端去的这个字端就是拿到了客户端的Messenger,就可以向客户端发送数据了。下面看一下更改后的代码:

//客户端
class ClientHandler extends Handler{
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
            case 1:
                Bundle data = msg.getData();
                String str = data.getString("data");
                Log.e("TAG","客户端已经收到服务端的信息"+str);
            break;
        }
    }
}
private Messenger mClient = new Messenger(new ClientHandler());
private ServiceConnection mConn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mMessenger = new Messenger(service);
        Message msg = Message.obtain(null,MessengerService.SAY_HELLO);
        Bundle data = new Bundle();
        data.putString("data","Hello from Client");
        msg.setData(data);
        msg.replyTo = mClient;
        try {
            mMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        mBond = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mBond = false;
    }
};
//服务端的代码
public class MessengerService extends Service {
public static final int  SAY_HELLO =1;
class MessengerHandler  extends Handler{
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
            case SAY_HELLO:
                Bundle data = msg.getData();
                String str = data.getString("data");
                Log.e("TAG","MessengerService Received say_hello: "+str);

                Messenger mClient = msg.replyTo;
                Message mMsgClient = new Message();
                mMsgClient.what = 1;
                Bundle bun = new Bundle();
                bun.putString("data","服务端已经收到你的消息,稍后会回复你的....");
                mMsgClient.setData(bun);
                try {
                    mClient.send(mMsgClient);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}
private Messenger mMessenger = new Messenger(new MessengerHandler());

@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    return mMessenger.getBinder();
}

}
ipc_2.png
AIDL(Android 接口定义语言)
  • 定义 AIDL 接口
    当每次新建一个aidl文件时,Android SDK 工具都会生成一个基于该 .aidl 文件的 IBinder 接口,并将其保存在项目的 gen/ 目录中。服务必须视情况实现 IBinder 接口。然后客户端应用便可绑定到该服务,并调用 IBinder 中的方法来执行 IPC。一般创建一个aidl服务执行下面的步骤:
    1.创建 .aidl 文件
    2.实现接口
    Android SDK 工具基于您的 .aidl 文件,使用 Java 编程语言生成一个接口。此接口具有一个名为 Stub 的内部抽象类,用于扩展 Binder 类并实现 AIDL 接口中的方法。您必须扩展 Stub 类并实现方法。

    3.向客户端公开该接口
    实现 Service 并重写 onBind() 以返回 Stub 类的实现。

上代码:

//新建AIDL文件
// First.aidl
package com.hq.demo.aidltest;

// Declare any non-default types here with import statements

interface First {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    String getStringFromClient(in String str);
}

//服务端的代码,实现其Stub类
public class FirstAidlService extends Service {
private FirstBinder mBinder;
@Override
public void onCreate() {
    super.onCreate();
    mBinder = new FirstBinder();
}

public FirstAidlService() {
}

@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    return mBinder;
}


class FirstBinder extends First.Stub{

    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

    }

    @Override
    public String getStringFromClient(String str) throws RemoteException {
        return "String from Server    "+str ;
    }
}
}

 //客户端绑定服务时需要的ServiceConnection
 private ServiceConnection mConn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mBinder = First.Stub.asInterface(service);//获得服务端的Binder对象,并将其转换成为First类,此时客户端就可以调用服务端的代码了,实现跨进程访问
        mBond = true;
        try {
            String str = mBinder.getStringFromClient("Client to Server");
            Log.e("TAG","Client : "+str);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mBond = false;
    }
};

看下日志:


ipc_aidl_4.png

客户端和服务端实现了跨进程调用,这是最简单的跨进程调用,我们客户端只是向服务端传递了一个字符串,并且服务端返回了一个字符串,下面就了解一下AIDL跨进程调用能够传递哪些数据。

  • 通过 IPC 传递对象

默认情况下,AIDL 支持下列数据类型:
Java 编程语言中的所有原语类型(如 int、long、char、boolean 等等),String,CharSequence
List
List 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 可选择将 List 用作“通用”类(例如,List<String>)。

另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。
Map
Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用 Map(如 Map<String,Integer> 形式的 Map)。 另一端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。

上面是基本的类型,通过 IPC 接口把某个类从一个进程发送到另一个进程是可以实现的。 不过,您必须确保该类的代码对 IPC 通道的另一端可用,并且该类必须支持 Parcelable 接口。因为是跨进程这个对象在两个进程中的包名都必须是一样的,因为在不同的进程,就是不同的对象,程序需要把另一个进程传递过来的对象,实例化为本进程的可识别的对象。把需要传递的对象实现Parcelable接口,并且要定义成.aidl接口的形式,在写aidl文件的时候必须为每个附加类型加入一个import语句,即使这些类型与你定义的aidl的接口在同一个包中。还有一点就是在写aidl方法时,如果方法的参数类型时非源语,那么就必须要加上指示数据走向的方向标记,可以是in,out,inout,原语默认是in,不能是其它方向

注意:您应该将方向限定为真正需要的方向,因为编组参数的开销极大。

上代码:

// First.aidl
package com.hq.demo.aidltest;
import com.hq.demo.aidltest.Book;   
interface First {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    String getStringFromClient(String str);
    Book addBook(in Book book);
    List<Book> getBookList();
}

//服务端
public class FirstAidlService extends Service {
    private FirstBinder mBinder;
    //因为通过Binder类调用服务端的方法,改方法会执行在Binder线程池里面,不是
    //主线程,所以需要关注线程间的同步问题。而CopyOnWriteArrayList已经帮我
    //们实现了线程同步的问题。
    private CopyOnWriteArrayList<Book> mData;

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

        mData = new CopyOnWriteArrayList<>();
        Book b1 = new Book();
        b1.setmBookName("book1");
        b1.setmPrice(1.0f);
        Book b2 = new Book();
        b2.setmBookName("book2");
        b2.setmPrice(2.0f);
        mData.add(b1);
        mData.add(b2);
        mBinder = new FirstBinder();
    }

    public FirstAidlService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }

    //实现First.Stub 接口,实现里面的方法,在onBind()里面返回相应的Binder类,
    class FirstBinder extends First.Stub{
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
        
        }

        @Override
        public String getStringFromClient(String str) throws RemoteException {
            return "String from Server    "+str ;
        }

        @Override
        public Book addBook(Book book){
            Book book1 = new Book();
            Log.e("TAG","Server addBook from Client Book:"+book);
            book1.setmBookName("ServerBookName");
            book1.setmPrice(10.2f);
            mData.add(book);
            return book1;
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mData;
        }

    }

}
// 客户端代码
private ServiceConnection mConn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mBinder = First.Stub.asInterface(service);//获得服务端的Binder接口
        mBond = true;
        try {
            String str = mBinder.getStringFromClient("Client to Server");
            Log.e("TAG","Client : "+str);
            Book b = new Book();
            b.setmBookName("Client Book");
            b.setmPrice(9.1f);
            //通过Binder调用服务端的方法
            Book book = mBinder.addBook(b);
            Log.e("TAG", "AIDL Base addBook(): " + book);

            List<Book> books  = mBinder.getBookList();
            Log.e("TAG",books.toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mBond = false;
    }
}
ipc_aidl_5.png
接下来,简单介绍两个很重要的方法,linkToDeath()和unlinkToDeath();我们知道,Binder运行在服务端的进程中,如果服务端进程由于某种原因异常终止,这个时候如果我们还在调用服务端的方法,这就会导致调用失败,这个时候急需要用到linkToDeath();当Binder死亡的时候我们就会收到相应的通知。

上代码:

 private IBinder.DeathRecipient mRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        Log.e("TAG","mRecipient binderDied");
        if(mBinder == null)
            return;
        mBinder.asBinder().unlinkToDeath(mRecipient,0);
        mBinder = null;
       //下面可以做重新绑定的操作
    }
};


 @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mBinder = First.Stub.asInterface(service);
        mBond = true;
        try {
        ........
            service.linkToDeath(mRecipient,0);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

上面就实现了当服务端的进程异常终止时,所要回调的方法,并且可以执行重新绑定服务的操作。

这篇文章就到这吧,太长了,接下来还会继续介绍AIDL和Binder相关的知识点。

相关文章

  • IPC的用法的学习 一

    IPC的用法的学习 这篇文章来小结一下自己学的IPC的知识,包括Messenger和AIDL,然后我在总结一下自己...

  • IPC之ContentProvider用法

    通过简单的例子通过ContentProvider实现进程间内容读写 服务端 1.服务端用来向外提供数据,首先创建2...

  • 安卓实现IPC(四)—— Broadcast

    上一篇文章学习了用AIDL实现IPC,地址安卓实现IPC(三)—— AIDL,这一篇来学习用广播实现IPC,效果图...

  • acccheck的使用

    介绍:一个基于微软SMB协议的口令攻击工具,IPC共享攻击工具用法:acccheck + [选项]参数: -t 后...

  • Binder学习(四)利用AIDL、Messenger实现IPC

    Binder学习(四)利用AIDL、Messenger实现IPC 概述 在利用Binder进行IPC的时候,会经常...

  • Android 学习IPC之个人总结

    以下知识都是学习《Android开发艺术探索》后的总结 IPC的简介 IPC是英文:Inter Process C...

  • IPC之AIDL入门用法

    目的:通过AIDL实现简单的IPC [该文例子参考自《Android开发艺术探索》一书] Step1 首先创建跨进...

  • IPC之AIDL进阶用法

    接上篇笔记 AIDL入门用法 主要包括下边四点: 跨进程的接口回调【观察者】 线程问题 断开重连问题 权限校验问题...

  • IPC之AIDL分析

    AIDL用法及代码分析 AIDL为安卓接口定义语言的简称,作用是利用binder机制,实现IPC。 1、AIDL用...

  • Android艺术探索读书笔记 -IPC机制

    写笔记的初衷是为了根据书籍系统的学习Andorid知识与方便回顾 Android IPC介绍 IPC含义为进程间的...

网友评论

      本文标题:IPC的用法的学习 一

      本文链接:https://www.haomeiwen.com/subject/xzglhxtx.html