Android开发艺术探索——IPC机制

这是我在学习过程中总结的知识
目的是希望日后回来看或者需要用的时候可以 一目了然 # 的回顾、巩固、查缺补漏
不追求详细相当于书本的精简版或者说是导读(想看详细的直接对应翻书),但会尽力保证读者都能快速理解和快速使用(随理解加深会总结的更加精简),但必要时会附上一些较详细解释的链接
脚注是空白的:表示还没弄懂的知识,了解后会添加

2.2 Android中的多进程模式

在Android中使用多进程只有一种方法:给四大组件注册android:process属性

2.2.1 关于进程命名

  1. android:process=" :remote" “:”的含义是加上包名的简写、并且使用“:”的进程属于当前应用的私有进程
  2. android:process=“com.ryg.chapter_2.remote” 这是一种完整的命名方式、不会附加包名信息、属于全局进程、其他应用可以通过ShareUID和他跑在同一个进程中

Android会为每一个应用分配一个唯一的UID
两个不同应用通过ShareUID并且签名相同可以互相访问私有数据(data目录,组件信息)
如果符合第二点还在同一进程中,则还可以共享内存数据

2.2.2 多进程模式的运行机制

Android为每一个进程都分配了一个独立的虚拟机,不同虚拟机在内存分配上有不同的地址空间,这就导致了不同虚拟机访问同一个对象的时候会产生多个副本

多进程缺点

  1. 静态成员和单实例模式完全失效
  2. 线程同步机制完全失效
  3. SharePreference的可靠性下降(因为SharePreference底层是通过读/写XML文件实现的,同时读写就容易出问题)
  4. Application会被多次创建(进程——虚拟机——Application挂钩)

2.3 IPC基础概念介绍

  1. Serrializable接口(序列化、对象持久化)
  2. Parcelable接口(序列化)
  3. Binder

2.3.1 Serialzable接口

只需要这个类实现Serializable接口并声明一个serialVersionUID
private static final long serialVersionUID = 8771141451212014L

序列化过程

//序列化过程
User user = new User("name");
ObjectOutputStream out = new ObjectOutputStream(
	new FileOutputStream("cache.txt");
out.writeObject(user);
out.close();

//反序列化过程
ObjectInputStream in = new ObjectInputStream(
	new FileInputStream("cache.txt");
User newUser = (User) in.readObject();
in.close;

newUser和user内容完全一样,但两者不是同一个对象

关于是否设置serialVersionUID

  • 序列化和反序列化需要serialVersionUID相同才可以进行
  • serialVersionUID可以不设置,系统会自动生成,但是单某些成员变量被增加或者删除后,系统会重新生成新的serialVersionUID,这就会导致反序列化失败
  • 如果改变了类的结构如:修改类名、修改成员变量类型。则直接失败

注意下面两种情况不参加序列化过程:
静态成员变量属于类不属于对象
被transient关键字标记

2.3.2 Parcelabel接口

使用这个接口后也可以实现序列化并可以通过Intent和Binder传递

package com.ryg.chapter_2.model;

import java.io.Serializable;

import com.ryg.chapter_2.aidl.Book;

import android.os.Parcel;
import android.os.Parcelable;

public class User implements Parcelable, Serializable {
    private static final long serialVersionUID = 519067123721295773L;

    public int userId;
    public String userName;
    public boolean isMale;

    public Book book;

    public User() {
    }

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

//返回当前对象的内容描述。基本上返回的是0,除非含有文件描述符
    public int describeContents() {
        return 0;
    }

//将当前对象写入序列化结构中、write
//flag几乎为0,为1时标识当前对象需要作为返回值返回
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(userId);
        out.writeString(userName);
        out.writeInt(isMale ? 1 : 0);
        out.writeParcelable(book, 0);
    }

//反序列化、read
    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
	//从序列化后的对象中创建原始对象
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

	//创建指定长度的原始对象数组
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    private User(Parcel in) {
        userId = in.readInt();
        userName = in.readString();
        isMale = in.readInt() == 1;
		//book是另一个可序列化的对象,所以需要传递当前线程的上下文类加载器,否则会报无法找到类的错误
        book = in .readParcelable(Thread.currentThread().getContextClassLoader());
    }

    @Override
    public String toString() {
        return String.format(
                "User:{userId:%s, userName:%s, isMale:%s}, with child:{%s}",
                userId, userName, isMale, book);
    }

}

文件描述符1

Intent、Bundle、Bitmap等都是已经实现了Parcelable接口的类
List、Map也可以序列化,前提是内部的每个元素都是可序列化的

总结Parcelable+Serializable

使用Serializable的情况

属于java中的序列化接口、使用简单开销大、序列过程需要大量I/O操作

  1. 将对象序列化到存储设备中
  2. 将对象序列化后通过网络传输

使用Parcelable的情况

更适用于Android、使用麻烦效率高、首选

  1. 主要用在内存序列化上

2.3.3 Binder

简介

  1. 直观来说:Binder是Android中的一个类,实现了IBinder接口
  2. IPC角度:一种跨进程通信方式
  3. 一种虚拟的物理设备,设备驱动是/dev/binder,在Linux中没有2
  4. 从Android Framework角度:是ServiceManager连接各种Manager(ActivityManager、WindowManager,等等)和相应ManagerService的桥梁(?)
  5. 应用层角度:是客户端与服务端进行通信的媒介。当bindService3时候服务端会返回一个包含了业务调用的Binder对象,通过这个Binder,客户端就可以获取服务端提供的服务(普通服务和基于AIDL的服务)或者数据

实例:分析Binder工作机制

Binder的基本使用

新建Book.java使用接口Parcelable,与上面的使用相同
新建Book.aidl

// Book.aidl
package com.mingrisoft.weimenu.aidl;

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

parcelable Book;

新建IBookManager.aidl,三个放在同一个包内

// IBookManager.aidl
package com.mingrisoft.weimenu.aidl;

// Declare any non-default types here with import statements
//就算与Book同处一个包都还是要导入
import  com.mingrisoft.weimenu.aidl.Book;

interface IBookManager {
    //用于从远程服务端获取图书列表
     List<Book> getBookList();
     //用于在图书列表中添加一本书
     void addBook(in Book book);
}

之后Rebuild一下Project,会发现gen目录下多了一个类
类的位置

接下来上代码,代码有点长但是问题不大,跟着注释走可以看懂

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: E:\\AsWorkSpace\\WeiMenu\\app\\src\\main\\aidl\\com\\mingrisoft\\weimenu\\aidl\\IBookManager.aidl
 */
package com.mingrisoft.weimenu.aidl;

public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.mingrisoft.weimenu.aidl.IBookManager {
        //Binder的唯一标识,一般是当前Binder的类名
        private static final java.lang.String DESCRIPTOR = "com.mingrisoft.weimenu.aidl.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.mingrisoft.weimenu.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        //将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这个过程是区分进程的
        public static com.mingrisoft.weimenu.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //如果客户端和服务端位于同一进程,返回服务端的Stub对象本身
            if (((iin != null) && (iin instanceof com.mingrisoft.weimenu.aidl.IBookManager))) {
                return ((com.mingrisoft.weimenu.aidl.IBookManager) iin);
            }
            //否则返回系统封装后的Stub.proxy对象
            return new com.mingrisoft.weimenu.aidl.IBookManager.Stub.Proxy(obj);
        }

        //返回当前的Binder对象
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        //运行于服务端的Binder线程池中,当客户端发起跨进程请求,远程请求会通过系统底层封装后交给这个方法处理
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            //通过code确定客户端请求的方法
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.mingrisoft.weimenu.aidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.mingrisoft.weimenu.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        //从data中取出目标方法所需的参数,之前的反序列化操作
                        _arg0 = com.mingrisoft.weimenu.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    //执行完毕后写入返回值,reply
                    reply.writeNoException();
                    return true;
                }
            }
            //如果返回的是false,则客户端请求失败
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.mingrisoft.weimenu.aidl.IBookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            //这个方法由客户端远程调用
            public java.util.List<com.mingrisoft.weimenu.aidl.Book> getBookList() throws android.os.RemoteException {
                //输入型Parcel对象
                android.os.Parcel _data = android.os.Parcel.obtain();
                //输出型Parcel对象
                android.os.Parcel _reply = android.os.Parcel.obtain();
                //返回值对象List
                java.util.List<com.mingrisoft.weimenu.aidl.Book> _result;
                try {
                    //把该方法的参数信息写入,钥匙:DESCRIPTOR
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //发起RPC(远程过程调用)请求,同时线程挂起,mRemote
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    //RPC过程返回后,当前线程继续执行,并从_reply中取出返回结果
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.mingrisoft.weimenu.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            //也是运行在客户端,也和上面的方法一样(取data、发起RPC请求、拿到返回数据
            public void addBook(com.mingrisoft.weimenu.aidl.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        //序列化过程
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.mingrisoft.weimenu.aidl.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.mingrisoft.weimenu.aidl.Book book) throws android.os.RemoteException;
}

总结一下

  1. 当客户端发起远程请求的时候,由于当前线程会被挂起直到服务端进程返回数据,所以不能在UI线程中发起此远程请求
  2. 由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现4,因为它已经运行在一个线程中了。
  3. 有实例后总结一下Binder的思路

Binder死亡

Binder运行在服务端,若服务端进程因异常终止,则称为Binder死亡,而且我们不知道这时Binder连接已经断开
linkToDeathunlinkToDeath可以给Binder设置一个死亡代理

声明一个DeathRecipient对象,DeathRecipient是一个接口,内部方法只有binderDied,当Binder死亡时(也可以用isBinderAlive判断是Binder是否死亡)就会调用这个方法可以在这里重新连接

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
	@Override
	public void binderDied() {
		if(mBookManager == null) {
			return;
		}
		//0是标记位,直接为0
		mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
		mBookManager = null;
		// 这里重新绑定远程Service
	}
}

