插件化笔记 - 动态代理

插件化离不开代理与反射,反射我们已经很熟悉了,我们可以在各种各样的三方库中找到类似的使用,但是代理却感觉是一个抽象的概念。

代理模式

为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用(结构型)。
使用代理可以屏蔽内部实现细节,后续内部有变动对于外部调用者来说是封闭的,符合开放-封闭原则。用户可以放心地请求代理,他只关心是否能得到想要的结果。在任何使用本体的地方都可以替换成使用代理,从而实现实现和调用松耦合。
换句话说,就是让别人帮我们去完成我们想去完成的事情。

在代理模式又可以分为动态代理和静态代理,我们以java层的代理来说明两种形式

需求: 通过使用代理,在点击事件的前后,加入我们需要的log信息

既然要通过代理模式,那么我们是不能直接去修改onClick回调的,所以我们先吧固定的点击事件准备好:

   btn1 = findViewById(R.id.btn1);
   btn1.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
                Log.e("onClick", ":onClick ");
                startActivity(new Intent(MainActivity.this, TestActivity.class));
          }
     });

在完成需求之前 ,我们要考虑以下几个问题

  1. 我要代理这个对象的什么方法
  2. 怎么将我们自己的代理对象塞进去

首先,点击事件的公共入口都是View.OnclickListener 接口,在为View设置点击事件的接口时,View对象会将当前的点击事件保存下来

public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        // 跟进getListenerInfo()
        getListenerInfo().mOnClickListener = l; 
    }
//.........
  
   // getListenerInfo为一个单例,返回了ListenerInfo 的对象
    ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

// .........
// ListenerInfo就是一个静态类,保存了所有关于事件的变量.
static class ListenerInfo {
        protected OnFocusChangeListener mOnFocusChangeListener;
        private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
        protected OnScrollChangeListener mOnScrollChangeListener;
        private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
        public OnClickListener mOnClickListener;
        protected OnLongClickListener mOnLongClickListener;
        protected OnContextClickListener mOnContextClickListener;
        protected OnCreateContextMenuListener mOnCreateContextMenuListener;
        private OnKeyListener mOnKeyListener;
        private OnTouchListener mOnTouchListener;
        private OnHoverListener mOnHoverListener;
        private OnGenericMotionListener mOnGenericMotionListener;
        private OnDragListener mOnDragListener;
        private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
        OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
        OnCapturedPointerListener mOnCapturedPointerListener;
        private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
    }

所以 , 我们可以得出结论

  1. hook的目标为View.setOnclickListener(View.OnClickListener listener)这个方法.这个方法本质调用了getListenerInfo().mOnClickListener=listener,我们可以拿到View的实例后获取该方法.
  2. 如何赋值,通过反射的filed.set(target,proxy)完成,其中,target为getListenerInfo().mOnClickListener,proxy为我们代理的函数。
    接下来我们使用两种形式的代理模式来完成需求

静态代理


