欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

phonegap底层原理学习和研究

shiping1 的头像

        在phonegap中,通过android和iphone的webview实现对html网页的展示。本文的目的采用android中webview实现对html调用以及js调用java方法的。其实质也是phonegap插件的原理。

PhoneGap如何工作?

PhoneGap架构拥有强大的跨平台访问设备能力,但是其工作原理并不神秘,下面以iPhone和Android平台为例进行分析。

iPhone和Android平台共同点是都有内置的WebView组件,其具备两个特性:

1、WebView组件实质是移动设备的内置浏览器

WebView这个内置浏览器特性是Web能被打包成本地客户端的基础,可方便的用HTML5、CSS3页面布局,这是移动Web技术的优势相对于原生开发。

2、WebView提供Web和设备本地API双向通信的能力

PhoneGap针对不同平台的WebView做了扩展和封装,使WebView这个组件变成可访问设备本地API的强大浏览器,所以开发人员在PhoneGap框架下可通过JavaScript访问设备本地API。

明白以上两个特性,参照下面PhoneGap与设备本地API通信图,一个成熟的PhoneGap技术客户端运行状况如下:

应用运行在WebView组件上 —》 通过PhoneGap在各平台的扩展 —》 最终访问设备本地资源

 

WebView可以显示Web页面,并且,可以像普通的View那样应用到任何布局中。

 

WebView提供了两个方法用于显示网页的内容:loadUrl()用于加载一个指定的URL,而loadData()则用于直接向WebView写入指定的HTML文本。此外,loadDataWithBaseURL()loadData()似,但是,如果写入的HTML文本包含图片、CSS等外部资源的相对链接时,WebView就会根据指定的baseURL来正确地加载这些资源比如:"file:///android_asset/", "<html><body background="lenovo.png">"这个方法正好符合我们显示Item的需求。

 

WebView的属性:Android中专门通过WebSettings来设置WebView的一些属性、状态等。可以通过WebView.getSettings()得到这个设置。

 

WebViewClient:专门辅助WebView处理各种通知、请求等事件的类,可以通过WebView的一个

setWebViewCilent()方法来指定一个WebViewClient对象。

如:setWebViewClient(new WebViewClient(){   

    @Override public boolean shouldOverrideUrlLoading(此方法:控制新的连接在当前WebView中打开)});

 

如果你在HTML中添加了一些JavaScript,你会发现WebView并不会执行JavaScript,这是因为WebView默认关闭了JavaScript功能。为了能执行JavaScript,需要在加载任何URL或写入HTML文本前开启JavaScript,代码如下:

WebView web = (WebView) this.findViewById(R.id.webview);
web.getSettings().setJavaScriptEnabled(true);

WebChromeClient:辅助WebView处理JavaScript的对话框、网站图标、网站Title、加载进度等

我们还可以向WebView添加更多的代码,甚至可以监听和控制WebView执行JavaScript

 

为了让Webview从apk载文件中加载assets,Android SDK提供了一个schema,前缀为file:///android_asset/WebView遇到这样的schema,就去当前包中的assets目录中找内容。

如:file:///android_asset/demo.html

 

一个简单的列子(不完整,主要说明使用WebViewClent控制新连接在当前WebView中打开):

WebView browser=(WebView)findViewById(R.id.webkit); 

browser.setWebViewClient(genWebViewClientForURL());

  
  WebSettings s = browser.getSettings();
    s.setSaveFormData(false);
    s.setSavePassword(false);
    s.setUseWideViewPort(true);
    s.setJavaScriptEnabled(true);
    s.setLightTouchEnabled(true);

 

private WebViewClient genWebViewClientForURL() {
  return new CiHaiWebViewClient(); 
 }

 private class CiHaiWebViewClient extends WebViewClient
 {
  @Override

  public boolean shouldOverrideUrlLoading(WebView view, String url) {
   String strURL = "";
   try {
    strURL = URLDecoder.decode(url.replaceAll("fake://wistron/", ""), "utf-8");
   } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
   }
   if (strURL.equals(STR_PRE))
   {  …… } else if (strURL.equals(STR_NEXT)) { …… } else { ……  }
  }

有代码:browser.loadDataWithBaseURL("fake://wistron/", htmlText, "text/html", "utf-8", null);

这里的baseUrl:fake://wistron/故这里需要WebViewClient shouldOverrideUrlLoading()方法,且要实现URLDecoder.decode(url.replaceAll("fake://wistron/", ""), "utf-8");

目录结构如下:

 

android的类如下:

