插件化筆記 - 動(dòng)態(tài)代理

插件化離不開代理與反射,反射我們已經(jīng)很熟悉了,我們可以在各種各樣的三方庫(kù)中找到類似的使用,但是代理卻感覺是一個(gè)抽象的概念。

代理模式

為其他對(duì)象提供一種代理,以控制對(duì)這個(gè)對(duì)象的訪問。代理對(duì)象在客戶端和目標(biāo)對(duì)象之間起到中介的作用(結(jié)構(gòu)型)。
使用代理可以屏蔽內(nèi)部實(shí)現(xiàn)細(xì)節(jié),后續(xù)內(nèi)部有變動(dòng)對(duì)于外部調(diào)用者來(lái)說(shuō)是封閉的,符合開放-封閉原則。用戶可以放心地請(qǐng)求代理,他只關(guān)心是否能得到想要的結(jié)果。在任何使用本體的地方都可以替換成使用代理,從而實(shí)現(xiàn)實(shí)現(xiàn)和調(diào)用松耦合。
換句話說(shuō),就是讓別人幫我們?nèi)ネ瓿晌覀兿肴ネ瓿傻氖虑椤?/p>

在代理模式又可以分為動(dòng)態(tài)代理和靜態(tài)代理,我們以java層的代理來(lái)說(shuō)明兩種形式

需求: 通過(guò)使用代理,在點(diǎn)擊事件的前后,加入我們需要的log信息

既然要通過(guò)代理模式,那么我們是不能直接去修改onClick回調(diào)的,所以我們先吧固定的點(diǎn)擊事件準(zhǔn)備好:

   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));
          }
     });

在完成需求之前 ,我們要考慮以下幾個(gè)問題

  1. 我要代理這個(gè)對(duì)象的什么方法
  2. 怎么將我們自己的代理對(duì)象塞進(jìn)去

首先,點(diǎn)擊事件的公共入口都是View.OnclickListener 接口,在為View設(shè)置點(diǎn)擊事件的接口時(shí),View對(duì)象會(huì)將當(dāng)前的點(diǎn)擊事件保存下來(lái)

public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        // 跟進(jìn)getListenerInfo()
        getListenerInfo().mOnClickListener = l; 
    }
//.........
  
   // getListenerInfo為一個(gè)單例,返回了ListenerInfo 的對(duì)象
    ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

// .........
// ListenerInfo就是一個(gè)靜態(tài)類,保存了所有關(guān)于事件的變量.
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;
    }

所以 , 我們可以得出結(jié)論

  1. hook的目標(biāo)為View.setOnclickListener(View.OnClickListener listener)這個(gè)方法.這個(gè)方法本質(zhì)調(diào)用了getListenerInfo().mOnClickListener=listener,我們可以拿到View的實(shí)例后獲取該方法.
  2. 如何賦值,通過(guò)反射的filed.set(target,proxy)完成,其中,target為getListenerInfo().mOnClickListener,proxy為我們代理的函數(shù)。
    接下來(lái)我們使用兩種形式的代理模式來(lái)完成需求

靜態(tài)代理