2.4 Android中的IPC方式

  1. 通过Intent中附加的extras传递信息
  2. 共享文件
  3. Binder
  4. ContentProvider专业跨进程访问
  5. Socket通过网络通信实现

2.4.1 使用Bundle

  • 这是一种最简单的进程通信方式
  • 当A进程启动了B进程的(三大组件)Activity、Service、Receiver
  • 这时使用Bundle附加信息并通过Intent发送
  • 传输的数据要求:能被序列化、基本类型、实现了Parcelable、Serializable接口对象以及一些Android支持的特殊对象

传输简单数据而Bundle又不支持的情况下

比如A进程计算一个数,算完之后就启动B进程并把结果给B
我们可以Intent一个Service组件,让Service在后台进行计算,算完后再启动目标组件,这样Service也运行在B进程中,目标组件就可以直接获取结果

2.4.2 使用文件共享

两个进程通过读/写同一个文件来交换数据
下面来看一个小例子顺便回顾一下序列化操作

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import com.ryg.chapter_2.R;
import com.ryg.chapter_2.aidl.Book;
import com.ryg.chapter_2.manager.UserManager;
import com.ryg.chapter_2.model.User;
import com.ryg.chapter_2.utils.MyConstants;
import com.ryg.chapter_2.utils.MyUtils;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        UserManager.sUserId = 2;
        findViewById(R.id.button1).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClass(MainActivity.this, SecondActivity.class);
                User user = new User(0, "jake", true);
                user.book = new Book();
                intent.putExtra("extra_user", (Serializable) user);
                startActivity(intent);
            }
        });
    }

    @Override
    protected void onResume() {
        Log.d(TAG, "UserManage.sUserId=" + UserManager.sUserId);
        persistToFile();

        super.onStart();
    }

