如何在Java回调函数中获取调用者对象?

时间:2016-10-12 08:26:57

标签: java android reflection

我正试图在android-21之前找不到shouldInterceptRequest(.. request)。所以我只有shouldInterceptRequest(android.webkit.WebView, java.util.String)。但是当在android-19上运行时,我可以在调用者中看到isMainFrame属性,我需要知道:

enter image description here

不幸的是,调用者没有在调用中提供this,因此我没有直接引用。如何在我的侦听器对象(IoThreadClientImpl中)的方法中获取调用者(WebViewClient)对象引用更详细?我相信应该使用常规的java反射(或某些特定的android反射)。

PS 1. Thread.currentThread().getStackTrace()不合适,因为它不提供调用者对象,类/方法/行。

PS 2.似乎setting自定义SecurityManager不是解决方案,因为它会引发异常..

1 个答案:

答案 0 :(得分:0)

似乎在一般情况下这是不可能的,但在我的情况下,我能够在IoThreadClient实例中为AwContents创建代理并拦截调用 - 如果它是我的必需方法shouldInterceptRequest,则为2 args - 我只记得它,然后将调用传递给原始实例(所需的值不在调用者对象中,而是在调用参数中)。

WebView继承者中的代理注入:

private synchronized void initIoThreadClient()
  {
    final Object awContents = ReflectionUtils.extractProperty(this, new String[]
      {
        "mProvider",
        "mAwContents"
      });

    final String ioThreadClientProperty = "mIoThreadClient";
    final Object originalClient = ReflectionUtils.extractProperty(
      awContents,
      new String[]
      {
        ioThreadClientProperty
      });

    // avoid injecting twice (already injected Proxy instance has another class name)
    if (!originalClient.getClass().getSimpleName().startsWith("$Proxy"))
    {
      Object proxyClient = Proxy.newProxyInstance(
        originalClient.getClass().getClassLoader(),
        originalClient.getClass().getInterfaces(),
        new IoThreadClientInvocationHandler(originalClient));

      // inject proxy instead of original client
      boolean injected = ReflectionUtils.injectProperty(awContents, ioThreadClientProperty, proxyClient);
      if (injected)
      {
        Integer mNativeAwContents = (Integer) ReflectionUtils.extractProperty(awContents, "mNativeAwContents");
        Object mWebContentsDelegate = ReflectionUtils.extractProperty(awContents, "mWebContentsDelegate");
        Object mContentsClientBridge = ReflectionUtils.extractProperty(awContents, "mContentsClientBridge");
        Object mInterceptNavigationDelegate = ReflectionUtils.extractProperty(awContents, "mInterceptNavigationDelegate");

        boolean invoked = ReflectionUtils.invokeMethod(awContents, "nativeSetJavaPeers", new Object[]
          {
            mNativeAwContents, awContents, mWebContentsDelegate,
            mContentsClientBridge, proxyClient, mInterceptNavigationDelegate
          });
        if (!invoked)
        {
          e("Failed to inject IoThreadClient proxy");
        }
      }
    }

IoThreadClientInvocationHandler类:

public class IoThreadClientInvocationHandler implements InvocationHandler
{
  private final String TAG = Utils.getTag(IoThreadClientInvocationHandler.class);

  public static transient Boolean isMainFrame;
  private Object wrappedObject;

  public IoThreadClientInvocationHandler(Object wrappedObject)
  {
    this.wrappedObject = wrappedObject;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  {
    // intercept invocation and store 'isMainFrame' argument
    if (method.getName().startsWith("shouldInterceptRequest") && args.length == 2)
    {
      String url = (String)args[0];
      isMainFrame = (Boolean)args[1];

      Log.d(TAG, "isMainFrame=" + isMainFrame + " for " + url);
    }
    return method.invoke(wrappedObject, args);
  }
}

ReflectionUtils类:

public class ReflectionUtils
{
  public static Object extractProperty(Object target, String field)
  {
    return extractProperty(target, target.getClass(), field);
  }

  public static Object extractProperty(Object target, Class clazz, String field)
  {
    try
    {
      Field f = clazz.getDeclaredField(field);
      if (!f.isAccessible())
      {
        f.setAccessible(true);
      }
      return f.get(target);
    }
    catch (NoSuchFieldException e)
    {
      return extractProperty(target, clazz.getSuperclass(), field);
    }
    catch (Exception e)
    {
      return null;
    }
  }

  public static Object extractProperty(Object target, String []fields)
  {
    Object eachTarget = target;
    for (int i = 0; i < fields.length; i++)
    {
      eachTarget = extractProperty(eachTarget, eachTarget.getClass(), fields[i]);
      if (eachTarget == null)
      {
        return null;
      }
    }
    return eachTarget;
  }

  public static boolean injectProperty(Object target, String field, Object value)
  {
    try
    {
      Field f = target.getClass().getDeclaredField(field);
      if (!f.isAccessible())
      {
        f.setAccessible(true);
      }
      f.set(target, value);
      return true;
    }
    catch (Exception e)
    {
      return false;
    }
  }

  public static boolean invokeMethod(Object target, String methodName, Object[] args)
  {
    Method[] methods = target.getClass().getDeclaredMethods();
    for (Method eachMethod : methods)
    {
      if (eachMethod.getName().equals(methodName))
      {
        try
        {
          if (!eachMethod.isAccessible())
          {
            eachMethod.setAccessible(true);
          }
          eachMethod.invoke(target, args);
          return true;
        }
        catch (Exception e)
        {
          return false;
        }
      }
    }
    return false;
  }
}