// 先拿到ListenerInfo對(duì)象, 再對(duì)對(duì)象中的mOnClickListener賦值
public static void hook(Context context, final View v) {
        try {
            //首先獲取getListenerInfo , 獲取到實(shí)際的復(fù)制對(duì)象ListenerInfo
            // 反射執(zhí)行View類的getListenerInfo()方法,拿到v的mListenerInfo對(duì)象,這個(gè)對(duì)象就是點(diǎn)擊事件的持有者
            Method method = View.class.getDeclaredMethod("getListenerInfo");
            method.setAccessible(true);//由于getListenerInfo()方法并不是public的,所以要加這個(gè)代碼來(lái)保證訪問權(quán)限
            //調(diào)用method方法 , 返回ListenerInfo的實(shí)例
            Object mListenerInfo = method.invoke(v);//這里拿到的就是mListenerInfo對(duì)象,也就是點(diǎn)擊事件的持有者

            //要從這里面拿到當(dāng)前的點(diǎn)擊事件對(duì)象
            Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");// 這是內(nèi)部類的表示方法
            Field field = listenerInfoClz.getDeclaredField("mOnClickListener");
            final View.OnClickListener onClickListenerInstance = (View.OnClickListener) field.get(mListenerInfo);//取得真實(shí)的mOnClickListener對(duì)象

            //2. 創(chuàng)建我們自己的點(diǎn)擊事件代理類
            ProxyOnClickListener proxyOnClickListener = new ProxyOnClickListener(onClickListenerInstance);
            //3. 用我們自己的點(diǎn)擊事件代理類,設(shè)置到"持有者"中
            field.set(mListenerInfo, proxyOnClickListener);
            //完成
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 自定義代理類 , 實(shí)現(xiàn)了對(duì) View.OnClickListener 的onClick的代理 , 我們可以在這里進(jìn)行l(wèi)og的輸出
    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", "點(diǎn)擊事件被hook到了");
            if (oriLis != null) {
                oriLis.onClick(v);
            }
        }
    }
點(diǎn)擊事件執(zhí)行結(jié)果

動(dòng)態(tài)代理

如果每一個(gè)需要代理的地方都需要去寫一個(gè)代理類豈不是很麻煩么 ,java提供了Proxy這個(gè)類來(lái)輔助我們?nèi)?chuàng)建代理類,接下來(lái)我們用動(dòng)態(tài)代理去完成上面的需求