//重点在这里
    private void persistToFile() {
        new Thread(new Runnable() {

            @Override
            public void run() {
			//写入要共享的数据,数据bean
                User user = new User(1, "hello world", false);
				//这部分应该是判断文件是否已经存在
                File dir = new File(MyConstants.CHAPTER_2_PATH);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
				
                File cachedFile = new File(MyConstants.CACHE_FILE_PATH);
				//初始化ObjectOutputStream
                ObjectOutputStream objectOutputStream = null;
                try {
				//new一个文件OutputStrem
                    objectOutputStream = new ObjectOutputStream(
                            new FileOutputStream(cachedFile));
							//写入
                    objectOutputStream.writeObject(user);
                    Log.d(TAG, "persist user:" + user);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
				//记得关闭
                    MyUtils.close(objectOutputStream);
                }
            }
        }).start();//记得start
    }
}

这个反序列化操作

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import com.ryg.chapter_2.R;
import com.ryg.chapter_2.model.User;
import com.ryg.chapter_2.utils.MyConstants;
import com.ryg.chapter_2.utils.MyUtils;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;

public class SecondActivity extends Activity {
    private static final String TAG = "SecondActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        findViewById(R.id.button1).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClass(SecondActivity.this, ThirdActivity.class);
                intent.putExtra("time", System.currentTimeMillis());
                startActivity(intent);
            }
        });
        Log.d(TAG, "onCreate");
    }

    @Override
    protected void onResume() {
        super.onResume();
        User user = (User) getIntent().getSerializableExtra("extra_user");
        Log.d(TAG, "user:" + user.toString());
        // Log.d(TAG, "UserManage.sUserId=" + UserManager.sUserId);
        recoverFromFile();
    }
	
//重点在这里
    private void recoverFromFile() {
        new Thread(new Runnable() {

            @Override
            public void run() {
			//初始化一个用来装数据的bean
                User user = null;
				//不懂
                File cachedFile = new File(MyConstants.CACHE_FILE_PATH);
                if (cachedFile.exists()) {
                    ObjectInputStream objectInputStream = null;
                    try {
                        objectInputStream = new ObjectInputStream(
                                new FileInputStream(cachedFile));
                        user = (User) objectInputStream.readObject();
                        Log.d(TAG, "recover user:" + user);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } finally {
                        MyUtils.close(objectInputStream);
                    }
                }
            }
        }).start();
    }
}

文件共享的方式对文件格式没有要求
只需读写双方约定好数据格式

局限性:
并发 读,可能导致数据不是最新的
并发 写,需要尽量避免(或者使用线程同步来限制多线程同时写)

SharePreference通过键值对存储数据,在底层上采用XML文件来存储键值对
SharePreference文件位于data/data/package name/shared_prefs目录下
类似文件存储、跨进程容易丢失数据

2.4.3使用Messenger

通过它可以在不同进程中传递Message对象
是一种轻量级的IPC方案,底层实现是AIDL
Messenger一次只处理一个请求,在服务端中不存在并发执行

1.服务端进程

概况一下:onBind返回mMessenger通过MessengerHandler生成的Binder

package com.ryg.chapter_2.messenger;

import com.ryg.chapter_2.model.User;
import com.ryg.chapter_2.utils.MyConstants;

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;

public class MessengerService extends Service {

