Giter VIP home page Giter VIP logo

safe-java-js-webview-bridge's People

Contributors

pedant avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

safe-java-js-webview-bridge's Issues

发现问题

关于网页调用玩Java后回调js的问题,例如弹出个dialog点击确定后后回调js,但却不成功

发现问题

发现一问题,JsCallback回调时报空指针错误:
Caused by: java.lang.NullPointerException
at android.webkit.WebViewClassic.loadUrlImpl(WebViewClassic.java:2678)
at android.webkit.WebViewClassic.loadUrlImpl(WebViewClassic.java:2694)
at android.webkit.WebViewClassic.loadUrl(WebViewClassic.java:2687)
at android.webkit.WebView.loadUrl(WebView.java:804)
at cn.pedant.SafeWebViewBridge.JsCallback.apply(JsCallback.java:42)
at com.jidian.android.edo.util.HostJsScope$2.onComplete(HostJsScope.java:137)

我猜测可能是这样的,因为回调有延迟,callback.apply()的时候可能webview已经被关闭,导致的这个错误

WebView默认接口searchBoxJavaBridge_依然存在js注入漏洞

       您好,我发现该库没有移除WebView的默认接口searchBoxJavaBridge_,使用该默认接口依然可以通过js注入漏洞,注入方法可以参考文章中第三小节中的js方法execute。
       在使用该库之前调用webView.removeJavascriptInterface("searchBoxJavaBridge_");应该就可以解决这个问题。

我把你的JsCallJava的代码改了一下,用起来会更加灵活:(你可以参考一下)

我把你的代码改了一下,用起来会更加灵活:(你可以参考一下)
new MyInjectedChromeClient(new JsCallJava.InjectObj("myjs.ui", MyJs.class),
new JsCallJava.InjectObj("myjs.ui2", MyJs2.class,MyJs3.class,MyJs4.class));

package cn.pedant.SafeWebViewBridge;

import android.text.TextUtils;
import android.webkit.WebView;
import android.util.Log;

import com.google.gson.Gson;

import org.json.JSONArray;
import org.json.JSONObject;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;

public class JsCallJava
{
private final static String TAG = "JsCallJava";
private final static String RETURN_RESULT_FORMAT = "{"code": %d, "result": %s}";
private HashMap<String, Method> mMethodsMap;
/////<private String mInjectedName;>/////
private String mPreloadInterfaceJS;
private Gson mGson;

/////<
public static class InjectObj
{
    String namespace;
    Class<?>[] interfaceClasses;

