在 WebView 中注入 Javascript 桥

Injecting Javascript bridge in WebView

我想从 Android 的网页中提取一些内容。我知道有库可以解析 HTML,但我想也许我可以作弊。

这是我正在做的..

  1. 使用应用程序上下文以编程方式创建 WebView,因此它不必显示在 UI 中。
  2. 加载网页
  3. 附加JS接口
  4. 注入一些 Javascript 以与主机应用程序交互

这是一些代码...

    public void getLatestVersion(){
        Log.e("Testing", "getLatestVersion called...");
        WebView webview = new WebView(context.getApplicationContext());
        webview.loadUrl("https://example.com");
        webview.addJavascriptInterface(new jsInterface(), "Droid");
        webview.loadUrl("javascript: window.onload=function(){ Droid.showToast('testing!'); }");
    }

    class jsInterface{
        @JavascriptInterface
        public void showToast(String message){
            Log.e("Testing", message);
            Toast.makeText(context, message, Toast.LENGTH_LONG).show();
        }
    }

由于 WebView 在 UI 中不可见,因此很难判断是哪个部分损坏了。我所知道的是调用了第一个调用的 Log,但是从未显示 JavascriptInterface 的 Log 和 Toast。

我想做的事情有可能吗?如果是这样,我做错了什么?如果不是,为什么不呢?

编辑

将视图卡在 UI 中进行测试,显然对 loadUrl 的第二次调用不起作用。 Javascript 无论我尝试注入什么,它都不起作用。

编辑 2

忘记启用 Javascript,我感到很愚蠢,但它仍然无法正常工作..我添加了以下几行..

    WebSettings webSettings = webview.getSettings();
    webSettings.setJavaScriptEnabled(true);
    webview.loadUrl("javascript: alert('farts0');");

    webview.loadUrl("https://example.com");
    setContentView(webview);

    String js = "document.body.innerHTML = '<p>test<p>';";
    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        webview.evaluateJavascript(js, null);
    }else{
        webview.loadUrl("javascript: "+js);
    }

编辑 3

感谢大家的建议,你很有帮助,但到目前为止它仍然无法正常工作,所以除非有人在下一个小时内提供工作代码,否则 Nainal 将获得一半的赏金。如果是这样,我不确定是否可以再悬赏一次,因为问题仍未解决。

在考虑了此页面上的建议并尝试了我不太理解的手册中的几个设置之后,这是我到目前为止的完整代码。

import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    WebView webView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webView = new WebView(getApplicationContext());


        if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
            webView.getSettings().setAllowFileAccessFromFileURLs(true);

        if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
            webView.getSettings().setAllowUniversalAccessFromFileURLs(true);

        webView.getSettings().setDomStorageEnabled(true);
        webView.getSettings().setJavaScriptEnabled(true);
        try {
            webView.setWebContentsDebuggingEnabled(true);
        }catch(Exception e){}
        webView.setWebChromeClient(new WebChromeClient());
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                webView.setVisibility(View.GONE);

            }
            @Override
            public void onPageFinished(final WebView view, String url) {
                Log.e("checking", "MYmsg");
                Log.e("content-url", webView.getSettings().getAllowContentAccess()?"y":"n");
                webView.loadUrl("javascript: void window.CallToAnAndroidFunction.setVisible(document.getElementsByTagName('body')[0].innerHTML);");



            }
        });
        webView.setVisibility(View.INVISIBLE);
        webView.addJavascriptInterface(new myJavaScriptInterface(), "CallToAnAndroidFunction");
        webView.loadUrl("http://example.com");
    }
    public class myJavaScriptInterface {
        @JavascriptInterface
        public void setVisible(final String aThing) {
            Handler handler = new Handler();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {

                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            webView.setVisibility(View.VISIBLE);
                            Toast.makeText(MainActivity.this, "Reached JS: "+aThing, Toast.LENGTH_LONG).show();

                        }
                    });


                }
            };handler.postDelayed(runnable,2000);

        }}



}

编辑 4

开始了新的悬赏任务并将奖励增加到 100 点。 Nainal 获得最后的赏金是因为他最有帮助,而不是因为他解决了问题。

