日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何理解苹果iOS版PhoneGap原理分析

發布時間:2024/4/14 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何理解苹果iOS版PhoneGap原理分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

PhoneGap,著名的跨平臺Hybrid框架,旨在讓開發者使用HTML、Javascript、CSS開發跨平臺的App。

最近的工作,就是做Hybrid方面的,很自然,方案就從PhoneGap入手。

下面就切入正題,分析下PhoneGap的原理,需要說明的是,我只針對iOS版本的PhoneGap做分析,android版本的原理大同小異。

安裝PhoneGap

現在使用PhoneGap非常方便,只需要安裝node,用簡單的命令就能完成安裝和使用的工作。

安裝PhoneGap:

sudo?npm?install?-g?phonegap

創建phoneGap應用:

phonegap?create?my-app
cd?my-app
phonegap?run?ios?

PhoneGap與Cordova的關系

Cordova是PhoneGap貢獻給Apache后的開源項目,是從PhoneGap中抽離出的核心代碼,是驅動PhoneGap的核心引擎。有點類似Webkit和Google Chrome的關系。

淵源就是:早在2011年10月,Adobe收購了Nitobi Software和它的PhoneGap產品,然后宣布這個移動Web開發框架將會繼續開源,并把它提交到Apache Incubator,以便完全接受ASF的管治。當然,由于Adobe擁有了PhoneGap商標,所以開源組織的這個PhoneGap v2.0版產品就更名為Apache Cordova。

為什么說這個?因為下面的文章中,會出現Cordova這個命令,大家不要覺得奇怪。

js與native通信的原理

但在切入正題前,需要先了解下iOS js與native通信的原理。了解這個原理,是理解PhoneGap代碼的關鍵。

?

js –> native

在iOS中,js調用native并沒有提供原生的實現,只能通過UIWebView相關的UIWebViewDelegate協議的

-?(BOOL)webView:(UIWebView?*)webView?shouldStartLoadWithRequest:(NSURLRequest?*)request?navigationType:(UIWebViewNavigationType)navigationType?

方法來做攔截,并在這個方法中,根據url的協議或特征字符串來做調用方法或觸發事件等工作,如

/*
* 方法的返回值是BOOL值。
* 返回YES:表示讓瀏覽器執行默認操作,比如某個a鏈接跳轉
* 返回NO:表示不執行瀏覽器的默認操作,這里因為通過url協議來判斷js執行native的操作,肯定不是瀏覽器默認操作,故返回NO
* /
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
? ?NSURL *url = [request URL];
? ?if ([[url scheme] isEqualToString:@"callFunction") {
? ? ? ?//調用原生方法

? ? ? ?return NO;
? ?} else if (([[url scheme] isEqualToString:@"sendEvent") {
? ? ? ?//觸發事件

? ? ? ?return NO;
? ?} else {
? ? ? ?return YES;
? ?}
}

值得注意的是,通過這個方式,js調用native是異步的。

native –> js

native調用js非常簡潔方便,只需要

[webView?stringByEvaluatingJavaScriptFromString:@"alert('hello?world!')"];

并且該方法是同步的。

native調用js非常簡單直接,所以PhoneGap解決的主要是js調用native的問題。

PhoneGap js –> native

我們通過一個js調用native的Dialog的例子做說明。

Dialog是一個PhoneGap的插件,可以看dialog 插件文檔,學習下載并使用該插件。

這里有個很重要的事需要說明一下:?目前PhoneGap的文檔更新非常不及時,特別是插件的使用方面,比如Dialog插件的使用,文檔中寫的是使用navigator.notification.alert,但是經過我的摸索,因為現在PhoneGap使用AMD的方式來管理插件,所以應該是使用cordova.require("cordova/plugin/notification").alert的方式來調用。?插件的合并方面,也有很多坑,主要是文檔不全?-?-|||

js部分

在html上添加一個button,然后通過下列代碼調用:

function alertDismissed() {
? ?// do something
}

function showAlert() {
? ?cordova.require("cordova/plugin/notification").alert(
? ? ? ?'You are the winner!', ?// message
? ? ? ?alertDismissed, ? ? ? ? // callback
? ? ? ?'Game Over', ? ? ? ? ? ?// title
? ? ? ?'Done' ? ? ? ? ? ? ? ? ?// buttonName
? ?);
}

再看下對應的cordova/plugin/notification的代碼:

var exec = cordova.require('cordova/exec');
var platform = cordova.require('cordova/platform');

module.exports = {

? ?/**
? ? * Open a native alert dialog, with a customizable title and button text.
? ? *
? ? * @param {String} message ? ? ? ? ? ? ?Message to print in the body of the alert
? ? * @param {Function} completeCallback ? The callback that is called when user clicks on a button.
? ? * @param {String} title ? ? ? ? ? ? ? ?Title of the alert dialog (default: Alert)
? ? * @param {String} buttonLabel ? ? ? ? ?Label of the close button (default: OK)
? ? */
? ?alert: function(message, completeCallback, title, buttonLabel) {
? ? ? ?var _title = (title || "Alert");
? ? ? ?var _buttonLabel = (buttonLabel || "OK");
? ? ? ?exec(completeCallback, null, "Notification", "alert", [message, _title, _buttonLabel]);
? ?}
}