Java代码  收藏代码
  1. package com.easyway.webview;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.view.KeyEvent;  
  6. import android.webkit.WebSettings;  
  7. import android.webkit.WebView;  
  8. /** 
  9.  * 为了方便网页和Android应用的交互,Android系统提供了WebView中JavaScript 
  10.  * 网页脚本调用Java类方法的机制。只要调用addJavascriptInterface方法即可映 
  11.  * 射一个Java对象到JavaScript对象上。 
  12.  * 1.先在layout文件中加入<WebView>元素  或WebView webView = new WebView(this); 
  13.  * 2.由于应用程序需要访问网络,所以需要在AndroidManifest.xml中请求网络权限的: 
  14.    3.使用Web View 
  15.    4.加载一个页面,可以用loadUrl()方法,例如: 
  16.          设置WevView要显示的网页: 
  17.   互联网用:webView.loadUrl("http://android.tgbus.com");  
  18.   本地文件用:webView.loadUrl(file:///android_asset/xx.html);固定格式 
  19.   本地文件存放在:assets文件中 
  20.    5.在Web View 中使用JavaScript 如果你加载到 Web View 中的网页使用了JavaScript,那么, 
  21.    需要在Websetting 中开启对JavaScript的支持,因为Web View 中默认的是JavaScript未启用。 
  22.  *  
  23.  * 如果希望点击链接由自己处理,而不是新开Android的系统browser中响应该链接。 
  24.   给WebView添加一个事件监听对象(WebViewClient) 
  25.  
  26.   并重写其中的一些方法 
  27.   shouldOverrideUrlLoading:对网页中超链接按钮的响应。 
  28.   当按下某个连接时WebViewClient会调用这个方法,并传递参数:按下的url  
  29.   onLoadResource    
  30.   onPageStart   
  31.   onPageFinish   
  32.   onReceiveError 
  33.   onReceivedHttpAuthRequest 
  34.  
  35.  
  36. 如果用webview点链接看了很多页以后,如果不做任何处理,点击系统“Back”键,整个浏览器会调用 
  37. finish()而结束自身,如果希望浏览的网页回退而不是退出浏览器,需要在当前Activity中处理并消 
  38. 费掉该Back事件。 
  39.  
  40.   覆盖Activity类的onKeyDown(int keyCoder,KeyEvent event)方法。 
  41.  
  42.   public boolean onKeyDown(int keyCoder,KeyEvent event){ 
  43.                         if(webView.canGoBack() && keyCoder == KeyEvent.KEYCODE_BACK){ 
  44.                                 webview.goBack();   //goBack()表示返回webView的上一页面 
  45.                                 return true; 
  46.                         } 
  47.                         return false; 
  48.                 } 
  49.  
  50.  
  51.  *  
  52.  * @Title:  
  53.  * @Description: 实现Android中WebView实现Javascript调用Java类方法 
  54.  * @Copyright:Copyright (c) 2011 
  55.  * @Company:易程科技股份有限公司 
  56.  * @Date:2012-5-21 
  57.  * @author  longgangbai 
  58.  * @version 1.0 
  59.  */  
  60. public class AndroidWebClientActivity extends Activity {  
  61.     private WebView mWebView;  
  62.     /** Called when the activity is first created. */  
  63.     @Override  
  64.     public void onCreate(Bundle savedInstanceState) {  
  65.         super.onCreate(savedInstanceState);  
  66.         setContentView(R.layout.main);  
  67.         mWebView = (WebView) findViewById(R.id.wv_content);  
  68.         //使用垂直滚动条  
  69.         mWebView.setVerticalScrollbarOverlay(true);  
  70.         //获取WebView相关的设置的对象  
  71.         final WebSettings settings = mWebView.getSettings();  
  72.         settings.setSupportZoom(true);  
  73.   
  74.         //WebView启用Javascript脚本执行  
  75.         settings.setJavaScriptEnabled(true);  
  76.         //设置js可以打开window  
  77.         settings.setJavaScriptCanOpenWindowsAutomatically(true);  
  78.         //映射Java对象到一个名为”js2java“的Javascript对象上  
  79.         //JavaScript中可以通过"window.js2java"来调用Java对象的方法  
  80.         //要在webview中,调用addJavascriptInterface(OBJ,interfacename)  
  81.         //其中,obj为和javascript通信的应用程序,interfacename为提供给JAVASCRIPT调用的 名称,设置如下  
  82.         mWebView.addJavascriptInterface(new JSInvokeClass(), "js2java");   
  83.         //index.html在android的assets目录中  
  84.         mWebView.loadUrl("file:///android_asset/index.html");  
  85.     }  
  86.     /**网页Javascript调用接口**/  
  87.     class JSInvokeClass {  
  88.         public String getText() {  
  89.             return "调用服务器的方法返回的信息";  
  90.         }  
  91.     }  
  92.    
  93.      
  94.    /** 
  95.     * 利用Web View的历史记录来实现页面navigate backword. 
  96.     *重载Activity中的onKeyDown方法,实现此功能: 
  97.     */  
  98.    @Override       
  99.    public boolean onKeyDown(int keyCode, KeyEvent event) {          
  100.     // Check if the key event was the BACK key and if there's history           
  101.    if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()){               
  102.        mWebView.goBack();               
  103.    return true;           
  104.    }          
  105.     // If it wasn't the BACK key or there's no web page history, bubble up to the default           
  106.    // system behavior (probably exit the activity)           
  107.    return super.onKeyDown(keyCode, event);       
  108.    }   
  109. }  

 

