кажется, что это невозможно в общем случае, но в моем случае я был в состоянии создать прокси-сервер для IoThreadClient
в AwContents
инстанции и вызов перехвата - если это мой обязательный метод shouldInterceptRequest
с 2 аргументами - я просто его помню, а затем передаю вызов в исходный экземпляр (требуемое значение не было в объекте invoker, а в аргументах вызова).
инъекции прокси в 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;
}
}