    public InjectObj(String namespace, Class<?>... interfaceClasses)
    {
        if (TextUtils.isEmpty(namespace))
        {
            throw new RuntimeException("namespace can not be null!");
        }
        this.namespace = namespace;
        this.interfaceClasses = interfaceClasses;
    }
}
/////>

public JsCallJava(InjectObj... injectObjs)
{
    try
    {

        mMethodsMap = new HashMap<String, Method>();
        StringBuilder sbuilder = new StringBuilder("javascript:");

        for (InjectObj injectObj : injectObjs)
        {
            injectOne(sbuilder, injectObj);
        }

        mPreloadInterfaceJS = sbuilder.toString();
    } catch (Exception e)
    {
        e.printStackTrace();
        throw new RuntimeException("init js error:" + e.getMessage());
    }
}

private void injectOne(StringBuilder sbuilder, InjectObj injectObj)
{
    String mInjectedName = injectObj.namespace;

    StringBuilder sb = new StringBuilder("(function(b){console.log(\"");/////去掉前面的javascript://////

    sb.append(mInjectedName);
    sb.append(
            " initialization begin\");var a={");

    sb.append("namespace:\"").append(mInjectedName).append("\",");//////添加一个namespace///////

    sb.append("queue:[],callback:function(){var d=Array.prototype.slice.call" +
            "(arguments,0);var c=d.shift();var e=d.shift();this.queue[c].apply(this,d);if(!e){delete " +
            "this.queue[c]}}};");


    //////////////<
    for (Class<?> c : injectObj.interfaceClasses)
    {
        searchClass(sb, c);
    }

    StringBuilder namespaces = new StringBuilder();

    {
        StringBuilder temp = new StringBuilder();
        String[] ss = injectObj.namespace.split("\\.");
        for (String s : ss)
        {
            if ("".equals(s))
            {
                continue;
            } else
            {
                temp.append(".").append(s);
                namespaces.append("b").append(temp).append("=").append("b").append(temp).append("||{};");
            }
        }
    }

    ///////////>

    sb.append("function(){var f=Array.prototype.slice.call(arguments,0);if(f.length<1){throw\"");
    sb.append(mInjectedName);
    sb.append(
            " call error, message:miss method name\"}var e=[];for(var h=1;h<f.length;h++){var c=f[h];var " +
                    "j=typeof c;e[e.length]=j;if(j==\"function\"){var d=a.queue.length;a.queue[d]=c;" +
                    "f[h]=d}}var g=JSON.parse(prompt(JSON.stringify({method:f.shift(),types:e,args:f");

    sb.append(",namespace:a.namespace");/////////加入namespace/////////

    sb.append("})));if(g" +
            ".code!=200){throw\"");
    sb.append(mInjectedName);
    sb.append(
            " call error, code:\"+g.code+\", message:\"+g.result}return g.result};Object.getOwnPropertyNames" +
                    "(a).forEach(function(d){var c=a[d];if(typeof c===\"function\"&&d!==\"callback\")" +
                    "{a[d]=function(){return c.apply(a,[d].concat(Array.prototype.slice.call(arguments,0)))" +
                    "}}});");

    sb.append(namespaces);//////////加入,如:b.ui={};b.ui.abc={};

    sb.append("b.").append(mInjectedName);
    sb.append("=a;console.log(\"");
    sb.append(mInjectedName);
    sb.append(" initialization end\")})(window);");

    /////////
    sbuilder.append(sb);
    ////////
}

private void searchClass(StringBuilder sb, Class<?> c)
{
    /////个人建议还是用getMethods,这样可以不用把所有的static函数都挤在一个类里,而可以把一部分放在父类中.//////
    Method[] methods = c.getMethods();

    for (Method method : methods)
    {
        String sign;
        if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (sign = genJavaMethodSign(
                method)) == null)
        {
            continue;
        }
        mMethodsMap.put(sign, method);
        sb.append(String.format("a.%s=", method.getName()));
    }
}

private String genJavaMethodSign(Method method)
{
    String sign = method.getName();
    Class[] argsTypes = method.getParameterTypes();
    int len = argsTypes.length;
    if (len < 1 || argsTypes[0] != WebView.class)
    {
        Log.w(TAG, "method(" + sign + ") must use webview to be first parameter, will be pass");
        return null;
    }
    for (int k = 1; k < len; k++)
    {
        Class cls = argsTypes[k];
        if (cls == String.class)
        {
            sign += "_S";
        } else if (cls == int.class ||
                cls == long.class ||
                cls == float.class ||
                cls == double.class)
        {
            sign += "_N";
        } else if (cls == boolean.class)
        {
            sign += "_B";
        } else if (cls == JSONObject.class)
        {
            sign += "_O";
        } else if (cls == JsCallback.class)
        {
            sign += "_F";
        } else
        {
            sign += "_P";
        }
    }
    return sign;
}

public String getPreloadInterfaceJS()
{
    return mPreloadInterfaceJS;
}