    private static final String TAG = "MessengerService";

//用于处理客户端发来的信息MessengerHandler
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
		//与线程处理类似
            switch (msg.what) {
            case MyConstants.MSG_FROM_CLIENT:
			//通过关键词“msg”获取传过来的“msg”绑定的信息
                Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
				//服务端通过replyTo这个参数回复客户端,快递员
                Messenger client = msg.replyTo;
				//标准操作具体不明,运货的车
                Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
				//类似一个包裹
                Bundle bundle = new Bundle();
				//往包裹里面放东西(关键词,内容)
                bundle.putString("reply", "嗯,你的消息我已经收到,稍后会回复你。");
				//把货装上车
                relpyMessage.setData(bundle);
                try {
				//快递员发货
                    client.send(relpyMessage);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

//它和MessengerHandler相关联,接收客户端信息生成它的Binder
    private final Messenger mMessenger = new Messenger(new MessengerHandler());

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

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

}

2.客户端进程

package com.ryg.chapter_2.messenger;

import com.ryg.chapter_2.R;
import com.ryg.chapter_2.R.layout;
import com.ryg.chapter_2.model.User;
import com.ryg.chapter_2.utils.MyConstants;

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;

public class MessengerActivity extends Activity {

    private static final String TAG = "MessengerActivity";

//首先要绑定服务端的Service
    private Messenger mService;
	//用于处理服务端返回的信息
    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
    
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MyConstants.MSG_FROM_SERVICE:
                Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

//首先要绑定服务端的Service
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
		//根据服务端返回的IBinder创建Messenger
            mService = new Messenger(service);
            Log.d(TAG, "bind service");
			//以下为和上面一样发送信息流程
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg", "hello, this is client.");
            msg.setData(data);
			//
            msg.replyTo = mGetReplyMessenger;
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        Intent intent = new Intent("com.ryg.MessengerService.launch");
		//不明
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
    
    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

3.小结

  • Messenger传递的数据必须放入Message中(都要实现Parcelable)
  • Message中可以使用的载体:what、arg1、arg2、Bundle、replyTo,object仅支持系统提供的Parcelable对象(无法使用自定义Parcelable对象)
  • 使用Messenger进程通信无法处理大量同时到达服务端的消息
  • Messenger主要用于传递信息,无法跨进程调用服务端的方法

2.4.4 使用AIDL

我们可以使用AIDL来实现跨进程的方法调用

1.服务端

  • 创建一个Service监听客户端连接请求
  • 创建一个AIDL文件,将暴露给客户端的接口声明
  • 在Service实现这个AIDL接口

2.客户端

  • 绑定服务端Service
  • 将服务端返回的Binder对象转换成AIDL接口所属的类型
  • 调用AIDL中的方法

3.AIDL接口的创建

package com.ryg.chapter_2.manualbinder;

import java.util.List;

import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;

public interface IBookManager extends IInterface {

    static final String DESCRIPTOR = "com.ryg.chapter_2.manualbinder.IBookManager";

    static final int TRANSACTION_getBookList = (IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addBook = (IBinder.FIRST_CALL_TRANSACTION + 1);

    public List<Book> getBookList() throws RemoteException;

    public void addBook(Book book) throws RemoteException;
}

AIDL可使用的数据类型

  • 基本数据类型(int、long、char、boolean、double等)
  • String和CharSequence
  • List:只支持ArrayList,里面每个元素都必须能被AIDL支持
  • Map:只支持HashMap5,里面每个元素都必须能被AIDL支持,包括key和value
  • Parcelable:所有实现了Parcelable的接口
  • AIDL:所有的AIDL接口

其中自定义Parcelable对象和AIDL对象都必须显示的import、无论是否在同一个包
在AIDL中使用的自定义Parcelable对象也必须创建一个同名的AIDL文件

package com.ryg.chapter_2.manualbinder;

parcelable Book;

strong text

4.远程服务端Service实现

创建一个Service

package com.ryg.chapter_2.aidl;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;

public class BookManagerService extends Service {

    private static final String TAG = "BMS";

    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

//这里使用了CopyOnWriteArrayList可支持并发读写,可自动线程同步
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
    // private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
    // new CopyOnWriteArrayList<IOnNewBookArrivedListener>();

    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();

//创建一个继承自IBookManager.Stub并实现它内部的AIDL方法的Binder对象
    private Binder mBinder = new IBookManager.Stub() {

        @Override
        public List<Book> getBookList() throws RemoteException {
            SystemClock.sleep(5000);
            return mBookList;
        }

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

        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
            Log.d(TAG, "check=" + check);
            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }

            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(
                    getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            Log.d(TAG, "onTransact: " + packageName);
            if (!packageName.startsWith("com.ryg")) {
                return false;
            }

            return super.onTransact(code, data, reply, flags);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            mListenerList.register(listener);

            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG, "registerListener, current size:" + N);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            boolean success = mListenerList.unregister(listener);

            if (success) {
                Log.d(TAG, "unregister success.");
            } else {
                Log.d(TAG, "not found, can not unregister.");
            }
            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG, "unregisterListener, current size:" + N);
        };

    };

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

    @Override
    public IBinder onBind(Intent intent) {
        int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
        Log.d(TAG, "onbind check=" + check);
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mBinder;
    }

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

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
            if (l != null) {
                try {
                    l.onNewBookArrived(book);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        mListenerList.finishBroadcast();
    }

    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            // do background processing here.....
            while (!mIsServiceDestoryed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

为什么和可以使用CopyOnWriteArrayList,因为Binder会按照List的规范去访问数据并最终形成一个ArrayList传给客户端

注册这个服务

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

5.客户端的实现

public class BookManagerActivity extends Activity {

    private static final String TAG = "BookManagerActivity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private IBookManager mRemoteBookManager;

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_NEW_BOOK_ARRIVED:
                Log.d(TAG, "receive new book :" + msg.obj);
                break;
            default:
                super.handleMessage(msg);
            }
        }
    };

    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
            if (mRemoteBookManager == null)
                return;
            mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mRemoteBookManager = null;
            // TODO:这里重新绑定远程Service
        }
    };

//绑定远程服务
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
		//绑定成后将服务端返回的Binder对象转换成AIDL接口
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            mRemoteBookManager = bookManager;
            try {
                mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
				//调用远程方法
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "query book list, list type:"
                        + list.getClass().getCanonicalName());
                Log.i(TAG, "query book list:" + list.toString());
				//在客户端给服务端添加的方法也很简单
				//new一个数据类,再调用远程方法add进去就行
                Book newBook = new Book(3, "Android进阶");
                bookManager.addBook(newBook);
                Log.i(TAG, "add book:" + newBook);
                List<Book> newList = bookManager.getBookList();
                Log.i(TAG, "query book list:" + newList.toString());
                bookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mRemoteBookManager = null;
            Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());
        }
    };

    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {

        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook)
                    .sendToTarget();
        }
    };

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

    public void onButton1Click(View view) {
        Toast.makeText(this, "click button1", Toast.LENGTH_SHORT).show();
        new Thread(new Runnable() {

            @Override
            public void run() {
                if (mRemoteBookManager != null) {
                    try {
                        List<Book> newList = mRemoteBookManager.getBookList();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        if (mRemoteBookManager != null
                && mRemoteBookManager.asBinder().isBinderAlive()) {
            try {
                Log.i(TAG, "unregister listener:" + mOnNewBookArrivedListener);
                mRemoteBookManager
                        .unregisterListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }

}

增加实时提醒用户新书上架功能
再添加一个AIDL接口IOnNewBookArrivedListener

package com.ryg.chapter_2.aidl;

import com.ryg.chapter_2.aidl.Book;

//AIDL中除了基本数据类型,其他参数必须标上方向
//in:输入型参数
//out:输出
//inout:输入输出
interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book newBook);
}

在原有的接口中添加两个注册方法

package com.ryg.chapter_2.aidl;

import com.ryg.chapter_2.aidl.Book;
import com.ryg.chapter_2.aidl.IOnNewBookArrivedListener;

interface IBookManager {
     List<Book> getBookList();
     void addBook(in Book book);
	 //注册方法
     void registerListener(IOnNewBookArrivedListener listener);
     void unregisterListener(IOnNewBookArrivedListener listener);
}

也要在服务端中的IBookManager.Stub中实现这两个方法

//注册监听者,如果这个监听者不在list内就添加
//注销监听者
//给每位监听者发送新书消息
//如果这个服务没有断开,就先休眠5s
//最后一本新书的id
public class BookManagerService extends Service {

    private static final String TAG = "BMS";

    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

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

   // private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
    //new CopyOnWriteArrayList<IOnNewBookArrivedListener>();

	//存放listenr的list
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();

    private Binder mBinder = new IBookManager.Stub() {

·····

        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
            Log.d(TAG, "check=" + check);
            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }

            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(
                    getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            Log.d(TAG, "onTransact: " + packageName);
            if (!packageName.startsWith("com.ryg")) {
                return false;
            }

            return super.onTransact(code, data, reply, flags);
        }

//注册listener
        @Override
        public void registerListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            mListenerList.register(listener);

            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG, "registerListener, current size:" + N);
        }


        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
				//一键解除监听
            boolean success = mListenerList.unregister(listener);

            if (success) {
                Log.d(TAG, "unregister success.");
            } else {
                Log.d(TAG, "not found, can not unregister.");
            }
			//应该是把上述操作发布出去
            final int N = mListenerList.beginBroadcast();
			//发布完就关闭
            mListenerList.finishBroadcast();
            Log.d(TAG, "unregisterListener, current size:" + N);
        };

    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
		//开启一个线程
        new Thread(new ServiceWorker()).start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
        Log.d(TAG, "onbind check=" + check);
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mBinder;
    }

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

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
            if (l != null) {
                try {
                    l.onNewBookArrived(book);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        mListenerList.finishBroadcast();
    }

    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            // do background processing here.....
            while (!mIsServiceDestoryed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

客户端代码


//接收远程的另一个载体
//切换到客户端主线程进行UI操作
//使用接口来调用远程方法
//记得注销监听者

public class BookManagerActivity extends Activity {

    private static final String TAG = "BookManagerActivity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private IBookManager mRemoteBookManager;

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_NEW_BOOK_ARRIVED:
                Log.d(TAG, "receive new book :" + msg.obj);
                break;
            default:
                super.handleMessage(msg);
            }
        }
    };

    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
            if (mRemoteBookManager == null)
                return;
            mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mRemoteBookManager = null;
            // TODO:这里重新绑定远程Service
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            mRemoteBookManager = bookManager;
            try {
               
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "query book list, list type:"
                        + list.getClass().getCanonicalName());
                Log.i(TAG, "query book list:" + list.toString());
                Book newBook = new Book(3, "Android进阶");
                bookManager.addBook(newBook);
                Log.i(TAG, "add book:" + newBook);
                List<Book> newList = bookManager.getBookList();
                Log.i(TAG, "query book list:" + newList.toString());
              //连接之后注册
			  bookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mRemoteBookManager = null;
            Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());
        }
    };

