Android Open Source - probe View Proxy Builder






From Project

Back to project page probe.

License

The source code is released under:

Apache License

If you think the Android project probe listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2014 Lucas Rocha//from   w w  w .  j  a v a  2 s.co  m
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.lucasr.probe;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import static org.lucasr.probe.ViewClassUtil.findProxyViewClass;

import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * Builds a proxy class that redirects {@link View} layout-related method
 * calls to an {@link Interceptor}.
 *
 * {@link ViewProxyBuilder} is used by {@link ProbeViewFactory} to
 * wrap the inflated {@link View} instances for a given {@link Probe}.
 *
 * @see Probe
 * @see ProbeViewFactory
 */
final class ViewProxyBuilder<T extends View> {
    private static final Map<Class<?>, Class<?>> sGeneratedProxyClasses =
            Collections.synchronizedMap(new HashMap<Class<?>, Class<?>>());

    static final Class<?>[] CONSTRUCTOR_ARG_TYPES = new Class<?>[] {
        Context.class, AttributeSet.class
    };
    private static final Object[] CONSTRUCTOR_ARG_VALUES = new Object[2];

    private final Context mContext;
    private final Class<T> mBaseClass;
    private final ClassLoader mParentClassLoader;
    private Interceptor mInterceptor;

    private ViewProxyBuilder(Context context, Class<T> clazz) {
        mContext = context;
        mBaseClass = clazz;
        mParentClassLoader = context.getClassLoader();
    }

    private static <T> Constructor<? extends T> getProxyClassConstructor(Class<? extends T> proxyClass) {
        final Constructor<? extends T> constructor;
        try {
            constructor = proxyClass.getConstructor(CONSTRUCTOR_ARG_TYPES);
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("No constructor for " + proxyClass.getName()
                    + " with parameter types " + Arrays.toString(CONSTRUCTOR_ARG_TYPES));
        }

        return constructor;
    }

    /**
     * Generates dynamic {@link View} proxy class.
     */
    @SuppressWarnings("unchecked")
    private Class<? extends T> generateProxyClass() throws IOException {
        if (ViewProxy.class.isAssignableFrom(mBaseClass)) {
            return mBaseClass;
        }

        Class<? extends T> proxyClass = (Class) sGeneratedProxyClasses.get(mBaseClass);
        if (proxyClass != null &&
                (proxyClass.getClassLoader() == mParentClassLoader ||
                 proxyClass.getClassLoader().getParent() == mParentClassLoader)) {
            // Cache hit; return immediately.
            return proxyClass;
        }

        proxyClass = (Class<? extends T>) findProxyViewClass(mContext, mBaseClass.getName());
        if (proxyClass != null) {
            // This app ships with the build-time proxy.
            sGeneratedProxyClasses.put(mBaseClass, proxyClass);
            return proxyClass;
        }

        try {
            Class.forName("com.google.dexmaker.DexMaker");
            return DexProxyBuilder.generateProxyClass(mContext, mBaseClass);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

    private static RuntimeException launderCause(InvocationTargetException e) {
        final Throwable cause = e.getCause();

        // Errors should be thrown as they are.
        if (cause instanceof Error) {
            throw (Error) cause;
        }

        // RuntimeException can be thrown as-is.
        if (cause instanceof RuntimeException) {
            throw (RuntimeException) cause;
        }

        // Declared exceptions will have to be wrapped.
        throw new UndeclaredThrowableException(cause);
    }

    static <T> ViewProxyBuilder forClass(Context context, Class<T> clazz) {
        return new ViewProxyBuilder(context, clazz);
    }

    ViewProxyBuilder interceptor(Interceptor interceptor) {
        mInterceptor = interceptor;
        return this;
    }

    ViewProxyBuilder constructorArgValues(Context context, AttributeSet attrs) {
        CONSTRUCTOR_ARG_VALUES[0] = context;
        CONSTRUCTOR_ARG_VALUES[1] = attrs;
        return this;
    }

    /**
     * Builds instance of the built {@link View} proxy class..
     */
    View build() throws IOException {
        final Class<? extends T> proxyClass = generateProxyClass();
        if (proxyClass == null) {
            return null;
        }

        final Constructor<? extends T> constructor = getProxyClassConstructor(proxyClass);

        final View result;
        try {
            result = constructor.newInstance(CONSTRUCTOR_ARG_VALUES);
        } catch (InstantiationException e) {
            // Should not be thrown, generated class is not abstract.
            throw new AssertionError(e);
        } catch (IllegalAccessException e) {
            // Should not be thrown, the generated constructor is accessible.
            throw new AssertionError(e);
        } catch (InvocationTargetException e) {
            // Thrown when the base class constructor throws an exception.
            throw launderCause(e);
        }

        ((ViewProxy) result).setInterceptor(mInterceptor);
        return result;
    }
}




Java Source Code List

org.lucasr.probe.DexProxyBuilder.java
org.lucasr.probe.Filter.java
org.lucasr.probe.Interceptor.java
org.lucasr.probe.ProbeViewFactory.java
org.lucasr.probe.Probe.java
org.lucasr.probe.ViewClassUtil.java
org.lucasr.probe.ViewProxyBuilder.java
org.lucasr.probe.ViewProxy.java
org.lucasr.probe.interceptors.LayoutBoundsInterceptor.java
org.lucasr.probe.interceptors.OvermeasureInterceptor.java
org.lucasr.probe.sample.MainActivity.java