IPC机制2

1、使用Messenger

 Messenger可以翻译为信使,通过它可以在不同进程中传递messenge对象,在messenge中放入我们需要传递的数据,就可以轻松实现数据在进程中传递。

 服务段进程:

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

  如果需要回复给客户端消息,可以通过Messange的replyTo参数创建一个Messenger,然后再创建一个想要传递的messenge,再然后使用Messenger传递这个messenge

public class MyService extends Service {
    private static final String TAG = "MessengerService";

    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 0:            //打印接收到的Message对象的内容
                    Log.i(TAG,"receive msg form Client:" + msg.getData().getString("msg"));                        //使用接收到的messenge对象的replyTo参数创建Messenger进行回复
                    Messenger client = msg.replyTo;
                    Message replyMessage = Message.obtain(null,1);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply","已收到消息");
                    replyMessage.setData(bundle);
                    try{
                        client.send(replyMessage);
                    }catch (RemoteException e){
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

  客户端进程:

    先绑定服务端的Service,绑定成功后使用服务端返回的使用服务端返回的IBinder创建Messenger并使用其发送消息。

    如果想要接受服务端的回复同样需要创建一个Handler并创建新的Messenger,并把这个Messenger通过replyTo传递给服务端

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MessengerService";

    //服务端传来的Messenger
    private Messenger mService;

    //客户端的Messnger
    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());

    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                    //打印服务端返回的消息
                    Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

            //使用服务端返回的IBinder创建Messenger并使用其发送消息
            mService = new Messenger(iBinder);
            Message msg = Message.obtain(null,0);
            Bundle data = new Bundle();
            data.putString("msg","hello this is client");
            msg.setData(data);

            //将客户端的Messenger通过replyTo传递给服务端
            msg.replyTo = mGetReplyMessenger;

            try{
                mService.send(msg);
            }catch (RemoteException e){
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this,MyService.class);
        //绑定Service
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

2、使用AIDL

  上面的Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个一个的处理,这种就可以使用AIDL

  首先是创建AIDL接口(Book类,Book.aidl,IBookManager.aidl 具体代码可以看IPC机制1

    在AIDL文件中,并不是所有的数据类型都是可以使用的,支持的类型有:

    • 基本类型(int、long、char、boolean等)
    •   List:只支持ArrayList,且每个元素都必须被AIDL支持
    • Map:只支持HashMap,且每个元素都必须被AIDL支持,包括key和value;
    • Parcelable:所有实现了Parcelable接口的对象
    •   AIDL:所有的AIDL接口本身也可以在AIDL中使用,

    以上为AIDL所支持的所有类型,其中现了Parcelable接口的对象和AIDL对象必须显式的important进来,不管它们是否与当前文件位于同一包内。

    如果AIDL文件用到 了自定义的Parcelable对象,就必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。

    还要注意的是,AIDL中除了基本数据类型,其他类型的参数必须标上方向:in、out或者inout,in表示输入型参数,out表示输出型参数,inout表示输入输出型参数。

    最后,AIDL接口中只支持方法,不支持声明静态变量。

  然后是远程服务端的Service的实现:

    上面只是定义了接口,现在就需要实现这个接口:

public class BookManagerService extends Service {

    private static final String TAG = "BMS";

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1,"Android"));
        mBookList.add(new Book(2,"ios"));
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

  这里采用了CopyOnWriteArrayList,这是因为CopyOnWriteArrayList支持并发读写,类似的还有ConcurrentHashMap,这是因为AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同步连接的时候,会出现多线程同时访问的情形。

  再然后就是客户端的实现:

public class BookManagerActivity extends AppCompatActivity {