请试试这个,它正在调用 javascript 函数并显示 toast 消息。

public class Main3Activity extends AppCompatActivity {
     WebView webView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        webView = new WebView(getApplicationContext());
        webView.getSettings().setJavaScriptEnabled(true);

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                webView.setVisibility(View.GONE);

            }
            @Override
            public void onPageFinished(final WebView view, String url) {
                Log.e("checking", "MYmsg");
                webView.loadUrl("javascript:(function() { " +
                        "document.body.innerHTML = '<p>test<p>';" + "})()");
                webView.loadUrl("javascript: window.CallToAnAndroidFunction.setVisible()");



            }
        });
        webView.setVisibility(View.INVISIBLE);
        webView.addJavascriptInterface(new myJavaScriptInterface(), "CallToAnAndroidFunction");
        webView.loadUrl("https://example.com");
    }
    public class myJavaScriptInterface {
        @JavascriptInterface
        public void setVisible() {

            Handler handler = new Handler();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {

                    Main3Activity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            webView.setVisibility(View.VISIBLE);
                            Log.e("Testing", "no");
                            Toast.makeText(Main3Activity.this, "Reached JS", Toast.LENGTH_LONG).show();

                        }
                    });


                }
            };handler.postDelayed(runnable,2000);

        }}
}

它不会在 UI 中显示 webView,因为 webview 未在 xml 布局中定义。

我突然想到了一些事情。

  1. 在加载任何 URL 之前,应附加 JavaScript 接口。

  2. 第二次加载URL window.onload 可能会在原始 URL 加载后分配。从 onPageFinished 方法内部调用 setWebViewClient() 和调用 Droid.showToast('testing!'); 会更有意义。

  3. @JavascriptInterface 不会 运行 在主 UI 线程上,这将阻止您从 运行ning 开始祝酒词。

  4. 您第二次编辑的 innerHTML 代码无法正常工作的问题与第 2 点有关。您在同步单块中进行调用,而它应该在页面 dom 已加载 AKA onPageFinished()

webview.addJavascriptInterface(new jsInterface(), "Droid");必须在webview.loadUrl("https://example.com");之前

然后使用Webview Listener。 onFinish() 方法.. 然后在 onFinish 方法

中注入你的 webview.loadUrl("javascript: window.onload=function(){ Droid.showToast('testing!'); }");

我已经做了很多 webview 注入修改 web..我认为它应该工作..

编辑
使用 chrome://inspect/#devices 在 webview 加载时检查您的应用程序

这是一个清理过的版本,最大限度地减少了不需要的代码。这在 API 级别 18 和 23 模拟器(以及我的 6.0.1 phone)上运行。 webView 永远不会添加到视图层次结构中。 toast 显示 HTML 无论如何都是从网站上拉下来的。使用 Java 8.

针对 API 25 编译
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webView = new WebView(getApplicationContext());

        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebChromeClient(new WebChromeClient());

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(final WebView view, String url) {
                webView.loadUrl("javascript: void AndroidHook.showToast(document.getElementsByTagName('body')[0].innerHTML);");
            }
        });

        webView.addJavascriptInterface(new JSInterface(), "AndroidHook");
        webView.loadUrl("http://example.com");
    }

    public class JSInterface {
        @JavascriptInterface
        public void showToast(final String html) {

            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "Reached JS: " + html, Toast.LENGTH_LONG).show();
                }
            });
        }
    }
}

布局如下。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="16dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    tools:context="com.foo.jsinjectiontest.MainActivity">

</RelativeLayout>

最后是清单。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.foo.jsinjectiontest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

    <uses-permission android:name="android.permission.INTERNET"/>

</manifest>

WebChromeClient 中使用 onProgressChanged() 怎么样?

我已经将一些代码从 Edit 3 更改为这样,

webView.setWebChromeClient(new WebChromeClient(){
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            super.onProgressChanged(view, newProgress);
            if(newProgress == 100){
                webView.loadUrl("javascript: void window.CallToAnAndroidFunction.setVisible(document.getElementsByTagName('body')[0].innerHTML);");
            }
        }
    });

变化是当 progress==100 而不是 onPageFinished()

时调用 javascript