// 先拿到ListenerInfo对象, 再对对象中的mOnClickListener赋值
public static void hook(Context context, final View v) {
        try {
            //首先获取getListenerInfo , 获取到实际的复制对象ListenerInfo
            // 反射执行View类的getListenerInfo()方法,拿到v的mListenerInfo对象,这个对象就是点击事件的持有者
            Method method = View.class.getDeclaredMethod("getListenerInfo");
            method.setAccessible(true);//由于getListenerInfo()方法并不是public的,所以要加这个代码来保证访问权限
            //调用method方法 , 返回ListenerInfo的实例
            Object mListenerInfo = method.invoke(v);//这里拿到的就是mListenerInfo对象,也就是点击事件的持有者

            //要从这里面拿到当前的点击事件对象
            Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");// 这是内部类的表示方法
            Field field = listenerInfoClz.getDeclaredField("mOnClickListener");
            final View.OnClickListener onClickListenerInstance = (View.OnClickListener) field.get(mListenerInfo);//取得真实的mOnClickListener对象

            //2. 创建我们自己的点击事件代理类
            ProxyOnClickListener proxyOnClickListener = new ProxyOnClickListener(onClickListenerInstance);
            //3. 用我们自己的点击事件代理类,设置到"持有者"中
            field.set(mListenerInfo, proxyOnClickListener);
            //完成
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 自定义代理类 , 实现了对 View.OnClickListener 的onClick的代理 , 我们可以在这里进行log的输出
    static class ProxyOnClickListener implements View.OnClickListener {
        View.OnClickListener oriLis;

        ProxyOnClickListener(View.OnClickListener oriLis) {
            this.oriLis = oriLis;
        }

        @Override
        public void onClick(View v) {
            Log.d("ProxyOnClickListener", "点击事件被hook到了");
            if (oriLis != null) {
                oriLis.onClick(v);
            }
        }
    }
点击事件执行结果

动态代理

如果每一个需要代理的地方都需要去写一个代理类岂不是很麻烦么 ,java提供了Proxy这个类来辅助我们去创建代理类,接下来我们用动态代理去完成上面的需求

public static void hook(Context context, final View v) {//
        try {
            //首先获取getListenerInfo , 获取到实际的复制对象ListenerInfo
            // 反射执行View类的getListenerInfo()方法,拿到v的mListenerInfo对象,这个对象就是点击事件的持有者
            Method method = View.class.getDeclaredMethod("getListenerInfo");
            method.setAccessible(true);//由于getListenerInfo()方法并不是public的,所以要加这个代码来保证访问权限
            //调用method方法 , 返回ListenerInfo的实例
            Object mListenerInfo = method.invoke(v);//这里拿到的就是mListenerInfo对象,也就是点击事件的持有者

            //要从这里面拿到当前的点击事件对象
            Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");// 这是内部类的表示方法
            Field field = listenerInfoClz.getDeclaredField("mOnClickListener");
            final View.OnClickListener onClickListenerInstance = (View.OnClickListener) field.get(mListenerInfo);//取得真实的mOnClickListener对象

            //2. 创建我们自己的点击事件代理类
            //   方式2:由于View.OnClickListener是一个接口,所以可以直接用动态代理模式
            // Proxy.newProxyInstance的3个参数依次分别是:
            Object proxyOnClickListener = Proxy.newProxyInstance(
                    context.getClass().getClassLoader(), // 本地的类加载器;
                    new Class[]{View.OnClickListener.class},// 代理类的对象所继承的接口(用Class数组表示,支持多个接口)
                    new InvocationHandler() {  // 代理类的实际逻辑,当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
                        /**
                         * @param proxy 被代理对象
                         * @param method 被代理的方法
                         * @param args 参数表
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Log.d("HookSetOnClickListener", "点击事件被hook到了");//加入自己的逻辑
                            return method.invoke(onClickListenerInstance, args);//执行被代理的对象的逻辑
                        }
                    });
            Log.e("hook: getClass ",  proxyOnClickListener.getClass().toString());
            Log.e("hook: getInterfaces", proxyOnClickListener.getClass().getInterfaces().toString());
            Log.e("hook: getMethods ", proxyOnClickListener.getClass().getMethods().toString());
           
//            //3. 用我们自己的点击事件代理类,设置到"持有者"中
            field.set(mListenerInfo, proxyOnClickListener);
            //完成
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

前面获取被代理的函数步骤都是一样的,区别在于我们的代理对象的创建,是通过
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)来完成,其中第二个参数是interfaces的数组,也就是说被代理类可以多实现。但是这里为什么只能是一个接口呢?这个问题我们后文继续讨论。
第三个参数InvocationHandler是一个创建代理类的接口,接口回调中的参数已经在代码中标出.
在中间我们获取了代理类的实例后,我打印了一些类的信息,来辅助分析。

点击事件结果

代理类的部分信息

这个代理类居然是$Proxy0? 我们并没有创建这个代理类啊,这个代理类究竟被创建到了什么地方?

动态代理分析:

  1. 为什么动态代理只能代理接口实现类而不是任意类呢?
  2. 动态代理的类放在那里?这个类是怎么凭空产生的?

准备工作: 谷歌翻译 AndroidXRef

首先,我们跟进Proxy.newInstance()的源码,看看官方对于这个参数的解释

@param  interfaces  the list of interfaces for the proxy class  to implement

这里已经说明了这个参数必须是接口数组。我们不能就这么死心吧?我们直接换一个非接口实现类来试试用动态代理去代理它
这里参考了[欢哥的博客 - 初识-hook-机制]https://zhaomenghuan.js.org/blog/android-plugin-framework-proxy-hook.html#%E5%88%9D%E8%AF%86-hook-%E6%9C%BA%E5%88%B6) , 借用他对于startActivityForResult的静态代理 , 我们使用动态代理来分析一波.

    public static void hookInstrumentation(MainActivity mainActivity) {
        try {
            // 拿到原始的 mInstrumentation字段
            Field mInstrumentationField = Activity.class.getDeclaredField("mInstrumentation");
            mInstrumentationField.setAccessible(true);
            final Instrumentation originalInstrumentation = (Instrumentation) mInstrumentationField.get(mainActivity);
            Object proxyIns = Proxy.newProxyInstance(
                    mainActivity.getClass().getClassLoader(), // 本地的类加载器;
                    new Class[]{originalInstrumentation.getClass()},// 代理类的对象所继承的接口(用Class数组表示,支持多个接口)
                    new InvocationHandler() {  // 代理类的实际逻辑,当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
                        /**
                         * @param proxy 被代理对象
                         * @param method 被代理的方法
                         * @param args 参数表
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Log.d("Instrumentation", "Instrumentation事件被hook到了");//加入自己的逻辑
                            return method.invoke(originalInstrumentation, args);//执行被代理的对象的逻辑
                        }
                    });
            //3. 用我们自己的点击事件代理类,设置到"持有者"中
            mInstrumentationField.set(mainActivity, proxyIns);
        } catch (Exception e) {
             Log.e("hookInstrumentation", e.toString());
        }
    }

我们点击跳转之后, 并没有输出我们想要的log信息, 网上追踪logcat ,发现我们在调用这个静态方法的时候就已经报错;
E/hookInstrumentation: java.lang.IllegalArgumentException: android.app.Instrumentation is not an interface
看来动态代理只能对接口实现类使用 , 而非接口实现类, 如Instrumentation 只能使用静态代理了.

接下来我们带着上面的问题来扒一波源码
API: < Android API 28 Platform >

    public static Object newProxyInstance(
        ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
        throws IllegalArgumentException{
        // 对InvocationHandler 判空
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        // 这个注释我们能看出来 , 这个版本对于之前版本做过一些修改:移除代理的安全校验
        // Android-removed: SecurityManager calls
        /*
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        */

        //  查找或生成指定的代理类。
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            // Android-removed: SecurityManager / permission checks.
            /*
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            */
           
            // 拿到代理类的构造函数
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                 cons.setAccessible(true);
            }
            // 返回创造完成的代理类的实例
            return cons.newInstance(new Object[]{h});
        } catch (Exception e) {
            ......
        }
    }

在这个方法中 , 仅仅是一局getProxyClass0(loader, intfs);就完成了代理类的创建.我们跟进代码

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // 接口数量上限控制
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

简单翻译一下:
如果由实现了给定接口的给定加载器定义的代理类存在,则将简单地返回缓存的副本; 否则,它将通过ProxyClassFactory创建代理类。虽然是一个get的操作 , 但总是不为空的。首先我们开搞清楚这是个什么东西
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
有点懵? 猜一下 , 像是一个缓存 , 这里我暂时不去深究了, 有兴趣的大佬可以去研究一波https://www.iteye.com/blog/xiaoxiaoher-2372315
。初始化时会传入两个工厂:KeyFactory和ProxyClassFactory。
在上面的方法中,第一个参数对应了classLoader,第二个参数为Interfaces ,为了尽快接近我们提出的问题1 ,我们先来看看第二个工厂类ProxyClassFactory。
又要贴出长长的代码了,这里我把代码分成两个部分来看。

    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 所有代理类名称的前缀
        private static final String proxyClassNamePrefix = "$Proxy";
        // 下一个用于生成唯一代理类名称的数字
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                // 验证指定的类加载器(loader)加载接口所得到的Class对象(interfaceClass)是否与intf对象相同
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                // 验证传入的class对象是不是接口 , 由此,我们的第一个问题得以解决: 因为SDK里面不让我们这么传 - - 
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                // 验证接口是否重复
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

我们的第一个问题的答案就在这里找到了, 动态代理传入的Class[]对象在内部会进行类型检查, 如果不是接口则会抛出异常。
其实我们也可以这么来理解:java是单继承多实现的 ,既然我们代理类的生成结果都是继承了Proxy类,然后再去实现了其他接口,那么必然不能直接传一个非接口对象,要不然和java的单继承就相悖了。我们继续看接下来的代码

   String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use the default package.
                proxyPkg = "";
            }

            {
                // Android-changed: Generate the proxy directly instead of calling
                // through to ProxyGenerator.
                List<Method> methods = getMethods(interfaces);
                Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                validateReturnTypes(methods);
                List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);

                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);

                // 代理类的计数器加一
                long num = nextUniqueNumber.getAndIncrement();
                // 拼接代理类的类名,我们大致能看到的样子就是 xxxx$proxy0.class
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
               // 生成代理类
                return generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
     

最够追到生成代理类的代码 ,却发现这是一个调用了JNI的函数

private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
                                                 ClassLoader loader, Method[] methods,
                                                 Class<?>[][] exceptions);