    private static final String TAG = "BMS";private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);
            try {
                List<Book> list = bookManager.getBookList();
                Log.i(TAG,"query book list, list type:" + list.getClass().getCanonicalName());
                for (Book book:list) {
                    Log.i(TAG, "query book list: [book id:" + book.bookId + " bookName:" +  book.bookName + "]");
                }
            }catch (RemoteException e){
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

  Log输出如下:

12-07 14:48:48.516 9198-9198/com.example.administrator.test I/BMS: query book list, list type:java.util.ArrayList
12-07 14:48:48.516 9198-9198/com.example.administrator.test I/BMS: query book list: [book id:1 bookName:Android]
12-07 14:48:48.516 9198-9198/com.example.administrator.test I/BMS: query book list: [book id:2 bookName:ios]

  可以发现,虽然我们在服务端返回的是CopyOnWriteArrayList,但是客户端收到的却是ArrayList.这是因为AIDL中支持的是抽象的List,而List只是一个接口,在Binder中是按照List的规范去访问数据,并最终返回ArrayList给客户端。

3、使用ContentProvider

  ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式。不过ContentProvider的底层实现同样也是Binder。

  首先我们需要创建一个继承ContentProvider的类,这里我命名为BookProvider,具体代码如下:

public class BookProvider extends ContentProvider {

    private static final String TAG = "BookProvider";
    @Override
    public boolean onCreate() {
        Log.d(TAG, "onCreate ,current thread:" + Thread.currentThread().getName());
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
        Log.d(TAG, "query ,current thread:" + Thread.currentThread().getName());
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        Log.d(TAG, "getType" );
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        Log.d(TAG, "insert" );
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
        Log.d(TAG, "delete" );
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
        Log.d(TAG, "update" );
        return 0;
    }

}

  想要实现一个自定义的ContentProvider类需要实现上面6个抽象方法:

  • onCreate代表 ContentProvider的创建,在这个方法中做一些初始化的工作
  • getType用来返回一个Uri请求所对应的MIME类型(媒体类型),如果我们的应用不关心这个选项可以直接返回null或是"*/*",
  • query、insert、delete、update就分别对应对数据表的增删改查功能

  这里的除了onCreate方法由系统回调并运行在主线程里,其他五个方法由外界回调并运行在Binder线程池中。

  定义了一个这样的类之后,还需要在AndroidManifest中注册这个ContentProvider

 <provider
            android:name="provider.BookProvider"
            android:authorities="com.xw.provider"
            android:permission="com.xw.test.BookProvider"
            android:process="book.test"
            android:exported="true">
        </provider>        

  其中前四个属性都是任意指定,android:exported表示是否允许外部程序访问ContentProvider,不过android:authorities是ContentProvider的唯一标识,外部应用就是通过这个属性来访问ContentProvider,所以android:authorities必须是唯一的。android:permission给我们的ContentProvider添加了权限,外部应用如果想要访问这个ContentProvider就必须声明这个"com.xw.test.BookProvider"权限,还要注意我们还需要这个权限属于我们自定义的所以需要加上:

    <permission android:name="com.xw.test.BookProvider"
                    android:protectionLevel="normal"/>

  接下来是客户端,首先是同应用的一个Activity:

public class ProviderActivity extends AppCompatActivity {

    private static final String TAG = "BookProvider";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_provider);
        Log.d(TAG, "ProviderActivity,onCreate ,current thread:" + Thread.currentThread().getName());
        Uri uri = Uri.parse("content://com.xw.provider");
        getContentResolver().query(uri,null,null,null,null);
    }
}

  ContentProvider是通过Uri来指定使用的,这个Uri被称作内容Uri,内容Uri给ContentProvider中的数据提供了唯一的标识符,通常就是"content://"+authorities+表名。这里我们就可以直接指定为"content://com.xw.provider"。

  这里访问ContentProvider使用的是调用getContentResolver(),这个方法能得到一个ContentResolver类,通过这个ContentResolver类和Uri就能使用ContentProvider的query、insert、delete、update、getType五个方法。

  这里的输出是:

12-11 09:57:21.977 5551-5551/com.example.administrator.test D/BookProvider: ProviderActivity,onCreate ,current thread:main
12-11 09:57:22.079 5590-5590/book.test D/BookProvider: BookProvider,onCreate ,current thread:main
12-11 09:57:22.082 5590-5629/book.test D/BookProvider: query ,current thread:Binder:5590_3

  从输出我们可以看出ContentProvider的onCreate方法是运行在主线程里的。

  然后我们再创建一个新的应用测试一下真正的跨应用使用这个ContentProvider:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Uri uri = Uri.parse("content://com.xw.provider");
        getContentResolver().query(uri,null,null,null,null);
    }
}

  Activity的代码和刚才一样,还要注意在AndroidManifest中加入权限:

    <uses-permission android:name="com.xw.test.BookProvider"/>

  输出:

12-11 10:50:54.304 16073-16073/? D/BookProvider: ProviderActivity,onCreate ,current thread:main
12-11 10:50:54.431 16092-16092/? D/BookProvider: BookProvider,onCreate ,current thread:main
12-11 10:50:54.434 16092-16105/? D/BookProvider: query ,current thread:Binder:16092_2

  如果是具体使用一个数据库的话,看这个android——实现跨程序访问数据

