Android 中 IPC 机制 (三)

883 阅读7分钟
原文链接: blog.csdn.net

Android中IPC机制(一)

Android中IPC机制(二)

1.Android中的IPC方式

(1)使用Bunder

四大组件中的三大组件(Activity、Service、Receiver)都支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以它可以在不同进程间传输。

(2)使用文件共享

文件共享也是一种不错的进程间通信方式,两个进程通过读/写一个文件来交换数据,A进程把数据写入文件,B进程读取文件获取数据。

MainActivity和SecondActivity属于不同的进程

在MainActivity写入数据,在SecondActivity读取数据

首先,在清单文件中加入sd卡读写的权限

  <!-- 在SDCard中创建与删除文件权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <!-- 往SDCard写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

实现Serializable接口的Java

package com.zhoujian.ipc.bean;

import java.io.Serializable;

/**
 * Created by zhoujian on 2017/2/24.
 */

public class User implements Serializable{

    private static final long serialVersionUID = 1L;



    private int userId;

    private String userName;

    private boolean isMale;


    public User(boolean isMale, int userId, String userName) {
        this.isMale = isMale;
        this.userId = userId;
        this.userName = userName;
    }

    public boolean isMale() {
        return isMale;
    }

    public void setMale(boolean male) {
        isMale = male;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "User{" +
                "isMale=" + isMale +
                ", userId=" + userId +
                ", userName='" + userName + '\'' +
                '}';
    }
}

