我正试图在android-21之前找不到shouldInterceptRequest(.. request)
。所以我只有shouldInterceptRequest(android.webkit.WebView, java.util.String)
。但是当在android-19上运行时,我可以在调用者中看到isMainFrame
属性,我需要知道:
不幸的是,调用者没有在调用中提供this
,因此我没有直接引用。如何在我的侦听器对象(IoThreadClientImpl
中)的方法中获取调用者(WebViewClient
)对象引用更详细?我相信应该使用常规的java反射(或某些特定的android反射)。
PS 1. Thread.currentThread().getStackTrace()
不合适,因为它不提供调用者对象,类/方法/行。
PS 2.似乎setting自定义SecurityManager不是解决方案,因为它会引发异常..
答案 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;
}
}