1. 程式人生 > >android 與JS之間的互動

android 與JS之間的互動

在頁面佈局很複雜並且是動態的時候,android本身的控制元件就變得不是那麼地靈活了,只有藉助於網頁的強大布局能力才能實現,但是在操作html頁面的同時也需要與android其它的元件存在互動,比如說

在load一個url時, 使用者點選頁面內的某個按鈕後, 頁面呼叫android內的元件函式或由android元件去呼叫JS程式碼去更新頁面,這都是互動問題,聽起來很複雜,其實不用擔心,webview這個類已經幫我們實現了,只需要直接用就好了。

 webview用法1

1.在要Activity中例項化WebView元件:WebView webView = new WebView(this);

2.呼叫WebView的loadUrl()方法,設定WevView要顯示的網頁:
  網際網路用:webView.loadUrl("http://www.google.com"); 
  本地檔案用:webView.loadUrl("file:///android_asset/XX.html"); 本地檔案存放在:assets 檔案中

3.呼叫Activity的setContentView)方法來顯示網頁檢視

4.用WebView點連結看了很多頁以後為了讓WebView支援回退功能,需要覆蓋覆蓋Activity類的onKeyDown()方法,如果不做任何處理,點選系統回退剪鍵,整個瀏覽器會呼叫finish()而結束自身,而不是回退到上一頁面

5.需要在AndroidManifest.xml檔案中新增許可權,否則會出現Web page not available錯誤。
  <uses-permission android:name="android.permission.INTERNET" />

webview用法2

1、在佈局檔案中宣告WebView

2、在Activity中例項化WebView

3、呼叫WebView的loadUrl( )方法,設定WevView要顯示的網頁

4、為了讓WebView能夠響應超連結功能,呼叫setWebViewClient( )方法,設定  WebView檢視

5、用WebView點連結看了很多頁以後為了讓WebView支援回退功能,需要覆蓋覆蓋Activity類的onKeyDown()方法,如果不做任何處理,點選系統回退剪鍵,整個瀏覽器會呼叫finish()而結束自身,而不是回退到上一頁面

6、需要在AndroidManifest.xml檔案中新增許可權,否則出現Web page not available錯誤。

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

設定webview物件顯示的網頁的函式為loadUrl();

  網際網路頁面直接用: 

myWebView.loadUrl(“http://www.google.com“);

  本地檔案用(本地檔案存放在:assets檔案中):

myWebView.loadUrl(“file:///android_asset/XX.html“);  

  還可以直接載入html的字串,如:

String htmlString = "<h1>Title</h1><p>This is HTML text<br /><i>Formatted in italics</i><br />Anothor Line</p>";
// 載入這個html頁面
myWebView.loadData(htmlString, "text/html", "utf-8");

 與JS互動呼叫必須進行下面的設定

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);  

 javascript與android互動

android端程式碼:

webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JSHook(), "hello"); //在JSHook類裡實現javascript想呼叫的方法,並將其例項化傳入webview, "hello"這個字串告訴javascript呼叫哪個例項的方法
複製程式碼
public class JSHook{
        public void javaMethod(String p){
            Log.d(tag , "JSHook.JavaMethod() called! + "+p);
        }
        
        public void showAndroid(){
            String info = "來自手機內的內容!!!";
            webView.loadUrl("javascript:show('"+info+"')");
        }
        
        public String getInfo(){
            return "獲取手機內的資訊!!";
        }
    }
複製程式碼

html端程式碼:

複製程式碼
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>呼叫Android元件測試</title>
<script type="text/javascript">
    function show(info){
        document.getElementById("shows").innerHTML = info;
    }
</script>
</head>

<body>
<b>測試</b>
<br />
<button onClick="window.hello.javaMethod('param')">啟動hello world Activity</button>
<br />
<hr  color="#99FF00"/>
<button onClick="window.hello.showAndroid()">顯示android內容</button>
<br />
<textarea id= "shows"  cols="20" rows="10"> 暫無記錄 </textarea>
<br />

</body>
</html>
複製程式碼

程式碼實現效果圖

補充一下:如果在呼叫js中的方法時,需要訪問值,只是採用 webView.loadUrl("javascript:show('"+info+"')");沒法得到,可通過兩種方式取得:

1) 在js中的show方法,繼續呼叫android 原生方法將返回值通過引數的形式傳過來

2)在API19後,android新增介面可直接得到返回值

複製程式碼
//第一個引數為js中的方法名,該段程式碼每執行一次則呼叫js方法一次
progressWebView.getWebview().evaluateJavascript("window.hasUserEditData()", new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String value) {
//這裡為返回值
                    boolean hasChanged = Boolean.parseBoolean(value);
                    //todo
                }
            });
