[Android]PhoneGap源碼分析——CallbackServer異步回調

    PhoneGap的js回調有幾種實現方式。其中一種是ajax。java

    咱們先來看一下js端相關代碼:android

// file: lib/android/plugin/android/callback.js
define("cordova/plugin/android/callback", function(require, exports, module) {

var port = null,
    token = null,
    xmlhttp;

function startXhr() {
    // cordova/exec depends on this module, so we can't require cordova/exec on the module level.
    var exec = require('cordova/exec'),
    xmlhttp = new XMLHttpRequest();

    // Callback function when XMLHttpRequest is ready
    xmlhttp.onreadystatechange=function(){
        if (!xmlhttp) {
            return;
        }
        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(startXhr, 1);
                exec.processMessages(msg);
            }

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

            // 0 == Page is unloading.
            // 400 == Bad request.
            // 403 == invalid token.
            // 503 == server stopped.
            else {
                console.log("JSCallback Error: Request failed with status " + xmlhttp.status);
                exec.setNativeToJsBridgeMode(exec.nativeToJsModes.POLLING);
            }
        }
    };

    if (port === null) {
        port = prompt("getPort", "gap_callbackServer:");
    }
    if (token === null) {
        token = prompt("getToken", "gap_callbackServer:");
    }
    xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true);
    xmlhttp.send();
}

module.exports = {
    start: function() {
        startXhr();
    },

    stop: function() {
        if (xmlhttp) {
            var tmp = xmlhttp;
            xmlhttp = null;
            tmp.abort();
        }
    },

    isAvailable: function() {
        return ("true" != prompt("usePolling", "gap_callbackServer:"));
    }
};


});

    主要的處理是startXhr函數。它向java的server端發起了ajax請求,在onreadystatechange這個回調函數中等待server端返回結果。若是server端返回的結果正確,則再經過setTimeout(startXhr, 1)函數,1豪秒後再從新向server端發起ajax請求。若是從server返回的結果狀態是404,則每隔10豪秒,從新向server端發起ajax請求。ajax

    接下來看看server端是如何處理的。server端的處理代碼是CallbackServer.java中的run()方法中。app

    先看server端的源碼:dom

    它經過serverSocket來模擬http的server端。其中,jsMessageQueue中是從java端發送的js消息,serverSocket在接收到客戶端(js端)的請求後,會將jsMessageQueue中的js代碼發送到客戶端(js端)。socket

/**
     * Start running the server.  
     * This is called automatically when the server thread is started.
     */
    public void run() {

        // Start server
        try {
            this.active = true;
            String request;
            waitSocket = new ServerSocket(0);
            this.port = waitSocket.getLocalPort();
            //Log.d(LOG_TAG, "CallbackServer -- using port " +this.port);
            this.token = java.util.UUID.randomUUID().toString();
            //Log.d(LOG_TAG, "CallbackServer -- using token "+this.token);

            while (this.active) {
                //Log.d(LOG_TAG, "CallbackServer: Waiting for data on socket");
                Socket connection = waitSocket.accept();
                BufferedReader xhrReader = new BufferedReader(new InputStreamReader(connection.getInputStream()), 40);
                DataOutputStream output = new DataOutputStream(connection.getOutputStream());
                request = xhrReader.readLine();
                String response = "";
                //Log.d(LOG_TAG, "CallbackServerRequest="+request);
                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))) {
                            //Log.d(LOG_TAG, "CallbackServer -- Processing GET request");
                        	String payload = null;

                            // Wait until there is some data to send, or send empty data every 10 sec 
                            // to prevent XHR timeout on the client 
                            while (this.active) {
                            	if (jsMessageQueue != null) {
                            		payload = jsMessageQueue.popAndEncode();
                            	    if (payload != null) {
                            	    	break;
                            	    }
                            	}
                            	synchronized (this) {
                                    try {
                                        this.wait(10000); // prevent timeout from happening
                                        //Log.d(LOG_TAG, "CallbackServer>>> break <<<");
                                        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 (payload == null) {
                                    //Log.d(LOG_TAG, "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 {
                                    //Log.d(LOG_TAG, "CallbackServer -- sending item");
                                    response = "HTTP/1.1 200 OK\r\n\r\n";
                                    response += encode(payload, "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 ";
                    }
                    //Log.d(LOG_TAG, "CallbackServer: response="+response);
                    //Log.d(LOG_TAG, "CallbackServer: closing output");
                    output.writeBytes(response);
                    output.flush();
                }
                output.close();
                xhrReader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.active = false;
        //Log.d(LOG_TAG, "CallbackServer.startServer() - EXIT");
    }
相關文章
相關標籤/搜索