public String call(WebView webView, String jsonStr)
{
    if (!TextUtils.isEmpty(jsonStr))
    {
        try
        {
            JSONObject callJson = new JSONObject(jsonStr);

            String mInjectedName = callJson.getString("namespace");//////得到namespace///////

            String methodName = callJson.getString("method");
            JSONArray argsTypes = callJson.getJSONArray("types");
            JSONArray argsVals = callJson.getJSONArray("args");
            String sign = methodName;
            int len = argsTypes.length();
            Object[] values = new Object[len + 1];
            int numIndex = 0;
            String currType;

            values[0] = webView;

            for (int k = 0; k < len; k++)
            {
                currType = argsTypes.optString(k);
                if ("string".equals(currType))
                {
                    sign += "_S";
                    values[k + 1] = argsVals.isNull(k) ? null : argsVals.getString(k);
                } else if ("number".equals(currType))
                {
                    sign += "_N";
                    numIndex = numIndex * 10 + k + 1;
                } else if ("boolean".equals(currType))
                {
                    sign += "_B";
                    values[k + 1] = argsVals.getBoolean(k);
                } else if ("object".equals(currType))
                {
                    sign += "_O";
                    values[k + 1] = argsVals.isNull(k) ? null : argsVals.getJSONObject(k);
                } else if ("function".equals(currType))
                {
                    sign += "_F";
                    values[k + 1] = new JsCallback(webView, mInjectedName, argsVals.getInt(k));
                } else
                {
                    sign += "_P";
                }
            }

            Method currMethod = mMethodsMap.get(sign);

            // 方法匹配失败
            if (currMethod == null)
            {
                return getReturn(jsonStr, 500, "not found method(" + sign + ") with valid parameters");
            }
            // 数字类型细分匹配
            if (numIndex > 0)
            {
                Class[] methodTypes = currMethod.getParameterTypes();
                int currIndex;
                Class currCls;
                while (numIndex > 0)
                {
                    currIndex = numIndex - numIndex / 10 * 10;
                    currCls = methodTypes[currIndex];
                    if (currCls == int.class)
                    {
                        values[currIndex] = argsVals.getInt(currIndex - 1);
                    } else if (currCls == long.class)
                    {
                        //WARN: argsJson.getLong(k + defValue) will return a bigger incorrect number
                        values[currIndex] = Long.parseLong(argsVals.getString(currIndex - 1));
                    } else
                    {
                        values[currIndex] = argsVals.getDouble(currIndex - 1);
                    }
                    numIndex /= 10;
                }
            }

            return getReturn(jsonStr, 200, currMethod.invoke(null, values));
        } catch (Exception e)
        {
            //优先返回详细的错误信息
            if (e.getCause() != null)
            {
                return getReturn(jsonStr, 500, "method execute error:" + e.getCause().getMessage());
            }
            return getReturn(jsonStr, 500, "method execute error:" + e.getMessage());
        }
    } else
    {
        return getReturn(jsonStr, 500, "call data empty");
    }
}

private String getReturn(String reqJson, int stateCode, Object result)
{
    String insertRes;
    if (result == null)
    {
        insertRes = "null";
    } else if (result instanceof String)
    {
        result = ((String) result).replace("\"", "\\\"");
        insertRes = "\"" + result + "\"";
    } else if (!(result instanceof Integer)
            && !(result instanceof Long)
            && !(result instanceof Boolean)
            && !(result instanceof Float)
            && !(result instanceof Double)
            && !(result instanceof JSONObject))
    {    // 非数字或者非字符串的构造对象类型都要序列化后再拼接
        if (mGson == null)
        {
            mGson = new Gson();
        }
        insertRes = mGson.toJson(result);
    } else
    {  //数字直接转化
        insertRes = String.valueOf(result);
    }
    String resStr = String.format(RETURN_RESULT_FORMAT, stateCode, insertRes);
    ////////Log.d(TAG, mInjectedName + " call json: " + reqJson + " result:" + resStr);
    return resStr;
}

}

请教个问题

在webview中加载的网页时常调用注入的js对象不存在,这个是什么问题呢,非网页加载完毕立即执行

JsCallback.apply(String对象)出错

Log:I/chromium: [INFO:CONSOLE(1)] "Uncaught SyntaxError: Unexpected identifier", source: (1)。
我在调用jsCallback.apply()时,传入一个String对象,在apply方法里,有这样的代码:

StringBuilder sb = new StringBuilder();
for (Object arg : args){
    sb.append(",");
    boolean isStrArg = arg instanceof String;
    if (isStrArg) {
        sb.append("\"");
    }
    sb.append(String.valueOf(arg));
    if (isStrArg) {
        sb.append("\"");
    }
}