....

可以看到alert最終其實是調用了exec方法來調用native代碼的,exec方法非常關鍵,是PhoneGap js調用native的核心代碼。

然后在源碼中搜索exec對應的cordova/exec,查看exec方法的源碼。

因為對應的cordova/exec源碼非常長,我只能截取最關鍵的代碼并做說明:

define("cordova/exec", function(require, exports, module) {

? ?...

? ?function iOSExec() {
? ? ? ?...

? ? ? ?var successCallback, failCallback, service, action, actionArgs, splitCommand;
? ? ? ?var callbackId = null;

? ? ? ?...

? ? ? ?// 格式化傳入參數
? ? ? ?successCallback = arguments[0]; //成功的回調函數
? ? ? ?failCallback = arguments[1]; ? ?//失敗的回調函數
? ? ? ?service = arguments[2]; ? ? ? ? //表示調用native類的類名
? ? ? ?action = arguments[3]; ? ? ? ? ?//表示調用native類的一個方法
? ? ? ?actionArgs = arguments[4]; ? ? ?//參數

? ? ? ?//默認callbackId為'INVALID',表示不需要回調
? ? ? ?callbackId = 'INVALID';

? ? ? ?...

? ? ? ?//如果傳入參數有successCallback或failCallback,說明需要回調,就設置callbackId,并存儲對應的回調函數
? ? ? ?if (successCallback || failCallback) {
? ? ? ? ? ?callbackId = service + cordova.callbackId++;
? ? ? ? ? ?cordova.callbacks[callbackId] =
? ? ? ? ? ? ? ?{success:successCallback, fail:failCallback};
? ? ? ?}

? ? ? ?//格式化傳入的service、action、actionArgs,并存儲,準備native代碼來調用
? ? ? ?actionArgs = massageArgsJsToNative(actionArgs);

? ? ? ?var command = [callbackId, service, action, actionArgs];

? ? ? ?commandQueue.push(JSON.stringify(command));

? ? ? ?...

? ? ? ?//通過創建一個iframe并設置src,給native代碼一個指令,開始執行js調用native的過程
? ? ? ?execIframe = execIframe || createExecIframe();
? ? ? ?if (!execIframe.contentWindow) {
? ? ? ? ? ?execIframe = createExecIframe();
? ? ? ?}
? ? ? ?execIframe.src = "gap://ready";

? ? ? ?...
? ?}

? ?module.exports = iOSExec;

});

為了調用native方法,exec方法做了大量初始化的工作,這么做的原因,還是因為iOS沒有提供直接的方法來執行js調用native,不能把參數直接傳遞給native,所以只能通過js端存儲對應操作的所有參數,然后通過指令來讓native代碼來回調的方式間接完成。

native部分

之后,就走到了native代碼的部分。

CDVViewController

前面js通過創建一個iframe并發送gap://ready這個指令來告訴native開始執行操作。native中對應的操作在CDVViewController.m文件中的webView:shouldStartLoadWithRequest:navigationType:方法:

- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
? ?NSURL* url = [request URL];

? ?/*
? ? * 判斷url的協議以"gap"開頭
? ? * 執行在js端調用cordova.exec()的command隊列
? ? * 注:這里的command表示js調用native
? ? */
? ?if ([[url scheme] isElaqualToString:@"gap"]) {
? ? ? //_commandQueue即CDVCommandQueue類
? ? ? ?//從js端拉取command,即存儲在js端commandQueue數組中的數據
? ? ? ?[_commandQueue fetchCommandsFromJs];
? ? ? ?//開始執行command
? ? ? ?[_commandQueue executePending];
? ? ? ?return NO;
? ?}
...
}

