Android中级——IPC

IPC

  • IPC是什么?
  • 多进程带来的问题
  • IPC前提
    • Serializable
    • Parcelable
    • Binder
  • Android中的IPC
    • Bundle
    • 文件共享
    • Messenger
    • AIDL
    • ContentProvider
    • Socket
    • 不同IPC优缺点
  • Binder连接池

IPC是什么?

Inter-Process Communcation,含义为进程间通信或者跨进程通信

Android中的多进程是指一个应用中存在多个进程的情况,默认进程名为包名,使用多进程需要给四大组件设置android:process属性




    
        
        
        
            
                

                
            
        
    


  • 以 : 开头的进程会附加包名,属于当前应用的私有进程,其他应用的组件不可以和它跑在同一进程
  • 不以 : 开头的进程为全局进程,其他应用可通过相同的ShareUID及签名和它跑在同一进程
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startActivity(new Intent(this, SecondActivity.class));
    }
}
public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        startActivity(new Intent(this, ThirdActivity.class));
    }
}
public class ThirdActivity extends AppCompatActivity {

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

对于如上的配置,启动3个Activity后,在AS中可看到有三个进程

在这里插入图片描述

多进程带来的问题

新建一个类UserManger

public class UserManager {
    public static int sUserId = 1;
}

在MainActivity中修改sUserId为2并打印

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        UserManager.sUserId = 2;
        Log.d(TAG, "onCreate: sUserId = " + UserManager.sUserId);
        startActivity(new Intent(this, SecondActivity.class));
    }
}

在SecondActivity再次打印

public class SecondActivity extends AppCompatActivity {

    private static final String TAG = "SecondActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Log.d(TAG, "onCreate: sUserId = " + UserManager.sUserId);
        startActivity(new Intent(this, ThirdActivity.class));
    }
}

结果却不同

在这里插入图片描述

不同进程的组件会拥有独立的虚拟机、Application和内存空间,会造成

  • 静态成员和单例模式完全失效
  • 线程同步机制完全失效
  • SharedPreferences的可靠性下降
  • Application会多次创建

IPC前提

Serializable

对于实现Serializable的类

public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private int userId;
    private String userName;
    private boolean isMale;

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

可通过ObjectOutputStream和ObjectInputStream实现序列化和反序列化,恢复后的对象内容完全一样但不是同一对象

  • 只有序列化后的数据中的serialVersionUID和当前类的serialVersionUID相同才能正常被反序列化
  • static和tranisient修饰的域不会参与序列化过程
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        User user = new User(0, "tom", true);
        try (ObjectOutputStream out = new ObjectOutputStream(openFileOutput("data", Context.MODE_PRIVATE))) {
            out.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (ObjectInputStream in = new ObjectInputStream(openFileInput("data"))) {
            User newUser = (User) in.readObject();
            Log.d(TAG, "onCreate: user == newUser " + (user == newUser));
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Parcelable

对于实现Parcelable的类可通过Intent和Binder传输

public class User implements Parcelable {

    private int userId;
    private String userName;
    private boolean isMale;

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

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(userId);
        out.writeString(userName);
        out.writeInt(isMale ? 1 : 0);
    }


    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    private User(Parcel in) {
        userId = in.readInt();
        userName = in.readString();
        isMale = in.readInt() == 1;
    }
}

各个方法功能如下

Android中级——IPC_第1张图片

Binder

以下通过AIDL分析Binder,创建Book.java

public class Book implements Parcelable {

    private int bookId;
    private String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel source) {
        bookId = source.readInt();
        bookName = source.readString();
    }
}

创建Book.aidl,声明Book

parcelable Book;

创建IBookManager.aidl,需要手动import

import com.demo.demo0.Book;

interface IBookManager {
   List getBookList();
   void addBook(in Book book);
}

在AS菜单栏的Build,点击Clean Project,再点击Rebuild Project,会生成IBookManager.java,位置如下图

Android中级——IPC_第2张图片

