这是我在学习过程中总结的知识
目的是希望日后回来看或者需要用的时候可以 一目了然 # 的回顾、巩固、查缺补漏
不追求详细相当于书本的精简版或者说是导读(想看详细的直接对应翻书),但会尽力保证读者都能快速理解和快速使用(随理解加深会总结的更加精简),但必要时会附上一些较详细解释的链接
脚注是空白的:表示还没弄懂的知识,了解后会添加
2.2 Android中的多进程模式
在Android中使用多进程只有一种方法:给四大组件注册android:process属性
2.2.1 关于进程命名
- android:process=" :remote" “:”的含义是加上包名的简写、并且使用“:”的进程属于当前应用的私有进程
- android:process=“com.ryg.chapter_2.remote” 这是一种完整的命名方式、不会附加包名信息、属于全局进程、其他应用可以通过ShareUID和他跑在同一个进程中
Android会为每一个应用分配一个唯一的UID
两个不同应用通过ShareUID并且签名相同可以互相访问私有数据(data目录,组件信息)
如果符合第二点还在同一进程中,则还可以共享内存数据
2.2.2 多进程模式的运行机制
Android为每一个进程都分配了一个独立的虚拟机,不同虚拟机在内存分配上有不同的地址空间,这就导致了不同虚拟机访问同一个对象的时候会产生多个副本
多进程缺点
- 静态成员和单实例模式完全失效
- 线程同步机制完全失效
- SharePreference的可靠性下降(因为SharePreference底层是通过读/写XML文件实现的,同时读写就容易出问题)
- Application会被多次创建(进程——虚拟机——Application挂钩)
2.3 IPC基础概念介绍
- Serrializable接口(序列化、对象持久化)
- Parcelable接口(序列化)
- 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操作
- 将对象序列化到存储设备中
- 将对象序列化后通过网络传输
使用Parcelable的情况
更适用于Android、使用麻烦效率高、首选
- 主要用在内存序列化上
2.3.3 Binder
简介
- 直观来说:Binder是Android中的一个类,实现了IBinder接口
- IPC角度:一种跨进程通信方式
- 一种虚拟的物理设备,设备驱动是/dev/binder,在Linux中没有2
- 从Android Framework角度:是ServiceManager连接各种Manager(ActivityManager、WindowManager,等等)和相应ManagerService的桥梁(?)
- 应用层角度:是客户端与服务端进行通信的媒介。当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;
}
总结一下
- 当客户端发起远程请求的时候,由于当前线程会被挂起直到服务端进程返回数据,所以不能在UI线程中发起此远程请求
- 由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现4,因为它已经运行在一个线程中了。
- 有实例后总结一下Binder的思路
Binder死亡
Binder运行在服务端,若服务端进程因异常终止,则称为Binder死亡,而且我们不知道这时Binder连接已经断开
linkToDeath和unlinkToDeath可以给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方式
- 通过Intent中附加的extras传递信息
- 共享文件
- Binder
- ContentProvider专业跨进程访问
- 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重连
- 给Binder设置DeathRecipient监听(之前提到)Binder死亡时会收到一个binderDied方法的回调、在客户端的Binder线程池中被回调
- 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的流程
- 创建一个Service和一个AIDL接口
- 创建一个类继承AIDL接口中的Stub类并实现其抽象方法
- Service的onBind方法中返回这个类的对象
- 客户端绑定服务端的Service,连接后即可访问
在大项目中AIDL的使用越来越多创建的服务也越来越多,为了减少Service的数量,使应用变得轻量,我们可以把所有的AIDL放在同一个Service中去管理
工作机制如下:
- 每个业务模块创建自己的的AIDL接口并单独实现,但同时会向服务端提供自己的唯一标识和其对应的Binder对象
- 服务端提供一个queryBinder接口,根据标识返回相应的Binder对象
- 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 | 网络数据交换 |