目标:
- 每周至少做一个 leetcode 的算法题
- 阅读并点评至少一篇英文技术文章
- 学习至少一个技术技巧
- 分享一篇有观点和思考的技术文章
第四周:
- LeetCode 15
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Set<List<Integer>> res = new HashSet<>();
if(nums.length==0) return new ArrayList<>(res);
Arrays.sort(nums);
for(int i=0; i<nums.length-2;i++){
int j =i+1;
int k = nums.length-1;
while(j<k){
int sum = nums[i]+nums[j]+nums[k];
if(sum==0)res.add(Arrays.asList(nums[i],nums[j++],nums[k--]));
else if ( sum >0) k--;
else if (sum<0) j++;
}
}
return new ArrayList<>(res);
}
}
-
阅读并点评至少一篇英文技术文章
- “Differences Between AI and Machine Learning and Why it Matters” by Roberto Iriondo https://link.medium.com/nyfTCMyKgZ
-
学习至少一个技术技巧 : Bindber分析
内存划分
[外链图片转存失败(img-i9n7z0lm-1566183328271)(/Users/zkingcobra/Documents/blog初稿/Android Blog/images/image-20190812141041545.png)]
传统IPC传输数据
[外链图片转存失败(img-zDccpMUo-1566183328272)(/Users/zkingcobra/Documents/blog初稿/Android Blog/images/image-20190813215045657.png)]
Binder传输数据
[外链图片转存失败(img-joLtcDJg-1566183328272)(/Users/zkingcobra/Documents/blog初稿/Android Blog/images/image-20190813215324668.png)]
Binder与传统IPC对比
Binder 共享内存 Socket 性能 需要拷贝一次注1 无需拷贝注3 需要拷贝两次 特点 基于C/S架构易用性高 控制复杂注2,易用性差 基于C/S架构作为一款通用结构,其传输效率低,开销大 安全性 为每个app分配UID同时支持实名和匿名 依赖上层协议访问接入点是开放的不安全 依赖上层协议访问接入点是开放的不安全 注1:为什么Binder是拷贝一次?因为,Binder通过copy_from_user()函数将数据拷贝到内核缓存区,内核缓存区和数据接收缓存区存在映射关系。接收方的数据也和数据接收缓存区存在映射关系,映射是MMP实现的
注2:为什么控制复杂,因为在客户端和服务端两个都同一片映射内存空间,就需要使用到同步机制,同步机制又有同步不一致,死锁等问题很容易出现 这里有疑问???好像共享内存没有实现同步
注3:共享内存就是通过MMP把发送方的数据映射到物理内存接收方同样映射同一块内存
MMP: 虚拟地址映射到物理地址
Binder源码分析
AIDL
- 问题:通过这个遥控器是如何调用到服务端的?
- 问题:服务端是如何处理的?
首先,我们先编码一个aidl的文件
interface ILeoAidl { void addPerson(in Person person); List<Person> getPersonList(); }
parcelable Person;
服务端App和客户端App的Aidl文件是一致的。就会自动生成一个ILeoAidl.java文件
public interface ILeoAidl extends android.os.IInterface { /** Local-side IPC implementation stub class. */ // Stub 接收数据 public static abstract class Stub extends android.os.Binder implements com.xx.leo_service.ILeoAidl { private static final java.lang.String DESCRIPTOR = "com.xx.leo_service.ILeoAidl"; /** Construct the stub at attach it to the interface. */ public Stub() { // Stub类的构造方法中传入实现的Binder和标记值 this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.xx.leo_service.ILeoAidl interface, * generating a proxy if needed. */ public static com.xx.leo_service.ILeoAidl asInterface(android.os.IBinder obj) { // 首先判断服务是否为空 if ((obj == null)) { return null; } // 查询本地接口 queryLocalInterface // 注4 才在构造方法中传入 DESCRIPTOR 这里就开始比较 DESCRIPTOR 这里有意义? android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); // 判断是否是本地 if (((iin != null) && (iin instanceof com.xx.leo_service.ILeoAidl))) { return ((com.xx.leo_service.ILeoAidl) iin); } return new com.xx.leo_service.ILeoAidl.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 { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_addPerson: { data.enforceInterface(DESCRIPTOR); com.xx.leo_service.Person _arg0; if ((0 != data.readInt())) { _arg0 = com.xx.leo_service.Person.CREATOR.createFromParcel(data); } else { _arg0 = null; } // 客户端是调用的Proxy中的addPerson方法 // 服务端调用的代码片段1的addPerson实现 this.addPerson(_arg0); reply.writeNoException(); return true; } case TRANSACTION_getPersonList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.xx.leo_service.Person> _result = this.getPersonList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } } return super.onTransact(code, data, reply, flags); } // 发送数据 private static class Proxy implements com.xx.leo_service.ILeoAidl { 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 void addPerson(com.xx.leo_service.Person person) throws android.os.RemoteException { // _data 存储发送数据 android.os.Parcel _data = android.os.Parcel.obtain(); // _reply 服务端返回的数据 android.os.Parcel _reply = android.os.Parcel.obtain(); try { // 校验 _data.writeInterfaceToken(DESCRIPTOR); if ((person != null)) { _data.writeInt(1); person.writeToParcel(_data, 0); } else { _data.writeInt(0); } // 调用服务端的代码 // 挂起客户端的线程,直到服务端返回 // flag 0 客户端可以发送到服务端也可以返回 // 1 是服务端不能返回 mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); // 调用服务端返回的数据 _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public java.util.List<com.xx.leo_service.Person> getPersonList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.xx.leo_service.Person> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.xx.leo_service.Person.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void addPerson(com.xx.leo_service.Person person) throws android.os.RemoteException; public java.util.List<com.xx.leo_service.Person> getPersonList() throws android.os.RemoteException; }
问题:客户端如何获取到AIDL的句柄?
也就是在注4的位置开始查询本地接口
//Binder.java /** * Use information supplied to attachInterface() to return the * associated IInterface if it matches the requested * descriptor. */ // 判断descriptor是否一致 public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) { if (mDescriptor != null && mDescriptor.equals(descriptor)) { return mOwner; } return null; }
//Binder.java /** * Convenience method for associating a specific interface with the Binder. * After calling, queryLocalInterface() will be implemented for you * to return the given owner IInterface when the corresponding * descriptor is requested. */ // 这里是mOwner和descriptor的入口 public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) { mOwner = owner; mDescriptor = descriptor; }
在调用xxxAdil.java的使用是:
// 客户端 private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e(TAG, "onServiceConnected: success"); // 这里并没有调用构造方法,所以这个时候Stub中的mDescriptor还是null // 获取到AIDL的句柄 iLeoAidl = ILeoAidl.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { Log.e(TAG, "onServiceDisconnected: success"); iLeoAidl = null; } };
代码片段1:
// 服务端 // 这里是调用了 stub的构造方法 // 所以服务端的Descriptor就被保存到了xxxAidl.java中 // 就是服务端本地进程中Descriptor就被赋值了 // 如果客户端和服务端是在同一个进程的时候,那么客户端中的Descriptor也会有值了而且是同一个,那么queryLocalInterface就会返回true private IBinder iBinder = new ILeoAidl.Stub() { @Override public void addPerson(Person person) throws RemoteException { persons.add(person); } @Override public List<Person> getPersonList() throws RemoteException { return persons; } };
Binder的客户端获取句柄之后的调用流程:
sequenceDiagram
Client->>Proxy: ILeoAidl.addPersion()
Proxy->>Binder: mRemote.transact()
Binder->>Stub: onTransact()
Stub->>Service: this.addPerson()
Service–>>Stub:
Stub–>>Binder:
Binder–>>Proxy:
Proxy–>>Client:**Stub是继承的Binder**
问题:如何绑定到服务端的?
在app中将客户端绑定到服务端:使用的Android 28分析
Android Api 29 xxxxActiviyt.java:
// 将客户端绑定到服务端 private void bindService() { Intent intent = new Intent(); intent.setComponent(new ComponentName("com.xx.leo_service", "com.xx.leo_service.LeoAidlService")); // 跟踪bindService源码,查看connection bindService(intent, connection, Context.BIND_AUTO_CREATE); } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e(TAG, "onServiceConnected: success"); iLeoAidl = ILeoAidl.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { Log.e(TAG, "onServiceDisconnected: success"); iLeoAidl = null; } };
小问题:为什么是在ContextImpl.java中?
bindService()-> ContextWapprer.java -->Context,然而Context是抽象方法它的实现类是ContextImpl.java
//ContextImpl.java .. private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser. IServiceConnection sd; if (conn == null) { throw new IllegalArgumentException("connection is null"); } if (mPackageInfo != null) { // 跟踪conn发现conn被传入IServiceConnection的构造方法 sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); } else { throw new RuntimeException("Not supported in system context"); } validateServiceIntent(service); try { IBinder token = getActivityToken(); if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null && mPackageInfo.getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { flags |= BIND_WAIVE_PRIORITY; } service.prepareToLeaveProcess(this); // 这里调用绑定 int res = ActivityManager.getService().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier()); if (res < 0) { throw new SecurityException( "Not allowed to bind to service " + service); } return res != 0; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
//ActivityManager.java 获取Service /** * @hide */ public static IActivityManager getService() { return IActivityManagerSingleton.get(); } private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() { @Override protected IActivityManager create() { // Context.ACTIVITY_SERVICE 等于 “Activity”,这里获取的Service是AMS final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); // asInteraface是返回的aidl的实现类也就是实现了aidl的Proxy final IActivityManager am = IActivityManager.Stub.asInterface(b); return am; } };
A进程访问B进程时的几种状态:
- 进程B,整个进程没有启动
- 进程B启动了,但是里面的Service没有创建出来
- 进程B启动了,里面的Service也创建了,但是Service没有被绑定过,回调onBind()
- 进程B启动了,里面的Service也创建了,但是Service被绑定过,回调onReBind()
Android 23
AIDL接口 | Stub抽象类 | Proxy类 | Stub的实现类 |
---|---|---|---|
ILeoAidl | Stub | Proxy | new ILeoAidl.stub |
IActivityManager | ActivityManagerNative | ActivtyManagerProxy | ActivityManagerService |
-
分享一篇有观点和思考的技术文章
https://blog.csdn.net/hl09083253cy/article/details/84862874 AIDL实现