public static void hook(Context context, final View v) {//
        try {
            //首先獲取getListenerInfo , 獲取到實(shí)際的復(fù)制對(duì)象ListenerInfo
            // 反射執(zhí)行View類的getListenerInfo()方法,拿到v的mListenerInfo對(duì)象,這個(gè)對(duì)象就是點(diǎn)擊事件的持有者
            Method method = View.class.getDeclaredMethod("getListenerInfo");
            method.setAccessible(true);//由于getListenerInfo()方法并不是public的,所以要加這個(gè)代碼來(lái)保證訪問權(quán)限
            //調(diào)用method方法 , 返回ListenerInfo的實(shí)例
            Object mListenerInfo = method.invoke(v);//這里拿到的就是mListenerInfo對(duì)象,也就是點(diǎn)擊事件的持有者

            //要從這里面拿到當(dāng)前的點(diǎn)擊事件對(duì)象
            Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");// 這是內(nèi)部類的表示方法
            Field field = listenerInfoClz.getDeclaredField("mOnClickListener");
            final View.OnClickListener onClickListenerInstance = (View.OnClickListener) field.get(mListenerInfo);//取得真實(shí)的mOnClickListener對(duì)象

            //2. 創(chuàng)建我們自己的點(diǎn)擊事件代理類
            //   方式2:由于View.OnClickListener是一個(gè)接口,所以可以直接用動(dòng)態(tài)代理模式
            // Proxy.newProxyInstance的3個(gè)參數(shù)依次分別是:
            Object proxyOnClickListener = Proxy.newProxyInstance(
                    context.getClass().getClassLoader(), // 本地的類加載器;
                    new Class[]{View.OnClickListener.class},// 代理類的對(duì)象所繼承的接口(用Class數(shù)組表示,支持多個(gè)接口)
                    new InvocationHandler() {  // 代理類的實(shí)際邏輯,當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
                        /**
                         * @param proxy 被代理對(duì)象
                         * @param method 被代理的方法
                         * @param args 參數(shù)表
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Log.d("HookSetOnClickListener", "點(diǎn)擊事件被hook到了");//加入自己的邏輯
                            return method.invoke(onClickListenerInstance, args);//執(zhí)行被代理的對(duì)象的邏輯
                        }
                    });
            Log.e("hook: getClass ",  proxyOnClickListener.getClass().toString());
            Log.e("hook: getInterfaces", proxyOnClickListener.getClass().getInterfaces().toString());
            Log.e("hook: getMethods ", proxyOnClickListener.getClass().getMethods().toString());
           
//            //3. 用我們自己的點(diǎn)擊事件代理類,設(shè)置到"持有者"中
            field.set(mListenerInfo, proxyOnClickListener);
            //完成
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

前面獲取被代理的函數(shù)步驟都是一樣的,區(qū)別在于我們的代理對(duì)象的創(chuàng)建,是通過(guò)
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)來(lái)完成,其中第二個(gè)參數(shù)是interfaces的數(shù)組,也就是說(shuō)被代理類可以多實(shí)現(xiàn)。但是這里為什么只能是一個(gè)接口呢?這個(gè)問題我們后文繼續(xù)討論。
第三個(gè)參數(shù)InvocationHandler是一個(gè)創(chuàng)建代理類的接口,接口回調(diào)中的參數(shù)已經(jīng)在代碼中標(biāo)出.
在中間我們獲取了代理類的實(shí)例后,我打印了一些類的信息,來(lái)輔助分析。

點(diǎn)擊事件結(jié)果

代理類的部分信息

這個(gè)代理類居然是$Proxy0? 我們并沒有創(chuàng)建這個(gè)代理類啊,這個(gè)代理類究竟被創(chuàng)建到了什么地方?

動(dòng)態(tài)代理分析:

  1. 為什么動(dòng)態(tài)代理只能代理接口實(shí)現(xiàn)類而不是任意類呢?
  2. 動(dòng)態(tài)代理的類放在那里?這個(gè)類是怎么憑空產(chǎn)生的?

準(zhǔn)備工作: 谷歌翻譯 AndroidXRef

首先,我們跟進(jìn)Proxy.newInstance()的源碼,看看官方對(duì)于這個(gè)參數(shù)的解釋

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

這里已經(jīng)說(shuō)明了這個(gè)參數(shù)必須是接口數(shù)組。我們不能就這么死心吧?我們直接換一個(gè)非接口實(shí)現(xiàn)類來(lái)試試用動(dòng)態(tài)代理去代理它
這里參考了[歡哥的博客 - 初識(shí)-hook-機(jī)制]https://zhaomenghuan.js.org/blog/android-plugin-framework-proxy-hook.html#%E5%88%9D%E8%AF%86-hook-%E6%9C%BA%E5%88%B6) , 借用他對(duì)于startActivityForResult的靜態(tài)代理 , 我們使用動(dòng)態(tài)代理來(lái)分析一波.

    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()},// 代理類的對(duì)象所繼承的接口(用Class數(shù)組表示,支持多個(gè)接口)
                    new InvocationHandler() {  // 代理類的實(shí)際邏輯,當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
                        /**
                         * @param proxy 被代理對(duì)象
                         * @param method 被代理的方法
                         * @param args 參數(shù)表
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Log.d("Instrumentation", "Instrumentation事件被hook到了");//加入自己的邏輯
                            return method.invoke(originalInstrumentation, args);//執(zhí)行被代理的對(duì)象的邏輯
                        }
                    });
            //3. 用我們自己的點(diǎn)擊事件代理類,設(shè)置到"持有者"中
            mInstrumentationField.set(mainActivity, proxyIns);
        } catch (Exception e) {
             Log.e("hookInstrumentation", e.toString());
        }
    }

我們點(diǎn)擊跳轉(zhuǎn)之后, 并沒有輸出我們想要的log信息, 網(wǎng)上追蹤logcat ,發(fā)現(xiàn)我們?cè)谡{(diào)用這個(gè)靜態(tài)方法的時(shí)候就已經(jīng)報(bào)錯(cuò);
E/hookInstrumentation: java.lang.IllegalArgumentException: android.app.Instrumentation is not an interface
看來(lái)動(dòng)態(tài)代理只能對(duì)接口實(shí)現(xiàn)類使用 , 而非接口實(shí)現(xiàn)類, 如Instrumentation 只能使用靜態(tài)代理了.

接下來(lái)我們帶著上面的問題來(lái)扒一波源碼
API: < Android API 28 Platform >

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

        final Class<?>[] intfs = interfaces.clone();
        // 這個(gè)注釋我們能看出來(lái) , 這個(gè)版本對(duì)于之前版本做過(guò)一些修改:移除代理的安全校驗(yàn)
        // 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);
            }
            */
           
            // 拿到代理類的構(gòu)造函數(shù)
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                 cons.setAccessible(true);
            }
            // 返回創(chuàng)造完成的代理類的實(shí)例
            return cons.newInstance(new Object[]{h});
        } catch (Exception e) {
            ......
        }
    }

在這個(gè)方法中 , 僅僅是一局getProxyClass0(loader, intfs);就完成了代理類的創(chuàng)建.我們跟進(jìn)代碼

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // 接口數(shù)量上限控制
        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);
    }

