【PhoneGAP學習】Android PhoneGap框架(3)--重要知識點的預先學習 (JS層與 Native 層之間通信)

JS與Native之間的通信

JS與Native之間的通信方式有以下 5種方式:

  1. 通過js對話框執行語句從JS到NATIVE通信,以及回調NATIVE對話框(JS->Native->JS)
  2. 通過對頁面的導向document.location重新指定URL(JS->Native)
  3. 通過Android的javascriptInterface接口(JS->Native)
  4. 通過loadurl加載網頁或執行html/javascript的語句(Native->JS)
  5. 通過XHR的方式訪問NATIVE的ServerSocket,(JS->Native)

  

      通過js對話框執行語句從JS到NATIVE通信,以及回調NATIVE對話框

JS的alert,confirm,promt的JS代碼呼出後,android的webview的代理叫做WebChromeClient類,裏面就有響應的HOOKalert confirm promt對話框的方法,分別是 onJsAlert,onJsConfirm,onJsPromt。參數message是傳入的數據,defaultValue則可以定義成內部的約定的關鍵字,根據關鍵字就可以判斷是否需要獨特處理的數據

         下面這個是Android WebChromeClient#onJsPromt方法

@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
	boolean result = true;
	if(defaultValue.equals("customize:")) {
		result = false;
	}
	return result;
}


       document.location重新指定URL

頁面document.location被重新設置後,android的webview的代理叫做WebViewClient類,裏面將有響應location被重置後的方法是shouldOverrideUrlLoading。

其中url就是重置後的新的URL數據。URL的shema可以是file:///,https:// http:// ftp等,也可以是自己定義的shema,通過解析這個shema,也可以判斷是否需要獨特處理的數據。

下面這個是Android WebViewClient#shouldOverrideUrlLoading的方法

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {

		boolean result = true;
		URI u = null;
		
		try {
			u = new URI(url);
		} catch (URISyntaxException e) {

			return result;
		}
		if(u.getScheme().compareTo("customize:") == 0) {
		}
		else if (u.getScheme().compareTo("http://") == 0) {
		} 
		else if(u.getScheme().compareTo("file://") == 0) {
		} 
		
		return result;
}

       javascriptInterface接口

Android的webview類,設置javascript訪問允許設置後,提供一個java的類給javascript直接調用。

Android的Webview的代碼

WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new javascriptInterface(), "interface");

class javascriptInterface {
	public void calledByJavascript(){
	}
}

Javascript端:

window.interface.calledByJavascript();

不過,JavascriptInterface接口因爲直接調用android的本地代碼,因此可以被惡意的js隨意注入,存在很大的安全問題,Android4.2以後有對所暴露的接口必須帶有@JavascriptInterface註釋語句,沒有這個註釋的區域則不能被js訪問。

通過loadurl加載網頁或執行html/javascript的語句

Android的組件webview有個loadurl的方法,該方法可以實現2個操作

  • url爲http file 的sheme的時候,加載整個網頁
  • url爲javascript:開頭的時候,運行Webview裏面加載的網頁運行,javascript:後面的內容。

這裏所提及的就是第二種方式,用loadurl將java的信息傳遞給js,因此,javascript:後面可以加上function,也可以加上屬性賦值。

loadurl爲異步方法。

通過XHR的方式訪問NATIVE的ServerSocket

前面一篇文章提到XHR是可以向服務器,或ASP等網頁進行訪問加載的,因此,當NATIVE裏面使用了ServerSocket,則可以相互通信。

PhoneGap對於本地文件的通信就是用XHR的方式,讀取回調函數列表。

借用PhoneGap裏面JS端和ServerSocket的方法,來舉例說明


JS端:

JSCallback = function() {

    var xmlhttp = new XMLHttpRequest();

    // Callback function when XMLHttpRequest is ready
    xmlhttp.onreadystatechange=function(){
        if(xmlhttp.readyState === 4){

            // If callback has JavaScript statement to execute
            if (xmlhttp.status === 200) {

                // Need to url decode the response
                var msg = decodeURIComponent(xmlhttp.responseText);
                setTimeout(function() {
                    try {
                        var t = eval(msg);
                    }
                    catch (e) {
                        // If we're getting an error here, seeing the message will help in debugging
                        console.log("JSCallback: Message from Server: " + msg);
                        console.log("JSCallback Error: "+e);
                    }
                }, 1);
            }

            // If callback ping (used to keep XHR request from timing out)
            else if (xmlhttp.status === 404) {
            }

            // If security error
            else if (xmlhttp.status === 403) {
                console.log("JSCallback Error: Invalid token.  Stopping callbacks.");
            }

            // If server is stopping
            else if (xmlhttp.status === 503) {
                console.log("JSCallback Error: Service unavailable.  Stopping callbacks.");
            }

            // If request wasn't GET
            else if (xmlhttp.status === 400) {
                console.log("JSCallback Error: Bad request.  Stopping callbacks.");
            }

            // If error, revert to polling
            else {
                console.log("JSCallback Error: Request failed.");
            }
        }
    };
//PhoneGap.JSCallbackPort  PhoneGap.JSCallbackToken 都是從native端獲取過來
 xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken/ , true);
    xmlhttp.send();
};

Native的ServerSocket端,

       try {
			this.active = true;
			String request;
	                ServerSocket waitSocket = new ServerSocket(0);
			this.port = waitSocket.getLocalPort();
			this.token = java.util.UUID.randomUUID().toString();

			 while (this.active) {
				 Socket connection = waitSocket.accept();
				 BufferedReader xhrReader = new BufferedReader(new InputStreamReader(connection.getInputStream()),40);
				 DataOutputStream output = new DataOutputStream(connection.getOutputStream());
				 request = xhrReader.readLine();
				 String response = "";
				 if (this.active && (request != null)) {
					 if (request.contains("GET")) {
						 
						 // Get requested file
						 String[] requestParts = request.split(" "); 
						 
						 // Must have security token
						 if ((requestParts.length == 3) && (requestParts[1].substring(1).equals(this.token))) {

							 // Wait until there is some data to send, or send empty data every 10 sec 
							 // to prevent XHR timeout on the client 
							 synchronized (this) { 
								 while (this.empty) { 
									 try { 
										 this.wait(10000); // prevent timeout from happening
										 break;
									 } 
									 catch (Exception e) { }
								 } 
							 }

							 // If server is still running
							 if (this.active) {

								 // If no data, then send 404 back to client before it times out
								 if (this.empty) {
									 //System.out.println("CallbackServer -- sending data 0");
									 response = "HTTP/1.1 404 NO DATA\r\n\r\n "; // need to send content otherwise some Android devices fail, so send space
								 }
								 else {
									 //System.out.println("CallbackServer -- sending item");
									 response = "HTTP/1.1 200 OK\r\n\r\n";
									 // 回調的內容在getJavascript的返回值裏添加
									 String js = this.getJavascript();
									 if (js != null) {
										 response += encode(js, "UTF-8");
									 }
								 }
							 }
							 else {
								 response = "HTTP/1.1 503 Service Unavailable\r\n\r\n ";							 
							 }
						 }
						 else {
							 response = "HTTP/1.1 403 Forbidden\r\n\r\n ";						 
						 }
					 }
					 else {
						 response = "HTTP/1.1 400 Bad Request\r\n\r\n ";
					 }
					 //System.out.println("CallbackServer: response="+response);
					 //System.out.println("CallbackServer: closing output");
					 output.writeBytes(response);
					 output.flush();
				 }
				 output.close();
				 xhrReader.close();
			 }
		 } catch (IOException e) {
			 e.printStackTrace();
		 }
		 this.active = false;


Native端創建ServerSocket,當JS通過XHR請求的時候,ServerSocket則將返回內容設置到response裏面去。當XHR的onreadystatechange的狀態爲4並且狀態碼爲200的時候,就可以通過responseText獲取到Native傳給JS的內容了。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章