//申明并使用这个接口,注意这里已经是使用远程方法了
//具体实现过程在服务端已经实现
    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {

        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook) .sendToTarget();
        }
    };

    @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() {
        if (mRemoteBookManager != null
                && mRemoteBookManager.asBinder().isBinderAlive()) {
            try {
                Log.i(TAG, "unregister listener:" + mOnNewBookArrivedListener);
				//这里也是调用服务端的注销方法
                mRemoteBookManager
                        .unregisterListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }

}

RemoteCallbackList
是系统专门提供用于删除跨进程listener的接口
工作原理:在它内部有一个Map结果专门用来保存所有AIDL回调、key是IBinder类型,value是Callback类型
下面是获取listener信息的操作

IBinder key = listener.asBinder();
Callback value = new Callback(listener,cookie);

多次跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是这个对象有一个共同点:它们的底层Binder对象是同一个
所以我们只需要遍历服务端所有的listener,找到和注册listener具有相同Binder对象的服务端listener删除即可
RemoteCallbackList能自定移除客户端注册的listener
另外它内部自动实现了线程同步的功能
注意beginBroadcast和finsinBroadcast必须要配对使用

注意在客户端和服务端中的耗时操作

背景:客户端调用远程服务的方法(此方法运行在服务端的Binder线程池中)客户端会被挂起
如果这个方法很耗时并且是处于客户端的UI线程的话,客户端就会崩
特别是客户端的onServiceConnected和onServiceDisconnected也是运行在UI线程中的

另外:服务端的方法本身运行在服务端的Binder线程池中,所以它本身就可以执行大量耗时操作,故不要在服务端中开线程去进行异步任务
以上的情况对于服务端调用客户端的远程耗时方法也是存在的

Binder重连

  1. 给Binder设置DeathRecipient监听(之前提到)Binder死亡时会收到一个binderDied方法的回调、在客户端的Binder线程池中被回调
  2. onServiceDisconnected中重连服务、在UI线程

Binder验证

第一种方法:
使用permission验证,要在AndroidMenifest申明所需的权限

    <permission android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE"
        android:protectionLevel="normal" />

接着在服务端的onBind方法中做权限验证

    @Override
    public IBinder onBind(Intent intent) {
        int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
        Log.d(TAG, "onbind check=" + check);
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mBinder;
    }

当一个应用来绑定我们服务的时候,就会验证这个权限。验证失败会返回一个null,这种方法同样适用于Messenger中
简而言之就是自定义的一个钥匙注册在AndroidMenifest中

第二种方法:
在服务端的onTransact方法中进行权限验证,验证失败返回fase
这样服务端就不会终止执行AIDL方法从而达到保护服务端的效果
另外还可以采用Uid和Pid做验证,通过getCallingUid
和getCallingPid可以拿到客户端所属应用的Uid和Pid

        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
            Log.d(TAG, "check=" + check);
            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }

            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(
                    getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            Log.d(TAG, "onTransact: " + packageName);
            if (!packageName.startsWith("com.ryg")) {
                return false;
            }

            return super.onTransact(code, data, reply, flags);
        }

2.4.5 使用ContentProvider

ContentProvider是Android中专门用来IPC的,底层实现也是Binder
实现自定义ContentProvider

只需要继承接口和实现六个抽象方法
其中onCreate由系统回调并运行在主线程里,其他五个方法由外界回调并运行在Binder线程池中

//getType用来返回一个Uri请求所对应的MIME类型,比如图片视频等

C主要表格的形式组织数据(类似数据库)
另外我们还需要注册这个BookProvier。android:authorities是C的唯一标识,外部应用通过这个就可以访问我们的BookProvier

        <provider
            android:name=".provider.BookProvider"
            android:authorities="com.ryg.chapter_2.book.provider"
            android:permission="com.ryg.PROVIDER"
            android:process=":provider" >
        </provider>