public interface IBookManager extends android.os.IInterface {
    /**
     * Default implementation for IBookManager.
     */
    public static class Default implements com.demo.demo0.IBookManager {
        @Override
        public java.util.List getBookList() throws android.os.RemoteException {
            return null;
        }

        @Override
        public void addBook(com.demo.demo0.Book book) throws android.os.RemoteException {
        }

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

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.demo.demo0.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.demo.demo0.IBookManager";

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

        /**
         * Cast an IBinder object into an com.demo.demo0.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.demo.demo0.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.demo.demo0.IBookManager))) {
                return ((com.demo.demo0.IBookManager) iin);
            }
            return new com.demo.demo0.IBookManager.Stub.Proxy(obj);
        }

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

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(descriptor);
                    java.util.List _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(descriptor);
                    com.demo.demo0.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.demo.demo0.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.demo.demo0.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 getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().getBookList();
                    }
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.demo.demo0.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.demo.demo0.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);
                    }
                    boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().addBook(book);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            public static com.demo.demo0.IBookManager sDefaultImpl;
        }

        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 static boolean setDefaultImpl(com.demo.demo0.IBookManager impl) {
            // Only one user of this interface can use this function
            // at a time. This is a heuristic to detect if two different
            // users in the same process use this function.
            if (Stub.Proxy.sDefaultImpl != null) {
                throw new IllegalStateException("setDefaultImpl() called twice");
            }
            if (impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static com.demo.demo0.IBookManager getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }

    public java.util.List getBookList() throws android.os.RemoteException;

    public void addBook(com.demo.demo0.Book book) throws android.os.RemoteException;
}
  • 所有在Binder中传输的接口都需要继承IInterface
  • DESCRIPTOR:唯一标识,用类名表示
  • asInterface():将服务端的Binder对象转换成客户端所需的AIDL接口对象,同一进程返回Stub,不同进程返回Stub.proxy
  • asBinder():返回当前Binder
  • onTransact():运行在服务端的Binder线程池中,处理跨进程请求,通过code判断目标方法,从data获取方法参数,向reply写入返回值
  • getBookList():客户端调用时,创建输入型Parcel对象_data(并写入参数)、输出型Parcel对象_reply和返回值对象List,接着调用transact()发起Remote Process Call并挂起线程。随后服务端的onTransact()被调用,直到结束后线程继续执行,并从reply获取返回结果
  • addBook():同上

Android中级——IPC_第3张图片

当Binder所在服务端进程异常终止时,会导致远程调用失败,通过linkToDeath()可为Binder设置一个死亡代理,当Binder死亡时回调binderDied()可再次重新发起请求

IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
    
    }
};
Binder binder = new Binder();
binder.linkToDeath(deathRecipient, 0);

或者在onServiceDisconnected()中重连远程服务,区别在于

  • onServiceDisconnected()在客户端的UI线程中被回调
  • binderDied()在客户端的Binder线程池中被回调,不能访问UI

Android中的IPC

Bundle

Activity、Service、Reciver都支持在Intent中传递Bundle数据,传输的数据必须能够被序列化

文件共享

Android中的文件可以支持并发读写,两个进程可通过读 / 写同一个文件来交换数据,但只适合对数据同步要求不高的进程之间进行通信

SharedPreference通过XML文件来存储键值对,但系统对其读写具有缓存策略,内存中只有一份SharedPreference文件的缓存,在多进程模式下会变得不可靠

Messenger

Messenger底层实现基于AIDL,一次处理一个请求,在服务端不用考虑线程同步

服务端需指定android:process,代码如下

  • 新建ServiceMessengerHandler处理客户端请求,并从replyTo获取clientMessenger返回响应数据
  • 新建ServiceMessenger在onBind()返回其Binder
public class MessengerService extends Service {

    public static final String TAG = "MessengerService";
    private final Messenger mServiceMessenger = new Messenger(new ServiceMessengerHandler());

    private static class ServiceMessengerHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            Log.d(TAG, "handleMessage: " + msg.getData().getString("client"));
            Messenger clientMessenger = msg.replyTo;
            Message serviceMsg = new Message();
            Bundle bundle = new Bundle();
            bundle.putString("service", "msg from service");
            serviceMsg.setData(bundle);
            try {
                clientMessenger.send(serviceMsg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    public MessengerService() {
    }

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

如下为客户端的代码

  • 新建ClientMessengerHandler处理服务端返回的数据
  • 新建ClientMessenger传给服务端,用于服务端传递数据
  • 在成功绑定服务时获取serviceMessenger发送数据
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private Messenger mClientMessenger = new Messenger(new ClientMessengerHandler());

    private static class ClientMessengerHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            Log.d(TAG, "handleMessage: " + msg.getData().getString("service"));
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Messenger serviceMessenger = new Messenger(service);
            Message clientMsg = new Message();
            Bundle data = new Bundle();
            data.putString("client", "msg from client");
            clientMsg.setData(data);
            clientMsg.replyTo = mClientMessenger;
            try {
                serviceMessenger.send(clientMsg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

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

运行程序,客户端和服务端都成功接收到数据

在这里插入图片描述

AIDL

Messenger只能串行处理客户端的请求,当需要并行处理客户端请求或需要调用服务端的方法时可以使用AIDL,AIDL支持的数据类型有

  • 基本数据类型,除此之外的参数需标上方向:in、out或inout
  • String和CharSequence
  • ArrayList,其元素都必须被AIDL所支持
  • HashMap,其元素都必须被AIDL所支持
  • 实现了Parcelable的对象,需新建同名aidl文件并进行声明,使用时需import
  • AIDL接口本身可以在AIDL文件中使用,使用时需import

需要注意,AIDL相关文件最好放在同一个包中,且包结构在服务端和客户端要保持一致,否则无法反序列化

创建Book.java,实现Parcelable

public class Book implements Parcelable {

    private int bookId;
    private String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel source) {
        bookId = source.readInt();
        bookName = source.readString();
    }
}

创建Book.aidl,声明Book

parcelable Book;

创建IOnNewBookArrivedListener.aidl,用于回调客户端

import com.demo.demo0.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book newBook);
}

创建IBookManager.aidl

import com.demo.demo0.Book;
import com.demo.demo0.IOnNewBookArrivedListener;

interface IBookManager {
   List getBookList();
   void addBook(in Book book);
   void registerListener(IOnNewBookArrivedListener listener);
   void unRegisterListener(IOnNewBookArrivedListener listener);
}

创建服务端的BookManagerService

public class BookManagerService extends Service {

    private static final String TAG = "BookManagerService";
    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
    private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>();
    private RemoteCallbackList mListenerList = new RemoteCallbackList<>();

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

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

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.register(listener);
            int N = mListenerList.beginBroadcast();
            Log.d(TAG, "registerListener: mListenerList size = " + N);
            mListenerList.finishBroadcast();
        }

        @Override
        public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.unregister(listener);
            int N = mListenerList.beginBroadcast();
            Log.d(TAG, "unRegisterListener: mListenerList size = " + N);
            mListenerList.finishBroadcast();
        }

        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            int check = checkCallingOrSelfPermission("com.demo.demo0.permission.ACCESS_BOOK_SERVICE");
            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }
            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            if (packageName != null && !packageName.startsWith("com.demo")) {
                return false;
            }
            return super.onTransact(code, data, reply, flags);
        }
    };

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

    @Override
    public IBinder onBind(Intent intent) {
        int check = checkCallingOrSelfPermission("com.demo.demo0.permission.ACCESS_BOOK_SERVICE");
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mBinder;
    }

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

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

    private class ServiceWorker implements Runnable {

        @Override
        public void run() {
            while (!mIsServiceDestroyed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int booId = mBookList.size() + 1;
                Book newBook = new Book(booId, "book" + booId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 使用CopyOnWriteArrayList支持并发读/写,Binder会按照List的规范去访问数据并返回一个ArrayList给客户端
  • 使用RemoteCallbackList存储客户端listener,内部实现了线程同步,客户端终止后还能自动移除,否则Binder会将客户端过来的对象反序化成新的对象,对象不一样无法unregister
  • 可以在onBiner()或onTransact()中校验权限
  • 内部创建ServiceWorker每5秒添加数据并回调客户端

创建客户端MainActivity

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
    private IBookManager mIBookManager;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            Log.d(TAG, "handleMessage: receive new book: " + msg.obj);
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIBookManager = IBookManager.Stub.asInterface(service);
            try {
                List bookList = mIBookManager.getBookList();
                Log.d(TAG, "onServiceConnected: bookList type = " + bookList.getClass().getCanonicalName());
                Log.d(TAG, "onServiceConnected: bookList = " + bookList.toString());
                mIBookManager.registerListener(mIOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mIBookManager = null;
            Log.d(TAG, "onServiceDisconnected: ");
        }
    };

    private IOnNewBookArrivedListener mIOnNewBookArrivedListener = 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_main);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mIBookManager != null && mIBookManager.asBinder().isBinderAlive()) {
            try {
                mIBookManager.unRegisterListener(mIOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
    }
}

manifest文件如下,为BookManagerService设置不同进程,申明权限,和使用权限



    

    

    

        

        
            
                

                
            
        
    


ContentProvider

可以看Android基础——ContentProvider和contentResolver,不再赘述

Socket

分为流式套接字和用户数据报套接字,分别对应网络传输控制层中的TCP和UDP协议,使用需声明网络权限



服务端需指定android:process,代码如下

  • 监听8688端口,每当有客户端连接时,就生成一个Socket
  • 客户端断开连接时,获取的输入流为null,服务端也断开连接
  • 当收到客户端信息后随机回复
public class TCPServerService extends Service {

    private boolean mIsServiceDestroy = false;
    private String[] mDefinedMessages = new String[]{
            "随机回复1",
            "随机回复2",
            "随机回复3",
            "随机回复4",
            "随机回复5",
    };

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

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

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

    private class TcpServer implements Runnable {

        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(8688);
            } catch (IOException e) {
                System.err.println("fail to connect 8688");
                e.printStackTrace();
                return;
            }
            while (!mIsServiceDestroy) {
                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 {
        try (
                BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true)
        ) {
            out.println("welcome to chatBox");
            while (!mIsServiceDestroy) {
                String str = in.readLine();
                System.out.println("client send: " + str);
                if (str == null) {
                    System.out.println("client quit");
                    break;
                }
                int i = new Random().nextInt(mDefinedMessages.length);
                String msg = mDefinedMessages[i];
                out.println(msg);
            }
        }
    }
}

客户端,代码如下

  • 创建线程连接服务端Socket,因为不能在主线程中访问网络,若连接失败会再1s后重连
  • 服务端断开连接时,获取的输入流为null,客户端也断开连接
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
    private static final int MESSAGE_SOCKET_CONNECTED = 2;
    private static final String TAG = "MainActivity";

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

    private PrintWriter mPrintWriter;
    private Socket mClientSocket;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull 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;
            }
        }
    };


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

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

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.send:
                String msg = mMessageEditText.getText().toString();
                if (TextUtils.isEmpty(msg)) {
                    return;
                }
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        if (mPrintWriter != null) {
                            mPrintWriter.println(msg);
                        }
                    }
                }).start();
                mMessageEditText.setText("");
                String time = formatDateTime(System.currentTimeMillis());
                final String showMsg = "client " + time + ": " + msg + "\n";
                mMessageTextView.setText(mMessageTextView.getText() + showMsg);
                break;
        }

    }

    private String formatDateTime(long time) {
        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
    }

    private void connectTCPServer() {
        while (mClientSocket == null) {
            try {
                mClientSocket = new Socket("localhost", 8688);
                mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mClientSocket.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(mClientSocket.getInputStream()))) {
            while (!isFinishing()) {
                String msg = br.readLine();
                System.out.println("server send: " + msg);
                if (msg == null) {
                    System.out.println("server quit...");
                    break;
                }
                String time = formatDateTime(System.currentTimeMillis());
                String showMsg = "server " + time + ": " + msg + "\n";
                mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showMsg).sendToTarget();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

activity_main.xml代码如下,用于显示信息




    

    

        

        

通信过程如下

在这里插入图片描述

不同IPC优缺点

Android中级——IPC_第4张图片

Binder连接池

AIDL使用过程:

  • 新建一个类继承Stub并实现抽象方法
  • 新建一个Service在onBind()中返回该类的对象
  • 在客户端绑定Service

Service会在后台运行占据内存,不可能为每一个AIDL都新建一个Service,这时候就需要使用Binder连接池将Binder请求统一转发到Service

Android中级——IPC_第5张图片

创建ISecurityCenter.aidl

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

创建SecurityCenterImpl.java

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);
    }
}

创建ICompute.aidl

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

创建ComputeImpl.java

public class ComputeImpl extends ICompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }
}

创建IBinderSelector.aidl

interface IBinderSelector {
    IBinder queryBinder(int binderCode);
}

创建BinderSelectorImpl.java

  • 利用不同的binderCode返回不同的Binder
  • 当要添加新业务时,只需要新增.aidl文件和修改BinderSelectorImpl
public class BinderSelectorImpl extends IBinderSelector.Stub {

    public static final int BINDER_COMPUTE = 1;
    public static final int BINDER_SECURITY_CENTER = 2;

    public BinderSelectorImpl() {
    }

    @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;
        }
        return binder;
    }
}

创建BinderPoolService.java,返回BinderSelectorImpl,但并不是给客户端使用,而是给BinderPool绑定

public class BinderPoolService extends Service {

    private Binder mBinderPool = new BinderSelectorImpl();

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

创建BinderPool.java

  • 使用单例,初始化时绑定BinderPoolService,并实现断线重连
  • 通过CountDownLatch将异步的bindService转为同步,确保初始化时成功连上Service,才能调用queryBinder()
public class BinderPool {

    private static final String TAG = "BinderPool";

    private Context mContext;
    private IBinderSelector mBinderSelector;
    private static volatile BinderPool sInstance;
    private CountDownLatch mCountDownLatch;

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

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

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

    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        if (mBinderSelector != null) {
            try {
                binder = mBinderSelector.queryBinder(binderCode);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return binder;
    }

    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderSelector = IBinderSelector.Stub.asInterface(service);
            try {
                mBinderSelector.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

        private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
            @Override
            public void binderDied() {
                Log.d(TAG, "binderDied: ");
                mBinderSelector.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
                mBinderSelector = null;
                connectBinderPoolService();
            }
        };
    };
}

MainActivity代码如下

  • 使用时需利用线程,因为连接Service或调用Binder方式可能都是耗时的
  • 根据code获取Binder,转为对应的实现即可调用
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private ISecurityCenter mSecurityCenter;
    private ICompute mCompute;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                work();
            }
        }).start();
    }

    public void work() {
        BinderPool binderPool = BinderPool.getInstance(this);
        IBinder securityBinder = binderPool.queryBinder(BinderSelectorImpl.BINDER_SECURITY_CENTER);
        mSecurityCenter = SecurityCenterImpl.asInterface(securityBinder);
        String msg = "hello";
        try {
            String encryptMsg = mSecurityCenter.encrypt(msg);
            Log.d(TAG, "onCreate: encrypt = " + encryptMsg);
            Log.d(TAG, "onCreate: decrypt = " + mSecurityCenter.decrypt(encryptMsg));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        IBinder computeBinder = binderPool.queryBinder(BinderSelectorImpl.BINDER_COMPUTE);
        mCompute = ComputeImpl.asInterface(computeBinder);
        try {
            Log.d(TAG, "onCreate: add = " + mCompute.add(1, 2));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(#,Android中级,android)