html代码如下:

Html代码  收藏代码
  1. <!DOCTYPE HTML>  
  2. <html>  
  3. <head>  
  4. <meta name="viewport" content="width=320; user-scalable=no" />  
  5. <meta http-equiv="Content-type" content="text/html; charset=utf-8">  
  6. <title>jQuery Google Maps Plugin</title>  
  7. <script>  
  8.  function init(){  
  9.      //js调用java的方法  
  10.      //返回值作为text的文本值  
  11.      //调用方式:window.暴露名称.方法  
  12.      var val=window.js2java.getText();  
  13.      var txt=document.getElementById("txt");  
  14.      txt.value=val;  
  15.  }  
  16. </script>  
  17. </head>  
  18. <body onload="init()">  
  19.   文件信息:<input id="txt" type="text">  
  20. </body>  
  21. </html>  

 

来自 http://topmanopensource.iteye.com/blog/1536256

 

  转载自IBM中国开发网站:

http://www.ibm.com/developerworks/cn/opensource/os-cn-android-phonegap/#major1

PhoneGap 简介

PhoneGap 是利用 HTML 来开发移动应用的一个开放源代码的框架,利用它就可以仅仅使用 HTML 和 Javascript 实现一次编写,多个移动平台运行的应用。目前已经可以支持 Android、iOS、BlackBerry 等 6 个平台。在完成这篇短文的过程中,PhoneGap 正式发布了 1.0.0 版本。因而短文中的版本使用的是 1.0.0rc2 版本的代码。

PhoneGap 在 Android 平台上的实现的框架是利用了在本地代码和浏览器间建立中间层来实现对 Java 代码的隔离。利用浏览器的接口和网络 socket 接口实现数据的通讯。因此在对 PhoneGap 框架的理解上,需要从 Java 端和浏览器端两部分来分别学习。

关于 Android 开发环境和 PhoneGap 开发环境的搭建,请参考参考资料中的开发者网站实施。

Java 端介绍

Java 端作为后台调用 Android 本地 SDK 的接口,主要实现了如下的功能:

  1. 建立通讯机制,提供接口给浏览器端,方便 JavaScript 进行调用。
  2. 数据队列的维护,以保证浏览器端的调用后产生的数据可以回送。
  3. 插件体系的建立,提供整个框架的可扩展性。

而这三部分的功能对应到代码中则是如下的几个重要的 Java 类:

  1. DroidGap、CallbackServer
  2. CallbackServer
  3. Plugin、PluginManager

因此我们需要依次来了解这几个重要的 Java 类的具体实现,这样才可以对 PhoneGap 在 Android 上的体系有一个很好的了解。

DroidGap 的实现

当我们完成一个基本的 PhoneGap 的示例后,我们就会发现,在使用 PhoneGap 进行开发的手机应用中,第一步就是将继承关系 extends Activity 修改为 extends DroidGap。因此,DroidGap 是整个应用开始的地点,首先需要了解 DroidGap 的内容。

在源码中可以看到 DroidGap 继承自 PhonegapActivity,而 PhonegapActivity 是一个抽象类,继承自 Activity,但是具体的实现都是集中在 DroidGap 类中。

因此,我们继续回到 DroidGap 类中,当我们知道 DroidGap 类也是一个 Acitvity 后,就会明白,DroidGap 在 onCreate 方法中实现了整个类最初的一些操作,代码的实现则集中在 onCreate() 和 init() 这两个方法中,具体的流程如下:

  1. 设置 WebView

设置 WebView 的代码都集中在 init() 方法中,其中一个值得注意的工作是设置 WebChromClient。代码中提供了一个继承自 WebChromClient 的类,重写了其中的 onJsAlert,onJsConfirm,onJsPrompt 等方法。而在 onJsPrompt() 方法中,实现了 PhoneGap 中 Java 端和浏览器端通讯的关键一步。因此,值得我们专门的关注其代码,具体的功能实现代码如下,我们已经过滤了一些错误处理、安全验证等等可能会妨碍我们对重要的功能实现理解的代码:


代码清单 1. WebView 代码

				
 if (reqOk && defaultValue != null && defaultValue.length() > 3 
 && defaultValue.substring(0, 4).equals("gap:")) { 
   JSONArray array; 
   try { 
 array = new JSONArray(defaultValue.substring(4)); 
 String service = array.getString(0); 
 String action = array.getString(1); 
 String callbackId = array.getString(2); 
 boolean async = array.getBoolean(3); 
 String r = pluginManager.exec(service, action, callbackId, message, async); 
 result.confirm(r); 
 } catch (JSONException e) { 
 e.printStackTrace(); 
 } 
 } 
 // Polling for JavaScript messages 
 else if ...... 

 

在此,我们就可以明白,实现 JavaScript 与 Java 端通讯的原理是 JavaScript 利用 prompt 来传递调用信息的数据,在 onJsPrompt 中,重写的方法截获了这些数据,在完成了对数据格式等等分析后,按照要求进行具体的调用。而具体的调用利用是的 PluginManager 类,这将在随后说明。

  1. 绑定 js

在完成 WebView 的设置后,再将 PhoneGap 和 js 绑定,实现 js 与 PhoneGap 的通讯。在这一步中主要的内容就是实例化了 CallbackServer 和 PluginManager 两个类。因此,更多的内容会在随后对这两个类的介绍中再说明。

  1. 载入 URL

LoadUrl 方法也是实现一个 PhoneGap 示例后很熟悉的方法,在 loadUrl 中,完成基本的字符串处理后,主要利用 runOnUiThread 开始处理,其中第一步检测是否需要提供载入的显示,之后使用 WebView 的 loadUrl 载入内容,并且设置超时时间。

至此,Java 端的准备工作也就完成,实现了 UI 的载入。可以看到,在 DroidGap 类中最重要的一个部分就是截获 JavaScript 的 prompt 数据。这是实现浏览器端和 Java 端通讯的基础。

Plugin 的实现

Plugin 是一个抽象类,实现了 IPlugin 接口,PhoneGap 中利用 Android SDK 实现逻辑代码的途径就是通过继承 Plugin 来实现。应此 Plugin 本身非常简单,最主要的部分是提供一个 execute 方法,如下:

 public abstract PluginResult execute(String action, JSONArray args, String callbackId); 

 

Plugin 的实现中的重要逻辑在其中来实现,它返回的是一个 PluginResult 对象,这个对象主要负责传递数据信息,包括的成员主要如下:

	 private final int status; 

 

用于返回状态,status 与一个 enum 类型相关,用于标注插件的执行结果如何。

	 private final String message; 

 

用于返回数据信息。

PluginManager 的实现

PluginManager 事实上是 DroidGap 类和具体的继承自 Plugin 的插件的联系纽带,由它来寻找和载入插件,并且调用。

首先,PluginManager 在构造函数中,调用了 loadPlugins 方法,该方法负责解析 xml 配置文件,对应每个 plugin,调用一次 addService 用于注册插件,具体的注册地点,是通过维护一个 HashMap<String, String> 来实现,分别保存 serviceType, className。在成员中,有另外一个 HashMap<String, IPlugin> 用于实现 className 和 IPlugin 类的绑定。这样就实现了 serviceType 和 IPlugin 的对应。

在 PluginManager 中,执行插件的方法 exec 是主要功能,我们将会详细说明一下这个方法的过程:

  1. 利用查询之前提到的 HashMap<String, String> 将 service 和 className 对应起来,这样我们就获得了提供功能的插件类的位置。
  2. 通过 getClassByName 来获得插件类。
  3. 完成类型检测,确定对应的类是一个插件类。
  4. 执行 addPlugin 方法,其中则将第 2 步中获得插件类的 className 和具体的 Plugin 接口的实现绑定,具体的实现就是上文中提到的 HashMap<Strig,IPlugin>。
  5. 判断 Plugin 是否需要异步执行,再根据情况,选择直接执行或者是建立新的线程来执行。
  6. 完成执行后,得到的数据会根据情况交给 CallbackServer 来处理,PluginManager 会调用 DroidGap 中的 sendJavaScript 来将数据交给 CallbackServer,事实上,DroidGap 中的 sendJavaScript 不过是对 CallbackServer 中的 sendJavaScript 包装,实际调用的是 CallbackServer 中的 sendJavaScript 方法,这涉及到了 CallbackServer 类,随后就会说明。

执行结果的转换则是利用 PluginResult 中的一些 to***String 方法。这些方法返回了一个 String 类型,其中包括了具体的 JSON 对象和 callbackId,具体的形式则是一个 js 代码,用于前端的调用。

CallbackServer 的实现

CallbackServer 实现了 Runnable 接口,具体的功能就是维护一个数据的队列,并且建立一个服务器,用于 XHR 的数据传递,对数据的队列的维护利用的是 LinkedList<String>。