权限还可以细分为读权限和写权限
android:readPermission
android:writePermission

模拟在外部应用中访问

//这是在同一个应用的其他进程中访问这个BookProvider
//这个Uri就是一个密钥,对应刚刚申明的权限
//通过ContentResolver对象的query方法查询BookProvider
//query调用了3次,并且这三次调用都不在同一个线程中(因为运行在Binder线程中)  
public class ProviderActivity extends Activity {
    private static final String TAG = "ProviderActivity";

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

在上面的基础上,继续完善BookProvider,使其能够对外部应用提供数据

建立一个数据库管理图书和用户信息DbOpenHelper

//借助SQLiteOpenHelper来管理数据库的创建、升级和降级
public class DbOpenHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "book_provider.db";
    public static final String BOOK_TABLE_NAME = "book";
    public static final String USER_TALBE_NAME = "user";

    private static final int DB_VERSION = 3;

    private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS "
            + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";

    private String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS "
            + USER_TALBE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,"
            + "sex INT)";

    public DbOpenHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK_TABLE);
        db.execSQL(CREATE_USER_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO ignored
    }

}

通过BookProvider向外界提供数据库的信息
ContentProvider通过Uri来区分外界所访问的内容
为了知道外界访问的是哪个表,我们需要为book和user表定义单独的Uri和Uri_Code并将它们关联在一起
外界请求发出Uri,Provider根据Uri得到Uri_Code再进行访问

public class BookProvider extends ContentProvider {

    private static final String TAG = "BookProvider";

    public static final String AUTHORITY = "com.ryg.chapter_2.book.provider";

//为两个表指定Uri"content://com.ryg.chapter_2.book.provider/book"
    public static final Uri BOOK_CONTENT_URI = Uri.parse("content://"
            + AUTHORITY + "/book");
    public static final Uri USER_CONTENT_URI = Uri.parse("content://"
            + AUTHORITY + "/user");

//这是相关联的Uri_Code
    public static final int BOOK_URI_CODE = 0;
    public static final int USER_URI_CODE = 1;
	//UriMatcher
    private static final UriMatcher sUriMatcher = new UriMatcher(
            UriMatcher.NO_MATCH);

    static {
	//通过这一句完成关联
        sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
        sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
    }

    private Context mContext;
	//增删查改是存在多线程并发访问的,但单个SQLiteDatabase内部对数据库的操作是有同步处理
	//如果多个SQLiteDatabase就要做好线程同步处理
    private SQLiteDatabase mDb;