在AndroidXRef中搜索 , 可以看到改JNI函数位于java_lang_reflect_Proxy.cc中

static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArray interfaces,
                                  jobject loader, jobjectArray methods, jobjectArray throws) {
  ScopedFastNativeObjectAccess soa(env);
  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
  return soa.AddLocalReference<jclass>(class_linker->CreateProxyClass(
      soa, name, interfaces, loader, methods, throws));
}

跟进class_linker->CreateProxyClass()这个函数 , 位于class_linker.cc中
ps:这个类比较大,用网页加载直接死掉了,建议下载下来再看。
由于鄙人对于C层的代码基本没怎么看过,所以只能以注释为主先来宏观理解一下了,欢迎有经验的前辈们指教。
这里贴出简(汉)化后的代码

mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
                                             jstring name,
                                             jobjectArray interfaces,
                                             jobject loader,
                                             jobjectArray methods,
                                             jobjectArray throws) {
  Thread* self = soa.Self();
  StackHandleScope<10> hs(self);
  MutableHandle<mirror::Class> temp_klass(hs.NewHandle(
      AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class))));
  if (temp_klass == nullptr) {
    CHECK(self->IsExceptionPending());  // OOME.
    return nullptr;
  }
  DCHECK(temp_klass->GetClass() != nullptr);
  temp_klass->SetObjectSize(sizeof(mirror::Proxy));
  // 设置包含类的访问标志AccessFlags,可以看到代理类是public final的
  temp_klass->SetAccessFlags(kAccClassIsProxy | kAccPublic | kAccFinal | kAccVerificationAttempted);
  temp_klass->SetClassLoader(soa.Decode<mirror::ClassLoader>(loader));
  DCHECK_EQ(temp_klass->GetPrimitiveType(), Primitive::kPrimNot);
  temp_klass->SetName(soa.Decode<mirror::String>(name));
  temp_klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache());
  // Object有一个空的 iftable, 所以这里直接拷贝.
  temp_klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable());
  mirror::Class::SetStatus(temp_klass, mirror::Class::kStatusIdx, self);
  std::string descriptor(GetDescriptorForProxy(temp_klass.Get()));
  const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str());

  // 在插入类之前需要先设置好内存分配器。
  LinearAlloc* const allocator = GetOrCreateAllocatorForClassLoader(temp_klass->GetClassLoader());

  // 在加载字段之前插入类,因为仅从类表访问字段根ArtField :: declaring_class_)
 // 所以插入类和设置下面的字段数组之间不能有任何暂停点。
  ObjPtr<mirror::Class> existing = InsertClass(descriptor.c_str(), temp_klass.Get(), hash);
  CHECK(existing == nullptr);

  // 实例字段是继承的,但是我们添加了两个静态字段...
  const size_t num_fields = 2;
  LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self, allocator, num_fields);
  temp_klass->SetSFieldsPtr(sfields);

 // 1.创建一个静态字段“ interfaces”,其中包含由_declared_接口实现的
 // 我们的代理,因此Class.getInterfaces不返回展开的集合。
  ArtField& interfaces_sfield = sfields->At(0);
  interfaces_sfield.SetDexFieldIndex(0);
  interfaces_sfield.SetDeclaringClass(temp_klass.Get());
  interfaces_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);

  // 2.创建一个静态字段“ throws”,其中包含我们的方法抛出的异常。
  ArtField& throws_sfield = sfields->At(1);
  throws_sfield.SetDexFieldIndex(1);
  throws_sfield.SetDeclaringClass(temp_klass.Get());
  throws_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);

  // 代理类会有一个直接方法, 即构造函数
  const size_t num_direct_methods = 1;
  // 它们具有与数组一样多的虚拟方法
  auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>>(methods));
  DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass())
      << mirror::Class::PrettyClass(h_methods->GetClass());
  const size_t num_virtual_methods = h_methods->GetLength();

  // 创建方法数组.
  LengthPrefixedArray<ArtMethod>* proxy_class_methods = AllocArtMethodArray(
        self, allocator, num_direct_methods + num_virtual_methods);
  // 当前,AllocArtMethodArray不能返回null,但是OOM逻辑保留在那里,以防将来我们要抛出OOM。
  if (UNLIKELY(proxy_class_methods == nullptr)) {
    self->AssertPendingOOMException();
    return nullptr;
  }
  temp_klass->SetMethodsPtr(proxy_class_methods, num_direct_methods, num_virtual_methods);

  // 创建一个直接方法
  CreateProxyConstructor(temp_klass, temp_klass->GetDirectMethodUnchecked(0, image_pointer_size_));

  // 使用指定的原型创建虚拟方法。
  // TODO These should really use the iterators.
  for (size_t i = 0; i < num_virtual_methods; ++i) {
    auto* virtual_method = temp_klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
    auto* prototype = h_methods->Get(i)->GetArtMethod();
    CreateProxyMethod(temp_klass, prototype, virtual_method);
    DCHECK(virtual_method->GetDeclaringClass() != nullptr);
    DCHECK(prototype->GetDeclaringClass() != nullptr);
  }

  // 父类是 java.lang.reflect.Proxy
  temp_klass->SetSuperClass(GetClassRoot(kJavaLangReflectProxy));
  // 现在有效地处于加载状态.
  mirror::Class::SetStatus(temp_klass, mirror::Class::kStatusLoaded, self);
  self->AssertNoPendingException();

 // 此时,该类已加载。 发布一个ClassLoad事件。 
