【Android】IPC介绍、IPC的六种方式、Binder死亡、Binder连接池
Android中的多进程模式
-
开始多线程模式:
-
在
android,大多数情况下的多进程模式指的是同一个应用中的多个进程的情况; -
使用多进程的方式:就是给四大组件指定
android:process;(常规模式); -
注册活动的属性:
android:label = ""就是值在任务执行列表时显示给用户的名字;如果在切换到这个活动有新指定的这个属性,那么就会发生覆盖;android:exported="true"其他应用是否允许启动这个活动;andorid:configChanges = ""当发生变化:屏幕旋转等等,默认的操作就是销毁这个活动重启,如果设置了代表我自己处理这个变化; -
android:process属性值默认的主活动已经有自己的进程了,默认为包名;
属性值为":名字"的时候,默认为包名+:+名字;
属性值"包名.名字",此时才是完整的命名方式;
- 以:命名,是当前应用的私有进程;其他应用的组件不能跟当前应用的组件跑在同一个进程中;
- 以包名.名字命名,是全局进程;可以在同一个进程,必须shardUID相同;
每个应用都有一个UID,相同的UID可以共享数据;
如果是相同的ShardUID,并且签名(APK数字身份证,签名相同,sharduid才能使用,为了覆盖原软件,说明发行人)也相同:
- 两个应用才能在同一个进程中:除了可以共享data信息,组件信息,还可以共享内存;
- 不在;
-
多进程模式的运行机制
-
基本知识:Android中,每个进程都有自己独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间;不同的虚拟机中访问一个类会产生多个副本;
-
所以在不同进程下的组件是无法同享内存来共享信息的;
-
问题:
- 静态成员和单例模式完全失效;
- 线程同步机制失效;锁的对象都不是同一块内存;
- sp可靠性下降;计数器问题(不同进程同时对一个变量进行增值,导致增值后的结果相同;ps是有缓存机制的,导致获取到的是同一个数值),sp的全量写入(因为每次改变一个值就会新增加一条,如果不同进程改变的是不同的值,那么就会发生覆盖,因为读到是同一个缓存);
- application会多次创建:创建一个进程,就会有新的application;
IPC机制的基础知识
序列化的两种方式
Serializable
Percelable
-
怎么写?
protected Book(Parcel in) { usrid = in.readInt(); name = in.readString(); ismale = in.readInt() == 1; in.readParcelable(Thread.currentThread().getContextClassLoader()); } public void writeToParcel(@NonNull Parcel parcel, int i) { //i == 1代表当前对象需要作为返回值返回,不能立刻释放资源 parcel.writeInt(usrid); parcel.writeString(name); parcel.writeInt(ismale ? 1 : 0); parcel.writeParcelable("可序列化的对象",0); }实现接口,写成员变量以及构造方法,写序列化和反序列化的方式;
- Parcel : Parcel内部包装了可序列化的数据;读写都是通过Parcel对象来进行读写;
- 如果我们序列化的对象本身就是一个序列化对象,那么当反序列化时,需要传递当前线程的上下文加载器,否则可能找不到这个类;
-
使用注意事项?
1.
describeContents():返回当前对象的内容描述,有两个值,通常为0,返回1时(CONTENTS_FILE_DESCRIPTOR))代表含有文件描述符;
2.writeToParcel第二个参数为1 代表当前对象需要作为返回值返回,不能立刻释放资源; -
完整结构
public class Book implements Parcelable { public int usrid; public String name; public boolean ismale; public Book(boolean ismale, String name, int usrid) { this.ismale = ismale; this.name = name; this.usrid = usrid; } protected Book(Parcel in) { usrid = in.readInt(); name = in.readString(); ismale = in.readInt() == 1; in.readParcelable(Thread.currentThread().getContextClassLoader()); } 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]; } }; //返回当前对象的内容描述,如果有文件描述符,返回1(CONTENTS_FILE_DESCRIPTOR); @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel parcel, int i) { //i == 1代表当前对象需要作为返回值返回,不能立刻释放资源 parcel.writeInt(usrid); parcel.writeString(name); parcel.writeInt(ismale ? 1 : 0); parcel.writeParcelable("可序列化的对象",0); } } -
与Serializable的选取
Serializable是Java中的序列化接口,简单但是开销很大,一般在对象序列化存储设备中或者对象序列化之后通过网络传输;
2.Parcelable是安卓中的序列化接口,复杂但是效率很高,安卓开发中一般使用这个,一般在内存的序列化上;
Binder
主播在之前的文章中介绍过Binder,大家移步:【Android】Binder原理初探:理解Android通信机制
死亡代理
- 因为在Binder运行在服务端进程,如果服务端进程由于某些原因异常停止,那么这个时候服务器的Binder断裂,就是Binder死亡;
方法一:
- 但是客户段的功能会有影响,所以需要给客户端连接到服务器之后,通过
binder.linkToDeath(m,0) - 第一个参数是
IBinder.DeathRecipient的对象,它的参数的匿名实现只有一个回调方法,在方法里实现断裂之后的代码通常会重新绑定远程服务;第二个参数是标志位;
方法二:
- 在
onServieceDisconnected中重连线程;
区别就是:
onServieceDisconnected在UI线程,BinderDied是在客户端的Binder线程池中运行;
Android中IPC的六种方式:
使用Bundle
- 四大组件中的三大组件
(Activity,Service,Receive)都支持在Intent中传递Bundle数据的; - 因为
Bundle实现了parcelable对象,而且做了处理,所以可以方便的在不同进程中进行ipc数据传输;
3.bundle数据传输的对象:基本数据类型,实现parcelable对象,实现了serilizable对象,特殊对象; - 使用场景:加入传递的对象需要计算,计算后的对象不是可传递的对象,那么可以放到新的进程中计算再在进程中调用;
使用文件共享:
-
安卓是基于Linux开发的,所以允许多个线程同时对一个文件进行读和写;两个进程读写同一个文件来进行交互;
而且对于内容没有具体要求,可以是文本文件也可以是xml文件;但是会存在一些并发问题;
-
但是
windows,一个文件加了排斥锁的话,其他线程就无法访问; -
SP:
- sp是一种轻量级存储方式,通过键值对存储,底层实现上通过xml来存储键值对,位置在当前包的data目录下就可以看到;
- 由于缓存策略,内存有一份sp的缓存,多进程模式下。读写不可靠,高并发的镀锡而,可能会丢失数据;线程a的内存缓存更新了,b的缓存没更新,a改变就会有问题;
使用Messenger
-
介绍:
1.
Messenger是一种ipc的方式,由于一次只能处理一个请求,所以不会出现并发的问题;
2. 底层实现是AIDL; -
使用:
在服务端中:
-
过程:在onbind方法里面返回一个
Messenger的Binder对象 ->new Messenger的参数是一个hanlder -> 创建我们自定类继承handler实现Handlemessage方法即可; -
自定类继承handler类中,因为客户端send才会调用,所以根据Message的第二个参数得到我们想得到的,然后根据
Messenger messenger1 = msg.replyTo;send,同时客户端才能得到服务端的响应; -
一些东东:
- 什么时候触发
handleMessage?
当Messager返回的Binder对象生成的Messager的send方法时,会回到这个进程的对象。
- Messager返回的Binder对象生成的Messager是一个吗?
不是哦,但是是使用同一个Binder对象;
public class MyService extends Service { public static final String TAG = "ipcMessenger"; public MyService() { } private static class messageHandelr extends Handler{ @Override public void handleMessage(@NonNull Message msg) { if(msg.what == 0){ Log.d(TAG,"你好呀,我收到了客户端发来的消息:"+msg.getData().getString("null")); Message message = Message.obtain(null,1); Bundle bundle = new Bundle(); bundle.putString("null","我是swy"); message.setData(bundle); Messenger messenger1 = msg.replyTo; try { messenger1.send(message); } catch (RemoteException e) { throw new RuntimeException(e); } super.handleMessage(msg); } } } Messenger messenger = new Messenger(new messageHandelr()); @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); } } - 什么时候触发
客户端:
-
过程:链接成功后,得到了Binder对想,通过这个对象得到messenger对象,然后messenger是通过message来传递数据的,同时创建一个新的Messenger对象,来接受服务端返回的信息;
public class MainActivity extends AppCompatActivity { public static final String TAG = "ipcMessenger"; private static class cmh extends Handler{ @Override public void handleMessage(@NonNull Message msg) { Log.d(TAG,"我得到了服务端的数据耶"+msg.getData().getString("null")); if(msg.what==1){ Log.d(TAG,"我得到了服务端的数据耶"+msg.getData().getString("null")); } super.handleMessage(msg); } } private Messenger messengers = new Messenger(new cmh()); private ServiceConnection serviceConnection= new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { Messenger messenger = new Messenger(iBinder); Message message = Message.obtain(null, 0); Bundle bundle = new Bundle(); bundle.putString("null","msg"); message.replyTo = messengers; message.setData(bundle); try { messenger.send(message); } catch (RemoteException e) { throw new RuntimeException(e); } } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); Intent intent = new Intent(this,MyService.class); intent.setPackage("com.example.binder"); bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE); } }Messenger是通过message来传递数据的,二者都实现了parcelable接口;
message的载体:
replyto(Messenger),arg1,arg2,what,bundle;
-
使用AIDL
./gradlew clean build
-
Messenger和AIDL的区别:
前者更聚焦于一条一条的处理,如果处理量过大,那么是不合适的;
如果我们要调用服务端中的方法,AIDL能实现,Messenger不合适;
-
实现:
生成AIDL类之后:尽量把aidl写在同一个包且包结构一致下;如果类的路径不一致,就会反序列化失败;
-
接口创建:
-
接口的支持类型:
基本数据类型,String,charSequence(字符串序列,可以支持string,StringBuffer,StringBuilder等等),List(只支持arraylist,元素必须支持),map(只支持hashmap,元素必须支持),AIDL,Parcelable; -
除了基本数据类型,其他数据类型必须标上方向:
in:客户端发给服务端,服务端的修改客户端不会知道,速度最快,序列化最少;
out:客户端发送一个空壳对象给服务端,服务端填写数据后返回;客户端传的初始值完全无效;
inout:客户端传,服务端修改后返回;序列化次数最多。
不可一味的out,inout开销很大
-
AIDL仅支持接口,不支持常量;
-
-
客户端:
利用服务端返回的Binder对象,装换为aidl接口对象,然后调用里面的方法即可;
-
服务端:
实现AIDL接口;
-
CopyOnWriteArrayList支持并发读写,虽然服务端返回的是它,但是Binder会按照List的规范生成一个arraylist传给客户端;
有一个图书管理:每当有新书的时候,服务端需要通知给客户端;
我们来想想:服务端需要主动给客户端通知,那么肯定需要一个aidl接口,然后让客户端实现这个接口,接着服务端调用接口放置数据就好了,,,但是服务端怎么通知?所以我们需要保存客户端Binder的引用,所以我们需要再用户注册的时候保存引用,销毁的时候去掉引用;然后有新书时候,通知给每一个用户;
把书通知给客户端的接口
// IonNewBookArrivedListener.aidl
package com.example.aidl;
import com.example.aidl.Book;
// Declare any non-default types here with import statements
interface IonNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
客户端通知服务端的接口:IonNewBookArrivedListener 就是客户端实现sub类之后的Binder引用给服务端
// IBookManager.aidl
package com.example.aidl;
import com.example.aidl.Book;
// Declare any non-default types here with import statements
import com.example.aidl.IonNewBookArrivedListener;
interface IBookManager {
void addBook(in Book book);
List<Book> getBookList();
void registerListener(IonNewBookArrivedListener lis);
void unregisterListener(IonNewBookArrivedListener lis);
}
声明类文件
// Book.aidl
package com.example.aidl;
// Declare any non-default types here with import statements
parcelable Book;
客户端:
public class MainActivity extends AppCompatActivity {
IBookManager iBookManager;
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
iBookManager = IBookManager.Stub.asInterface(iBinder);
try {
List<Book> myBookList = iBookManager.getBookList();
Log.d("ljxljx","mybooklistsize: "+myBookList.size());
Book book = new Book(3,"ljxaswy");
iBookManager.addBook(book);
Log.d("ljxljx","andBook" + book.getBookName());
iBookManager.registerListener(ionNewBookArrivedListener);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private IonNewBookArrivedListener ionNewBookArrivedListener = new IonNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
Log.d("ljxljx","newbookname"+newBook.getBookName());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
Intent intent = new Intent(this,MyService1.class);
intent.setPackage("com.example.aidl");
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if(iBookManager !=null && iBookManager.asBinder().isBinderAlive()){
try {
iBookManager.unregisterListener(ionNewBookArrivedListener);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
unbindService(serviceConnection);
super.onDestroy();
}
}
服务端:
public class MyService1 extends Service {
public MyService1() {
}
CopyOnWriteArrayList<Book> booklist = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<IonNewBookArrivedListener> mlistenerlist = new CopyOnWriteArrayList<>();
private Binder mybinder = new IBookManager.Stub() {
@Override
public void addBook(Book book) throws RemoteException {
booklist.add(book);
}
@Override
public List<Book> getBookList() throws RemoteException {
return booklist;
}
@Override
public void registerListener(IonNewBookArrivedListener lis) throws RemoteException {
if(!mlistenerlist.contains(lis)){
mlistenerlist.add(lis);
}
}
@Override
public void unregisterListener(IonNewBookArrivedListener lis) throws RemoteException {
if(mlistenerlist.contains(lis)){
mlistenerlist.remove(lis);
}
}
};
@Override
public void onCreate() {
booklist.add(new Book(1,"李佳雪"));
booklist.add(new Book(2,"swy"));
new Thread(new ServiceWorker()).start();
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return mybinder;
}
private class ServiceWorker implements Runnable{
@Override
public void run() {
while (true){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int bookid = booklist.size() +1;
Book newbook = new Book(bookid,"newBook"+bookid);
onNewBookArrived(newbook);
}
}
}
private void onNewBookArrived(Book newbook) {
booklist.add(newbook);
for (IonNewBookArrivedListener listener : mlistenerlist) {
try {
listener.onNewBookArrived(newbook);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
}
RemoteCallBackList
-
在跨进程传输中,客户端传给服务端会生成不同的对象,但是他们的底层对象Binder都是同一个;->远程调用方法可以,但是如果我们客户端取消注册时,发现找不到对应的客户端对象;
-
RemoteCallBackList的介绍:- 系统提供专门用户跨进程删除接口的,它是一个泛型,支持接管所有aidl接口;
- 内部是map,第一个参数是IBinder,第二个参数是callback类型;
- 当进程终止后,它能够自动移除客户端注册的接口回调;
- 内部实现了线程同步的功能;
- 必须这样遍历使用,它不是List;
-
应用:
添加元素
mlistenerlist.register(lis);删除
mlistenerlist.unregister(lis);遍历
int n = mlistenerlist.beginBroadcast(); for (int i = 0; i < n; i++) { IonNewBookArrivedListener broadcastItem = mlistenerlist.getBroadcastItem(i); try { broadcastItem.onNewBookArrived(newbook); } catch (RemoteException e) { throw new RuntimeException(e); } } mlistenerlist.finishBroadcast();
ANR
前提:客户端调用服务端的方法,那么这个方法会运行在服务端的Binder线程池中;
客户端:
- 客户端线程会被挂起,如果运行在UI线程,那么可能会导致ANR;
- 如果方法是耗时操作,建议避免在UI线程访问远程方法;
服务端:
- 服务端的方法运行在Binder线程池中,可以执行耗时操作,不要新开线程啦;
前提:服务端调用客户端的方法,那么这个方法会运行在客户端的Binder线程池中;
客户端:
- 此时方法运行在Binder线程池中,如果要使用UI元素,记得切换UI线程;
服务端:
- 同理,调用的方法耗时,别在UI线程;
权限验证
在onBind中进行验证
服务端的权限:
<permission android:name="com.ryg.ms1"
android:protectionLevel="normal">
@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("com.ryg.ms1");
if(check == PackageManager.PERMISSION_DENIED){
return null;
}
return mybinder;
}
客户端绑定权限:
<uses-permission android:name="com.ryg.ms1">
</uses-permission>
在onTransact方法中验证
包名必须要com.ryg开头
int check = checkCallingOrSelfPermission("com.ryg.ms1");
if(check == PackageManager.PERMISSION_DENIED){
return false;
}
使用ContentProvider
-
介绍:是安卓中专门用户不同应用之间传递数据的组件;
底层实现是Binder,但是做了封装,比aidl简单;
-
使用:
-
extends ContentProvider; -
实现六种方法:
- Oncreate用于初始化操作,也是唯一一个由系统回调在主线程中的方法,其他都在外界回调运行在线程池中;
- getType: 用来返回一个URI请求所对应的MIME类型(多媒体类型);
- 剩下的四个方法是增删改查;
-
其他:
-
主要用表格的形式来组织数据;
-
支持文件数据(图片,视频),与表格类型不同,处理时可以在内容提供器中返回文件的句柄给外界,让外界访问
contentprovider中的文件信息; -
对底层的储存信息没有任何的要求,可以使用SQLite数据库,也可以使用普通的文件;
-
-
模拟实现:
没啥好说的;
public class BookProvider extends ContentProvider {
public BookProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.d("ljxswy","delete");
// Implement this to handle requests to delete one or more rows.
return 0;
}
@Override
public String getType(Uri uri) {
Log.d("ljxswy","getType");
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.d("ljxswy","insert");
// TODO: Implement this to handle requests to insert a new row.
return null;
}
@Override
public boolean onCreate() {
Log.d("ljxswy","oncreate");
// TODO: Implement this to initialize your content provider on startup.
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Log.d("ljxswy","threadname"+Thread.currentThread().getName());
// TODO: Implement this to handle query requests from clients.
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.d("ljxswy","update");
return 0;
}
}
- 在注册的时候,加上:,其实还有readpermission,writepermission;其他应用有这个权限,才能访问这个内容提供器;
android:permission="com.ryg.PROVIDER"
- 这是provider 的唯一标识,便于外部应用访问
android:authorities="com.ryg.aidl.Book.Provider"
活动中:
Uri uri = Uri.parse("content://com.ryg.aidl.Book.Provider");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Log.d("ljxswy","newbookname");
getContentResolver().query(uri,null,null,null,null);
getContentResolver().query(uri,null,null,null,null);
getContentResolver().query(uri,null,null,null,null);
}
- 使用
content://com.ryg.aidl.Book.Provider唯一标识了内容提供器; - 使用
ContentResolver()的query方法查询;
以上是简单的实现,我们还可以加入数据库的交互,其实很简单;
- 创建自己的
openhelper类; - 在内容提供器的oncreate里,创建我们的数据库;
- 由于不知道访问的是哪个表,所以为他们定义单独的
uri,uricode->将两者用UriMatcher相关联->当我们知道了uri得到uricode->访问表中数据;
好吧,还是代码为主吧;
private static final String AUTHORITY = "com.ryg.aidl.Book.Provider";
public static final Uri BOOK_URL = Uri.parse("content://"+AUTHORITY + "/Book");
public static final int BOOK_URL_CODE = 0;
private static final UriMatcher urimatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
urimatcher.addURI(AUTHORITY,"Book",BOOK_URL_CODE);
}
然后写四个方法:
- 每次根据uri,然后根据
urimacher.match(uri),来获得对应的表名,对对应表操作; - 除了查询,其他方法都会provider的内容修改,此时都要调用
mcontext.contentresolver().notifychange(uri,null);
SQLite内部对数据库做了同步的处理,但是如果通过多个对象来访问的话,还是有线程安全问题的;
使用Socket
-
介绍:socker也称为套接字,分为流式套接字和用户数据报套接字;
-
流式套接字:TCP协议;
- TCP:是面向连接的协议,连接的建立需要经过三次握手才能完成;
- 有超时重传机制,有很高的稳定性;
-
用户数据报套接字:UDP协议:
- 是无连接的,提供不稳定的单向通信功能,也可以双向;
- 在网络堵塞的情况下,数据不一定能正确传输;
- 优点是效率高;
-
不能在主线程中访问网络:
- 访问会报错;
- 网络是耗时的,不推荐;
-
通信过程:
大致流程就是
- 服务端Service中,新开线程:通过
servicesocker.accpect()得到客户端是socket对象,然后读数据,写数据; - 客户端中,通过
startservice();然后新开线程:生成对应的socket对象,读写数据;
- 服务端Service中,新开线程:通过
Binder连接池
-
解决场景:如果有很多不同线程通信很多aidl->有很多serviece->我们想实现只有一个service->需要一个Binder连接池类来进行管理;
-
思路:
-
建立不同的aidl文件->同时建立一个aidl文件来进行管理;
-
逻辑:
客户端:通过一个方法连接到服务端->根据不同的参数得到自己的Binder->通信;
Binder连接池:通过一个aidl与服务器通信->连接服务器->得到服务器返回的大Binder->实现这个类的内部方法(根据不同的请求返回不同的小Binder)->写小Binder的stub->客户端通过大Binder调用方法得到小Binder通信;
-
-
代码:
Binder连接池类:
public class BinderPool { private Context mcontext; private static volatile BinderPool sInstance; private CountDownLatch countDownLatch; private IBinderPool mibinder; public BinderPool(Context mcontext) { this.mcontext = mcontext; conncectBinderPool(); } private void conncectBinderPool() { Log.d("BinderPool", "bindService result: f"); countDownLatch = new CountDownLatch(1); Log.d("BinderPool", "bindService result: f"); Intent intent = new Intent(mcontext, binderpoolservice.class); intent.setPackage("com.example.aidl"); Log.d("BinderPool", "bindService result: d"); boolean b = mcontext.bindService(intent, serviceConnection, mcontext.BIND_AUTO_CREATE); Log.d("BinderPool", "bindService result: " +b); // 使用新的线程来等待服务连接,避免主线程阻塞 try { countDownLatch.await(); // ✔ 在子线程阻塞 } catch (InterruptedException e) { e.printStackTrace(); } } private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { Log.d("ljxaswy","contecyed"); mibinder = (IBinderPool) IBinderPool.Stub.asInterface(iBinder); try { mibinder.asBinder().linkToDeath(deathRecipient,0); } catch (RemoteException e) { throw new RuntimeException(e); } countDownLatch.countDown(); } private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { mibinder.asBinder().unlinkToDeath(deathRecipient,0); mibinder = null; conncectBinderPool(); } }; @Override public void onServiceDisconnected(ComponentName componentName) { Log.d("ljxaswy","notcontecyed"); } }; public IBinder queryBinder(int code){ IBinder m= null; if(mibinder != null){ try { m = mibinder.querybinder(code); } catch (RemoteException e) { throw new RuntimeException(e); } } if(m == null){ Log.d("ljxaswy","nulll"); } return m; } public static class binderpoolitem extends IBinderPool.Stub{ public binderpoolitem() { } @Override public IBinder querybinder(int a) throws RemoteException { IBinder iBinder = null; Log.d("ljxaswy","leil"); if(a == 0){ iBinder = new printljx(); } else if(a == 1) { iBinder = new printswy(); } return iBinder; } } public static class printljx extends Iprintljx.Stub{ @Override public void printLjx() throws RemoteException { Log.d("ljxaswy","雪雪砂仁女魔头"); } } public static class printswy extends Iprintswy.Stub{ @Override public void printSwy() throws RemoteException { Log.d("ljxaswy","万事都灵"); } } public static BinderPool getInstance(Context context){ Log.d("BinderPool", "bindService result: df"); if(sInstance == null){ Log.d("BinderPool", "bindService result: ddf"); synchronized (BinderPool.class){ if(sInstance == null){ Log.d("BinderPool", "bindService result: dddf"); sInstance = new BinderPool(context); } } } return sInstance; } }
服务端
package com.example.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class binderpoolservice extends Service {
@Override
public void onCreate() {
Log.d("ljxaswy","oncreate");
super.onCreate();
}
BinderPool.binderpoolitem binderpoolitem = new BinderPool.binderpoolitem();
public binderpoolservice() {
}
@Override
public IBinder onBind(Intent intent) {
return binderpoolitem;
}
}
客户端:
new Thread(new Runnable() {
@Override
public void run() {
BinderPool binderPool = BinderPool.getInstance(getApplicationContext());
Log.d("ljxaswy","ljxaswy1");
IBinder iBinder = binderPool.queryBinder(0);
Iprintljx iprintljx = Iprintljx.Stub.asInterface(iBinder);
Log.d("ljxaswy","ljxaswy");
try {
iprintljx.printLjx();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}).start();
有几个注意的点:
1. private static volatile BinderPool sInstance;为什么要用volatile?
保持多个线程访问sinstance的一致性,禁止指令重排(创建的对象是完整的,有序的),避免创造单例出问题,内存的可见性;
有序性:jvm会进行优化,可能会分配内存,直接把引用赋值给sinstance,然后调用构造函数;
可见性:(保证一个线程修改了元素的值,能立马写到内存中和从内存中读出,这样其他线程进行读写的时候都能得到最新值);
- 客户端记得写在子线程中,否则客户端在主线成,导致
countDownLatch堵塞主线程,服务连接成功无法回调,会造成死锁;
好啦,本次分享到此结束,不喜勿喷哦嘻嘻;
更多推荐
所有评论(0)