複製程式碼

例項程式碼

複製程式碼
package com.example.jscallandroid;

import android.support.v7.app.ActionBarActivity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.JavascriptInterface;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;


public class MainActivity extends ActionBarActivity {

    //private static final String URL = "http://shouji.baidu.com/";
    private static final String URL = "file:///android_asset/helloworld.html";
    private WebView webView;
    public String tag = "MainActivity";
    private Context mContext;
    
    @SuppressLint("JavascriptInterface") 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        //this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        // 進行全屏

        mContext = this;
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        webView = (WebView) this.findViewById(R.id.wv);

        webView.loadUrl(URL);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.addJavascriptInterface(new JSHook(), "hello");
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                Log.d(tag, " url:"+url);
              view.loadUrl(url);// 當開啟新連結時,使用當前的 WebView,不會使用系統其他瀏覽器
                return true;
            } 
        });
    }
  
    public class JSHook{
        @JavascriptInterface
        public void javaMethod(String p){
            Log.d(tag , "JSHook.JavaMethod() called! + "+p);
        }
        @JavascriptInterface
        public void showAndroid(){
            final String info = "來自手機內的內容!!!";
            MainActivity.this.runOnUiThread(new Runnable(){
                @Override
                public void run() {
                    webView.loadUrl("javascript:show('"+info+"')");
                }
            });
        }
        public String getInfo(){
            return "獲取手機內的資訊!!";
        }
    }
    @Override 
    //設定回退  
    //覆蓋Activity類的onKeyDown(int keyCoder,KeyEvent event)方法  
    public boolean onKeyDown(int keyCode, KeyEvent event) {  
        if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {  
            webView.goBack(); //goBack()表示返回WebView的上一頁面 
            this.finish();
            return true;  
        }  
        return false;  }
}
複製程式碼

可能的異常:

複製程式碼
public class JSHook{
        public void javaMethod(String p){
            Log.d(tag , "JSHook.JavaMethod() called! + "+p);
        }
        
        public void showAndroid(){
            String info = "來自手機內的內容!!!";
            webView.loadUrl("javascript:show('"+info+"')");
        }
        
        public String getInfo(){
            return "獲取手機內的資訊!!";
        }
    }
複製程式碼

上面這段程式碼在android4.4版本及之前是沒有問題的, 4.4之後就會出現以下兩個異常錯誤。

07-10 10:25:21.417: I/chromium(27333): [INFO:CONSOLE(19)] "Uncaught TypeError: Object [object Object] has no method 'showAndroid'", source: file:///android_asset/helloworld.html (19)

解決方法:在js呼叫方法上面加註解@JavascriptInterface

07-10 10:42:58.437: I/chromium(27621): [INFO:CONSOLE(19)] "Uncaught Error: Error calling method on NPObject.", source: file:///android_asset/helloworld.html (19)

解決方法:在對介面進行修改時必須在UI執行緒進行,即便它是Html的介面,因此在出現這個錯誤的時候可以用handler或runOnUiThread()方法去執行更新UI操作。

注:例項程式碼是排除了在高版本中出現異常的最終程式碼。

異常原因:

webview允許JavaScript 控制宿主應用程式,這是個很強大的特性,但同時,在4.2的版本前存在重大安全隱患,因為JavaScript 可以使用反射訪問注入webview的java物件的public fields,在一個包含不信任內容的WebView中使用這個方法,會允許攻擊者去篡改宿主應用程式,使用宿主應用程式的許可權執行java程式碼。因此4.2以後,任何為JS暴露的介面,都需要加

@JavascriptInterface

註解,這樣,這個Java物件的fields 將不允許被JS訪問。

API

public void addJavascriptInterface 

Injects the supplied Java object into this WebView. The object is injected into the JavaScript context of the main frame, using the supplied name. This allows the Java object's methods to be accessed from JavaScript. For applications targeted to API level  and above, only public methods that are annotated with  can be accessed from JavaScript. For applications targeted to API level  or below, all public methods (including the inherited ones) can be accessed, see the important security note below for implications.

Note that injected objects will not appear in JavaScript until the page is next (re)loaded. For example:

 class JsObject {
    @JavascriptInterface
    public String toString() { return "injectedObject"; }
 }
 webView.addJavascriptInterface(new JsObject(), "injectedObject");
 webView.loadData("", "text/html", null);
 webView.loadUrl("javascript:alert(injectedObject.toString())");