Android工程师之ARTS挑战第六期第4周.md

目标:

  1. 每周至少做一个 leetcode 的算法题
  2. 阅读并点评至少一篇英文技术文章
  3. 学习至少一个技术技巧
  4. 分享一篇有观点和思考的技术文章

第四周:

  • 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进程时的几种状态:

  1. 进程B,整个进程没有启动
  2. 进程B启动了,但是里面的Service没有创建出来
  3. 进程B启动了,里面的Service也创建了,但是Service没有被绑定过,回调onBind()
  4. 进程B启动了,里面的Service也创建了,但是Service被绑定过,回调onReBind()

Android 23

AIDL接口Stub抽象类Proxy类Stub的实现类
ILeoAidlStubProxynew ILeoAidl.stub
IActivityManagerActivityManagerNativeActivtyManagerProxyActivityManagerService

  • 分享一篇有观点和思考的技术文章

    https://blog.csdn.net/hl09083253cy/article/details/84862874 AIDL实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值