在MainActivity中写入数据

 private void saveData()
    {

        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                    File  file = Environment.getExternalStorageDirectory();
                    File saveFile =  new File(file,"zhoujian.txt");
                    ObjectOutputStream outputStream = null;

                    try {
                        User use = new User(true,1,"周建");
                        outputStream = new ObjectOutputStream(new FileOutputStream(saveFile));
                        outputStream.writeObject(use);
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

在SecondActivity中读取数据

 private void readData()
    {
        new Thread()
        {
            @Override
            public void run()
            {
                if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                    File file = Environment.getExternalStorageDirectory();
                    File files = new File(file,"zhoujian.txt");
                    ObjectInputStream in = null;
                    try
                    {
                        in = new ObjectInputStream(new FileInputStream(files));
                        User usr = (User)in.readObject();
                        Log.d(TAG, "获取数据成功:"+ usr.toString());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

注意:SharedPreferences是Android中提供的轻量级存储方案,它通过键值对来存储数据,在底层是采用XML来存储键值对,每个应用的SharedPreferences文件都可以在当前包所在的data目录下查看到。一般位于/data/data/package name/shared_prefs目录下,由于系统对SharedPreferences的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,因此在多进程模式下,系统对它的读写就变得不可靠

(3)使用Messenger

Messenger:信使,通过它可以在不同进程中传递Message对象,Messenger是一种轻量级的IPC,底层实现是AIDL

我们从Messenger的构造方法可以看出

  public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

 public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

Messenger对AIDL进行了封装,Messenger一次处理一个请求,因此服务端不用考虑线程同步的问题,这是因为服务端中不存在并发执行的情形。

下面以一个具体的实例,来说明Messenger如何实现进程间通信

服务端:MessengerService,服务端与客户端位于不同的进程

MessengerService.java

package com.zhoujian.ipc.service;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

/**
 * Created by zhoujian on 2017/2/28.
 */

public class MessengerService extends Service{


    public static final String TAG = "MessengerService";
    public static final int MSG_FROM_CLIENT = 1;
    public static final int MSG_FROM_SERVICE = 2;


    class  mHandler extends Handler
    {



        @Override
        public void handleMessage(Message msg)
        {
            switch (msg.what)
            {
                case MSG_FROM_CLIENT:
                    String message = msg.getData().getString("msg");
                    Log.d(TAG, "消息来自客户端:"+message);
                    //接受到客户端的消息后,给客户端回复消息
                    Messenger  mMessenger = msg.replyTo;
                    //创建消息
                    Message mMessage = Message.obtain(null,MSG_FROM_SERVICE);
                    Bundle mBundle = new Bundle();
                    mBundle.putString("message","您好客户端!你的消息我已经收到");
                    mMessage.setData(mBundle);
                    try {
                        //使用信使Messenger发送消息
                        mMessenger.send(mMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;

                default:
                    super.handleMessage(msg);
            }
        }
    }

    //创建信使Messenger,传入Handler
    Messenger mMessenger = new Messenger(new mHandler());

    @Override
    public IBinder onBind(Intent intent)
    {
        //返回Messenger的getBinder对象
        return mMessenger.getBinder();
    }
}

客户端:MessengerActivity.java

package com.zhoujian.ipc.activity;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import com.zhoujian.ipc.R;
import com.zhoujian.ipc.service.MessengerService;

/**
 * Created by zhoujian on 2017/2/28.
 */

public class MessengerActivity extends Activity
{

    public static final String TAG = "MessengerActivity";
    public static final int MSG_FROM_CLIENT = 1;
    public static final int MSG_FROM_SERVICE = 2;

    //接受服务端发送过来的消息
    Messenger mMsg = new Messenger(new Handler(){

        private String mMessage;

        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case MSG_FROM_SERVICE:

                    mMessage = msg.getData().getString("message");

                    Log.d(TAG, "消息来自服务端:"+mMessage);
                    break;

                default:
                    super.handleMessage(msg);
            }
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_messenger);

        Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
        //绑定服务
        bindService(intent, comm, Context.BIND_AUTO_CREATE);

    }

    ServiceConnection comm = new ServiceConnection()
    {

        private Messenger mMessenger;

        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {

            //创建信使
            mMessenger = new Messenger(service);
            //创建消息
            Message message =  Message.obtain(null,MSG_FROM_CLIENT);
            //创建Bundle,存入消息
            Bundle bundle = new Bundle();
            bundle.putString("msg","你好!我是客户端");
            message.setData(bundle);
            //重要
            message.replyTo= mMsg;

            try
            {
                //使用信使发送消息
                mMessenger.send(message);

            }
            catch (RemoteException e)
            {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name)
        {

        }
    };
}

(4)使用AIDL

Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端也只能一个一个处理,如果有大量的并发请求,Messenger就不合适了。

AIDL进行进程间通信

服务端

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

客户端

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

AIDL接口的创建

IBookManager.aidl

import com.zhoujian.ipc.aidl.Book;

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

interface IBookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */

     List<Book> getBookList();

     void addBook(in Book book);
}

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

  • 基本数据类型
  • String和CharSequence
  • List,只支持ArrayList,里面的每一个元素都必须能够被AIDL支持
  • Map,只支持HashMap,里面的每一个元素都必须能够被AIDL支持,包括key和value
  • Parcelable:所有实现Parcelable接口的对象
  • AIDL:所有的AIDL接口本身也可以在AIDL文件中使用

以上6种数据类型就是AIDL所支持的所有类型,其中自定义Parcelable对象和AIDL对象必须显示import进来。

注意:如果AIDL文件中使用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明Parcelable类型。在上面的IBookManager.aidl中,我们用到了Book这个类,所以我们必须创建Book.aidl

Book.aidl

// Book.aidl
package com.zhoujian.ipc.aidl;

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

parcelable Book;

注意:AIDL中每个实现了Parcelable接口的类都需要按照上面方式去创建相应的AIDL文件并声明那个类为parcelable。除此之外,AIDL中除了基本数据类型,其他类型的参数必须标上方向:in、out或者inout。in表示输入型参数,out表示输出型参数,inout表示输入输出型参数。AIDL接口只支持方法,不支持声明静态常量。

远程服务端的实现,服务端和客户端位于不同进程


<service 
android:name=".service.BookManagerService"
android:process=":romote"/>

BookManagerService.java


package com.zhoujian.ipc.service;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import com.zhoujian.ipc.aidl.Book;
import com.zhoujian.ipc.aidl.IBookManager;
import com.zhoujian.ipc.aidl.INewBookArrivedListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by zhoujian on 2017/2/28.
 */

public class BookManagerService extends Service {


    private AtomicBoolean mBoolean = new AtomicBoolean(false);

    public static final String TAG = "BookManagerService";

    //CopyOnWriteArrayList支持并发的读写

    CopyOnWriteArrayList<Book> bookList= new CopyOnWriteArrayList<Book>();


    RemoteCallbackList<INewBookArrivedListener> listeners = new RemoteCallbackList<>();
    private int mInt;


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


       // AtomicBoolean

        //添加两本书

        bookList.add(new Book(1,"安卓书籍一"));

        bookList.add(new Book(2,"安卓书籍二"));

        //开启一个线程,每隔5秒向书库中增加一本书,并通知感兴趣的用户

        new Thread(new ServiceWork()).start();



    }


    class ServiceWork implements Runnable
    {
        @Override
        public void run()
        {
            while (! mBoolean.get())
            {
                try
                {
                    Thread.sleep(5000);
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                int bookId =  bookList.size()+1;
                Book newBook = new Book(bookId,"安卓书籍"+bookId);
                //有新的书籍到了
                onNewBookArrived(newBook);
            }
        }
    }

    private void onNewBookArrived(Book book)
    {

        bookList.add(book);

        mInt = listeners.beginBroadcast();

        for (int i = 0; i <mInt; i++)
        {
            INewBookArrivedListener listener = listeners.getBroadcastItem(i);
            if (listener != null)
            {
                try {
                    listener.onNewBookArrived(book);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        listeners.finishBroadcast();
    }

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


    //IBookManager类是AIDL文件对应的生成的java类

    Binder mBinder = new IBookManager.Stub() {

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

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

        @Override
        public void registListenner(INewBookArrivedListener listener) throws RemoteException
        {
            listeners.register(listener);
        }

        @Override
        public void unregistListenner(INewBookArrivedListener listener) throws RemoteException
        {
            listeners.unregister(listener);
        }
    };
}

BookManagerActivity.java


package com.zhoujian.ipc.activity;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;

import com.zhoujian.ipc.R;
import com.zhoujian.ipc.aidl.Book;
import com.zhoujian.ipc.aidl.IBookManager;
import com.zhoujian.ipc.aidl.INewBookArrivedListener;
import com.zhoujian.ipc.service.BookManagerService;

import java.util.List;


/**
 *
 * Created by zhoujian on 2017/2/28.
 */

public class BookManagerActivity extends Activity
{

    public static final String TAG = "BookManagerActivity";

    public static final int NEW_BOOK_ARRIVED= 0;

    private IBookManager mIBookManager;


    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case NEW_BOOK_ARRIVED:

                    Log.e(TAG, "receive new book:"+(Book)msg.obj);
            }

        }
    };




    private INewBookArrivedListener listenner = new INewBookArrivedListener.Stub()
    {
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException
        {

            mHandler.obtainMessage(NEW_BOOK_ARRIVED,newBook).sendToTarget();



        }
    };

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

        Intent intent = new Intent(BookManagerActivity.this, BookManagerService.class);

        bindService(intent,comm, Context.BIND_AUTO_CREATE);

    }

    ServiceConnection comm =  new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取IBookManager.aidl 文件
            IBookManager bookManager =  IBookManager.Stub.asInterface(service);
            try
            {

                mIBookManager =bookManager;
                //在客户端调用方法获取书籍
                List<Book> bookList =  bookManager.getBookList();
                // Log.e(TAG, "查询的书籍为"+bookList.toString());
                //在客户端再添加一本书
                Book book = new Book(3,"安卓开发三");
                bookManager.addBook(book);
                Log.e(TAG, "新添加的书籍为:"+book.toString());
                //再次获取所有书籍
                List<Book> bookLists = bookManager.getBookList();
                Log.e(TAG, "查询的书籍为"+bookLists.toString());


                bookManager.registListenner(listenner);

            }
            catch (RemoteException e)
            {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if(mIBookManager!=null && mIBookManager.asBinder().isBinderAlive()){

            try {
                mIBookManager.unregistListenner(listenner);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        unbindService(comm);
    }
}

(5)使用ContentPrivider

请阅读我的另一篇博客:

ContentPrivider实现IPC:http://blog.csdn.net/u014005316/article/details/53407674

(6)使用Socket

请阅读我的另一篇博客:

Socket实现进程间通信:http://blog.csdn.net/u014005316/article/details/53419839