时间: 2024-08-01 02:47:32

IPC机制2的相关文章

ndk学习15: IPC机制

Linux IPC机制 来自为知笔记(Wiz)

为什么Android要采用Binder作为IPC机制?

作者:Gityuan链接:https://www.zhihu.com/question/39440766/answer/89210950来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 在开始回答 前,先简单概括性地说说Linux现有的所有进程间IPC方式: 1. 管道:在创建时分配一个page大小的内存,缓存区大小比较有限:2. 消息队列:信息复制两次,额外的CPU消耗:不合适频繁或信息量大的通信:3. 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,

Android开发艺术探索——第二章:IPC机制(上)

Android开发艺术探索--第二章:IPC机制(上) 本章主要讲解Android的IPC机制,首先介绍Android中的多进程概念以及多进程开发模式中常见的注意事项,接着介绍Android中的序列化机制和Binder,然后详细的介绍Bundle,文件共享,AIDL,Messenger,ContentProvider和Socker等进程间通讯的方法,为了更好的使用AIDL进行进程间通讯,本章引入了Binder连接池的概念,最后,本章讲解各种进程间通信方式的优缺点和使用场景,通过本章,可以让读者对

Android艺术开发探索——第二章:IPC机制(下)

Android艺术开发探索--第二章:IPC机制(下) 我们继续来讲IPC机制,在本篇中你将会学习到 ContentProvider Socket Binder连接池 一.使用ContentProvider ContentProvider是Android中提供的专门用来不同应用之间数据共享的方式,从这一点来看,他天生就是适合进程间通信,和Messenger一样,ContentProvider的底层实现同样也是Binder,由此可见,Binder在Android系统中是何等的重要,虽然Conten

Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户端显然是不合适的.另外,Messenger用来进程间进行数据传递但是却不能满足跨进程的方法调用,接下来我们来使用AIDL来实现跨进程方法调用,此前我们都是用Eclipse来实现的,这次我们看看在Android Studio中使用AI

第二章——第二节 IPC机制的概述和使用

一.Serialiable与Paracle ①.作用    ②.使用 二.Binder与AIDL ①.各自的作用 三.如何使用IPC机制 四.IPC机制的原理 ①.流程图  ②.自己编译自动生成的Aidl代码 回答: Serialiable的使用 步骤:1.创建类并继承Serializable接口  2.将对象序列化到文件中 3.从文件中获取序列化的文件 详细步骤: 1.创建Book类,并继承Serializable,编写serialVersionUID降低反序列化失败的概率 public cl

Handler消息机制与Binder IPC机制完全解析

1.Handler消息机制 序列 文章 0 Android消息机制-Handler(framework篇) 1 Android消息机制-Handler(native篇) 2 Android消息机制-Handler(实战篇) 2.Binder IPC机制 序列 文章 0 Binder开篇 1 Binder Driver初探 2 Binder Driver再探 3 启动Service Manager 4 获取Service Manager 5 注册服务(addService) 6 获取服务(getS

linux各种IPC机制(进程通信)

linux各种IPC机制 (2011-07-08 16:58:35)     原文地址:linux各种IPC机制(转)作者:jianpengliu 原帖发表在IBM的developerworks网站上,是一个系列的文章,作者郑彦兴,通过讲解和例子演示了Linux中几种IPC的使用方式,我觉得很好,在这里做一个保留,能看完的话Linux IPC的基础是没有问题的了.一)Linux环境进程间通信(一)管道及有名管道http://www.ibm.com/developerworks/cn/linux/

Android IPC机制(五)用Socket实现跨进程聊天程序

相关文章: Android IPC机制(一)开启多进程 Android IPC机制(二)用Messenger进行进程间通信 Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用 Android IPC机制(四)用ContentProvider进行进程间通信 1.Socket简介 Socket也称作"套接字",是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信.它分为流式套接字和

IPC机制key值的各位组成

key_t ftok(const char *_pathname, int _proj_id) key值的第31~24位为ftok()第二个参数的低8位: key值的第23~16位为ftok()第一个参数文件属性的st_dev成员的低8位: key值的第15~0位为ftok()第一个参数文件属性的st_ino属性的低16位. #include <sys/ipc.h> #include <sys/types.h> #include <stdio.h> #include &