到這里,其實已經走完js調用native的主要過程了。

之后,讓我們再看下CDVCommandQueue中的fetchCommandsFromJs方法與executePending方法中做的事。

CDVCommandQueue

- (void)fetchCommandsFromJs
{
? ?// 獲取js端存儲的command,并在native暫存
? ?NSString* queuedCommandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString:
? ? ? ?@"cordova.require('cordova/exec').nativeFetchMessages()"];
? ?[self enqueueCommandBatch:queuedCommandsJSON];
}

fetchCommandsFromJs方法非常簡單,不細說了。

executePending方法稍微復雜些,因為js是單線程的,而iOS是典型的多線程,所以executePending方法做的工作主要是讓command一個一個執行,防止線程問題。

executePending方法其實與之后的execute方法緊密相連,這里一起列出,只保留關鍵代碼:

- (void)executePending
{
? ?...
? ?//_queue即command隊列,依次執行
? ?while ([_queue count] > 0) {
? ? ? ?...
? ? ? ?//取出從js中獲取的command字符串,解析為native端的CDVInvokedUrlCommand類
? ? ? ?CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
? ? ? ?...
? ? ? ?//執行command
? ? ? ?[self execute:command])
? ? ? ?...
? ?}
}

- (BOOL)execute:(CDVInvokedUrlCommand*)command
{
? ?...
? ?BOOL retVal = YES;
? ?//獲取plugin對應的實例
? ?CDVPlugin* obj = [_viewController.commandDelegate getCommandInstance:command.className];
? ?//調用plugin實例的方法名
? ?NSString* methodName = [NSString stringWithFormat:@"%@:", command.methodName];
? ?SEL normalSelector = NSSelectorFromString(methodName);
? ?if ([obj respondsToSelector:normalSelector]) {
? ? ? ?//消息發送,執行plugin實例對應的方法,并傳遞參數
? ? ? ?objc_msgSend(obj, normalSelector, command);
? ?} else {
? ? ? ?// There's no method to call, so throw an error.
? ? ? ?NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", methodName, command.className);
? ? ? ?retVal = NO;
? ?}
? ?...
? ?return retVal;
}

可以看到js調用native plugin最終執行的是objc_msgSend(obj, normalSelector, command);這塊代碼,這里我們再拿js端的代碼來進行理解。

之前js中的showAlert方法中我們書寫了 exec(completeCallback, null, "Notification", "alert", [message, _title, _buttonLabel]);

故,這里的對應關系:

obj:“Notification”

normalSelector:“alert”

command:[message, title, buttonLabel]

CDVNotification

“Notification”真正對應的iOS類是CDVNotification。js端調用的插件名字”Notification”與真正的native類名并非完全對應,因為native因為平臺的不同,有不同的命名規范。

看下CDVNotification的代碼:

- (void)alert:(CDVInvokedUrlCommand*)command
{
? ?NSString* callbackId = command.callbackId;
? ?NSString* message = [command argumentAtIndex:0];
? ?NSString* title = [command argumentAtIndex:1];
? ?NSString* buttons = [command argumentAtIndex:2];

? ?[self showDialogWithMessage:message title:title buttons:@[buttons] defaultText:nil callbackId:callbackId dialogType:DIALOG_TYPE_ALERT];
}

前面用objc_msgSend(obj, normalSelector, command);做消息發送,執行的便是這塊代碼,代碼很好理解,就是對command再做解析,并顯示。

最終效果:

點擊”Done”,native會再回調執行js端的成功回調,這里對應的就是js里設置的alertDismissed方法。

到此為止,我們已經走完從js端調用native alert的全部過程了。

列下過程的核心代碼:

js部分:cordova.js中的iOSExec()方法,指定js調用native的初始化工作,并發送開始執行的指令

native部分:CDVViewController:攔截js調用native的url協議,執行調用;CDVCommandQueue:執行js調用native的隊列,調用對應的plugin

時序圖

以上Dialog例子中,PhoneGap js調用native的時序圖:?

?

結語

PhoneGap還是很給力的,能做到主流平臺全兼容著實不容易。

iOS端因為沒有提供js調用native的直接方法,做的處理也算合理到位。

特別是插件化的支持做的很好,但是文檔著實不夠給力。

轉載于:https://www.cnblogs.com/lxf1016/p/4753938.html

總結

以上是生活随笔為你收集整理的如何理解苹果iOS版PhoneGap原理分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。