上面即是使用web技术来开发本地iphone app的一幅运行图,关于web的开发技术,这里就不多说了,这里重点提及Phonegap,利用它我们就可以把web app转换成各种移动平台的APP。
PhoneGap is an HTML5 app platform that allows you to author native applications with web technologies and get access to APIs and app stores. PhoneGap leverages web technologies developers already know best… HTML and JavaScript
这里简单介绍一下PhoneGap,它对各个移动平台的API进行了一次封装,屏蔽了每种移动平台的具体细节,而设计了一个公共的API层,对开发者来说,只要调用统一的API来进行开发就可以了。
把PhoneGap的源代码下载来通读了一下,了解了一下背后的实现原理,下面以Camera的功能为例来进行说明。
API 开发文档请参照 http://docs.phonegap.com/en/1.1.0/index.html,其中有关Camera的开发API请参照http://docs.phonegap.com/en/1.1.0/phonegap_camera_camera.md.html#Camera,我们的代码就可以写成
- navigator.camera.getPicture(onSuccess, onFail, { quality: 50 });
- function onSuccess(imageData) {
- var image = document.getElementById('myImage');
- image.src = "data:image/jpeg;base64," + imageData;
- }
- function onFail(message) {
- alert('Failed because: ' + message);
- }
是不是很简单,具体后台平台的差异我们根本不需要来进行区别,全部由PhoneGap来给处理了,那么它是怎么处理的呢,继续往下看。
先来看看JS端的API实现,它主要是定义了web层开发的接口,也即我们上面例子中的camera.getPicture 函数
- /**
- * Gets a picture from source defined by "options.sourceType", and returns the
- * image as defined by the "options.destinationType" option.
- * The defaults are sourceType=CAMERA and destinationType=DATA_URL.
- *
- * @param {Function} successCallback
- * @param {Function} errorCallback
- * @param {Object} options
- */
- Camera.prototype.getPicture = function(successCallback, errorCallback, options) {
- // successCallback required
- if (typeof successCallback !== "function") {
- console.log("Camera Error: successCallback is not a function");
- return;
- }
- // errorCallback optional
- if (errorCallback && (typeof errorCallback !== "function")) {
- console.log("Camera Error: errorCallback is not a function");
- return;
- }
- if (options === null || typeof options === "undefined") {
- options = {};
- }
- if (options.quality === null || typeof options.quality === "undefined") {
- options.quality = 80;
- }
- if (options.maxResolution === null || typeof options.maxResolution === "undefined") {
- options.maxResolution = 0;
- }
- if (options.destinationType === null || typeof options.destinationType === "undefined") {
- options.destinationType = Camera.DestinationType.DATA_URL;
- }
- if (options.sourceType === null || typeof options.sourceType === "undefined") {
- options.sourceType = Camera.PictureSourceType.CAMERA;
- }
- if (options.encodingType === null || typeof options.encodingType === "undefined") {
- options.encodingType = Camera.EncodingType.JPEG;
- }
- if (options.mediaType === null || typeof options.mediaType === "undefined") {
- options.mediaType = Camera.MediaType.PICTURE;
- }
- if (options.targetWidth === null || typeof options.targetWidth === "undefined") {
- options.targetWidth = -1;
- }
- else if (typeof options.targetWidth == "string") {
- var width = new Number(options.targetWidth);
- if (isNaN(width) === false) {
- options.targetWidth = width.valueOf();
- }
- }
- if (options.targetHeight === null || typeof options.targetHeight === "undefined") {
- options.targetHeight = -1;
- }
- else if (typeof options.targetHeight == "string") {
- var height = new Number(options.targetHeight);
- if (isNaN(height) === false) {
- options.targetHeight = height.valueOf();
- }
- }
- PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [options]);
- };
- 上段代码中的最后一句就是呼叫本地的Camera模块的takePicture函数,具体的PhoneGap.exec实现代码如下:
- /**
- * Execute a PhoneGap command. It is up to the native side whether this action is synch or async.
- * The native side can return:
- * Synchronous: PluginResult object as a JSON string
- * Asynchrounous: Empty string ""
- * If async, the native side will PhoneGap.callbackSuccess or PhoneGap.callbackError,
- * depending upon the result of the action.
- *
- * @param {Function} success The success callback
- * @param {Function} fail The fail callback
- * @param {String} service The name of the service to use
- * @param {String} action Action to be run in PhoneGap
- * @param {Array.<String>} [args] Zero or more arguments to pass to the method
- */
- PhoneGap.exec = function(success, fail, service, action, args) {
- try {
- 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) {
- // If there is a success callback, then call it now with
- // returned value
- if (success) {
- try {
- success(v.message);
- } catch (e) {
- console.log("Error in success callback: " + callbackId + " = " + e);
- }
- // Clear callback if not expecting any more results
- if (!v.keepCallback) {
- delete PhoneGap.callbacks[callbackId];
- }
- }
- return v.message;
- }
- // If no result
- else if (v.status === PhoneGap.callbackStatus.NO_RESULT) {
- // Clear callback if not expecting any more results
- if (!v.keepCallback) {
- delete PhoneGap.callbacks[callbackId];
- }
- }
- // If error, then display error
- else {
- console.log("Error: Status="+v.status+" Message="+v.message);
- // If there is a fail callback, then call it now with returned value
- if (fail) {
- try {
- fail(v.message);
- }
- catch (e1) {
- console.log("Error in error callback: "+callbackId+" = "+e1);
- }
- // Clear callback if not expecting any more results
- if (!v.keepCallback) {
- delete PhoneGap.callbacks[callbackId];
- }
- }
- return null;
- }
- }
- } catch (e2) {
- console.log("Error: "+e2);
- }
- };
至此为止,web端js的代码工作就完毕了,程序流程转移到了本地代码中,我们以Android的本地代码为例,看看是怎么具体实现的。
- package com.phonegap;
- import android.content.ContentResolver;
- import android.content.ContentValues;
- import android.content.Intent;
- import android.database.Cursor;
- import android.graphics.Bitmap;
- import android.graphics.Bitmap.CompressFormat;
- import android.graphics.BitmapFactory;
- import android.net.Uri;
- import android.provider.MediaStore.Images.Media;
- import com.phonegap.api.PhonegapActivity;
- import com.phonegap.api.Plugin;
- import com.phonegap.api.PluginResult;
- import com.phonegap.api.PluginResult.Status;
- import java.io.ByteArrayOutputStream;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.io.PrintStream;
- import org.apache.commons.codec.binary.Base64;
- import org.json.JSONArray;
- import org.json.JSONException;
- import org.json.JSONObject;
- public class CameraLauncher extends Plugin
- {
- private static final int DATA_URL = 0;
- private static final int FILE_URI = 1;
- private static final int PHOTOLIBRARY = 0;
- private static final int CAMERA = 1;
- private static final int SAVEDPHOTOALBUM = 2;
- private static final int PICTURE = 0;
- private static final int VIDEO = 1;
- private static final int ALLMEDIA = 2;
- private static final int JPEG = 0;
- private static final int PNG = 1;
- private static final String GET_PICTURE = "Get Picture";
- private static final String GET_VIDEO = "Get Video";
- private static final String GET_All = "Get All";
- private static final String LOG_TAG = "CameraLauncher";
- private int mQuality;
- private int targetWidth;
- private int targetHeight;
- private Uri imageUri;
- private int encodingType;
- private int mediaType;
- public String callbackId;
- private int numPics;
- public PluginResult execute(String action, JSONArray args, String callbackId)
- {
- PluginResult.Status status = PluginResult.Status.OK;
- String result = "";
- this.callbackId = callbackId;
- try
- {
- if (action.equals("takePicture")) {
- int srcType = 1;
- int destType = 0;
- this.targetHeight = 0;
- this.targetWidth = 0;
- this.encodingType = 0;
- this.mediaType = 0;
- this.mQuality = 80;
- JSONObject options = args.optJSONObject(0);
- if (options != null) {
- srcType = options.getInt("sourceType");
- destType = options.getInt("destinationType");
- this.targetHeight = options.getInt("targetHeight");
- this.targetWidth = options.getInt("targetWidth");
- this.encodingType = options.getInt("encodingType");
- this.mediaType = options.getInt("mediaType");
- this.mQuality = options.getInt("quality");
- }
- if (srcType == 1) {
- takePicture(destType, this.encodingType);
- }
- else if ((srcType == 0) || (srcType == 2)) {
- getImage(srcType, destType);
- }
- PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
- r.setKeepCallback(true);
- return r;
- }
- return new PluginResult(status, result);
- } catch (JSONException e) {
- e.printStackTrace();
- }return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
- }
- public void takePicture(int returnType, int encodingType)
- {
- this.numPics = queryImgDB().getCount();
- Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
- File photo = createCaptureFile(encodingType);
- intent.putExtra("output", Uri.fromFile(photo));
- this.imageUri = Uri.fromFile(photo);
- this.ctx.startActivityForResult(this, intent, 32 + returnType + 1);
- }
- private File createCaptureFile(int encodingType)
- {
- File photo = null;
- if (encodingType == 0)
- photo = new File(DirectoryManager.getTempDirectoryPath(this.ctx), "Pic.jpg");
- else {
- photo = new File(DirectoryManager.getTempDirectoryPath(this.ctx), "Pic.png");
- }
- return photo;
- }
- public void getImage(int srcType, int returnType)
- {
- Intent intent = new Intent();
- String title = "Get Picture";
- if (this.mediaType == 0) {
- intent.setType("image/*");
- }
- else if (this.mediaType == 1) {
- intent.setType("video/*");
- title = "Get Video";
- }
- else if (this.mediaType == 2)
- {
- intent.setType("*/*");
- title = "Get All";
- }
- intent.setAction("android.intent.action.GET_CONTENT");
- intent.addCategory("android.intent.category.OPENABLE");
- this.ctx.startActivityForResult(this, Intent.createChooser(intent, new String(title)), (srcType + 1) * 16 + returnType + 1);
- }
- public Bitmap scaleBitmap(Bitmap bitmap)
- {
- int newWidth = this.targetWidth;
- int newHeight = this.targetHeight;
- int origWidth = bitmap.getWidth();
- int origHeight = bitmap.getHeight();
- if ((newWidth <= 0) && (newHeight <= 0)) {
- return bitmap;
- }
- if ((newWidth > 0) && (newHeight <= 0)) {
- newHeight = newWidth * origHeight / origWidth;
- }
- else if ((newWidth <= 0) && (newHeight > 0)) {
- newWidth = newHeight * origWidth / origHeight;
- }
- else
- {
- double newRatio = newWidth / newHeight;
- double origRatio = origWidth / origHeight;
- if (origRatio > newRatio)
- newHeight = newWidth * origHeight / origWidth;
- else if (origRatio < newRatio) {
- newWidth = newHeight * origWidth / origHeight;
- }
- }
- return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
- }
- public void onActivityResult(int requestCode, int resultCode, Intent intent)
- {
- int srcType = requestCode / 16 - 1;
- int destType = requestCode % 16 - 1;
- if (srcType == 1)
- {
- if (resultCode == -1) {
- try
- {
- ExifHelper exif = new ExifHelper();
- if (this.encodingType == 0) {
- exif.createInFile(DirectoryManager.getTempDirectoryPath(this.ctx) + "/Pic.jpg");
- exif.readExifData();
- }
- try
- {
- bitmap = MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), this.imageUri);
- } catch (FileNotFoundException e) {
- Uri uri = intent.getData();
- ContentResolver resolver = this.ctx.getContentResolver();
- bitmap = BitmapFactory.decodeStream(resolver.openInputStream(uri));
- }
- Bitmap bitmap = scaleBitmap(bitmap);
- if (destType == 0) {
- processPicture(bitmap);
- checkForDuplicateImage(0);
- }
- else if (destType == 1)
- {
- ContentValues values = new ContentValues();
- values.put("mime_type", "image/jpeg");
- Uri uri = null;
- try {
- uri = this.ctx.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
- } catch (UnsupportedOperationException e) {
- System.out.println("Can't write to external media storage.");
- try {
- uri = this.ctx.getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
- } catch (UnsupportedOperationException ex) {
- System.out.println("Can't write to internal media storage.");
- failPicture("Error capturing image - no media storage found.");
- return;
- }
- }
- OutputStream os = this.ctx.getContentResolver().openOutputStream(uri);
- bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
- os.close();
- if (this.encodingType == 0) {
- exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.ctx));
- exif.writeExifData();
- }
- success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
- }
- bitmap.recycle();
- bitmap = null;
- System.gc();
- checkForDuplicateImage(1);
- } catch (IOException e) {
- e.printStackTrace();
- failPicture("Error capturing image.");
- }
- }
- else if (resultCode == 0) {
- failPicture("Camera cancelled.");
- }
- else
- {
- failPicture("Did not complete!");
- }
- }
- else if ((srcType == 0) || (srcType == 2))
- if (resultCode == -1) {
- Uri uri = intent.getData();
- ContentResolver resolver = this.ctx.getContentResolver();
- if (this.mediaType != 0) {
- success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
- }
- else if (destType == 0) {
- try {
- Bitmap bitmap = BitmapFactory.decodeStream(resolver.openInputStream(uri));
- bitmap = scaleBitmap(bitmap);
- processPicture(bitmap);
- bitmap.recycle();
- bitmap = null;
- System.gc();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- failPicture("Error retrieving image.");
- }
- }
- else if (destType == 1)
- {
- if ((this.targetHeight > 0) && (this.targetWidth > 0)) {
- try {
- Bitmap bitmap = BitmapFactory.decodeStream(resolver.openInputStream(uri));
- bitmap = scaleBitmap(bitmap);
- String fileName = DirectoryManager.getTempDirectoryPath(this.ctx) + "/resize.jpg";
- OutputStream os = new FileOutputStream(fileName);
- bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
- os.close();
- bitmap.recycle();
- bitmap = null;
- success(new PluginResult(PluginResult.Status.OK, "file://" + fileName), this.callbackId);
- System.gc();
- } catch (Exception e) {
- e.printStackTrace();
- failPicture("Error retrieving image.");
- }
- }
- else {
- success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
- }
- }
- }
- else if (resultCode == 0) {
- failPicture("Selection cancelled.");
- }
- else {
- failPicture("Selection did not complete!");
- }
- }
- private Cursor queryImgDB()
- {
- return this.ctx.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { "_id" }, null, null, null);
- }
- private void checkForDuplicateImage(int type)
- {
- int diff = 1;
- Cursor cursor = queryImgDB();
- int currentNumOfImages = cursor.getCount();
- if (type == 1) {
- diff = 2;
- }
- if (currentNumOfImages - this.numPics == diff) {
- cursor.moveToLast();
- int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex("_id"))).intValue() - 1;
- Uri uri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "/" + id);
- this.ctx.getContentResolver().delete(uri, null, null);
- }
- }
- public void processPicture(Bitmap bitmap)
- {
- ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream();
- try {
- if (bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, jpeg_data)) {
- byte[] code = jpeg_data.toByteArray();
- byte[] output = Base64.encodeBase64(code);
- String js_out = new String(output);
- success(new PluginResult(PluginResult.Status.OK, js_out), this.callbackId);
- js_out = null;
- output = null;
- code = null;
- }
- }
- catch (Exception e) {
- failPicture("Error compressing image.");
- }
- jpeg_data = null;
- }
- public void failPicture(String err)
- {
- error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
- }
- }
可以看到Camera本身被设计一个插件的形式,实现了具体的takePicture函数,至于具体的js端代码和Android本地端代码的通信 是怎么样建立起来的,也即js代码怎么和Android的Activity的代码进行数据的交互,那么下面的代码会给你答案了。
- public class GapViewClient extends WebViewClient
- {
- DroidGap ctx;
- public GapViewClient(DroidGap ctx)
- {
- this.ctx = ctx;
- }
- public boolean shouldOverrideUrlLoading(WebView view, String url)
- {
- if (!this.ctx.pluginManager.onOverrideUrlLoading(url))
- {
- if (url.startsWith("tel:")) {
- try {
- Intent intent = new Intent("android.intent.action.DIAL");
- intent.setData(Uri.parse(url));
- DroidGap.this.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- System.out.println("Error dialing " + url + ": " + e.toString());
- }
- }
- else if (url.startsWith("geo:")) {
- try {
- Intent intent = new Intent("android.intent.action.VIEW");
- intent.setData(Uri.parse(url));
- DroidGap.this.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- System.out.println("Error showing map " + url + ": " + e.toString());
- }
- }
- else if (url.startsWith("mailto:")) {
- try {
- Intent intent = new Intent("android.intent.action.VIEW");
- intent.setData(Uri.parse(url));
- DroidGap.this.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- System.out.println("Error sending email " + url + ": " + e.toString());
- }
- }
- else if (url.startsWith("sms:")) {
- try {
- Intent intent = new Intent("android.intent.action.VIEW");
- String address = null;
- int parmIndex = url.indexOf('?');
- if (parmIndex == -1) {
- address = url.substring(4);
- }
- else {
- address = url.substring(4, parmIndex);
- Uri uri = Uri.parse(url);
- String query = uri.getQuery();
- if ((query != null) &&
- (query.startsWith("body="))) {
- intent.putExtra("sms_body", query.substring(5));
- }
- }
- intent.setData(Uri.parse("sms:" + address));
- intent.putExtra("address", address);
- intent.setType("vnd.android-dir/mms-sms");
- DroidGap.this.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- System.out.println("Error sending sms " + url + ":" + e.toString());
- }
- }
- else if ((this.ctx.loadInWebView) || (url.startsWith("file://")) || (url.indexOf(this.ctx.baseUrl) == 0) || (DroidGap.this.isUrlWhiteListed(url) != 0)) {
- try
- {
- HashMap params = new HashMap();
- this.ctx.showWebPage(url, true, false, params);
- } catch (ActivityNotFoundException e) {
- System.out.println("Error loading url into DroidGap - " + url + ":" + e.toString());
- }
- }
- else {
- try
- {
- Intent intent = new Intent("android.intent.action.VIEW");
- intent.setData(Uri.parse(url));
- DroidGap.this.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- System.out.println("Error loading url " + url + ":" + e.toString());
- }
- }
- }
- return true;
- }
- public void onPageFinished(WebView view, String url)
- {
- super.onPageFinished(view, url);
- DroidGap.access$208(this.ctx);
- if (!url.equals("about:blank")) {
- DroidGap.this.appView.loadUrl("javascript:try{ PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}");
- }
- Thread t = new Thread(new Runnable() {
- public void run() {
- try {
- Thread.sleep(2000L);
- DroidGap.GapViewClient.this.ctx.runOnUiThread(new Runnable() {
- public void run() {
- DroidGap.this.appView.setVisibility(0);
- DroidGap.GapViewClient.this.ctx.spinnerStop();
- }
- });
- }
- catch (InterruptedException e)
- {
- }
- }
- });
- t.start();
- if (this.ctx.clearHistory) {
- this.ctx.clearHistory = false;
- this.ctx.appView.clearHistory();
- }
- if (url.equals("about:blank")) {
- if (this.ctx.callbackServer != null) {
- this.ctx.callbackServer.destroy();
- }
- this.ctx.finish();
- }
- }
- public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
- {
- System.out.println("onReceivedError: Error code=" + errorCode + " Description=" + description + " URL=" + failingUrl);
- DroidGap.access$208(this.ctx);
- this.ctx.spinnerStop();
- this.ctx.onReceivedError(errorCode, description, failingUrl);
- }
- public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
- {
- String packageName = this.ctx.getPackageName();
- PackageManager pm = this.ctx.getPackageManager();
- try
- {
- ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 128);
- if ((appInfo.flags & 0x2) != 0)
- {
- handler.proceed();
- return;
- }
- super.onReceivedSslError(view, handler, error);
- }
- catch (PackageManager.NameNotFoundException e)
- {
- super.onReceivedSslError(view, handler, error);
- }
- }
- }
- public class GapClient extends WebChromeClient
- {
- private String TAG = "PhoneGapLog";
- private long MAX_QUOTA = 104857600L;
- private DroidGap ctx;
- public GapClient(Context ctx)
- {
- this.ctx = ((DroidGap)ctx);
- }
- public boolean onJsAlert(WebView view, String url, String message, JsResult result)
- {
- AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx);
- dlg.setMessage(message);
- dlg.setTitle("Alert");
- dlg.setCancelable(false);
- dlg.setPositiveButton(17039370, new DialogInterface.OnClickListener(result)
- {
- public void onClick(DialogInterface dialog, int which) {
- this.val$result.confirm();
- }
- });
- dlg.create();
- dlg.show();
- return true;
- }
- public boolean onJsConfirm(WebView view, String url, String message, JsResult result)
- {
- AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx);
- dlg.setMessage(message);
- dlg.setTitle("Confirm");
- dlg.setCancelable(false);
- dlg.setPositiveButton(17039370, new DialogInterface.OnClickListener(result)
- {
- public void onClick(DialogInterface dialog, int which) {
- this.val$result.confirm();
- }
- });
- dlg.setNegativeButton(17039360, new DialogInterface.OnClickListener(result)
- {
- public void onClick(DialogInterface dialog, int which) {
- this.val$result.cancel();
- }
- });
- dlg.create();
- dlg.show();
- return true;
- }
- public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)
- {
- boolean reqOk = false;
- if ((url.indexOf(this.ctx.baseUrl) == 0) || (DroidGap.this.isUrlWhiteListed(url) != 0)) {
- reqOk = true;
- }
- if ((reqOk) && (defaultValue != null) && (defaultValue.length() > 3) && (defaultValue.substring(0, 4).equals("gap:")))
- {
- try {
- JSONArray 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 = DroidGap.this.pluginManager.exec(service, action, callbackId, message, async);
- result.confirm(r);
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- else if ((reqOk) && (defaultValue != null) && (defaultValue.equals("gap_poll:"))) {
- String r = DroidGap.this.callbackServer.getJavascript();
- result.confirm(r);
- }
- else if ((reqOk) && (defaultValue != null) && (defaultValue.equals("gap_callbackServer:"))) {
- String r = "";
- if (message.equals("usePolling")) {
- r = "" + DroidGap.this.callbackServer.usePolling();
- }
- else if (message.equals("restartServer")) {
- DroidGap.this.callbackServer.restartServer();
- }
- else if (message.equals("getPort")) {
- r = Integer.toString(DroidGap.this.callbackServer.getPort());
- }
- else if (message.equals("getToken")) {
- r = DroidGap.this.callbackServer.getToken();
- }
- result.confirm(r);
- }
- else if ((reqOk) && (defaultValue != null) && (defaultValue.equals("gap_init:"))) {
- DroidGap.this.appView.setVisibility(0);
- this.ctx.spinnerStop();
- result.confirm("OK");
- }
- else
- {
- JsPromptResult res = result;
- AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx);
- dlg.setMessage(message);
- EditText input = new EditText(this.ctx);
- if (defaultValue != null) {
- input.setText(defaultValue);
- }
- dlg.setView(input);
- dlg.setCancelable(false);
- dlg.setPositiveButton(17039370, new DialogInterface.OnClickListener(input, res)
- {
- public void onClick(DialogInterface dialog, int which) {
- String usertext = this.val$input.getText().toString();
- this.val$res.confirm(usertext);
- }
- });
- dlg.setNegativeButton(17039360, new DialogInterface.OnClickListener(res)
- {
- public void onClick(DialogInterface dialog, int which) {
- this.val$res.cancel();
- }
- });
- dlg.create();
- dlg.show();
- }
- return true;
- }
- public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
- {
- Log.d(this.TAG, "event raised onExceededDatabaseQuota estimatedSize: " + Long.toString(estimatedSize) + " currentQuota: " + Long.toString(currentQuota) + " totalUsedQuota: " + Long.toString(totalUsedQuota));
- if (estimatedSize < this.MAX_QUOTA)
- {
- long newQuota = estimatedSize;
- Log.d(this.TAG, "calling quotaUpdater.updateQuota newQuota: " + Long.toString(newQuota));
- quotaUpdater.updateQuota(newQuota);
- }
- else
- {
- quotaUpdater.updateQuota(currentQuota);
- }
- }
- public void onConsoleMessage(String message, int lineNumber, String sourceID)
- {
- Log.d(this.TAG, sourceID + ": Line " + Integer.toString(lineNumber) + " : " + message);
- }
- public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback)
- {
- super.onGeolocationPermissionsShowPrompt(origin, callback);
- callback.invoke(origin, true, false);
- }
- }
相信开发过webview的同学都知道其中的奥秘了,通过重载webview的WebChromeClient和WebViewClient接口,实现其中的js代码的处理部分,即可和phoneGap中js端的代码进行交互了。
相信现在大家都熟悉了一种利用web方式开发移动APP的解决方式了吧,期待更好的解决方案能够出来。
最后再向大家推荐一个js开发库,来方便的定制APP的样式,怎么样让你的APP看起来更本地化,它就是http://jqtouch.com/,运行结果见开始的图片,如果我把浏览器的部分隐藏掉,谁能轻易分辨出来是一个本地化的APP,还是web的APP?
原文链接:http://blog.zhourunsheng.com/2011/10/%E7%A7%BB%E5%8A%A8-app-%E4%B9%8B%E8%B7%A8%E5%B9%B3%E5%8F%B0%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/