2016-08-05 33 views
8

我想从Android的网页中抽取一些东西。我知道有些库可以解析HTML,但我想也许我可以作一点点的欺骗。在WebView中注入Javascript桥梁

下面是我在做什么..

  1. 使用应用程序上下文编程方式创建的WebView因此它不必被显示在用户界面。
  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中不在用户界面中可见,很难分辨哪个部分是坏的。我所知道的是调用的第一个日志被调用,但JavaScriptInterface中的日志和Toast从不显示。

我试图做甚至可能吗?如果是这样,我做错了什么?如果不是,为什么不呢?

编辑

停留在测试的UI来看,显然是为了使用loadURL第二个电话是行不通的。无论我尝试注入什么Javascript,它都不起作用。

EDIT 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

开始了新的奖金,并增加了奖励100pts。奈纳尔获得了最有益的最后一笔奖金,并非为了解决问题。

+0

,很难遵循“问题”。完成这些改进后,目前存在的问题是什么? –

+0

另有7人了解这个问题就好了..查看更多信息的接受答案。 –

+0

我只想追赶和帮助,因为在我写作的时候你还没有一个可以接受的答案,而且信息乱七八糟。希望你也能理解解决方案和根源。 –

回答

1

这是一个清理版本,尽量减少不必要的代码。这运行在API级别18和23模拟器(和我的6.0.1手机)上。 webView永远不会添加到视图层次结构中。无论如何,吐司显示从网站拉出的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> 
+0

我真的很想在我回家的时候尝试一下。非常感谢。将在一天结束时报告。 –

+0

你知道什么....我不认为我曾经在清单中包含Internet权限。这可能一直是问题。我可能必须增加赏金之前,我把它给你,如果这个工程.. –

+1

原来这是不相关的这种用法,但我觉得这个迷人。你有时想添加一个对'void(0);'的调用来获得注入JavaScript的结果。看到这两个链接:http://stackoverflow.com/questions/27523040/javascript-injection-into-webview?rq=1 http://stackoverflow.com/questions/1291942/what-does-javascriptvoid0-mean – Hod

4

请试试这个,它调用javascript函数并显示吐司消息。

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布局中定义。

+0

啊,这是有道理的把它放在一个新的线程。期待着尝试这一点。谢谢! –

+0

它工作吗? – Nainal

+0

它似乎是工作的,因为JS接口被调用,但它似乎无法与DOM交互......我尝试这样做,但得到一个空字符串:'webView.loadUrl(“javascript:window .CallToAnAndroidFunction.setVisible(document.getElementsByTagName('body')[0] .innerHTML);“);' –

2

一些事情,弹出给我。

  1. 在加载任何URL之前,应该附加JavaScript界面​​。

  2. 第二个loadURL window.onload可能在加载原始URL后分配。从onPageFinished方法中调用setWebViewClient()并呼叫Droid.showToast('testing!');会更有意义。

  3. @JavascriptInterface不会在主UI线程上运行,这会阻止您的面包运行。

  4. 这个问题你的第二个编辑的innerHTML代码不工作点2.你在同步单块使您的通话,而应该是与后页面的DOM加载AKA onPageFinished()

+0

我已经尝试了几种不同的方式,但它似乎并不像javascript可以访问被注入页面上的dom。该页面的html对javascript显示为空白。这是为什么? –

+0

您是否介意发送github当前活动的要点(以及您认为可能相关的其他课程)? 一旦我能看到你正在做的事情的全貌,我应该能够很容易地解决这个问题。 您还应该在桌面上使用'chrome:// inspect /#devices'工具来调试问题,甚至使用Droid.showToast('test')'调用它! –

+0

我已经将我的完整活动添加到了问题中(所以通常在提供来自异地源代码(例如github)的代码时会皱眉)。如果你有任何进一步的建议他们会有所帮助,我也增加并延长了100分。 –

2

webview.addJavascriptInterface(new jsInterface(), "Droid");必须早于webview.loadUrl("https://example.com");

然后使用Webview Listener。 onFinish()方法..然后在onFinish方法注入你webview.loadUrl("javascript: window.onload=function(){ Droid.showToast('testing!'); }");

我已经这样做了修改Web吨的WebView注射的..我认为它应该工作..

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

+0

我已经尝试了几种不同的方式,但它似乎并不像javascript可以访问被注入页面上的dom。该页面的html对javascript显示为空白。这是为什么? –

+0

感谢您使用'chrome:// inspect /#devices'这个方便的工具。如果你有进一步的建议,我已经增加了100分。谢谢。 –

0

如何在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);"); 
      } 
     } 
    }); 

的变化是,你调用JavaScript的progress==100代替onPageFinished()

0

当我创建了一个示例应用程序使用最新版本的代码,找出以下问题:

  1. URL从未为我加载。若要解决此问题,i've added the internet permission to AndroidManifest

  2. WebView从不显示任何内容,因为它从未从代码附加到屏幕。尝试使用此示例代码(如果您使用Android种子应用程序生成的布局资源,此代码应该可以工作;否则,您需要以其他方式将webview附加到屏幕上 - 请告诉我是否属于这种情况):

    public class myJavaScriptInterface { 
    
        @JavascriptInterface 
        public void setVisible(final String aThing) { 
    
         Handler handler = new Handler(Looper.getMainLooper()); 
    
         Runnable runnable = new Runnable() { 
          @Override 
          public void run() { 
           webView.setVisibility(View.VISIBLE); 
           // attach the view to the screen 
           ((RelativeLayout)findViewById(R.id.activity_main)).addView(webView, 0); 
    
           Toast.makeText(MainActivity.this, "Reached JS: "+aThing, Toast.LENGTH_LONG).show(); 
          } 
         }; 
    
         handler.postDelayed(runnable,2000); 
        } 
    } 
    

请让我知道,如果有该帖子被修改过很多次的任何其他问题,您的实验