簡(jiǎn)單翻譯一下:
如果由實(shí)現(xiàn)了給定接口的給定加載器定義的代理類存在,則將簡(jiǎn)單地返回緩存的副本; 否則,它將通過(guò)ProxyClassFactory創(chuàng)建代理類。雖然是一個(gè)get的操作 , 但總是不為空的。首先我們開搞清楚這是個(gè)什么東西
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
有點(diǎn)懵? 猜一下 , 像是一個(gè)緩存 , 這里我暫時(shí)不去深究了, 有興趣的大佬可以去研究一波https://www.iteye.com/blog/xiaoxiaoher-2372315
。初始化時(shí)會(huì)傳入兩個(gè)工廠:KeyFactory和ProxyClassFactory。
在上面的方法中,第一個(gè)參數(shù)對(duì)應(yīng)了classLoader,第二個(gè)參數(shù)為Interfaces ,為了盡快接近我們提出的問題1 ,我們先來(lái)看看第二個(gè)工廠類ProxyClassFactory。
又要貼出長(zhǎng)長(zhǎng)的代碼了,這里我把代碼分成兩個(gè)部分來(lái)看。

    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 所有代理類名稱的前綴
        private static final String proxyClassNamePrefix = "$Proxy";
        // 下一個(gè)用于生成唯一代理類名稱的數(shù)字
        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) {
                // 驗(yàn)證指定的類加載器(loader)加載接口所得到的Class對(duì)象(interfaceClass)是否與intf對(duì)象相同
                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");
                }
                // 驗(yàn)證傳入的class對(duì)象是不是接口 , 由此,我們的第一個(gè)問題得以解決: 因?yàn)镾DK里面不讓我們這么傳 - - 
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                // 驗(yàn)證接口是否重復(fù)
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

我們的第一個(gè)問題的答案就在這里找到了, 動(dòng)態(tài)代理傳入的Class[]對(duì)象在內(nèi)部會(huì)進(jìn)行類型檢查, 如果不是接口則會(huì)拋出異常。
其實(shí)我們也可以這么來(lái)理解:java是單繼承多實(shí)現(xiàn)的 ,既然我們代理類的生成結(jié)果都是繼承了Proxy類,然后再去實(shí)現(xiàn)了其他接口,那么必然不能直接傳一個(gè)非接口對(duì)象,要不然和java的單繼承就相悖了。我們繼續(xù)看接下來(lái)的代碼

   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()][]);

                // 代理類的計(jì)數(shù)器加一
                long num = nextUniqueNumber.getAndIncrement();
                // 拼接代理類的類名,我們大致能看到的樣子就是 xxxx$proxy0.class
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
               // 生成代理類
                return generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
     

最夠追到生成代理類的代碼 ,卻發(fā)現(xiàn)這是一個(gè)調(diào)用了JNI的函數(shù)

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

在AndroidXRef中搜索 , 可以看到改JNI函數(shù)位于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));
}