// 注意:这可能是临时类。需要监听者去处理。
  Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(temp_klass);

  MutableHandle<mirror::Class> klass = hs.NewHandle<mirror::Class>(nullptr);
  {
    // 处理时加锁
    ObjectLock<mirror::Class> resolution_lock(self, temp_klass);
    // Link the fields and virtual methods, creating vtable and iftables.
    // The new class will replace the old one in the class table.
    Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
        hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces)));
    if (!LinkClass(self, descriptor.c_str(), temp_klass, h_interfaces, &klass)) {
      mirror::Class::SetStatus(temp_klass, mirror::Class::kStatusErrorUnresolved, self);
      return nullptr;
    }
  }
  CHECK(temp_klass->IsRetired());
  CHECK_NE(temp_klass.Get(), klass.Get());

  CHECK_EQ(interfaces_sfield.GetDeclaringClass(), klass.Get());
  interfaces_sfield.SetObject<false>(
      klass.Get(),
      soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces));
  CHECK_EQ(throws_sfield.GetDeclaringClass(), klass.Get());
  throws_sfield.SetObject<false>(
      klass.Get(),
      soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws));

  Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(temp_klass, klass);

  {
    //释放对klass的锁定。 锁定新的类对象。
    ObjectLock<mirror::Class> initialization_lock(self, klass);
    mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized, self);
  }

  // 健全性检查
  if (kIsDebugBuild) {
    CHECK(klass->GetIFieldsPtr() == nullptr);
    CheckProxyConstructor(klass->GetDirectMethod(0, image_pointer_size_));

    for (size_t i = 0; i < num_virtual_methods; ++i) {
      auto* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
      auto* prototype = h_methods->Get(i++)->GetArtMethod();
      CheckProxyMethod(virtual_method, prototype);
    }

    StackHandleScope<1> hs2(self);
    Handle<mirror::String> decoded_name = hs2.NewHandle(soa.Decode<mirror::String>(name));
    std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces",
                                                   decoded_name->ToModifiedUtf8().c_str()));
    CHECK_EQ(ArtField::PrettyField(klass->GetStaticField(0)), interfaces_field_name);

    std::string throws_field_name(StringPrintf("java.lang.Class[][] %s.throws",
                                               decoded_name->ToModifiedUtf8().c_str()));
    CHECK_EQ(ArtField::PrettyField(klass->GetStaticField(1)), throws_field_name);

    CHECK_EQ(klass.Get()->GetProxyInterfaces(),
             soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces));
    CHECK_EQ(klass.Get()->GetProxyThrows(),
             soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws));
  }
  return klass.Get();
}

朦朦胧胧的通过注释, 大概理清楚了创建一个类需要的成分以及步骤. 也大致对类所包含的元素有了个更深的概念,如AccessFlags ,iftable, MethodArray等,以后用到了这部分的东西再回过头来补全吧。勉强解决我们了的第二个问题。

参考:
https://zhaomenghuan.js.org/blog/android-plugin-framework-proxy-hook.html
https://www.cnblogs.com/MOBIN/p/5597215.html
http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容