Android的IPC机制
一、IPC简介
二、Android中的多进程模式
1、在Android中使用多进程只有一种方法:
- <activity
- android:name=".MainActivity"
- android:configChanges="orientation|screenSize"
- android:label="@string/app_name"
- android:launchMode="standard" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- </intent-filter>
- </activity>
- <activity
- android:name=".SecondActivity"
- android:configChanges="screenLayout"
- android:label="@string/app_name"
- android:process=":remote" />
- <activity
- android:name=".ThirdActivity"
- android:configChanges="screenLayout"
- android:label="@string/app_name"
- android:process="com.ryg.chapter_2.remote" />
注意点一:Android系统会为每一个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。要求两个应用具有相同的ShareUID并且签名相同才可以跑在同一个进程中。在这种情况下,它们可以互相访问对方的私有数据,比如data目录、组件信息等,不管它们是否跑在同一个进程中。当然如果它们跑在同一个进程中,那么除了能共享data目录、组件信息,还可以共享内存数据,或者说它们看起来就像是一个应用的两个部分。
2、多进程模式的运行机制
三、IPC基础概念介绍
1、Serializable接口
- private static final long serialVersionUID = 8723148825838841922L;
- public class User implements Serializable{
- private static final long serialVersionUID = 8723148825838841922L;
- public int userId;
- public String userName;
- public boolean isMale;
- }
- // 序列化过程:
- User user = new User(0,"jake",true);
- 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();
(3)一般来说,我们应该收到指定serialVersionUID的值,比如1L,也可以让Eclipse根据当前类的结构自动去生成它的hash值。当我们手动指定了它以后,就可以在很大程度上避免反序列化过程的失败。
2、Parcelable接口
- public class User implements Parcelable {
- public int userId;
- public String userName;
- public boolean isMale;
- public Book book;
- public User(int userId, String userName, boolean isMale) {
- this.userId = userId;
- this.userName = userName;
- this.isMale = isMale;
- }
- /*
- * 内容描述功能几乎都是直接返回0的。
- * */
- public int describeContents() {
- return 0;
- }
- /*
- * 序列化由writeToParcel方法来完成,最终是通过Parcel中一系列write方法来完成的。
- * 其中flags标识有两种值:0和1(PARCELABLE_WRITE_RETURN_VALUE)。
- * 为1时标识当前对象需要作为返回值返回,不能立即释放资源,
- * 几乎所有情况都为0。
- * */
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(userId);
- out.writeString(userName);
- out.writeInt(isMale? 1:0);
- out.writeParcelable(book, 0);
- }
- /*
- * 反序列化功能是由CREATOR来完成,其内部标明了如何创建序列化对象和数组,
- * 并通过Parcel的一些了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];
- }
- };
- /*
- * Parcel内部包装了可序列化的数据,可以在Binder中自由传输。
- * 从序列化后的对象中创建原始对象。
- * */
- private User(Parcel in) {
- userId = in.readInt();
- userName = in.readString();
- isMale = in.readInt() == 1;
- /*
- * 由于book是另一个可序列化对象,所以它的反序列化过程需要传递当前线程的上下文类加载器,
- * 否则会报无法找到类的错误。
- * */
- book = in.readParcelable(Thread.currentThread().getContextClassLoader());
- }
- }
四、Binder
1、Binder简介
2、在分析Binder的工作原理之前,我们先补充一下Android设计模式之Proxy模式
(1)Proxy代理模式简介
抽象对象角色AbstarctObject:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
目标对象角色RealObject:定义了代理对象所代表的目标对象。
代理对象角色ProxyObject:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。
(2)Proxy代理模式的简单实现
抽象对象角色:
- public abstract class AbstractObject {
- //操作
- public abstract void operation();
- }
- public class RealObject extends AbstractObject {
- @Override
- public void operation() {
- //一些操作
- System.out.println("一些操作");
- }
- }
- public class ProxyObject extends AbstractObject{
- RealObject realObject = new RealObject();//目标对象角色
- @Override
- public void operation() {
- //调用目标对象之前可以做相关操作
- System.out.println("before");
- realObject.operation(); //目标对象角色的操作函数
- //调用目标对象之后可以做相关操作
- System.out.println("after");
- }
- }
- public class Client {
- public static void main(String[] args) {
- AbstractObject obj = new ProxyObject();
- obj.operation();
- }
- }
(3)代理模式在Binder中的使用
直观来说,Binder是Android中的一个类,它继承了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在linux中没有;从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,etc)和相应ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
Binder一个很重要的作用是:将客户端的请求参数通过Parcel包装后传到远程服务端,远程服务端解析数据并执行对应的操作,同时客户端线程挂起,当服务端方法执行完毕后,再将返回结果写入到另外一个Parcel中并将其通过Binder传回到客户端,客户端接收到返回数据的Parcel后,Binder会解析数据包中的内容并将原始结果返回给客户端,至此,整个Binder的工作过程就完成了。由此可见,Binder更像一个数据通道,Parcel对象就在这个通道中跨进程传输,至于双方如何通信,这并不负责,只需要双方按照约定好的规范去打包和解包数据即可。
为了更好地说明Binder,这里我们先手动实现了一个Binder。为了使得逻辑更清晰,这里简化一下,我们来模拟一个银行系统,这个银行提供的功能只有一个:即查询余额,只有传递一个int的id过来,银行就会将你的余额设置为id*10,满足下大家的发财梦。
1)先定义一个Binder接口(抽象对象角色):
- package com.ryg.design.manualbinder;
- import android.os.IBinder;
- import android.os.IInterface;
- import android.os.RemoteException;
- public interface IBank extends IInterface {
- /*
- * Binder的唯一标识符:
- * */
- static final String DESCRIPTOR = "com.ryg.design.manualbinder.IBank";
- /*
- * queryMoney方法的code标识:
- * */
- static final int TRANSACTION_queryMoney = (IBinder.FIRST_CALL_TRANSACTION + 0);
- /*
- * queryMoney方法声明:
- * */
- public long queryMoney(int uid) throws RemoteException;
- }
- package com.ryg.design.manualbinder;
- import android.os.Binder;
- import android.os.IBinder;
- import android.os.Parcel;
- import android.os.RemoteException;
- public class BankImpl extends Binder implements IBank {
- public BankImpl() {
- this.attachInterface(this, DESCRIPTOR);
- }
- /*
- * 如果在同一进程,则返回目标对象本身,
- * 如果在不同仅此,则返回代理类
- * */
- public static IBank asInterface(IBinder obj) {
- if ((obj == null)) {
- return null;
- }
- android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
- if (((iin != null) && (iin instanceof IBank))) {
- return ((IBank) iin);
- }
- return new BankImpl.Proxy(obj);
- }
- @Override
- public IBinder asBinder() {
- return this;
- }
- /*
- * 这个onTransact方法是在目标对象角色中重写的,
- * 在目标对象角色调用Transact方法时回调的!
- * */
- @Override
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- switch (code) {
- case INTERFACE_TRANSACTION: {
- reply.writeString(DESCRIPTOR);
- return true;
- }
- case TRANSACTION_queryMoney: {
- data.enforceInterface(DESCRIPTOR);
- int uid = data.readInt();
- long result = this.queryMoney(uid);
- reply.writeNoException();
- reply.writeLong(result);
- return true;
- }
- }
- return super.onTransact(code, data, reply, flags);
- }
- /*
- * 这是正儿八经的目标对象角色的queryMoney函数:
- * */
- @Override
- public long queryMoney(int uid) throws RemoteException {
- return uid * 10l;
- }
- /*
- * 内部代理类(代理对象角色)
- * */
- private static class Proxy implements IBank {
- /*
- * 代表目标对象角色:
- * */
- private IBinder mRemote;
- /*
- * 构造函数:
- * */
- Proxy(IBinder remote) {
- >// 接收目标对象角色:
- mRemote = remote;
- }
- /*
- * 返回目标对象角色:
- * */
- @Override
- public IBinder asBinder() {
- return mRemote;
- }
- /*
- * 返回Binder唯一标识符:
- * */
- public java.lang.String getInterfaceDescriptor() {
- return DESCRIPTOR;
- }
- /*
- * 这是代理类Proxy中的queryMoney方法:
- * */
- @Override
- public long queryMoney(int uid) throws RemoteException {
- /*
- * 先创建两个Parcel对象
- * */
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- long result;
- try {
- *
- * 在操作前向data中写入一些数据:
- * */
- data.writeInterfaceToken(DESCRIPTOR);
- data.writeInt(uid);
- /*
- * 这里执行的mRemote.transact其实是目标对象角色的transact函数。
- * 因为mRemote是IBinder对象,所以调用它的transact函数会回调它的onTransact方法,
- * 这个onTransact方法是在mRemote这个目标对象角色中重写了的,哈
- * 然后要根据TRANSACTION_queryMoney的code代码来执行相应的函数。
- * data负责传递信息,
- * reply负责回收信息。
- * */
- mRemote.transact(TRANSACTION_queryMoney, data, reply, 0);
- /*
- * 这里是返回的数据。
- * */
- reply.readException();
- result = reply.readLong();
- } finally {
- reply.recycle();
- data.recycle();
- }
- return result;
- }
- }
- }
ok,到此为止,我们的Binder就完成了,这里只要创建服务端和客户端,二者就能通过我们的Binder来通信了。这里就不做这个示例了,我们的目的是分析代理模式在Binder中的使用。
我们看上述Binder的实现中,有一个叫做“Proxy”的类,它的构造方法如下:
- Proxy(IBinder remote) {
- mRemote = remote;
- }
3、我们通过一个案例来分析Binder工作原理
- package com.ryg.chapter_2.aidl;
- import android.os.Parcel;
- import android.os.Parcelable;
- /*
- * (1)它是一个表示图示信息的类,
- * 它实现了Parcelable接口,因为实现了Parcelable接口便可以进行序列化
- * (2)Book.aidl是Book类在ADIL中的声明。
- * (3)IBookManager.aidl是我们定义的一个接口,里面有两个方法:getBookList和addBook,
- * 其中getBookList用于从远程服务端获取图书列表,而addBook用于往图书列表中添加一本书,
- * 当然这两个方法主要是示例用,不一定要有实际意义。
- * (4)尽管Book类和IBookManager位于相同的包中,但是在IBookManager中仍然要导入Book类,
- * 这就是AIDL的特殊之处。
- * */
- public class Book implements Parcelable {
- <span style="white-space:pre"> </span>public int bookId;
- public String bookName;
- /*
- * 普通构造函数:
- * */
- public Book() {
- <span style="white-space:pre"> </span>
- }
- /*
- * 普通构造函数:
- * */
- public Book(int bookId, String bookName) {
- this.bookId = bookId;
- this.bookName = bookName;
- }
- public int describeContents() {
- return 0;
- }
- /*
- * 序列化:
- * */
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(bookId);
- out.writeString(bookName);
- }
- /*
- * 反序列化,
- * 这个creator就是通过一个Parcle来创建一个book对象或者数组。
- * */
- public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
- public Book createFromParcel(Parcel in) {
- return new Book(in);
- }
- public Book[] newArray(int size) {
- return new Book[size];
- }
- };
- /*
- * 用于反序列化的构造函数:
- * */
- private Book(Parcel in) {
- bookId = in.readInt();
- bookName = in.readString();
- }
- @Override
- public String toString() {
- return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
- }
- }
- package com.ryg.chapter_2.aidl;
- parcelable Book;
- 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);
- }
- /*
- * This file is auto-generated. DO NOT MODIFY.
- */
- package com.ryg.chapter_2.aidl;
- /*
- * IBookManager它继承了IInterface这个接口,同时它自己也还是个接口,
- * 所有可以在Binder中传输的接口都要继承IInterface接口。
- * 首先,它声明了两个方法getBookList和addBook,显然这就是我们在IBookManager.aidl中所声明的方法,
- * 同时它还声明了两个整型的id分别用于标识这两个方法。
- * 接着,它声明了一个内部类Stub,这个Stub就是一个Binder类,
- * 当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程,
- * 而当两者位于不同进程时,方法调用需要走transact过程,
- * 这个逻辑由Stub的内部代理类Proxy来完成。
- * */
- public interface IBookManager extends android.os.IInterface
- {
- /** Local-side IPC implementation stub class. */
- /*
- * 首先这个Stub,它是一个内部类,它继承了Binder,所以它是一个Binder,
- * 同时Stub还实现了IBookManager中的方法。
- * */
- public static abstract class Stub extends android.os.Binder implements com.ryg.chapter_2.aidl.IBookManager
- {
- /*
- * Binder的唯一标识符。
- * */
- private static final java.lang.String DESCRIPTOR = "com.ryg.chapter_2.aidl.IBookManager";
- /** Construct the stub at attach it to the interface. */
- public Stub()
- {
- this.attachInterface(this, DESCRIPTOR);
- }
- /**
- * Cast an IBinder object into an com.ryg.chapter_2.aidl.IBookManager interface,
- * generating a proxy if needed.
- */
- /*
- * 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,
- * 这种转换过程是区分进程的,
- * 如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,
- * 否则返回的是系统封装后的Stub.proxy代理对象。
- * */
- public static com.ryg.chapter_2.aidl.IBookManager asInterface(android.os.IBinder obj)
- {
- if ((obj==null)) {
- return null;
- }
- android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
- // 同一进程
- if (((iin!=null)&&(iin instanceof com.ryg.chapter_2.aidl.IBookManager))) {
- return ((com.ryg.chapter_2.aidl.IBookManager)iin);
- }
- // 不同进程
- return new com.ryg.chapter_2.aidl.IBookManager.Stub.Proxy(obj);
- }
- /*
- * 此方法用于返回当前Binder对象,也就是内部类Stub。
- * */
- @Override public android.os.IBinder asBinder()
- {
- return this;
- }
- /*
- * 这个方法运行在服务端中的Binder线程池中,
- * 当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。
- * 服务端通过code可以确定客户端所请求的目标方法是什么,
- * 接着从data中取出目标方法所需的参数,
- * 然后执行目标方法。
- * 当目标方法执行完毕后,就向reply中写入返回值。
- * 如果此方法返回false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证。
- * */
- @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
- {
- switch (code)
- {
- case INTERFACE_TRANSACTION:
- {
- reply.writeString(DESCRIPTOR);
- return true;
- }
- case TRANSACTION_getBookList:
- {
- data.enforceInterface(DESCRIPTOR);
- /*
- * 这句才是调用了真正的执行过程呢
- * */
- java.util.List<com.ryg.chapter_2.aidl.Book> _result = this.getBookList();
- reply.writeNoException();
- reply.writeTypedList(_result);
- return true;
- }
- case TRANSACTION_addBook:
- {
- data.enforceInterface(DESCRIPTOR);
- com.ryg.chapter_2.aidl.Book _arg0;
- if ((0!=data.readInt())) {
- _arg0 = com.ryg.chapter_2.aidl.Book.CREATOR.createFromParcel(data);
- }
- else {
- _arg0 = null;
- }
- /*
- * 这句才是调用了真正的执行过程呢
- * */
- this.addBook(_arg0);
- reply.writeNoException();
- return true;
- }
- case TRANSACTION_registerListener:
- {
- data.enforceInterface(DESCRIPTOR);
- com.ryg.chapter_2.aidl.IOnNewBookArrivedListener _arg0;
- _arg0 = com.ryg.chapter_2.aidl.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
- this.registerListener(_arg0);
- reply.writeNoException();
- return true;
- }
- case TRANSACTION_unregisterListener:
- {
- data.enforceInterface(DESCRIPTOR);
- com.ryg.chapter_2.aidl.IOnNewBookArrivedListener _arg0;
- _arg0 = com.ryg.chapter_2.aidl.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
- this.unregisterListener(_arg0);
- reply.writeNoException();
- return true;
- }
- }
- return super.onTransact(code, data, reply, flags);
- }
- /*
- * 代理类Proxy。
- * */
- private static class Proxy implements com.ryg.chapter_2.aidl.IBookManager
- {
- /*
- * 这个mRemote代表的就是目标对象角色,
- * */
- 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;
- }
- /*
- * 这个方法运行在客户端,
- * 因为当客户端和服务端不在同一进程时,服务端返回代理类Proxy,所以客户端会通过Proxy调用到代理类的getBookList方法,
- * 当客户端远程调用此方法时,它的内部实现是这样的:
- * 首先创建该方法所需要的输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象List,
- * 然后把该方法的参数信息写入_data中,
- * 接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起,
- * 然后服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,
- * 并从_reply中取出RPC过程的返回结果。
- * 最后返回_reply中的数据。
- * */
- @Override public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList() throws android.os.RemoteException
- {
- android.os.Parcel _data = android.os.Parcel.obtain();
- android.os.Parcel _reply = android.os.Parcel.obtain();
- java.util.List<com.ryg.chapter_2.aidl.Book> _result;
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
- _reply.readException();
- _result = _reply.createTypedArrayList(com.ryg.chapter_2.aidl.Book.CREATOR);
- }
- finally {
- _reply.recycle();
- _data.recycle();
- }
- return _result;
- }
- @Override public void addBook(com.ryg.chapter_2.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();
- }
- }
- @Override public void registerListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException
- {
- android.os.Parcel _data = android.os.Parcel.obtain();
- android.os.Parcel _reply = android.os.Parcel.obtain();
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
- mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
- _reply.readException();
- }
- finally {
- _reply.recycle();
- _data.recycle();
- }
- }
- @Override public void unregisterListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException
- {
- android.os.Parcel _data = android.os.Parcel.obtain();
- android.os.Parcel _reply = android.os.Parcel.obtain();
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
- mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);
- _reply.readException();
- }
- finally {
- _reply.recycle();
- _data.recycle();
- }
- }
- }
- /*
- * 用于标识方法的整型id。
- * 它们用于在transact过程总客户端所请求的到底是哪个方法。
- * */
- static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
- static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
- static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
- static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
- }
- /*
- * 声明了在IBookManager.aidl中所声明的方法。
- * 这里才是真正的方法声明。具体实现我们仍然没有看到呢。
- * */
- public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList() throws android.os.RemoteException;
- public void addBook(com.ryg.chapter_2.aidl.Book book) throws android.os.RemoteException;
- public void registerListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
- public void unregisterListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
- }
这次应该完全懂了吧,再不懂去屎吧!
4、linkToDeath和unlinkToDeath
Binder运行在服务端进程,如果服务端进程由于某些原因异常终止,这个时候我们到服务端的Binder连接断裂,会导致我们的远程调用失败。Binder提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath我们可以给Binder设置一个死亡代理,当Binder死亡时,我们会收到通知,这个时候我们就可以重新发起连接请求从而恢复连接。
- /*
- * 声明这个接口就好:
- * */
- private IBinder.DeathRecipient mDeathRecipient = new IBinder.DearhRecipient(){
- // 只需要重写这个方法就可以了。
- @Override
- public void binderDied(){
- if(mBookManager == null)
- return;
- mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
- mBookManager = null;
- // TODO: 这里重新绑定远程Service。
- }
- }
在客户端绑定远程服务之后,给Binder设置死亡代理:
- mService = IMessageBoxManager.Stub.asInterface(binder);
- binder.linkToDeath(mDeathRecipient, 0);
五、使用Messenger
1、特点:
(2)通过它可以在不同进程中传递Message对象,在Message中仿佛我们需要传递的数据,就可以轻松地实现数据的进程间传递了。
(3)有两个构造函数,分别接收Handler对象和IBinder对象。
2、 实现一个Messenger有如下步骤:
首先需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并以它作为参数来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。关键点就在于它的返回是返回给了要绑定这个服务端的客户端,然后客户端拿到这个Binder再去创建Messenger,再去发送Message等等。
(2)客户端进程:
客户端进程中,首先要绑定服务端的Service,绑定后服务端的onBind会返回一个Binder对象,然后客户端用服务端返回的这个Binder对象创建一个Messenger,通过这个Messenger就可以向服务器端发送消息了,发送消息类型为Message对象,如果需要服务端能够回应客户端,就像和服务端一个,我们还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象在第一次客户端像服务端发送消息时通过Message的replyTo参数传递给服务端,服务端通过读取Message中的replyTo参数就是服务端给客户端的的Messenger,然后就可以回应客户端。
(3)注意点:
客户端给服务端发送消息的时候所用的Messenger是通过绑定服务端,然后依据onBind返回的Binder对象为参数来创建Messenger,而服务端在回应客户端的时候所用的Messenger是客户端在刚刚发送消息的时候将自身创建的Messenger作为刚刚发送消息的Message的replyTo参数传递给服务端的,所以在服务端直接读取出这个Messenger。
3、举例:客户端像服务端发送消息,服务端回应客户端
- 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;
- /*
- * 首先,这是一个服务。
- * 其次,这个服务是需要注册的,并且要给它另起一个进程。
- * <service
- android:name=".messenger.MessengerService"
- android:process=":remote" >
- <intent-filter>
- <action android:name="com.ryg.MessengerService.launch" />
- </intent-filter>
- </service>
- * */
- public class MessengerService extends Service {
- private static final String TAG = "MessengerService";
- /*
- * 继承Handler,
- * MessengerHandler用来处理客户端发送的消息,
- * 并从消息中取出客户端发来的文本信息。
- * */
- private static class MessengerHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- /*
- * MyConstants是我们这个应用中的一个类,其中包含了几个变量的声明:
- * public static final int MSG_FROM_CLIENT = 0;
- public static final int MSG_FROM_SERVICE = 1;
- * */
- case MyConstants.MSG_FROM_CLIENT:
- /*
- * 这一条语句是在处理从客户端发来的消息,用Log日志打印出来:
- * */
- Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
- /*
- * 这下面的语句是用来响应客户端,给客户端回馈消息的。
- * (1)第一步是通过replyTo来获取客户端的Messenger对象。
- * (2)第二步是创建一个Message消息,
- * Message.obtain这个方法的第一个参数是Handler,第二个参数是消息的what字段。
- * (3)第三步创建一个Bundle对象,然后向这个对象中添加String内容。
- * (4)第四步是将Bundle对象设置给Message。
- * (5)第五步是通过Messenger将Message发送出去,
- * 因为我们的Messenger是通过客户端来获取的,而在客户端那边这个Messenger是以Handler为参数创建的,
- * 所以在服务端通过客户端的Messenger发送消息后,在客户端的Handler就会处理这条消息,嘻嘻,就达到了消息传送的目的。
- * */
- 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);
- }
- }
- }
- /*
- * 这是我们服务端自己的Messenger,它是以上面的Handler对象为参数创建的,
- * 这个Messenger是要通过绑定该服务器的时候onBind方法传递给客户端,
- * 然后客户端获取了该Messenger,再以该Messenger来发送消息,
- * 这样服务端就可以接收到该消息并处理。
- * */
- private final Messenger mMessenger = new Messenger(new MessengerHandler());
- /*
- * 这个方法是在绑定服务的过程中调用的并将结果返回给客户端的,
- * 所以通过onBind方法客户端就可以获取我们Messenger的Binder对象了,
- * 然后客户端可以根据该Binder对象来创建一个Messenger,
- * 这样客户端中用的Messenger和这里的Messenger就是向对应的了。
- * */
- @Override
- 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";
- // 用来获取服务端的Messenger,用来给服务端传递消息用的。
- private Messenger mService;
- // 这是客户端自己的Messenger,传递给服务端,让服务端返回消息用的。
- private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
- /*
- * 这个Handler是用来处理服务端返回的消息的,
- * 这个Handler将作为一个参数来创建自己的Messenger,
- * 然后将这个Messenger传递给服务端,让服务端根据它返回消息。
- * */
- private static class MessengerHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MyConstants.MSG_FROM_SERVICE:
- // 处理消息,以Log日志显示出来。
- Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
- break;
- default:
- super.handleMessage(msg);
- }
- }
- }
- /*
- * 这个是客户端用来绑定服务端用的,
- * 在绑定过程中会调用onServiceConnected,
- * 它的第二个参数IBinder service,就是在服务端中onBind方法返回的结果,
- * 这个结果是服务端的Messenger对象的Binder对象,
- * 然后客户端通过这个Binder对象就可以创建一个Messenger,
- * 所以就是在绑定服务的过程中将服务端的Messenger传递给了客户端,建立起了两者之间的桥梁
- * */
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- /*
- * (1)第一步是根据服务端的IBinder service对象为参数创建Messenger。
- * (2)第二步是创建一个Message消息,其中第二个参数是msg的what字段。
- * 这里有个重要的点就是设置msg的replyTo字段,这个字段保存了客户端自己的Messenger,
- * 客户端将自己的Messenger传递给服务端,然后方便服务端根据这个Messenger将反馈消息用同样的方法传递回来。
- * (3)第三步是创建一个Bundle对象,这个对象中添加了要返回的消息内容。
- * (4)第四步将Bundle对象赋给Message。
- * (5)第五步用Messenger的send方法将消息发送出去。
- * */
- 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的跳转是需要服务端设置的:
- * <service
- android:name=".messenger.MessengerService"
- android:process=":remote" >
- <intent-filter>
- <action android:name="com.ryg.MessengerService.launch" />
- </intent-filter>
- </service>
- * */
- Intent intent = new Intent("com.ryg.MessengerService.launch");
- /*
- * 在bindService的时候,服务端会通过onBind方法返回一个包含了服务端业务调用的Binder对象,
- * 通过这个对象,客户端就可以获取服务端提供的服务或者数据,
- * 具体情况去下面的第二个参数mConnection中查看。
- * */
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
- @Override
- protected void onDestroy() {
- unbindService(mConnection);
- super.onDestroy();
- }
- }
(3)看完了是不是觉得很简单呀,嘿嘿。
六、使用AIDL
1、对比Messenger和AIDL:
2、AIDL使用的基本思想:
- package com.ryg.chapter_2.aidl;
- import android.os.Parcel;
- import android.os.Parcelable;
- /*
- * (1)它是一个表示图示信息的类,
- * 它实现了Parcelable接口,
- * (2)Book.aidl是Book类在ADIL中的声明。
- * (3)IBookManager.aidl是我们定义的一个接口,里面有两个方法:getBookList和addBook,
- * 其中getBookList用于从远程服务端获取图书列表,而addBook用于往图书列表中添加一本书,
- * 当然这两个方法主要是示例用,不一定要有实际意义。
- * (4)尽管Book类和IBookManager位于相同的包中,但是在IBookManager中仍然要导入Book类,
- * 这就是AIDL的特殊之处。
- * */
- public class Book implements Parcelable {
- public int bookId;
- public String bookName;
- public Book() {
- }
- public Book(int bookId, String bookName) {
- this.bookId = bookId;
- this.bookName = bookName;
- }
- public int describeContents() {
- return 0;
- }
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(bookId);
- out.writeString(bookName);
- }
- public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
- public Book createFromParcel(Parcel in) {
- return new Book(in);
- }
- public Book[] newArray(int size) {
- return new Book[size];
- }
- };
- private Book(Parcel in) {
- bookId = in.readInt();
- bookName = in.readString();
- }
- @Override
- public String toString() {
- return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
- }
- }
(1)服务端:
(2)客户端:
(3)AIDL接口的创建:
- package com.ryg.chapter_2.aidl;
- import com.ryg.chapter_2.aidl.Book;
- interface IBookManager {
- List<Book> getBookList();
- void addBook(in Book book);
- }
(其中自定义的Parcelable对象和AIDL对象必须要显示import进来,不管它们是否和当前的AIDL文件位于同一个包内)
- package com.ryg.chapter_2.aidl;
- parcelable Book;
(4)远程服务端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;
- /*
- * 这是一个服务端Service的典型实现。
- * 首先在onCreate中初始化添加两本图书,
- * 然后创建了一个Binder对象并在onBind方法中返回它。
- * */
- public class BookManagerService extends Service {
- private static final String TAG = "BMS";
- /*
- * 注意这里采用了CopyOnWriteArrayList,这个CopyOnWriteArrayList支持并发读/写。
- * 因为AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,
- * 会存在多个线程同时访问的情形,所以我们要在AIDL方法中处理线程同步,
- * 而我们这里直接使用CopyOnWriteArrayList来进行自动的线程同步。
- * */
- /*
- * 在AIDL中能够使用的List只有ArrayList,但是我们这里却使用了CopyOnWriteArrayList(它并不是继承子ArrayList的),
- * 但为什么还能工作呢?
- * 因为AIDL中所支持的是抽象List,而List只是一个接口,
- * 因此虽然服务端返回的是CopyOnWriteArrayList,
- * 但是Binder中会按照List的规范去访问数据并最终形成一个ArrayList传递给客户端。
- * 所以我们在服务端采用CopyOnWriteArrayList是完全可以的,
- * 和此类似的还有ConcurrentHashMap。
- * */
- private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
- /*
- * 创建一个Binder对象,并在onBind方法中返回它。
- * 这个Binder对象继承自IBookManager.Stub,并实现了它内部的AIDL方法,
- * 这里主要看getBookList和addBook这两个AIDL方法的实现,实现过程也比较简单,
- * */
- private Binder mBinder = new IBookManager.Stub() {
- @Override
- public List<Book> getBookList() throws RemoteException {
- return mBookList;
- }
- @Override
- public void addBook(Book book) throws RemoteException {
- mBookList.add(book);
- }
- };
- @Override
- public void onCreate() {
- super.onCreate();
- mBookList.add(new Book(1, "Android"));
- mBookList.add(new Book(2, "Ios"));
- }
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
- }
还需要注意的是,我们需要注册这个服务端,并让它运行在独立的进程中,这样它和客户端的Activity不在同一个进程中,这样就构成了进程间通信的场景:
- package com.ryg.chapter_2.aidl;
- import java.util.List;
- import com.ryg.chapter_2.R;
- import com.ryg.chapter_2.aidl.IBookManager;
- import com.ryg.chapter_2.utils.MyConstants;
- import android.annotation.SuppressLint;
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Message;
- import android.os.RemoteException;
- import android.util.Log;
- import android.view.View;
- import android.widget.Toast;
- public class BookManagerActivity extends Activity {
- private static final String TAG = "BookManagerActivity";
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- /*
- * 这里的实现方式和Messenger简直一样样的,
- * 都是在绑定服务端的过程中,服务端通过onBind方法将它的Binder传递过来,
- * 然后在客户端以这个传递来的Binder创建对应的对象
- * */
- IBookManager bookManager = IBookManager.Stub.asInterface(service);
- 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());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- public void onServiceDisconnected(ComponentName className) {
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_book_manager);
- Intent intent = new Intent(this, BookManagerService.class);
- /*
- * 绑定服务:
- * */
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
- @Override
- protected void onDestroy() {
- unbindService(mConnection);
- super.onDestroy();
- }
- }
3、AIDL使用过程中的一些问题,应用观察者模式
- 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);
- private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
- // private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
- // new CopyOnWriteArrayList<IOnNewBookArrivedListener>();
- /*
- * 用来保存申请了新书通知的用户。
- * (1)这里有一个注意点,RemoteCallbackList是系统专门提供的用于删除跨进程listener的接口。
- * RemoteCallbackList是一个泛型,支持管理任意的AIDL接口。
- * 它的内部有一个Map结构专门用来保存所有的AIDL回调,
- * 这个Map的key是IBinder类型,value是Callback类型,如下所示:
- * ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();
- * 其中Callback中封装了真正的远程listener。当客户端注册listener的时候,它会把这个listener的信息存入mCallbacks中,
- * 其中key和value分别通过下面的方式获得:
- * IBinder key = listener.asBinder();
- * Callback value = new Callback(listener, cookie);
- * (2)注意点二:也就是说,虽然多次跨进程传输客户端的同一个对象会在服务端生成不同的对象,
- * 但这些新生成的对象有一个共同点,那就是它们的底层Binder对象是同一个。也就是说key相同。
- * (3)注意点三:RemoteCallbackList在客户端进程终止后,能够自动移除客户端所注册的listener。
- * 另外RemoteCallbackList内部自动实现了线程同步的功能,
- * 所以我们使用它来注册和解注册时,不需要做额外的线程同步工作。
- * */
- private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();
- /*
- * 服务端的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 {
- <span style="white-space:pre"> </span>// 首先查看自定义权限com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE
- 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 {
- /*
- * 用RemoteCallbackList,key和value都是通过listener来获取的:
- * IBinder key = listener.asBinder();
- * Callback value = new Callback(listener, cookie);
- * 这个Binder是IOnNewBookArrivedListener这个aidl的Binder,
- * 和IBookManager这个aidl的binder不是同一个啦。
- * */
- mListenerList.register(listener);
- /*
- * RemoteCallbackList并不是一个List,
- * 遍历RemoteCallbackList必须要使用beginBroadcast和finishBroadcast来配对使用,
- * 哪怕只是为了获取RemoteCallbackList中的元素个数。
- * */
- 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.");
- }
- /*
- * RemoteCallbackList并不是一个List,
- * 遍历RemoteCallbackList必须要使用beginBroadcast和finishBroadcast来配对使用,
- * 哪怕只是为了获取RemoteCallbackList中的元素个数。
- * */
- 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) {
- /*
- * 我们可以在onBind方法中进行权限验证,验证不能通过就直接返回null。
- * 这种方法需要服务端在AndroidManifest中声明所需的权限:
- * <permission
- * android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE"
- * android:protectionLevel="normal" />
- * 在客户端AndroidManifest中这样声明才可以:
- * <uses-permission
- * android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE" />
- * */
- 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();
- }
- /*
- * 当有新书到来的时候,通知每一位用户:
- * 这里需要注意一下的是,当新书到达的时候,
- * 服务端会回调客户端的IOnNewBookArrivedListener对象中的onNewBookArrived方法,
- * 这个方法是在客户端的Binder线程池中执行的
- * */
- 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();
- }
- /*
- * 我们设定每隔5m添加一本新书:
- * */
- 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();
- }
- }
- }
- }
- }
- package com.ryg.chapter_2.aidl;
- import java.util.List;
- import com.ryg.chapter_2.R;
- import com.ryg.chapter_2.aidl.IBookManager;
- import com.ryg.chapter_2.utils.MyConstants;
- import android.annotation.SuppressLint;
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Message;
- import android.os.RemoteException;
- import android.util.Log;
- import android.view.View;
- import android.widget.Toast;
- public class BookManagerActivity extends Activity {
- private static final String TAG = "BookManagerActivity";
- private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
- private IBookManager mRemoteBookManager;
- /*
- * 当有新书到来的时候,服务端通知每一位用户:
- * 这里需要注意一下的是,当新书到达的时候,
- * 服务端会回调客户端的IOnNewBookArrivedListener对象中的onNewBookArrived方法,
- * 这个方法是在客户端的Binder线程池中执行的,
- * 因此为了便于进行UI操作,我们需要一个Handler可以将其切换到客户端的主线程中去执行。
- * */
- @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
- }
- };
- /*
- * 连接服务器,这个IBinder service就是服务器返回给我们的Binder对象。
- * */
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- /*
- * 如果客户端和服务端在同一进程,那么asInterface返回内部类Stub,
- * 否则返回内部类Stub的内部代理类Proxy:
- * */
- IBookManager bookManager = IBookManager.Stub.asInterface(service);
- mRemoteBookManager = bookManager;
- try {
- /*
- * 给Binder设置死亡代理:
- * */
- 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());
- 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() {
- /*
- * 当有新书到来的时候,服务端通知每一位用户:
- * 这里需要注意一下的是,当新书到达的时候,
- * 服务端会回调客户端的IOnNewBookArrivedListener对象中的onNewBookArrived方法,
- * 这个方法是在客户端的Binder线程池中执行的,
- * 因此为了便于进行UI操作,我们需要一个Handler可以将其切换到客户端的主线程中去执行。
- * */
- @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);
- }
- 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使用方法总结:
七、Binder连接池
1、问题:随着AIDL数量的增加,我们不能无限制的增加Service。
2、工作机制
3、举例说明吧
- package com.ryg.chapter_2.binderpool;
- interface ISecurityCenter {
- String encrypt(String content);
- String decrypt(String password);
- }
- package com.ryg.chapter_2.binderpool;
- interface ICompute {
- int add(int a, int b);
- }
- package com.ryg.chapter_2.binderpool;
- import android.os.RemoteException;
- 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);
- }
- }
- package com.ryg.chapter_2.binderpool;
- import android.os.RemoteException;
- public class ComputeImpl extends ICompute.Stub {
- @Override
- public int add(int a, int b) throws RemoteException {
- return a + b;
- }
- }
- 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);
- }
- @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;
- }
- package com.ryg.chapter_2.binderpool;
- import android.app.Service;
- import android.content.Intent;
- import android.os.Binder;
- import android.os.IBinder;
- import android.util.Log;
- public class BinderPoolService extends Service {
- private static final String TAG = "BinderPoolService";
- /*
- * 在服务端创建一个连接池,BinderPoolImpl是BinderPool的内部类,
- * 它继承了IBinderPool.Stub,并实现了queryBinder方法。
- * */
- 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();
- }
- }
- package com.ryg.chapter_2.binderpool;
- import java.util.concurrent.CountDownLatch;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Binder;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.util.Log;
- 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;
- private IBinderPool mBinderPool;
- private static volatile BinderPool sInstance;
- private CountDownLatch mConnectBinderPoolCountDownLatch;
- private BinderPool(Context context) {
- mContext = context.getApplicationContext();
- connectBinderPoolService();
- }
- /*
- * 返回BinderPool的实例,如果没有的话就创建,有的话就直接返回。
- * */
- public static BinderPool getInsance(Context context) {
- if (sInstance == null) {
- synchronized (BinderPool.class) {
- if (sInstance == null) {
- sInstance = new BinderPool(context);
- }
- }
- }
- return sInstance;
- }
- /*
- * 连接BinderPoolService服务器。
- * */
- private synchronized void connectBinderPoolService() {
- /*
- * mConnectBinderPoolCountDownLatch这个东西是干嘛的?
- * */
- 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.
- */
- /*
- * queryBinder,
- * */
- public IBinder queryBinder(int binderCode) {
- IBinder binder = null;
- try {
- /*
- * 这个mBinderPool是一个BinderPool.BinderPoolImpl对象。
- * 对于客户端来说调用的是BinderPool的queryBinder方法,
- * 而BinderPool的queryBinder方法又调用了BinderPool.BinderPoolImpl对象的queryBinder方法。
- * mBinderPool这个对象是服务端返回给BinderPool的,对客户端是隐藏的,
- * 客户端只知道BinderPool,
- * mBinderPool是服务端和连接池的桥梁,
- * BinderPool是客户端和连接池的桥梁
- * */
- if (mBinderPool != null) {
- binder = mBinderPool.queryBinder(binderCode);
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return binder;
- }
- /*
- * 连接服务器的时候用的,里面有连接成功和连接断开后的操作。
- * */
- private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // ignored.
- }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- /*
- * 将服务器端的Binder转换成客户端所需的AIDL接口对象:
- * 服务端返回的是BinderPool连接池,而不是单纯的一个Binder对象。
- * */
- 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();
- }
- };
- /*
- * (1)这个是我们的Binder连接池,它源于IBinderPool.aidl这个AIDL,它里面包含一个queryBinder方法,
- * 我们的Binder连接池是放在服务端用,
- * 所以在服务端需要有这样一个BinderPoolImpl的实例,并且它是一个Binder:
- * private Binder mBinderPool = new BinderPool.BinderPoolImpl();
- * (2)那怎么用呢?
- * 我们当前所在的类BinderPool.java就是用来绑定服务端的客户端,
- * 在BinderPool绑定服务端的时候,服务端会将mBinderPool返回给客户端也就是我们这个类,
- * 然后我们可以根据服务端返回的这个Binder来转换成客户端所需的AIDL接口对象,还是叫mBinderPool,
- * 然后我们这个类中就可以调用mBinderPool中的方法:
- * binder = mBinderPool.queryBinder(binderCode);
- * (3)那另外的两个AIDL呢?ICompute.aidl和ISecurityCenter.aidl呢?
- * 由于另外的两个AIDL的使用都是和服务端相关联的,是服务端的queryBinder方法将它们的Binder返回给客户端的,
- * 客户端接到这两个AIDL的Binder以后,依旧是通过转换成AIDL接口对象来使用这两个AIDL中的方法的。
- * */
- 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;
- }
- }
- }
- package com.ryg.chapter_2.binderpool;
- import com.ryg.chapter_2.R;
- import android.app.Activity;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.util.Log;
- /*
- * 这里是客户端
- * */
- public class BinderPoolActivity extends Activity {
- private static final String TAG = "BinderPoolActivity";
- 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() {
- // 首先获取一个BinderPool的实例:这里是带了上下文的,避免创建多个。
- BinderPool binderPool = BinderPool.getInsance(BinderPoolActivity.this);
- /*
- * 然后根据客户端编号bindercode查询Binder,返回的是对应的客户端的Binder。
- * 在binderPool.queryBinder中,是根据在绑定服务端过程中返回的BinderPoolImpl的Binder,
- * 这个BinderPoolImpl就是继承了IBinderPool的,所以也实现了其中的queryBinder的。
- * 这样返回的才是真正对应的securityBinder。
- * */
- IBinder securityBinder = binderPool
- .queryBinder(BinderPool.BINDER_SECURITY_CENTER);
- ;
- /*
- * 查到对应的Binder以后,就可以根据这个Binder来转换成客户端所需的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();
- }
- /*
- * 下面这是另一个AIDL模块,使用方法和上面是一样的。
- * */
- 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();
- }
- }
- }
八、使用Bundle
1、四大组件中的三大组件(Activity、Service、Receiver)都是支持在Intent中传递Bundle数据的
2、一个特殊的使用场景
九、使用ContentProvider
十、使用Socket
1、Socket套接字
2、使用Socket进行通信,首先需要声明权限
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
3、使用Socket进行通信,不能再主线程中访问网络
4、案例:跨进程的聊天程序
- package com.ryg.chapter_2.socket;
- import java.io.BufferedReader;
- import java.io.BufferedWriter;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.Random;
- import com.ryg.chapter_2.utils.MyUtils;
- import android.app.Service;
- import android.content.Intent;
- import android.os.IBinder;
- public class TCPServerService extends Service {
- private boolean mIsServiceDestoryed = false;
- private String[] mDefinedMessages = new String[] {
- "你好啊,哈哈",
- "请问你叫什么名字呀?",
- "巴拉巴拉",
- "巴拉巴拉小魔仙",
- "艹"
- };
- @Override
- public void onCreate() {
- <span style="white-space:pre"> </span>/*
- <span style="white-space:pre"> </span> * 开启一个线程,TcpServer是实现了Runnable的,
- <span style="white-space:pre"> </span> * 里面开启了服务端这边的Socket。
- <span style="white-space:pre"> </span> * 这里的TcpServer是继承自Runnable的。
- <span style="white-space:pre"> </span> * */
- new Thread(new TcpServer()).start();
- super.onCreate();
- }
- /*
- * 这次不需要绑定服务端。
- * */
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
- @Override
- public void onDestroy() {
- <span style="white-space:pre"> </span>/*
- <span style="white-space:pre"> </span> * 用来告诉Socket线程,我们的服务结束了。
- <span style="white-space:pre"> </span> * */
- mIsServiceDestoryed = true;
- super.onDestroy();
- }
- /*
- * 在线程里面开启Socket通信。
- * 对于服务端,就是开启一个Socket端口,等待客户端来发起连接请求:
- * */
- private class TcpServer implements Runnable {
- @SuppressWarnings("resource")
- @Override
- public void run() {
- <span style="white-space:pre"> </span>/*
- <span style="white-space:pre"> </span> * ServerSocket这种东西是系统自带的啦,直接拿来用就好了,
- <span style="white-space:pre"> </span> * 就这么容易服务端就开启了Socket等待客户端来连接:
- <span style="white-space:pre"> </span> * */
- ServerSocket serverSocket = null;
- try {
- <span style="white-space:pre"> </span>// 监听本地8688端口:
- serverSocket = new ServerSocket(8688);
- } catch (IOException e) {
- System.err.println("establish tcp server failed, port:8688");
- e.printStackTrace();
- return;
- }
- /*
- * 开启Socket以后,服务端只需要一直等待就好了。
- * */
- while (!mIsServiceDestoryed) {
- try {
- // 接收客户端请求,如果一直没有客户端,程序就会卡在这句,卡着!
- final Socket client = serverSocket.accept();
- System.out.println("accept");
- /*
- * 注意:不能在主线程中访问网络。
- * 一来是不允许的,
- * 二来放在主线程也会影响程序的响应效率。
- * 每来一个客户端连接就开启一个线程。
- * */
- new Thread() {
- @Override
- public void run() {
- try {
- <span style="white-space:pre"> </span>// 这个响应方法在下面定义的。
- responseClient(client);
- } catch (IOException e) {
- e.printStackTrace();
- }
- };
- }.start();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- /*
- * 用来相应客户端:
- * */
- private void responseClient(Socket client) throws IOException {
- // 用于接收客户端消息:
- <span style="white-space:pre"> </span>// BufferedReader用于接收:
- BufferedReader in = new BufferedReader(new InputStreamReader(
- client.getInputStream()));
- // 用于向客户端发送消息:
- // PrintWriter用于发送:
- PrintWriter out = new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(client.getOutputStream())), true);
- out.println("欢迎来到聊天室");
- /*
- * 当客户端与服务端的连接没有断开时,服务器就一直监听来自客户端的Socket:
- * */
- while (!mIsServiceDestoryed) {
- String str = in.readLine();
- System.out.println("msg from client:" + str);
- /*
- * 当客户端断开连接后,服务端这边的输入流in会接收到null,
- * 这个时候就要break退出了。
- * */
- 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();
- }
- }
- package com.ryg.chapter_2.socket;
- import java.io.*;
- import java.net.Socket;
- import java.sql.Date;
- import java.text.SimpleDateFormat;
- import com.ryg.chapter_2.R;
- import com.ryg.chapter_2.utils.MyUtils;
- import android.annotation.SuppressLint;
- import android.app.Activity;
- import android.content.Context;
- import android.content.Intent;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.os.SystemClock;
- import android.text.TextUtils;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.TextView;
- 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);
- /*
- * 先把服务端启动了:startService(service);
- * 然后在一个线程中去连接服务端的Socket:connectTCPServer();
- * */
- Intent service = new Intent(this, TCPServerService.class);
- startService(service);
- /*
- * 开启一个线程去连接服务端Socket:
- * */
- new Thread() {
- @Override
- public void run() {
- connectTCPServer();
- }
- }.start();
- }
- /*
- * 当Activity退出的时候,记得关闭和服务端的Socket连接:
- * */
- @Override
- protected void onDestroy() {
- if (mClientSocket != null) {
- try {
- mClientSocket.shutdownInput();
- mClientSocket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- super.onDestroy();
- }
- /*
- * 点击Button发送消息给服务端:
- * */
- @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));
- }
- /*
- * 连接服务端的Socket。
- * */
- 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);
- /*
- * 用一个Handler来进行和UI交互,因为我们的客户端是在线程中与服务端进行连接的:
- * */
- mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
- System.out.println("connect server success");
- } catch (IOException e) {
- /*
- * 为了降低重试机制的开销,我们加入了休眠机制,
- * 即每次重试的时间间隔为1000毫秒。
- * */
- SystemClock.sleep(1000);
- System.out.println("connect tcp server failed, retry...");
- }
- }
- try {
- // 用于接收服务器端的消息
- BufferedReader br = new BufferedReader(new InputStreamReader(
- socket.getInputStream()));
- // 不断循环的接收消息,当Activity退出时,循环也退出并终止线程:
- 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();
- }
- }
- }