2016-08-05 56 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); 
        } 
    } 
    

請讓我知道,如果有該帖子被修改過很多次的任何其他問題,您的實驗