由于实现的是 Runnable 接口,在 CallbackServer 中,最主要的方法就是 run() 方法,run() 方法的具体内容简介如下:

  1. 首先利用 ServerSocket 监听端口,具体端口则自由分配。
  2. 在 accept 后则是对 HTTP 协议的解析,和对应的返回 status code。
  3. 在验证正确后,利用 getJavascript 方法得到维护的 LinkedList<String>() 中的保存的 js 代码,如果为空则返回 null。
  4. 这些具体的 string 类型的 js 代码则利用 socket 作为 response 返回给前端。

之后就是对队列维护的方法,这时理解之前的 sendJavaScript 则很简单,该方法与 getJavaScript 相反,一个是从 LinkedList 中取出 js 代码,一个则是加入。

综上,CallbackServer 实现的是两个功能,一个是 XHR 的 SocketServer,一个是对队列的维护。而完成 CallbackServer 类的说明后,Java 端的主要功能也都说明完毕。随后会说明浏览器端的说明,在完成浏览器端说明后,我们就会明白整个 PhoneGap 框架运行的原理。

 

 

浏览器端介绍

浏览器端的 JavaScript 代码较多,但是实现其中核心功能的代码都在 PhoneGap 类中,其他的都是 PhoneGap 框架中自带的一些 Plugin 的 JavaScript 代码。

在浏览器端,其中一个最重要的函数就是 PhoneGap.exec 函数,因为大部分的函数调用,最终都会在这里实现与本地 Java 端的数据通讯,转而去调用 Java 端的 Plugin 来实现具体的执行。主要的内容如下,我们已经去除一些非功能的错误处理代码段,集中于具体的功能实现:


代码清单 2. PhonGap.exec 代码

				
 var callbackId = service + PhoneGap.callbackId++; 
 if (success || fail) { 
    PhoneGap.callbacks[callbackId] = {success:success, fail:fail}; 
 } 
 var r = prompt(PhoneGap.stringify(args), "gap:"+ 
 PhoneGap.stringify([service, action, callbackId, true])); 
 // If a result was returned 
 if (r.length > 0) { 
    eval("var v="+r+";"); 
    // If status is OK, then return value back to caller 
    if (v.status === PhoneGap.callbackStatus.OK) { 
       ...... 
    }else if ...... 
 } 

 

在以上标红加粗的代码段上,我们可以看到,调用的是 prompt 方法 , 我们在之前特别提到了,PhoneGap 在 DroidGap 类中,继承了一个 WebChromeClient,其中重写了方法 onJsPrompt,这样在 Java 端,就截获了浏览器端的调用。现在可以回到之前的 Java 端的说明,查看之前列出的 onJsPrompt 代码,联系之前的 Java 端的介绍,就会觉得豁然开朗了,剩下了流程也就顺理成章了。通过 r 的返回值,就可以轻松的返回数据给浏览器端了。

这时我们会有一个好奇,那就是 CallbackServer 的 SocketServer 的作用在哪里,因为至此我们已经看到了整个数据在浏览器端和 Java 端的通讯,那么这样的一个 SocketServer 又是提供的什么数据通讯呢?

显然,在 PluginManager 中的 exec中,我们会返回执行获得的数据,但是,在使用异步调用的时候,立即返回的则是一个空字符串,也即没有数据返回,之后插件会在一个新的线程中执行,等到异步的插件执行的结果返回数据后,会放入到队列中,等待 XHR 的获得。而 XHR 的调用是在 JavaScript 代码初始化中开始调用的,其中由于 JsCallBack 函数有使用轮询保证自己循环执行。这样就可以不断的获得异步数据。

 

 

Hello 插件的实现

掌握了上文中对 PhoneGap 框架的分析,我们就可以通过自己实现相关的插件来完成自己希望实现的功能,这样就可以不断丰富自己应用的功能。我们假设开发者已经熟悉了 PhoneGap 和 Android 的基本开发知识,Android 和 PhoneGap 开发环境搭建已经完成。

在具体的 Plugin 实现中,要分两个部分来实现,Java 代码和 JavaScript 代码,就此流程,我们来通过一个简单的例子来看一下如何具体的完成 Plugin 的实现。

在 Java 端,我们要继承一个 Plugin 类,实现其中的 execute 方法,在这里,我们省略具体的功能逻辑。

在 JavaScript 端,我们要利用现有 PhoneGap.exec 来调用具体的 Java 端,而且需要注意的是要在 PhoneGap 中注册这一个插件,PhoneGap.addConstructor就在 JavaScript 端调用 Java 端代码,完成添加,当然直接修改 XML 文件来实现也可以。

首先,完成 Java 端的代码实现。

我们实现一个简单的字符串回射程序,来模拟具体的逻辑,代码如下:


清单 3. 插件实例 Java 端代码

				
 @Override 
 public PluginResult execute(String action, JSONArray args, String callbackId) { 
 PluginResult.Status status = PluginResult.Status.OK; 
 PluginResult r=null; 
 if (action.equals("sayHello")){ 
 r = new PluginResult(status,"Hello"); 
 }else if (action.equals("saySth")){ 
 r = new PluginResult(status,args); 
 } 
 return r; 
 } 

 