此方法会在参数首尾加上双引号。此时当传入的String对象包含双引号时(JSONObject.toString()方法生成的String对象就会包含双引号),就会出错。不包含则不会出现问题。
将append里面的双引号改成单引号能解决此问题。但是如果String对象包含单引号就又会导致这个问题了- -
延伸到既包含双引号又包含单引号的String对象又该如何传递呢?

我把你的JsCallJava.java修改了一下,这样使用起来更加灵活:(你可以参考一下)

我把你的JsCallJava.java修改了一下,这样使用起来更加灵活:(你可以参考一下)

new MyInjectedChromeClient(new JsCallJava.InjectObj("myjs.ui", MyJs.class),new JsCallJava.InjectObj("myjs.ui2", MyJs2.class,MyJs3.class,MyJs4.class))

package cn.pedant.SafeWebViewBridge;

import android.text.TextUtils;
import android.webkit.WebView;
import android.util.Log;

import com.google.gson.Gson;

import org.json.JSONArray;
import org.json.JSONObject;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;

public class JsCallJava
{
private final static String TAG = "JsCallJava";
private final static String RETURN_RESULT_FORMAT = "{"code": %d, "result": %s}";
private HashMap<String, Method> mMethodsMap;
/////private String mInjectedName;/////
private String mPreloadInterfaceJS;
private Gson mGson;

/////
public static class InjectObj
{
    String namespace;
    Class<?>[] interfaceClasses;

    public InjectObj(String namespace, Class<?>... interfaceClasses)
    {
        if (TextUtils.isEmpty(namespace))
        {
            throw new RuntimeException("namespace can not be null!");
        }
        this.namespace = namespace;
        this.interfaceClasses = interfaceClasses;
    }
}
/////

public JsCallJava(InjectObj... injectObjs)
{
    try
    {

        mMethodsMap = new HashMap<String, Method>();
        StringBuilder sbuilder = new StringBuilder("javascript:");

        for (InjectObj injectObj : injectObjs)
        {
            injectOne(sbuilder, injectObj);
        }

        mPreloadInterfaceJS = sbuilder.toString();
    } catch (Exception e)
    {
        e.printStackTrace();
        throw new RuntimeException("init js error:" + e.getMessage());
    }
}

private void injectOne(StringBuilder sbuilder, InjectObj injectObj)
{
    String mInjectedName = injectObj.namespace;

    StringBuilder sb = new StringBuilder("(function(b){console.log(\"");/////去掉前面的javascript://////

    sb.append(mInjectedName);
    sb.append(
            " initialization begin\");var a={");

    sb.append("namespace:\"").append(mInjectedName).append("\",");//////添加一个namespace///////

    sb.append("queue:[],callback:function(){var d=Array.prototype.slice.call" +
            "(arguments,0);var c=d.shift();var e=d.shift();this.queue[c].apply(this,d);if(!e){delete " +
            "this.queue[c]}}};");


    ///////
    for (Class<?> c : injectObj.interfaceClasses)
    {
        searchClass(sb, c);
    }

    StringBuilder namespaces = new StringBuilder();

    {
        StringBuilder temp = new StringBuilder();
        String[] ss = injectObj.namespace.split("\\.");
        for (String s : ss)
        {
            if ("".equals(s))
            {
                continue;
            } else
            {
                temp.append(".").append(s);
                namespaces.append("b").append(temp).append("=").append("b").append(temp).append("||{};");
            }
        }
    }

    //////

    sb.append("function(){var f=Array.prototype.slice.call(arguments,0);if(f.length<1){throw\"");
    sb.append(mInjectedName);
    sb.append(
            " call error, message:miss method name\"}var e=[];for(var h=1;h<f.length;h++){var c=f[h];var " +
                    "j=typeof c;e[e.length]=j;if(j==\"function\"){var d=a.queue.length;a.queue[d]=c;" +
                    "f[h]=d}}var g=JSON.parse(prompt(JSON.stringify({method:f.shift(),types:e,args:f");

    sb.append(",namespace:a.namespace");/////////加入namespace/////////

    sb.append("})));if(g" +
            ".code!=200){throw\"");
    sb.append(mInjectedName);
    sb.append(
            " call error, code:\"+g.code+\", message:\"+g.result}return g.result};Object.getOwnPropertyNames" +
                    "(a).forEach(function(d){var c=a[d];if(typeof c===\"function\"&&d!==\"callback\")" +
                    "{a[d]=function(){return c.apply(a,[d].concat(Array.prototype.slice.call(arguments,0)))" +
                    "}}});");

    sb.append(namespaces);//////////加入,如:b.ui={};b.ui.abc={};

    sb.append("b.").append(mInjectedName);
    sb.append("=a;console.log(\"");
    sb.append(mInjectedName);
    sb.append(" initialization end\")})(window);");

    /////////
    sbuilder.append(sb);
    ////////
}

private void searchClass(StringBuilder sb, Class<?> c)
{
    /////个人建议还是用getMethods,这样可以不用把所有的static函数都挤在一个类里,而可以把一部分放在父类中.//////
    Method[] methods = c.getMethods();

    for (Method method : methods)
    {
        String sign;
        if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (sign = genJavaMethodSign(
                method)) == null)
        {
            continue;
        }
        mMethodsMap.put(sign, method);
        sb.append(String.format("a.%s=", method.getName()));
    }
}