跟進(jìn)class_linker->CreateProxyClass()這個(gè)函數(shù) , 位于class_linker.cc中
ps:這個(gè)類比較大,用網(wǎng)頁(yè)加載直接死掉了,建議下載下來(lái)再看。
由于鄙人對(duì)于C層的代碼基本沒怎么看過(guò),所以只能以注釋為主先來(lái)宏觀理解一下了,歡迎有經(jīng)驗(yàn)的前輩們指教。
這里貼出簡(jiǎn)(漢)化后的代碼

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));
  // 設(shè)置包含類的訪問標(biāo)志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有一個(gè)空的 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());

  // 在插入類之前需要先設(shè)置好內(nèi)存分配器。
  LinearAlloc* const allocator = GetOrCreateAllocatorForClassLoader(temp_klass->GetClassLoader());

  // 在加載字段之前插入類,因?yàn)閮H從類表訪問字段根ArtField :: declaring_class_)
 // 所以插入類和設(shè)置下面的字段數(shù)組之間不能有任何暫停點(diǎn)。
  ObjPtr<mirror::Class> existing = InsertClass(descriptor.c_str(), temp_klass.Get(), hash);
  CHECK(existing == nullptr);

  // 實(shí)例字段是繼承的,但是我們添加了兩個(gè)靜態(tài)字段...
  const size_t num_fields = 2;
  LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self, allocator, num_fields);
  temp_klass->SetSFieldsPtr(sfields);

 // 1.創(chuàng)建一個(gè)靜態(tài)字段“ interfaces”,其中包含由_declared_接口實(shí)現(xiàn)的
 // 我們的代理,因此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.創(chuàng)建一個(gè)靜態(tài)字段“ throws”,其中包含我們的方法拋出的異常。
  ArtField& throws_sfield = sfields->At(1);
  throws_sfield.SetDexFieldIndex(1);
  throws_sfield.SetDeclaringClass(temp_klass.Get());
  throws_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);

  // 代理類會(huì)有一個(gè)直接方法, 即構(gòu)造函數(shù)
  const size_t num_direct_methods = 1;
  // 它們具有與數(shù)組一樣多的虛擬方法
  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();

  // 創(chuàng)建方法數(shù)組.
  LengthPrefixedArray<ArtMethod>* proxy_class_methods = AllocArtMethodArray(
        self, allocator, num_direct_methods + num_virtual_methods);
  // 當(dāng)前,AllocArtMethodArray不能返回null,但是OOM邏輯保留在那里,以防將來(lái)我們要拋出OOM。
  if (UNLIKELY(proxy_class_methods == nullptr)) {
    self->AssertPendingOOMException();
    return nullptr;
  }
  temp_klass->SetMethodsPtr(proxy_class_methods, num_direct_methods, num_virtual_methods);

  // 創(chuàng)建一個(gè)直接方法
  CreateProxyConstructor(temp_klass, temp_klass->GetDirectMethodUnchecked(0, image_pointer_size_));

  // 使用指定的原型創(chuàng)建虛擬方法。
  // 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));
  // 現(xiàn)在有效地處于加載狀態(tài).
  mirror::Class::SetStatus(temp_klass, mirror::Class::kStatusLoaded, self);
  self->AssertNoPendingException();

 // 此時(shí),該類已加載。 發(fā)布一個(gè)ClassLoad事件。 
// 注意:這可能是臨時(shí)類。需要監(jiān)聽者去處理。
  Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(temp_klass);

  MutableHandle<mirror::Class> klass = hs.NewHandle<mirror::Class>(nullptr);
  {
    // 處理時(shí)加鎖
    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);

  {
    //釋放對(duì)klass的鎖定。 鎖定新的類對(duì)象。
    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();
}

朦朦朧朧的通過(guò)注釋, 大概理清楚了創(chuàng)建一個(gè)類需要的成分以及步驟. 也大致對(duì)類所包含的元素有了個(gè)更深的概念,如AccessFlags ,iftable, MethodArray等,以后用到了這部分的東西再回過(guò)頭來(lái)補(bǔ)全吧。勉強(qiáng)解決我們了的第二個(gè)問題。

參考:
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/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容