实现的功能如下:

  1. 如果 action 的参数是 sayHello,则返回字符串“Hello”。
  2. 如果是 saySth,则返回 args 中的内容。

之后,完成 JavaScript 代码

请注意 JavaScipt 代码是需要 HTML 代码包含后才能使用的:


清单 4. 插件实例 JavaScript 端代码

				
     var Hello=function(){ 
     } 
     Hello.prototype.saySth = function(msg, callback, fail) { 
 return PhoneGap.exec(function(args) { 
     callback(args); 
 }, function(args) { 
 if(typeof fail == 'function') { 
     fail(args); 
 } 
 }, 'Hello', 'saySth', [msg]); 
     } 
     Hello.prototype.sayHello = function(callback, fail) { 
 return PhoneGap.exec(function(args) { 
  callback(args); 
 }, function(args) { 
 if(typeof fail == 'function') { 
 fail(args); 
 } 
 }, 'Hello', 'sayHello',['sayHello']); 
     } 
     PhoneGap.addConstructor(function() { 
    PhoneGap.addPlugin('hello', new Hello()); 
    //PluginManager.addService("Hello","qj.Hello"); 
     }) 

 

在这里我们需要有一个特别的说明:

在上述 JavaScript 代码中,我们注释了 addConstructor 函数中的一行,因为我们在开发过程中发现这条代码的执行在 Javascript 端没有被实现。

因此这段代码具体的执行则需要我们在 Java 端实现代码,那么为了实现这段 JavaScript 代码所做的补偿性的 Java 代码要在自己实现的 Java 端的 onCreate 函数中实现,内容是很简单的一句(当然,也可以通过修改 xml 文件来实现这一功能,上文提到过,PluginManager 类会扫描 xml 文件来依次以对应 xml 文件中的插件名作为参数执行这一调用):

 this.pluginManager.addService("Hello", "qj.Hello"); 

 

注意第二个参数是实现的插件的代码的位置,Hello 插件的位置是“src.qj.Hello.java”,对应的位置信息就是去除 .java 后缀名后的位置信息,整个项目的组织结构如图。


图 1. 项目组织结构图示例
图 1. 项目组织结构图示例

至此,Hello 插件已经实现,如果需要调用,则 JavaScript 代码如下:


清单 5. 插件调用代码

				
 window.plugins.hello.sayHello(function(arg){alert(arg);}, 
 function(arg){alert(arg);}); 
 window.plugins.hello.saySth("Bingo",function(arg){alert(arg);}, 
 function(arg){alert(arg);}); 

 

我们简单的将返回的数据通过 alert() 展示出来,插件的执行结果如下:

点击 SayHello,


图 2. 点击 SayHello 示例
图 2. 点击 SayHello 示例

点击 SayBingo


图 3. 点击 SayBingo 示例
图 3. 点击 SayBingo 示例

结束语

至此插件的开发已经完成,其中很大的篇幅都是通过对 PhoneGap 的框架的说明,让我们可以对插件的运行过程有一个更加底层的认识,我想这是比实现一个简单的插件更加重要的东西。如果读者需要了解更多的信息,请浏览参考资料中的开发者网站,获得更加实时的信息。

<!-- CMA ID: 812102 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-document-html-6.0.xsl -->

来自 http://topmanopensource.iteye.com/blog/1558195


phonegap底层原理学习和研究(三)

              针对Phonegap开发中常用的DroidGap类继承自PhonegapActivity,PhonegapActivity继承自Activity。源代码如下:

Java代码  收藏代码
  1. /* 
  2.  * PhoneGap is available under *either* the terms of the modified BSD license *or* the 
  3.  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. 
  4.  *  
  5.  * Copyright (c) 2005-2010, Nitobi Software Inc. 
  6.  * Copyright (c) 2010, IBM Corporation 
  7.  */  
  8. package com.phonegap.api;  
  9.   
  10. import android.app.Activity;  
  11. import android.content.Intent;  
  12.   
  13. /** 
  14.  * The Phonegap activity abstract class that is extended by DroidGap. 
  15.  * It is used to isolate plugin development, and remove dependency on entire Phonegap library. 
  16.  */  
  17. public abstract class PhonegapActivity extends Activity {  
  18.   
  19.     /** 
  20.      * Add a class that implements a service. 
  21.      *  
  22.      * @param serviceType 
  23.      * @param className 
  24.      */  
  25.     abstract public void addService(String serviceType, String className);  
  26.       
  27.     /** 
  28.      * Send JavaScript statement back to JavaScript. 
  29.      *  
  30.      * @param message 
  31.      */  
  32.     abstract public void sendJavascript(String statement);  
  33.   
  34.     /** 
  35.      * Launch an activity for which you would like a result when it finished. When this activity exits,  
  36.      * your onActivityResult() method will be called. 
  37.      *   
  38.      * @param command           The command object 
  39.      * @param intent            The intent to start 
  40.      * @param requestCode       The request code that is passed to callback to identify the activity 
  41.      */  
  42.     abstract public void startActivityForResult(IPlugin command, Intent intent, int requestCode);  
  43.   
  44.     /** 
  45.      * Set the plugin to be called when a sub-activity exits. 
  46.      *  
  47.      * @param plugin            The plugin on which onActivityResult is to be called 
  48.      */  
  49.     abstract public void setActivityResultCallback(IPlugin plugin);  
  50.   
  51.     /** 
  52.      * Load the specified URL in the PhoneGap webview. 
  53.      *  
  54.      * @param url               The URL to load. 
  55.      */  
  56.     abstract public void loadUrl(String url);  
  57. }  

 

