「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」
多进程
定义:进程指一个执行单元,在移动设备上就是一个程序或应用,我们所说的多进程一般是指一个应用包含多个进程。
使用多进程的原因:某些模块由于特殊的需求要在单独的进程中,增加应用的可用内存控件。
开启多进程
开启多进程方式只有一种,在AndroidManifest.xml中注册Service、Activity、Provider、Receiver时指定 android:process 属性,例如:
<service android:name=".BleService"
android:process=":remote"/>
<activity android:name=".DeviceActivity"
android:process="com.king.app.remote2"/>
进程名可使用缩写开头:remote,也可以使用完整命名com.king.app.remote2
多进程引发的问题
- 1.静态成员和单例模式失效
- 2.线程同步机制失效
- 3.SharedPreferences可靠性降低
- 4.Application被多次创建
前两个问题:系统为每个进程、应用分配独立的虚拟机,不同的虚拟机占有不同的内存地址,所以同一个类会产生不同的副本,导致共享数据失败,必然也不能实现线程同步。
SharedPreference底层使用XML文件的读写来实现,多进程并发的读写很可能导致数据异常。
系统分配多个虚拟机时相当于把同一个应用重新启动多次,必然会导致Application多次创建。
多进程通信方式
名称 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Bundle | 简单易用 | 只能传输Bundle支持的数据类型 | 四大组件间的进程间通信 |
文件共享 | 简单易用 | 不适合高并发场景,并且无法做到进程间的即时通信 | 无并发访问情形,交换简单的数据实时性不高的场景 |
AIDL | 功能强大,支持一对多并发通信,支持实时通信 | 使用稍复杂,需要处理好线程同步 | 一对多通信且有RPC需求 |
Messenger | 功能一般,支持一对多的串行通信,支持实时通信 | 不能很好的处理高并发情形,不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型 | 低并发的一对多即时通信,无RPC需求,或者无需返回结果的RPC需求 |
Contentprovider | 在数据访问方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作 | 可理解为受约束的AIDL,主要提供数据的CRUD操作 | 一对多的进程间数据共享 |
Socket | 可以通过网络传输字节流,支持一对多并发实时通信 | 不支持RPC | 网络数据交换 |
RPC:远程过程调用。
Bundle
四大组件直接可以使用Intent.putExtras(Bundle bunle)方法来传递数据
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
Bundle bundle = new Bundle();
bundle.putString("msg", "This is a message");
intent.putExtras(bundle);
startActivity(intent);
文件共享
使用Serializable序列化对象,使用ObjectOutputSteam将数据写入到文件中
NoteBook noteBook = new NoteBook();
noteBook.setName("cr");
noteBook.setContent("Empty");
File file = new File("xxx");
ObjectOutputStream outputStream = null;
try{
outputStream = new ObjectOutputStream(new FileOutputStream(file));
outputStream.writeObject(noteBook);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
NoteBook.java
public class NoteBook implements Serializable {
private String mName;
private String mContent;
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
public String getContent() {
return mContent;
}
public void setContent(String content) {
mContent = content;
}
}
使用ObjectInputStream读取文件中的数据
File file = new File("xxx");
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream(file));
NoteBook noteBook = (NoteBook) objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
AIDL
支持的数据类型
- 基本数据类型
- String、CharSequence
- ArrayList、HashMap(其内部元素也需要被AIDL支持)
- 实现了Parcelable接口的对象
- AIDL类型的接口,非普通接口
实现:
1.定义暴露给客户端的AIDL接口
2.实现服务端服务,构造Binder对象
3.客户端bind服务,并通过AIDL的代理对象的asInterface获取服务端AIDL接口对象
例:
定义服务端暴露给客户端的AIDL接口
1.定义Book类,实现Parcelable接口
Book.java位于com.pass.animationtest.Bean包下
package com.pass.animationtest.Bean;
import android.os.Parcel;
import android.os.Parcelable;
/**
* @author houki
* @since 2021-08-12
*/
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
}
2.定义Book.aidl
AIDL无法直接使用普通类中的对象,需要使用相同名字的引用,
Book.aidl位于com.pass.animationtest.Bean包下
// Book.aidl
package com.pass.animationtest.Bean;
parcelable Book;
3.定义暴露给客户端的AIDL接口
使用到Book类或其他AIDL接口时,需要使用import引入
// ILibraryManager.aidl
package com.pass.animationtest;
// Declare any non-default types here with import statements
import com.pass.animationtest.Bean.Book;
import com.pass.animationtest.IBookListener;
interface ILibraryManager {
List<Book> getBookList();
void addBook(in Book book);
void register(IBookListener listener);
void unregister(IBookListener listener);
}
实现服务端服务
主要目的是构造出Binder对象,定义Binder类型成员变量,实现ILibraryManager.Stub
public class RemoteService extends Service {
private final static String TAG = "RemoteService";
//暂不考虑原子问题
private boolean mIsStop = false;
// 系统提供的专门用于保存、删除跨进程 listener 的类
private final RemoteCallbackList<IBookListener> mListenerList = new RemoteCallbackList<>();
private final List<Book> mBookList = new ArrayList<>();
private final Binder binder = new ILibraryManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
Log.e(TAG, "获取图书列表");
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
Log.e(TAG, "I get a new Book " + book.bookName);
mBookList.add(book);
}
@Override
public void register(IBookListener listener) throws RemoteException {
Log.e(TAG, "注册通知");
mListenerList.register(listener);
}
@Override
public void unregister(IBookListener listener) throws RemoteException {
Log.e(TAG, "注销通知");
mListenerList.unregister(listener);
}
};
@Override
public void onCreate() {
mBookList.add(new Book(1024, "金瓶梅1"));
mBookList.add(new Book(1025, "金瓶梅2"));
mBookList.add(new Book(1026, "金瓶梅3"));
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
startBuy();
return binder;
}
@Override
public void onDestroy() {
mIsStop = true;
super.onDestroy();
}
private void startBuy() {
new Thread(new Runnable() {
@Override
public void run() {
while (!mIsStop) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Book newBook = new Book(new Random().nextInt(10000), "新书");
mBookList.add(newBook);
notifyBook(newBook);
}
}
}).start();
}
private void notifyBook(Book book) {
int n = mListenerList.beginBroadcast();
for (int i = 0; i < n; i++) {
IBookListener listener = mListenerList.getBroadcastItem(i);
try {
listener.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mListenerList.finishBroadcast();
}
}
客户端实现
使用ILibraryManager代理的asInterface方法获取AIDL的接口对象
private ILibraryManager iLibraryManager;
private final ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iLibraryManager = ILibraryManager.Stub.asInterface(service);
try {
iLibraryManager.register(mListener);
List<Book> bookList = iLibraryManager.getBookList();
for (Book book : bookList) {
Log.e(TAG, book.bookId + " - " + book.bookName);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
try {
iLibraryManager.unregister(mListener);
} catch (RemoteException e) {
e.printStackTrace();
}
iLibraryManager = null;
}
};
//回调,线程为Binder线程,若需更新UI,使用Handler处理
private IBookListener mListener = new IBookListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
Log.e(TAG, "新书已到:" + book.bookId + " - " + book.bookName);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//启动远程服务端Service
bindService(new Intent(this, RemoteService.class),
mConnection,
Context.BIND_AUTO_CREATE);
//添加图书
try {
iLibraryManager.addBook(new Book(2048, "玉蒲团1"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
重新连接
创建DeatchRecipient对象
private IBinder.DeathRecipient mAidlDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (iLibraryManager != null) {
iLibraryManager.asBinder().unlinkToDeath(mAidlDeathRecipient, 0);
iLibraryManager = null;
//重新连接 bindService
}
}
};
在客户端得到Binder对象时,注册一个到DeatchRecipient监听中,在服务进程终止时,可以重新bindService
iLibraryManager = ILibraryManager.Stub.asInterface(service);
try {
iLibraryManager.asBinder().linkToDeath(mAidlDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
Messenger
Messenger是一种轻量级的多进程通信方式,它是在AIDL基础上封装而成的,可以看做AIDL的简化版,支持一对多的串行试试通信,一次只处理一个请求,不存在并发的问题,和AIDL使用方式类似,但是简单的多,同样需要C/S
定义Service
初始化Messenger时,传入Handler,并在handleMessage中接收和处理消息;
public class RemoteService2 extends Service {
private final static String TAG = "RemoteService2";
private Messenger mRemoteMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mRemoteMessenger.getBinder();
}
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
Log.e(TAG, "Messenger get message " + bundle.getString("msg"));
Message replyMsg = Message.obtain();
replyMsg.what = 1025;
Bundle replyBundle = new Bundle();
replyBundle.putString("msg", "I am remote service");
replyMsg.setData(replyBundle);
try {
msg.replyTo.send(replyMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
定义客户端
//服务端Messenger,通过Binder获取
private Messenger mServiceMessenger;
private ServiceConnection mMessengerConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServiceMessenger = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mServiceMessenger = null;
}
};
//客户端Messenger,用于接收消息
private Messenger mClientMessenger = new Messenger(new MessengerHandler());
//客户端发送消息,消息内容使用Bundle作为载体,不能直接使用obj
Message msg = Message.obtain();
msg.what = 1024;
//msg.obj = "Hi, I am Main.";
Bundle bundle = new Bundle();
bundle.putString("msg", "Hi, I am Main.");
msg.setData(bundle);
msg.replyTo = mClientMessenger;//回复消息
try {
mServiceMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
ContentProvider
Uri统一资源标识符
唯一标识ContentProvider其中的数据,外界的进程、应用可以通过Uri找到对应的ContentProvider
具体划分:
content://com.king.long/Book?name=110
content:scheme,URI前缀
com.king.long:授权信息AUTHORITY,唯一标识
book:path
name=110:query
MIME
指定某个扩展名的文件使用哪种应用程序来打开
ContentProvider更加URI返回MIME类型
ConentProvider.getType(uri)
MIME组成:类型+子类型
如:text/html、text/css
单条记录:vnd.android.cursor.item/自定义
多条记录:vnd.android.cursor.dir/自定义
ContentProvider类
本质是:添加、删除、获取 & 修改(更新)数据
ContentResolver类
统一管理ContentProvider
具体使用
// 使用ContentResolver前,需要先获取ContentResolver
// 可通过在所有继承Context的类中 通过调用getContentResolver()来获得ContentResolver
ContentResolver resolver = getContentResolver();
// 设置ContentProvider的URI
Uri uri = Uri.parse("content://cn.scu.myprovider/user");
// 根据URI 操作 ContentProvider中的数据
// 此处是获取ContentProvider中 user表的所有记录
Cursor cursor = resolver.query(uri, null, null, null, "userid desc");
Android提供了3个辅助类
- ContentUris
- UriMatch
- ContentObserver
ContentUris
// withAppendedId()作用:向URI追加一个id
Uri uri = Uri.parse("content://cn.scu.myprovider/user")
Uri resultUri = ContentUris.withAppendedId(uri, 7);
// 最终生成后的Uri为:content://cn.scu.myprovider/user/7
// parseId()作用:从URL中获取ID
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7")
long personid = ContentUris.parseId(uri);
//获取的结果为:7
UriMatcher
- 在ContentProvider中注册URI
- 根据URI匹配ContentProvider中对应方法
// 步骤1:初始化UriMatcher对象
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
//常量UriMatcher.NO_MATCH = 不匹配任何路径的返回码
// 即初始化时不匹配任何东西
// 步骤2:在ContentProvider 中注册URI(addURI())
int URI_CODE_a = 1;
int URI_CODE_b = 2;
matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a);
matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b);
// 若URI资源路径 = content://cn.scu.myprovider/user1 ,则返回注册码URI_CODE_a
// 若URI资源路径 = content://cn.scu.myprovider/user2 ,则返回注册码URI_CODE_b
// 步骤3:根据URI 匹配 URI_CODE,从而匹配ContentProvider中相应的资源(match())
@Override
public String getType(Uri uri) {
Uri uri = Uri.parse(" content://cn.scu.myprovider/user1");
switch(matcher.match(uri)){
// 根据URI匹配的返回码是URI_CODE_a
// 即matcher.match(uri) == URI_CODE_a
case URI_CODE_a:
return tableNameUser1;
// 如果根据URI匹配的返回码是URI_CODE_a,则返回ContentProvider中的名为tableNameUser1的表
case URI_CODE_b:
return tableNameUser2;
// 如果根据URI匹配的返回码是URI_CODE_b,则返回ContentProvider中的名为tableNameUser2的表
}
}
ContentObserver类
当数据发生变化是,触发
// 步骤1:注册内容观察者ContentObserver
getContentResolver().registerContentObserver(uri);
// 通过ContentResolver类进行注册,并指定需要观察的URI
// 步骤2:当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
public class UserContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("user", "userid", values);
getContext().getContentResolver().notifyChange(uri, null);
// 通知访问者
}
}
// 步骤3:解除观察者
getContentResolver().unregisterContentObserver(uri);
// 同样需要通过ContentResolver类进行解除
\