Android中级——IPC

news2024/11/26 5:49:30

IPC

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

IPC是什么?

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

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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.demo.demo0">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".ThirdActivity"
            android:process="com.demo.demo0.remote" />
        <activity
            android:name=".SecondActivity"
            android:process=":remote" />
        <activity
            android:name=".MainActivity"
            android:process="">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
  • 以 : 开头的进程会附加包名,属于当前应用的私有进程,其他应用的组件不可以和它跑在同一进程
  • 不以 : 开头的进程为全局进程,其他应用可通过相同的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<User> CREATOR = new Parcelable.Creator<User>() {

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

各个方法功能如下

在这里插入图片描述

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<Book> CREATOR = new Parcelable.Creator<Book>() {

        @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<Book> getBookList();
   void addBook(in Book book);
}

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

在这里插入图片描述

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<com.demo.demo0.Book> 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<com.demo.demo0.Book> _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<com.demo.demo0.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.demo.demo0.Book> _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<com.demo.demo0.Book> 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():同上

在这里插入图片描述

当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<Book> CREATOR = new Parcelable.Creator<Book>() {

        @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<Book> 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<Book> mBookList = new CopyOnWriteArrayList<>();
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();

    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 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<Book> 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设置不同进程,申明权限,和使用权限

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.demo.demo0">

    <permission
        android:name="com.demo.demo0.permission.ACCESS_BOOK_SERVICE"
        android:protectionLevel="normal" />

    <uses-permission android:name="com.demo.demo0.permission.ACCESS_BOOK_SERVICE" />

    <application
        android:name=".Config"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

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

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

ContentProvider

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

Socket

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

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

服务端需指定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代码如下,用于显示信息

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/msg_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/msg_container"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/msg"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="8"
            android:hint="输入你要发送的内容" />

        <Button
            android:id="@+id/send"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:text="发送" />
    </LinearLayout>
</RelativeLayout>

通信过程如下

在这里插入图片描述

不同IPC优缺点

在这里插入图片描述

Binder连接池

AIDL使用过程:

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

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

在这里插入图片描述

创建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();
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/720705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NB-IoT学习笔记 —— NB-IoT介绍

一、简介 NB-IoT 是指窄带物联网&#xff08;Narrow Band Internet of Things&#xff09;技术&#xff0c;是一种低功耗广域&#xff08;LPWA&#xff09;网络技术标准&#xff0c;基于蜂窝技术&#xff0c;用于连接使用无线蜂窝网络的各种智能传感器和设备&#xff0c;聚焦于…

Vite+Vue3+Cesium工程搭建及初始化

现如今的前端更新太快了,我记得我一年前还在写 vue2+cesium 的工程构建方式。现在转眼都 vue3 了。而且 vite 也越来越火逐渐成为趋势。那么这篇文章我们就来介绍一下如何使用 vite 构建一个 vue3+cesium 的工程。 首先我们可以打开 vite 的官网学习如何构建 vite 项目。第一…

Nginx缓存代理服务器

Nginx缓存代理服务器 一、实验部署 二、搭建Nginx缓存代理服务器 1.nginx反向缓存代理服务配置 ###关闭和禁止防火墙开机自启功能 systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i s/enforcing/disabled/ /etc/selinux/config2.安装nginx服务 v…

基于SSM的人力资源管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

剑指offer12.矩阵中的路径

太难了&#xff0c;想了一会儿没有头绪就直接看了题解。 class Solution {public boolean exist(char[][] board, String word) {int clowns board.length;int rows board[0].length;boolean[][] visited new boolean[clowns][rows];for(int i 0;i<clowns;i){for(int j0…

【QT】——QWidget窗口类

1.QWidget基本概念 QWidget 类是所有窗口类的父类 (控件类是也属于窗口类),QWidget 类的父类的 QObject, 也就意味着所有的窗口类对象只要指定了父对象, 都可以实现内存资源的自动回收.可以内嵌到其他窗口的内部&#xff0c;没有边框,需要指定父类窗口可以作为独立的窗口显示&…

DBeaver(一款非常好用的数据库管理工具)大批量数据导入导出非常友好

DBeaver(一款非常好用的数据库管理工具)大批量数据导入导出非常友好 背景说明&#xff1a; 最近因工作需要进行大批量的数据导入导出&#xff0c;很多工具都很难完成千万级以上的单表数据导入导出工作。后来找到了DBeaver这款工具&#xff0c;可以将千万级数据进行分批写入或导…

安装部署mysql

1.二进制方式安装 (1)下载压缩包 (2)创建用户和组 (3)解压 &#xff08;4&#xff09;创建软链接 (5)初始化 &#xff08;6&#xff09; 2.使用yum方式安装 (1)用yum install wget unzip bash-completion tree vim lrzsz net-tools -y命令安装常用软件 (2)用yum install mysql…

【数据仓库】FineBI数据可视化使用体验

FineBI介绍 FineBI是新一代自助式BI工具,企业客户多,服务范围广.凭借finebi简单流畅的操作,强劲的大数据性能和自助式的分析体验。 1&#xff0c;对个人用户来说&#xff0c;免费的无限期试用&#xff0c;解锁所有功能&#xff0c;除了限制两个并发访问&#xff0c;个人用户可以…

vue门户系统实现顶部菜单下拉效果

门户系统实现顶部菜单下拉效果 组件封装 直接上代码&#xff0c;已经封装成组件&#xff0c;只要传输正确的数据格式即可使用。 <template> <div class"head-v3" id"index"><div class"navigation-up"><div class"…

Java SSM 重制版(二)SpringMvc

SpringMVC基础 进入之前&#xff1a;《Spring核心内容》《JavaWeb》《JDK9-17新特性篇》 在前面学习完Spring框架技术之后&#xff0c;差不多会出现两批人&#xff1a;一批是听得云里雾里&#xff0c;依然不明白这个东西是干嘛的&#xff1b;还有一批就是差不多理解了核心思想…

大的zip文件使用7-zip软件分卷压缩和还原

使用7-zip软件进行分卷压缩和还原 1、7-zip软件的安装 官网&#xff1a;https://www.7-zip.org。 2、将比较大的zip文件拆分 设置压缩格式zip,分卷最大150M 拆分后 3、还原 还原后&#xff0c;又重新生成了原zip文件

qt各控件总结

qt控件又称组件或者部件&#xff0c;指用户看到的所有可视化界面以及界面中的各个元素&#xff0c;比如按钮&#xff0c;文本框&#xff0c;输入框等。1.QMainWindow类生成的窗口自带菜单栏&#xff0c;工具栏和状态栏&#xff0c;中央区域还可添加多个控件&#xff0c;常用来作…

Python_关于python2的encode(编码)和decode(解码)的使用

目录 python执行过程的编解码 encode&decode 中文乱码解决方法 常见问题 格式化带有中文的字符串报错 带有中文的字符串反转后乱码 在使用Python2时&#xff0c;我们习惯于在文件开头声明编码 # coding: utf-8 不然在文件中出现中文&#xff0c;运行时就会报错 Syn…

查看和指定GPU服务器显卡训练模型

查看和指定GPU服务器显卡 1.查看显卡2.间隔查看GPU使用情况3.查看当前显卡信息4. 使用os指定使用的显卡 1.查看显卡 nvidia-smiGPU&#xff1a;GPU 编号&#xff1b;与实际编号不一定一致 Name&#xff1a;GPU 型号&#xff1b; Persistence-M&#xff1a;持续模式的状态。持续…

【TEVC 2023】用于进化计算的知识学习 + 进化计算(Evolutionary computation (EC) )其中的一些概念

Knowledge Learning for Evolutionary Computation 进化计算&#xff08;Evolutionary computation (EC) &#xff09;是一种从自然进化和群体智能行为&#xff08;swarm intelligence behaviors&#xff09;中汲取灵感的元启发式算法。 目前&#xff0c;EC以其解决优化问题的…

《Redis 核心技术与实战》课程学习笔记(二)

数据结构&#xff1a;快速的 Redis 有哪些慢操作 数据库这么多&#xff0c;为啥 Redis 能有这么突出的表现呢&#xff1f; 一方面&#xff0c;因为它是内存数据库&#xff0c;所有操作都在内存上完成&#xff0c;内存的访问速度本身就很快。另一方面&#xff0c;因为&#xff…

SQL入门教程(非常详细)从零基础入门到精通,看完这一篇就够了

导读&#xff1a; SQL语言有40多年的历史&#xff0c;从它被应用至今几乎无处不在。我们消费的每一笔支付记录&#xff0c;收集的每一条用户信息&#xff0c;发出去的每一条消息&#xff0c;都会使用数据库或与其相关的产品来存储&#xff0c;而操纵数据库的语言正是 SQL &…

vue页面中一个小列表中多选框的选中状态的两种设置方法

第一种方法:所有类型都是固定的、后台提供了选中状态的接口(页面进入时默认展示所有类型和类型的选中状态 思路: 1、列出所有类型同时与后台规定好每种类型的id与对应的名称 2、在mounted中执行获取后台给定的选中状态(包含1个或多个的id数组) 3、将得到的结构绑定到el-ch…

单元测试基础

一、什么是单元测试&#xff1a; 单元测试是指&#xff0c;对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作&#xff0c;这里的最小可测试单元通常是指函数或者类&#xff1b;单元测试属于最严格的软件测试手段&#xff0c;是最接近代码底层实现…