在使用这个类启动Webapp移动项目时候可以配置许多东西,如下:

Java代码  图 1. 项目组织结构图示例

至此,Hello 插件已经实现,如果需要调用,则 JavaScript 代码如下:


清单 5. 插件调用代码

				
 window.plugins.hello.sayHello(function(arg){alert(arg);}, 
 function(arg){alert(arg);}); 
 window.plugins.hello.saySth("Bingo",function(arg){alert(arg);}, 
 function(arg){alert(arg);}); 

 

我们简单的将返回的数据通过 alert() 展示出来,插件的执行结果如下:

点击 SayHello,


图 2. 点击 SayHello 示例
图 2. 点击 SayHello 示例

点击 SayBingo


图 3. 点击 SayBingo 示例
图 3. 点击 SayBingo 示例

结束语

至此插件的开发已经完成,其中很大的篇幅都是通过对 PhoneGap 的框架的说明,让我们可以对插件的运行过程有一个更加底层的认识,我想这是比实现一个简单的插件更加重要的东西。如果读者需要了解更多的信息,请浏览参考资料中的开发者网站,获得更加实时的信息。

<!-- CMA ID: 812102 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-document-html-6.0.xsl -->

来自 http://topmanopensource.iteye.com/blog/1558195


phonegap底层原理学习和研究(三)

              针对Phonegap开发中常用的DroidGap类继承自PhonegapActivity,PhonegapActivity继承自Activity。源代码如下:

Java代码  收藏代码
  1. This class is the main Android activity that represents the PhoneGap  
  2. application.  It should be extended by the user to load the specific  
  3. html file that contains the application.  
  4.   
  5. As an example:  
  6.   
  7.     package com.phonegap.examples;  
  8.     import android.app.Activity;  
  9.     import android.os.Bundle;  
  10.     import com.phonegap.*;  
  11.       
  12.     public class Examples extends DroidGap {  
  13.       @Override  
  14.       public void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.                    
  17.         // Set properties for activity  
  18.         super.setStringProperty("loadingDialog""Title,Message"); // show loading dialog  
  19.         super.setStringProperty("errorUrl""file:///android_asset/www/error.html"); // if error loading file in super.loadUrl().  
  20.   
  21.         // Initialize activity  
  22.         super.init();  
  23.           
  24.         // Clear cache if you want  
  25.         super.appView.clearCache(true);  
  26.           
  27.         // Load your application  
  28.         super.setIntegerProperty("splashscreen", R.drawable.splash); // load splash.jpg image from the resource drawable directory  
  29.         super.loadUrl("file:///android_asset/www/index.html"3000); // show splash screen 3 sec before loading app  
  30.       }  
  31.     }  

 

DroidGap中可以配置的属性如下:

Properties: The application can be configured using the following properties:


     //加载时候加载对话框的信息
     // Display a native loading dialog when loading app.  Format for value = "Title,Message".  
     // (String - default=null)
     super.setStringProperty("loadingDialog", "Wait,Loading Demo...");
 
     //加载对话对话框
     // Display a native loading dialog when loading sub-pages.  Format for value = "Title,Message".  
     // (String - default=null)
     super.setStringProperty("loadingPageDialog", "Loading page..."); 
    
     // Cause all links on web page to be loaded into existing web view, 
     // instead of being loaded into new browser. (Boolean - default=false)
     super.setBooleanProperty("loadInWebView", true);
    //加载相关的动画信息
     // Load a splash screen image from the resource drawable directory.
     // (Integer - default=0)
     super.setIntegerProperty("splashscreen", R.drawable.splash); 
    //设置默认的背景色
     // Set the background color.
     // (Integer - default=0 or BLACK)
     super.setIntegerProperty("backgroundColor", Color.WHITE);
     //设置超时时间
     // Time in msec to wait before triggering a timeout error when loading
     // with super.loadUrl().  (Integer - default=20000)
     super.setIntegerProperty("loadUrlTimeoutValue", 60000); 
    //设置请求错误时候的提示页面
     // URL to load if there's an error loading specified URL with loadUrl().  
     // Should be a local URL starting with file://. (String - default=null)
     super.setStringProperty("errorUrl", "file:///android_asset/www/error.html"); 
    //是否在后台运行的功能
     // Enable app to keep running in background. (Boolean - default=true)
     super.setBooleanProperty("keepRunning", false);
 
     