private String genJavaMethodSign(Method method)
{
    String sign = method.getName();
    Class[] argsTypes = method.getParameterTypes();
    int len = argsTypes.length;
    if (len < 1 || argsTypes[0] != WebView.class)
    {
        Log.w(TAG, "method(" + sign + ") must use webview to be first parameter, will be pass");
        return null;
    }
    for (int k = 1; k < len; k++)
    {
        Class cls = argsTypes[k];
        if (cls == String.class)
        {
            sign += "_S";
        } else if (cls == int.class ||
                cls == long.class ||
                cls == float.class ||
                cls == double.class)
        {
            sign += "_N";
        } else if (cls == boolean.class)
        {
            sign += "_B";
        } else if (cls == JSONObject.class)
        {
            sign += "_O";
        } else if (cls == JsCallback.class)
        {
            sign += "_F";
        } else
        {
            sign += "_P";
        }
    }
    return sign;
}

public String getPreloadInterfaceJS()
{
    return mPreloadInterfaceJS;
}

public String call(WebView webView, String jsonStr)
{
    if (!TextUtils.isEmpty(jsonStr))
    {
        try
        {
            JSONObject callJson = new JSONObject(jsonStr);

            String mInjectedName = callJson.getString("namespace");//////得到namespace///////

            String methodName = callJson.getString("method");
            JSONArray argsTypes = callJson.getJSONArray("types");
            JSONArray argsVals = callJson.getJSONArray("args");
            String sign = methodName;
            int len = argsTypes.length();
            Object[] values = new Object[len + 1];
            int numIndex = 0;
            String currType;

            values[0] = webView;

            for (int k = 0; k < len; k++)
            {
                currType = argsTypes.optString(k);
                if ("string".equals(currType))
                {
                    sign += "_S";
                    values[k + 1] = argsVals.isNull(k) ? null : argsVals.getString(k);
                } else if ("number".equals(currType))
                {
                    sign += "_N";
                    numIndex = numIndex * 10 + k + 1;
                } else if ("boolean".equals(currType))
                {
                    sign += "_B";
                    values[k + 1] = argsVals.getBoolean(k);
                } else if ("object".equals(currType))
                {
                    sign += "_O";
                    values[k + 1] = argsVals.isNull(k) ? null : argsVals.getJSONObject(k);
                } else if ("function".equals(currType))
                {
                    sign += "_F";
                    values[k + 1] = new JsCallback(webView, mInjectedName, argsVals.getInt(k));
                } else
                {
                    sign += "_P";
                }
            }

            Method currMethod = mMethodsMap.get(sign);

            // 方法匹配失败
            if (currMethod == null)
            {
                return getReturn(jsonStr, 500, "not found method(" + sign + ") with valid parameters");
            }
            // 数字类型细分匹配
            if (numIndex > 0)
            {
                Class[] methodTypes = currMethod.getParameterTypes();
                int currIndex;
                Class currCls;
                while (numIndex > 0)
                {
                    currIndex = numIndex - numIndex / 10 * 10;
                    currCls = methodTypes[currIndex];
                    if (currCls == int.class)
                    {
                        values[currIndex] = argsVals.getInt(currIndex - 1);
                    } else if (currCls == long.class)
                    {
                        //WARN: argsJson.getLong(k + defValue) will return a bigger incorrect number
                        values[currIndex] = Long.parseLong(argsVals.getString(currIndex - 1));
                    } else
                    {
                        values[currIndex] = argsVals.getDouble(currIndex - 1);
                    }
                    numIndex /= 10;
                }
            }

            return getReturn(jsonStr, 200, currMethod.invoke(null, values));
        } catch (Exception e)
        {
            //优先返回详细的错误信息
            if (e.getCause() != null)
            {
                return getReturn(jsonStr, 500, "method execute error:" + e.getCause().getMessage());
            }
            return getReturn(jsonStr, 500, "method execute error:" + e.getMessage());
        }
    } else
    {
        return getReturn(jsonStr, 500, "call data empty");
    }
}

private String getReturn(String reqJson, int stateCode, Object result)
{
    String insertRes;
    if (result == null)
    {
        insertRes = "null";
    } else if (result instanceof String)
    {
        result = ((String) result).replace("\"", "\\\"");
        insertRes = "\"" + result + "\"";
    } else if (!(result instanceof Integer)
            && !(result instanceof Long)
            && !(result instanceof Boolean)
            && !(result instanceof Float)
            && !(result instanceof Double)
            && !(result instanceof JSONObject))
    {    // 非数字或者非字符串的构造对象类型都要序列化后再拼接
        if (mGson == null)
        {
            mGson = new Gson();
        }
        insertRes = mGson.toJson(result);
    } else
    {  //数字直接转化
        insertRes = String.valueOf(result);
    }
    String resStr = String.format(RETURN_RESULT_FORMAT, stateCode, insertRes);
    ////////Log.d(TAG, mInjectedName + " call json: " + reqJson + " result:" + resStr);
    return resStr;
}

}

建议

在很多时候用webview打开别人的网页需要向网页中注入js代码,以检测某些操作,可不可以把这部分功能也加入进去呢?
还有个疑问,HostJsScope类中方法的多少会不会影响效率或是页面打开的速度呢?

请教一个问题

您好,能请教一下,你的这种实现方式比在shouldOverrideUrlLoading拦截js再去处理的优势在哪儿吗

JS怎么传方法给JAVA进行执行

在 JsCallJava的call方法中 有一个类型判断:values[k + 1] = new JsCallback(webView, mInjectedName, argsVals.getInt(k));进行执行的时候有一个argsVals.getInt(k)获取到对应的全局方法索引,但是假如我需要传入一个对应的方法名称(String 类型),需要怎么做,我没有看到onJsPrompt代码是怎么对message进行封装的,例如我检测到的:
message="{"method":"delayJsCallBack1","types":["number","string","function"],"args":[1,"call back haha","1"]}";其中1是message传过来的,请问我需要怎么做,期待您的回复。

import error

JsCallback.java:
import java.lang.ref.SoftReference;
->
import java.lang.ref.WeakReference;

很多机型上注入不成功

您好,进度大于25%才进行注入,在某些机型上注入不成功,或者偶尔成功偶尔不成功,这个方案不稳定啊。能再优化不? 目前验证注入失败机型:小米 MIUI-JLB52.0 android4.1.1 偶尔成功机型:索尼android4.4.2版本

动态注入的js内容怎么查看?

通过view.loadUrl("javascript:(function(b){fuction test(){}})(window))注入的类容注入到webview的哪个地方,在注入完成后通过document.getElementsByTagName('html')[0].innerHTML获取不到注入的类容。

为什么不用gson呢

大部分人的项目中都用了gson,如果这个项目集成gson,那么就不需要Jackson了,省点空间

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.