    @Override
    public boolean onCreate() {
        Log.d(TAG, "onCreate, current thread:"
                + Thread.currentThread().getName());
        mContext = getContext();
        initProviderData();
        return true;
    }

//初始化数据库
    private void initProviderData() {
	//获得一个可以写入的数据库
        mDb = new DbOpenHelper(mContext).getWritableDatabase();
        mDb.execSQL("delete from " + DbOpenHelper.BOOK_TABLE_NAME);
        mDb.execSQL("delete from " + DbOpenHelper.USER_TALBE_NAME);
        mDb.execSQL("insert into book values(3,'Android');");
        mDb.execSQL("insert into book values(4,'Ios');");
        mDb.execSQL("insert into book values(5,'Html5');");
        mDb.execSQL("insert into user values(1,'jake',1);");
        mDb.execSQL("insert into user values(2,'jasmine',0);");
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        Log.d(TAG, "query, current thread:" + Thread.currentThread().getName());
		//根据Uri获得需要查询的表名
        String table = getTableName(uri);
        if (table == null) {
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
		//继承的数据库查询方法
        return mDb.query(table, projection, selection, selectionArgs, null, null, sortOrder, null);
    }

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

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.d(TAG, "insert");
        String table = getTableName(uri);
        if (table == null) {
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        mDb.insert(table, null, values);
		//用这个方法通知外界当前Provider中的数据已经改变
        mContext.getContentResolver().notifyChange(uri, null);
        return uri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.d(TAG, "delete");
        String table = getTableName(uri);
        if (table == null) {
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        int count = mDb.delete(table, selection, selectionArgs);
        if (count > 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        Log.d(TAG, "update");
        String table = getTableName(uri);
        if (table == null) {
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        int row = mDb.update(table, values, selection, selectionArgs);
        if (row > 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return row;
    }

    private String getTableName(Uri uri) {
        String tableName = null;
        switch (sUriMatcher.match(uri)) {
        case BOOK_URI_CODE:
            tableName = DbOpenHelper.BOOK_TABLE_NAME;
            break;
        case USER_URI_CODE:
            tableName = DbOpenHelper.USER_TALBE_NAME;
            break;
            default:break;
        }

        return tableName;
    }
}

在外部访问的类ProviderActivity

public class ProviderActivity extends Activity {
    private static final String TAG = "ProviderActivity";

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

//相当于需要查询的表的钥匙
        Uri bookUri = Uri.parse("content://com.ryg.chapter_2.book.provider/book");
		//以下4行是插入数据操作
        ContentValues values = new ContentValues();
        values.put("_id", 6);
        values.put("name", "程序设计的艺术");
		//Provider中也有这个getContentResolver
        getContentResolver().insert(bookUri, values);
		//这应该是开启一个查询的线程
        Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
        while (bookCursor.moveToNext()) {
            Book book = new Book();
            book.bookId = bookCursor.getInt(0);
            book.bookName = bookCursor.getString(1);
            Log.d(TAG, "query book:" + book.toString());
        }
        bookCursor.close();

        Uri userUri = Uri.parse("content://com.ryg.chapter_2.book.provider/user");
        Cursor userCursor = getContentResolver().query(userUri, new String[]{"_id", "name", "sex"}, null, null, null);
        while (userCursor.moveToNext()) {
            User user = new User();
            user.userId = userCursor.getInt(0);
            user.userName = userCursor.getString(1);
            user.isMale = userCursor.getInt(2) == 1;
            Log.d(TAG, "query user:" + user.toString());
        }
        userCursor.close();
    }
}

2.4.6 使用Socket

Socket称为“套接字”是网络通信中的概念
流式套接字:对应TCP,数据传输稳定
用户数据报套接字:对应UDP,效率高

先申明权限

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

下面开始演示一个跨进程的聊天程序,Socket支持传输任意字节流

注意不能在主线程中访问网络

接下来是服务端代码


public class TCPServerService extends Service {

    private boolean mIsServiceDestoryed = false;
    private String[] mDefinedMessages = new String[] {
            "你好啊,哈哈",
            "请问你叫什么名字呀?",
            "今天北京天气不错啊,shy",
            "你知道吗?我可是可以和多个人同时聊天的哦",
            "给你讲个笑话吧:据说爱笑的人运气不会太差,不知道真假。"
    };

    @Override
    public void onCreate() {
        new Thread(new TcpServer()).start();
        super.onCreate();
    }

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

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

    private class TcpServer implements Runnable {

        @SuppressWarnings("resource")
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
			//监听本地的8688端口
                serverSocket = new ServerSocket(8688);
            } catch (IOException e) {
                System.err.println("establish tcp server failed, port:8688");
                e.printStackTrace();
                return;
            }

            while (!mIsServiceDestoryed) {
                try {
                    // 接受客户端请求
                    final Socket client = serverSocket.accept();
                    System.out.println("accept");
                    new Thread() {
                        @Override
                        public void run() {
                            try {
							//回复客户端消息
                                responseClient(client);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        };
                    }.start();

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

    private void responseClient(Socket client) throws IOException {
        // 用于接收客户端消息
        BufferedReader in = new BufferedReader(new InputStreamReader(
                client.getInputStream()));
        // 用于向客户端发送消息
        PrintWriter out = new PrintWriter(new BufferedWriter(
                new OutputStreamWriter(client.getOutputStream())), true);
        out.println("欢迎来到聊天室!");
        while (!mIsServiceDestoryed) {
		//读取客户端发来的消息
            String str = in.readLine();
            System.out.println("msg from client:" + str);
            if (str == null) {
                break;
            }
            int i = new Random().nextInt(mDefinedMessages.length);
            String msg = mDefinedMessages[i];
            out.println(msg);
            System.out.println("send :" + msg);
        }
        System.out.println("client quit.");
        // 关闭流
        MyUtils.close(out);
        MyUtils.close(in);
        client.close();
    }

}

客户端代码

public class TCPClientActivity extends Activity implements OnClickListener {

    private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
    private static final int MESSAGE_SOCKET_CONNECTED = 2;

    private Button mSendButton;
    private TextView mMessageTextView;
    private EditText mMessageEditText;

    private PrintWriter mPrintWriter;
    private Socket mClientSocket;

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_RECEIVE_NEW_MSG: {
                mMessageTextView.setText(mMessageTextView.getText()
                        + (String) msg.obj);
                break;
            }
            case MESSAGE_SOCKET_CONNECTED: {
                mSendButton.setEnabled(true);
                break;
            }
            default:
                break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tcpclient);
        mMessageTextView = (TextView) findViewById(R.id.msg_container);
        mSendButton = (Button) findViewById(R.id.send);
        mSendButton.setOnClickListener(this);
        mMessageEditText = (EditText) findViewById(R.id.msg);
        Intent service = new Intent(this, TCPServerService.class);
        startService(service);
        new Thread() {
            @Override
            public void run() {
                connectTCPServer();
            }
        }.start();
    }

    @Override
    protected void onDestroy() {
        if (mClientSocket != null) {
            try {
                mClientSocket.shutdownInput();
                mClientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        super.onDestroy();
    }

    @Override
    public void onClick(View v) {
        if (v == mSendButton) {
            final String msg = mMessageEditText.getText().toString();
            if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                mPrintWriter.println(msg);
                mMessageEditText.setText("");
                String time = formatDateTime(System.currentTimeMillis());
                final String showedMsg = "self " + time + ":" + msg + "\n";
				//这个操作是保留原来的文本
                mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
            }
        }
    }

//这个要注意,可以很简便的获取实时时间
    @SuppressLint("SimpleDateFormat")
    private String formatDateTime(long time) {
        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
    }

//连接服务器
    private void connectTCPServer() {
        Socket socket = null;
        while (socket == null) {
            try {
                socket = new Socket("localhost", 8688);
                mClientSocket = socket;
                mPrintWriter = new PrintWriter(new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream())), true);
                mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
                System.out.println("connect server success");
            } catch (IOException e) {
                SystemClock.sleep(1000);
                System.out.println("connect tcp server failed, retry...");
            }
        }

        try {
            // 接收服务器端的消息
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            while (!TCPClientActivity.this.isFinishing()) {
                String msg = br.readLine();
                System.out.println("receive :" + msg);
                if (msg != null) {
                    String time = formatDateTime(System.currentTimeMillis());
                    final String showedMsg = "server " + time + ":" + msg
                            + "\n";
                    mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg)
                            .sendToTarget();
                }
            }
            System.out.println("quit...");
            MyUtils.close(mPrintWriter);
            MyUtils.close(br);
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.5 Binder连接池

AIDL是最常用的进程间通信方式,首选
回顾一下使用AIDL的流程

  1. 创建一个Service和一个AIDL接口
  2. 创建一个类继承AIDL接口中的Stub类并实现其抽象方法
  3. Service的onBind方法中返回这个类的对象
  4. 客户端绑定服务端的Service,连接后即可访问

在大项目中AIDL的使用越来越多创建的服务也越来越多,为了减少Service的数量,使应用变得轻量,我们可以把所有的AIDL放在同一个Service中去管理
工作机制如下:

  1. 每个业务模块创建自己的的AIDL接口并单独实现,但同时会向服务端提供自己的唯一标识和其对应的Binder对象
  2. 服务端提供一个queryBinder接口,根据标识返回相应的Binder对象
  3. Binder连接池的作用就是将每个业务模块的Binder请求统一转发到远程Service中执行,从而避免了重复创建Service的过程

ISecurityCenter提供加解密过程

package com.ryg.chapter_2.binderpool;

interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
}

ICompute提供计算加法的功能

package com.ryg.chapter_2.binderpool;

interface ICompute {
    int add(int a, int b);
}

SecurityCenterImpl、业务模块AIDL接口的实现、注意这里并没有为每个模块的AIDL单独创建Service

public class SecurityCenterImpl extends ISecurityCenter.Stub {

    private static final char SECRET_CODE = '^';

    @Override
    public String encrypt(String content) throws RemoteException {
        char[] chars = content.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            chars[i] ^= SECRET_CODE;
        }
        return new String(chars);
    }

    @Override
    public String decrypt(String password) throws RemoteException {
        return encrypt(password);
    }

}

首先为Binder连接池创建AIDL接口IBinderPool

package com.ryg.chapter_2.binderpool;

interface IBinderPool {

    /**
     * @param binderCode, the unique token of specific Binder<br/>
     * @return specific Binder who's token is binderCode.
     */
    IBinder queryBinder(int binderCode);
}

远程Service

public class BinderPoolService extends Service {

    private static final String TAG = "BinderPoolService";

    private Binder mBinderPool = new BinderPool.BinderPoolImpl();

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

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return mBinderPool;
    }

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

}

Binder的具体实现
为Binder连接池创建远程Service并实现IBinderPool6

public class BinderPool {
    private static final String TAG = "BinderPool";
    public static final int BINDER_NONE = -1;
    public static final int BINDER_COMPUTE = 0;
    public static final int BINDER_SECURITY_CENTER = 1;

    private Context mContext;
	//AIDL接口
    private IBinderPool mBinderPool;
    private static volatile BinderPool sInstance;
    private CountDownLatch mConnectBinderPoolCountDownLatch;

    private BinderPool(Context context) {
        mContext = context.getApplicationContext();
        connectBinderPoolService();
    }

    public static BinderPool getInsance(Context context) {
        if (sInstance == null) {
            synchronized (BinderPool.class) {
                if (sInstance == null) {
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }

    private synchronized void connectBinderPoolService() {
        mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
		//启动服务
        Intent service = new Intent(mContext, BinderPoolService.class);
        mContext.bindService(service, mBinderPoolConnection,
                Context.BIND_AUTO_CREATE);
        try {
            mConnectBinderPoolCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * query binder by binderCode from binder pool
     * 
     * @param binderCode
     *            the unique token of binder
     * @return binder who's token is binderCode<br>
     *         return null when not found or BinderPoolService died.
     */
    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        try {
            if (mBinderPool != null) {
                binder = mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
		//根据不同模块的标识即binderCode返回不同的Binder对象
		//通过这个Binder对象所执行的操作全部发生在远程服务端
        return binder;
    }

    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // ignored.
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();
        }
    };

    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.w(TAG, "binder died.");
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };

    public static class BinderPoolImpl extends IBinderPool.Stub {

        public BinderPoolImpl() {
            super();
        }

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode) {
            case BINDER_SECURITY_CENTER: {
                binder = new SecurityCenterImpl();
                break;
            }
            case BINDER_COMPUTE: {
                binder = new ComputeImpl();
                break;
            }
            default:
                break;
            }

            return binder;
        }
    }

}

用Activity连接验证

public class BinderPoolActivity extends Activity {
    private static final String TAG = "BinderPoolActivity";

//这是之前创建的AIDL接口
    private ISecurityCenter mSecurityCenter;
    private ICompute mCompute;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_binder_pool);
		//耗时操作在线程中执行
        new Thread(new Runnable() {

            @Override
            public void run() {
                doWork();
            }
        }).start();
    }

    private void doWork() {
	//初始化Binder连接池,Context
        BinderPool binderPool = BinderPool.getInsance(BinderPoolActivity.this);
		//初始化一个Binder对象,传入BinderCode获取对应的Binder
        IBinder securityBinder = binderPool
                .queryBinder(BinderPool.BINDER_SECURITY_CENTER);
        ;
		//SecurityCenterImpl是AIDL接口的具体实现
        mSecurityCenter = (ISecurityCenter) SecurityCenterImpl
                .asInterface(securityBinder);
        Log.d(TAG, "visit ISecurityCenter");
        String msg = "helloworld-安卓";
        System.out.println("content:" + msg);
        try {
            String password = mSecurityCenter.encrypt(msg);
            System.out.println("encrypt:" + password);
            System.out.println("decrypt:" + mSecurityCenter.decrypt(password));
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        Log.d(TAG, "visit ICompute");
        IBinder computeBinder = binderPool
                .queryBinder(BinderPool.BINDER_COMPUTE);
        ;
        mCompute = ComputeImpl.asInterface(computeBinder);
        try {
            System.out.println("3+5=" + mCompute.add(3, 5));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

}

注意到BinderPool是一个单例实现,因此在同一个进程中只会初始化一次,所以提前对BinderPool进行初始化可以优化程序体验

2.6 选用合适的IPC方式

名称优点缺点适用场景
Bundle简单易用只能传输Bundle支持的数据类型四大组件间的进程间通信
文件共享简单易用不适合高并发场景,无法做到进程间的即时通信无并发访问,交换简单且实时性不高的数据
支持一对多并发通信,实时通信一对多并发通信,实时通信使用复杂,需处理线程同步一对多且有RPC需求
Messager一对多串行通信,实时通信不能很好处理高并发,不支持RPC,只能传输Bundle支持的数据类型低并发一对多的即时通信,唔RPC需求
ContentProvider数据源访问方面功能强大,支持一对多并发数据共享,可通过Call拓展其他操作可以理解为受约束的AIDL,主要提供数据源的CRUD一对多的进程间数据共享
Socket通过网络传输字节流,支持一对多并发实时通信实现细节稍微繁琐,不支持直接RPC网络数据交换

  1. ↩︎

  2. ↩︎

  3. ↩︎

  4. ↩︎

  5. ↩︎

  6. ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值