Phonegap.xml的配置如下:
     PhoneGap uses a configuration file at res/xml/phonegap.xml to specify the following settings.
     //允许phonegap访问的路径和域
     Approved list of URLs that can be loaded into DroidGap
         <access origin="http://server regexp" subdomains="true" / >

   //日志的模式
     Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
         <log level="DEBUG" /> 

Phonegap plugins:
     PhoneGap uses a file at res/xml/plugins.xml to list all plugins that are installed.
     Before using a new plugin, a new element must be added to the file.
         name attribute is the service name passed to PhoneGap.exec() in JavaScript
         value attribute is the Java class name to call.
     
     <plugins>
         <plugin name="App" value="com.phonegap.App"/> 
         ...
     </plugins>

分享到:  
评论
4 楼 longgangbai 2012-08-24  
zhiduo5 写道
longgangbai 写道
zhiduo5 写道
你的文章果然好用,能告诉我怎么设置super.loadUrl();时的黑屏,我想把它换成本地页面(.html),谢谢!

谢谢,分享,沟通交流


我想准确的判断页面加载是否完成,不知是否可有方法,不想延迟6秒

页面加载与网络有关,目前暂时没有相关的方法。
3 楼 zhiduo5 2012-08-23  
longgangbai 写道
zhiduo5 写道
你的文章果然好用,能告诉我怎么设置super.loadUrl();时的黑屏,我想把它换成本地页面(.html),谢谢!

zhiduo5 写道
你的文章果然好用,能告诉我怎么设置super.loadUrl();时的黑屏,我想把它换成本地页面(.html),谢谢!

谢谢,分享,沟通交流


我想准确的判断页面加载是否完成,不知是否可有方法,不想延迟6秒
2 楼 longgangbai 2012-08-22  
zhiduo5 写道
你的文章果然好用,能告诉我怎么设置super.loadUrl();时的黑屏,我想把它换成本地页面(.html),谢谢!

zhiduo5 写道
你的文章果然好用,能告诉我怎么设置super.loadUrl();时的黑屏,我想把它换成本地页面(.html),谢谢!

谢谢,分享,沟通交流
1 楼 zhiduo5 2012-08-11  
你的文章果然好用,能告诉我怎么设置super.loadUrl();时的黑屏,我想把它换成本地页面(.html),谢谢!

来自 http://topmanopensource.iteye.com/blog/1559276


         在phonegap的android开发中主要通过WebView显示相关的本地页面。针对WebView设置的代码如下:

 

       //创建相关的Web容器
        this.appView = new WebView(DroidGap.this);
        this.appView.setId(100);
        //设置WebView的布局
        this.appView.setLayoutParams(new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT,
                ViewGroup.LayoutParams.FILL_PARENT, 
                1.0F));
      //获取WebView的WebSetting的几个方法以便于后面通过反射注入相关的存储
        WebViewReflect.checkCompatibility();
      
        this.appView.setWebChromeClient(new GapClient(DroidGap.this));
        this.setWebViewClient(this.appView, new GapViewClient(this));

        this.appView.setInitialScale(100);
        this.appView.setVerticalScrollBarEnabled(false);
        this.appView.requestFocusFromTouch();

        // Enable JavaScript
        WebSettings settings = this.appView.getSettings();

       //启用js脚本的运行
        settings.setJavaScriptEnabled(true);
        //js是否可以打开窗体

        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);

        // Enable database
        settings.setDatabaseEnabled(true);
        String databasePath = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); 
        settings.setDatabasePath(databasePath);

        // Enable DOM storage
        WebViewReflect.setDomStorage(settings);
        
        // Enable built-in geolocation
        WebViewReflect.setGeolocationEnabled(settings, true);

        // Create callback server and plugin manager
        this.callbackServer = new CallbackServer();
        this.pluginManager = new PluginManager(this.appView, this);        

        // Add web view but make it invisible while loading URL
        this.appView.setVisibility(View.INVISIBLE);
        root.addView(this.appView);

分享到:  
评论
1 楼 zhiduo5 2012-09-08  
我有个问题啊,就是想实现super.loadUrl();时等页面加载成功在显示,不用开场动话那种,求代码,请多指点!

来自 http://topmanopensource.iteye.com/blog/1559312
普通分类: