作為一個WEB開發者,HTML5讓我興奮,因為它可以將桌面應用程序功能帶入瀏覽器中。但在國內,看着到處橫行的IE8版本以下的瀏覽器,覺得到能大規模使用HTML5技術的那天,還遙遙無期。但面對iOS及Android等平台的手機用戶越來越多,基於Webkit內核的移動瀏覽器一定能讓HTML5先大規模應用起來。這將對對移動 Web 應用程序開發具有重大影響。
作為非常看好未來手機網絡的我,也在一直研究Android平台的應用的開發,也許是因為自己更熟悉HTML及CSS、JS,並受到之前使用HTML和VC開發程序的影響,我也更願意使用HTML來做Android程序的UI….
09年,在開發《華夏風雲》游戲的時候,使用了基於Google Gear插件來做了很多離線應用,可惜Gear已經不在更新開發,被HTML5取代。下面介紹基於HTML 5 的Web 應用程序的本地存儲,不再廢話,例子說明一切。
一、離線應用緩存 HTML 5 Offline Application Cache
- 在服務器上添加MIME TYPE支:text/cache-manifest
如果在Apache下添加:
1 |
AddType text/cache-manifest manifest |
如果為Nginx,在添加:
1 |
text/cache-manifest manifest; |
或者通過動態程序生成:
1 |
header( 'Content-type: text/cache-manifest; charset=UTF-8' ); |
- 創建 NAME.manifest:
新建清單文件 manifest
01 |
CACHE MANIFEST |
02 |
# This is a comment. |
03 |
# Cache manifest version 0.0.1 |
04 |
# If you change the version number in this comment, |
05 |
# the cache manifest is no longer byte-for-byte |
06 |
# identical. |
07 |
08 |
/app/static/default/js/models/prototype.js |
09 |
/app/static/default/js/controllers/init.js |
10 |
11 |
NETWORK: |
12 |
# All URLs that start with the following lines |
13 |
# are whitelisted. |
14 |
16 |
17 |
CACHE: |
18 |
# Additional items to cache. |
19 |
/app/static/default/images/main/ bg .png |
20 |
21 |
FALLBACK: |
22 |
demoimages/ images/ |
- 給 <html> 標簽加 manifest 屬性:
建立manifest文件之后,需要在HTML文檔中聲明:
聲明清單文件 manifest
<!doctype html> <html manifest="notebook.manifest"> <head> <meta charset="UTF-8" /> <meta name = "viewport" content = "width = device-width, user-scalable = no"> <title>NoteBook</title> </head> <body> </body> </html>
二、Key-Value Storage
三、Using the JavaScript Database
四、Android下使用WebView來做基於HTML5的App
見如下AndroidManifest.xml
01 |
< ? xml version = "1.0" encoding = "utf-8" ?> |
02 |
< manifest android:versionName = "1.0" android:versionCode = "1" package = "com.xinze.joke" xmlns:android = "http://schemas.android.com/apk/res/android" > |
03 |
< application android:label = "@string/app_name" android:icon = "@drawable/icon" > |
04 |
< activity android:label = "@string/app_name" android:theme = "@android:style/Theme.NoTitleBar" android:configChanges = "orientation|keyboardHidden|navigation" android:name = ".Joke" > |
05 |
< intent -filter = "" > |
06 |
< action android:name = "android.intent.action.MAIN" > |
07 |
< category android:name = "android.intent.category.LAUNCHER" > |
08 |
</ category ></ action ></ intent > |
09 |
</ activity > |
10 |
</ application > |
11 |
< uses android:name = "android.permission.INTERNET" -permission = "" > |
12 |
</ uses ></ manifest > |
注意:
1 |
< uses android:name = "android.permission.INTERNET" -permission = "" ></ uses > |
, 允許網絡應用,必須!!
Android主程序代碼:
001 |
package com.xinze.joke; |
002 |
003 |
import android.app.Activity; |
004 |
import android.app.AlertDialog; |
005 |
import android.app.AlertDialog.Builder; |
006 |
007 |
import android.content.DialogInterface; |
008 |
009 |
import android.os.Bundle; |
010 |
import android.view.KeyEvent; |
011 |
import android.view.View; |
012 |
import android.view.Window; |
013 |
import android.view.WindowManager; |
014 |
import android.webkit.JsPromptResult; |
015 |
import android.webkit.JsResult; |
016 |
import android.webkit.WebChromeClient; |
017 |
import android.webkit.WebView; |
018 |
import android.webkit.WebViewClient; |
019 |
020 |
import android.webkit.WebStorage ; |
021 |
022 |
public class Joke extends Activity { |
023 |
024 |
/** Called when the activity is first created. */ |
025 |
@Override |
026 |
public void onCreate(Bundle savedInstanceState) { |
027 |
super .onCreate(savedInstanceState); |
028 |
029 |
final WebView wv = new WebView( this ); |
030 |
031 |
// 覆蓋默認后退按鈕的作用,替換成WebView里的查看歷史頁面 |
032 |
wv.setOnKeyListener( new View.OnKeyListener() { |
033 |
034 |
@Override |
035 |
public boolean onKey(View v, int keyCode, KeyEvent event) { |
036 |
if (event.getAction() == KeyEvent.ACTION_DOWN) { |
037 |
if ((keyCode == KeyEvent.KEYCODE_BACK) && wv.canGoBack()) { |
038 |
wv.goBack(); |
039 |
return true ; |
040 |
} |
041 |
} |
042 |
return false ; |
043 |
} |
044 |
}); |
045 |
046 |
// 設置支持Javascript |
047 |
wv.getSettings().setJavaScriptEnabled( true ); |
048 |
049 |
wv.getSettings().setJavaScriptCanOpenWindowsAutomatically( true ); |
050 |
wv.getSettings().setDatabaseEnabled( true ); |
051 |
wv.getSettings().setDatabasePath( "/data/data/com.xinze.joke/databases" ); |
052 |
053 |
// 創建WebViewClient對象 |
054 |
WebViewClient wvc = new WebViewClient() { |
055 |
056 |
@Override |
057 |
public boolean shouldOverrideUrlLoading(WebView view, String url) { |
058 |
wv.loadUrl(url); |
059 |
// 記得消耗掉這個事件。給不知道的朋友再解釋一下,Android中返回True的意思就是到此為止吧,事件就會不會冒泡傳遞了,我們稱之為消耗掉 |
060 |
return true ; |
061 |
} |
062 |
}; |
063 |
064 |
// 設置WebViewClient對象 |
065 |
wv.setWebViewClient(wvc); |
066 |
067 |
// 創建WebViewChromeClient |
068 |
WebChromeClient wvcc = new WebChromeClient() { |
069 |
070 |
// 處理Alert事件 |
071 |
@Override |
072 |
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { |
073 |
// 構建一個Builder來顯示網頁中的alert對話框 |
074 |
Builder builder = new Builder(Joke. this ); |
075 |
builder.setTitle( "笑死不償命" ); |
076 |
builder.setMessage(message); |
077 |
builder.setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() { |
078 |
@Override |
079 |
public void onClick(DialogInterface dialog, int which) { |
080 |
result.confirm(); |
081 |
} |
082 |
}); |
083 |
builder.setCancelable( false ); |
084 |
builder.create(); |
085 |
builder.show(); |
086 |
return true ; |
087 |
} |
088 |
089 |
// 處理Confirm事件 |
090 |
@Override |
091 |
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { |
092 |
Builder builder = new Builder(Joke. this ); |
093 |
builder.setTitle( "刪除確認" ); |
094 |
builder.setMessage(message); |
095 |
builder.setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() { |
096 |
097 |
@Override |
098 |
public void onClick(DialogInterface dialog, int which) { |
099 |
result.confirm(); |
100 |
} |
101 |
102 |
}); |
103 |
builder.setNeutralButton(android.R.string.cancel, new AlertDialog.OnClickListener() { |
104 |
105 |
@Override |
106 |
public void onClick(DialogInterface dialog, int which) { |
107 |
result.cancel(); |
108 |
} |
109 |
110 |
}); |
111 |
builder.setCancelable( false ); |
112 |
builder.create(); |
113 |
builder.show(); |
114 |
return true ; |
115 |
} |
116 |
117 |
// 處理提示事件 |
118 |
@Override |
119 |
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, |
120 |
JsPromptResult result) { |
121 |
// 看看默認的效果 |
122 |
return super .onJsPrompt(view, url, message, defaultValue, result); |
123 |
} |
124 |
125 |
@Override |
126 |
public void onExceededDatabaseQuota(String url, String |
127 |
databaseIdentifier, long currentQuota, long estimatedSize, long |
128 |
totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { |
129 |
quotaUpdater.updateQuota( 204801 ); |
130 |
} |
131 |
132 |
}; |
133 |
134 |
wv.loadUrl( "http://192.168.1.14/index.html" ); |
135 |
136 |
// 設置setWebChromeClient對象 |
137 |
wv.setWebChromeClient(wvcc); |
138 |
setContentView(wv); |
139 |
} |
140 |
} |
使用 JavaScript Database 的時候,需要特別注意:setDatabaseEnabled 以及 onExceededDatabaseQuota!