面向大眾的移動技術(shù):簽名,封裝和發(fā)布Android app
面向大從的移動打樁其它四篇文章地址(校對添加):
(一)、android簡介;
(二)、輕輕一劃,在android中為手勢編碼;
(三)、Android 應(yīng)用程序生命周期中的活動與圖標;
添加一個多選擇quiz到你的Android手機app,然后用一個安全數(shù)字證書簽名
用網(wǎng)絡(luò)邏輯,內(nèi)容為王。但是對與手機用戶來說,交互規(guī)則才是王道。對移動app靜態(tài)信息設(shè)計在減少,并且游戲化正在增多。這個月 Andrew Glover決定通過將一個多選擇的quiz特性加入到一個示例app(Overheard Word,前一篇介紹的。) 中來介紹 Android移動開發(fā)。之后他將展示給你如何生成一個數(shù)字證書和如何發(fā)布和如何提升你的在Google Play或者Amazon Appstore上 已經(jīng)簽名的app。
目前為止在這個Mobil for the masses系列中,我們已經(jīng)使用Android作為學習怎樣做移動開發(fā)的一個實例,其中包括 《Android應(yīng)用程序生命周期》教程,在你的Android apps中實現(xiàn)《手勢滑動功能》。并且《與第三方庫工作》來簡化開發(fā)并且增強app功 能。當我不確定做Android時候,我對瀏覽其它的手機環(huán)境和技術(shù)感興趣。所以這個月我們來 通過添加一個quiz特性到Overheard Word 示例app 總結(jié)我們的 Android-intensive(加強安卓)文章 并且準備部署它到兩個流行的 Adroid app stores:Google Play和Amazon Appstore。所有的這些將是下一節(jié)的基礎(chǔ):HTML5侵襲移動開發(fā)!
游戲化我的app
在我們簽名Overheard Word并且把它推送到Google Play和Amazon Appstore的Android市場同數(shù)百萬的其 它apps競爭前,我想要確定它是***的Overheard Word app(對我們的Overheard Word app不熟悉么?回顧下介紹這 個 示例 的文章)。如你所知,游戲是當前推動移動生態(tài)系統(tǒng)的強勁動力,并且一系列的apps都被期望有高效的交互。移動apps即使當他們的目標是提供 信息價值的時候,點燃好奇心和獲勝的欲望的移動應(yīng)用也做的很好。那也是為什么Overheard Word 不僅僅只是一個頁面上的單詞列表;相反,它被 設(shè)計用來煽動讀者來挖掘詞匯量,接著獎勵他們來堅持學習它!(順便說下Gamification 游戲化 是一個正在開始流行的設(shè)計技術(shù)的術(shù)語)
在我們簽名Overheard Word并且把它推送到Google Play和Amazon Appstore的Android市場同數(shù)百萬的其它 apps競爭前,我想要確定它是***的Overheard Word app(對我們的Overheard Word app不熟悉么?回顧下介紹這 個 示例 的文章)。如你所知,游戲是當前推動移動生態(tài)系統(tǒng)的強勁動力,并且一系列的apps都被期望有高效的交互。移動apps即使當他們的目標是提供 信息價值的時候,點燃好奇心和獲勝的欲望的移動應(yīng)用也做的很好。那也是為什么Overheard Word 不僅僅只是一個頁面上的單詞列表;相反,它被 設(shè)計用來煽動讀者來挖掘詞匯量,接著獎勵他們來堅持學習它!(順便說下Gamification 游戲化 是一個正在開始流行的設(shè)計技術(shù)的術(shù)語)
在我們簽名Overheard Word并且把它推送到Google Play和Amazon Appstore的Android市場同數(shù)百萬的其它 apps競爭前,我想要確定它是***的Overheard Word app(對我們的Overheard Word app不熟悉么?回顧下介紹這 個 示例 的文章)。如你所知,游戲是當前推動移動生態(tài)系統(tǒng)的強勁動力,并且一系列的apps都被期望有高效的交互。移動apps即使當他們的目標是提供 信息價值的時候,點燃好奇心和獲勝的欲望的移動應(yīng)用也做的很好。那也是為什么Overheard Word 不僅僅只是一個頁面上的單詞列表;相反,它被 設(shè)計用來煽動讀者來挖掘詞匯量,接著獎勵他們來堅持學習它!(順便說下Gamification 游戲化 是一個正在開始流行的設(shè)計技術(shù)的術(shù)語)
Overheard Word探詢
我們將啟動正定義的一個新的布局來使Overheard Word的測試視圖保持一致,接下來我們定義一個來展示布局的Activity。正如前文所說,我正使用Eclipse的ADT作為我的開發(fā)環(huán)境,我假設(shè)你也是使用它。
我們將啟動正定義的一個新的布局來使Overheard Word的測試視圖保持一致,接下來我們定義一個來展示布局的Activity。正如前文所說,我正使用Eclipse的ADT作為我的開發(fā)環(huán)境,我假設(shè)你也是使用它。
Figure 1. Creating a new layout in Eclipse
我們將啟動正定義的一個新的布局來使Overheard Word的測試視圖保持一致,接下來我們定義一個來展示布局的Activity。正如前文所說,我正使用Eclipse的ADT作為我的開發(fā)環(huán)境,我假設(shè)你也是使用它。
下一步,拷貝下面的XML到你的新文件。
Listing 1. Quiz layout for Overheard Word
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".OverheardWord" >
- <LinearLayout
- android:id="@+id/widget33"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_marginLeft="20dp"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/quiz_definition"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="20dp"
- android:layout_marginLeft="13dp"
- android:layout_marginRight="10dp"
- android:layout_marginTop="48dp"
- android:text="Definition"
- android:textSize="18sp" />
- <RadioGroup
- android:id="@+id/quiz_answers"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="17dp" >
- <RadioButton
- android:id="@+id/quiz_answer_1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Answer 1" />
- <RadioButton
- android:id="@+id/quiz_answer_2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Answer 2" />
- <RadioButton
- android:id="@+id/quiz_answer_3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Answer 3" />
- </RadioGroup>
- <TextView
- android:id="@+id/quiz_result"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="10dp"
- android:layout_marginLeft="40dp"
- android:layout_marginTop="20dp"
- android:lines="2"
- android:text="Result"
- android:textSize="18sp" />
- <TextView
- android:id="@+id/quiz_number"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginRight="10dp"
- android:layout_marginTop="30dp"
- android:gravity="right"
- android:text="1/10" />
- </LinearLayout>
- </RelativeLayout>
Overheard Word的存在的布局文件定義了學習指南的UI,此布局定義了一系列UI元素樣例文本,目的是你們能夠得到一個想法 當你的 app上線時東西是怎么樣的。這測試的布局包括一個保存詳細定義的TextView和一個用來各種適合那個定義單詞的RadioGroup。當用戶選擇一 個單詞,提交一個新問題或再試一次的機會時候,app將立即通知一個事件并相應(yīng),也有通過這個小測試跟蹤用戶進程的計數(shù)器。
我們下一步是創(chuàng)建一個新的Activity類。這個類應(yīng)該繼承Activity類且提供一個onCreate方法,如Listing2所示。(注意, setContentView指定那個Listing1創(chuàng)建的新布局文件)
Listing 2. New Activity class: OverheardQuiz
- import android.app.Activity;
- import android.os.Bundle;
- public class OverheardQuiz extends Activity {
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_overheard_quiz);
- }
- }
我們下一步是實例一個新Activity,這將最終顯示Overheard word的測試布局。不僅僅創(chuàng)建一個可選菜單項,讓我們嘗試用手勢滑動功能來啟動一個測試。稍后我們也會把app菜單加到測試中。
Intents編程
正如我們之前討論的,一個Android設(shè)備極其受電池時間周期、執(zhí)行能力和內(nèi)存的約束,大多數(shù)個人設(shè)備也有許多像電話和文本特性的apps,所以 同時他們有多個激活的程序運行。Android平臺通過給作為開發(fā)者的你在任何時間使你的app應(yīng)用處于某些約束中來管理程序。例如,你不能強迫一個 Activity類啟動并且顯示它的視圖。而是,你要通過給activity發(fā)送給平臺一個intent來啟動。平臺會在能啟動Activity的時候啟 動它。
想象一個Intent是一個異步執(zhí)行任務(wù)的機制。從用戶角度看,你建議平臺應(yīng)該做一些事情并且它能做的時候它立刻做了。
所以不能僅僅啟動我們的Activity,我們需要把它包裝在一個Intent里。之后Android將為我們啟動Activity。由于我們正在 用手勢啟動一個新的測試Activity,我們將把intent插到一個GestureDetector實例。(這系列中更多關(guān)于 GestureDetector看 前兩篇文章)在isUpSwip條件中不是只能返回false,我們會發(fā)送那樣的一個新Intent:
Listing 3. Adding the new Activity to GestureDetector
- //.....
- if (detector.isDownSwipe()) {
- return false;
- } elseif (detector.isUpSwipe()) {
- startActivity(new Intent(getApplicationContext(), OverheardQuiz.class));
- } elseif (detector.isLeftSwipe()) {
- //.....
注意 一個Intent接受一個上下文和一個你希望啟動的類,startActivity 是Activity實例的方法。
試一試,啟動你的模擬器實例,當顯示一個單詞時候向上滑。你應(yīng)該看到一個新Activity,如Figure2。
Figure 2. A quiz is born!
試一試,啟動你的模擬器實例,當顯示一個單詞時候向上滑。你應(yīng)該看到一個新Activity出現(xiàn)在Figure2那樣
上滑,下滑
我們首先要做的就是添加一個手勢檢測以便能夠讓用戶離開這測試并回到學習單詞。由于上滑是讓用戶進入到測驗,憑直覺下滑是離開測驗比較好。我們調(diào)用finish來退出測驗Activity回到學習模式。
Listing 4. Calling finish inside a swipe gesture
- private GestureDetector initGestureDetector() {
- return new GestureDetector(new SimpleOnGestureListener() {
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- try {
- final SwipeDetector detector = new SwipeDetector(e1, e2, velocityX, velocityY);
- if (detector.isDownSwipe()) {
- finish();
- } else if (detector.isUpSwipe()) {
- return false;
- } else if (detector.isLeftSwipe()) {
- return false;
- } else if (detector.isRightSwipe()) {
- return false;
- }
- } catch (Exception e) {
- // nothing
- }
- return false;
- }
- });
- }
上篇文章說過,我們在Overheard Word里的學習Activity中實現(xiàn)了一個操作欄,用來我們退出app。這里我們重用那段代碼來創(chuàng)建新的測驗退出函數(shù)。
Listing 5. An action bar to quit the quiz
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.overheard_quiz, menu);
- return true;
- }
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.quit_quiz:
- this.finish();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
注意 我們并不想退出我們的app,只是離開測驗;所以我們更新了操作欄文本‘退出測試’而不是‘退出’。
單詞和定義
下一步,我們設(shè)定一個有一個正確答案和兩個錯誤答案的定義。由于我們使用Thingamejig來處理邏輯,我們唯一需要做的就是輸入相關(guān)字段,如Listing6所示:
Listing 6. Setting up a testable word with definitions
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_overheard_quiz);
- initializeGestures();
- List<Word> words = buildWordList();
- if (engine == null) {
- engine = WordTestEngine.getInstance(words);
- }
- final TestableWord firstWord = engine.getTestableWord();
- final TextView testDefinition = (TextView)findViewById(R.id.quiz_definition);
- testDefinition.setText(formatDefinition(firstWord.getValidDefinition()));
- final List<String> possibleAnswers = possibleAnswersFrom(firstWord);
- final int[] radios = { R.id.quiz_answer_1, R.id.quiz_answer_2, R.id.quiz_answer_3 };
- for (int x = 0; x < radios.length; x++) {
- final RadioButton rButton = (RadioButton)findViewById(radios[x]);
- rButton.setText(possibleAnswers.get(x));
- }
- }
在Listing 5 中,onCreate方法先構(gòu)建我們提供的基礎(chǔ)視圖。下一步,初始化手勢。就像原來學習Activity那樣工作,一個 WordTestEngine(而不是WordStudyEngine)被實例化且鏈接到Overheard Word的單詞存儲(這個例子中是JSON 文件)。possibleAnswersForm方法返回一個有三個定義其中一個正確定義的列表,為了使測驗不是一直顯示相同順序 WordTestEngine實例會打亂定義順序。
最終,循環(huán)輸入匹配所給定義的單詞到我們的RadioButtons。如果你啟動app實例,你將會看到一個漂亮的 有著一個單詞定義和三個單詞選項的表單測驗接口。
Figure 3. The quiz UI with words and a definition
還有注意在屏幕底部有個靜態(tài)Result按鈕和一個顯示所有的測驗問題數(shù)量的計數(shù)器,截圖中10個問題已經(jīng)使用了1個。
目前為止,我們使用Thingamejig和一些原來的Activity邏輯快速創(chuàng)建了測驗的樣本邏輯。接下來我們將手動處理測驗的單選框的按鈕部 分。我們回調(diào)到單選按鈕組的行為并且監(jiān)聽什么時候選項被選,而不需要用戶額外多點擊按鈕(類似提交)來表明選中選項。這種方式用戶不需要額外操作就立即得 到反饋。
處理事件調(diào)度
我提到我想要基于他們的單詞選項來提供立即反饋。還有,我還想程序Overheard Word 對于反饋可以短暫的暫停。如果用戶給了一個正確的 回應(yīng),我想他或她在進入下一題前有幾秒中來體會這種感覺。如果回答錯誤,我應(yīng)該讓他們再試或者保持一兩秒后再顯示正確答案。任何方式,用戶都有機會了解他 們在測驗中的操作。
但是記住,我們不僅做一些我們喜歡的Android app;沒有強迫app隨時睡眠或啟動線程。(在Android app中啟動線程是可能的, 當然,但是各種相關(guān)因素是不受控制的。)就像我們使用Intents來告訴Android app 我們意圖讓它有怎樣的行為,我們用Handlers來 指示我們渴望推遲或立即執(zhí)行;Android怎樣處理請求是Android的事了。
請暫停
在Android中,你使用Handlers來處理事件調(diào)度,并且也通過線程間傳遞信息。這種情況,我們將一直用Handlers調(diào)度一個事件,它將是一個密封的Runnable實例。
在響應(yīng)用戶選中一個單詞的事件中,我們將創(chuàng)建一個Handler實例,在Runnable內(nèi)部傳遞某寫邏輯(如展示一個新的Activity)并且設(shè)置幾秒延遲。
我們實現(xiàn)onCheckedChangeListener監(jiān)聽附加到RadioGroup上來回調(diào)敲擊單選按鈕,如下所示:Listing 7
Listing 7. setOnCheckedChangeListener
- final RadioGroup group = (RadioGroup)findViewById(R.id.quiz_answers);
- group.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(final RadioGroup group, final int checkedId) {
- final RadioButton selected = (RadioButton)findViewById(checkedId);
- final String answer = (String) selected.getText();
- if (answer.equals(firstWord.getSpelling())) {
- final TextView result = (TextView)findViewById(R.id.quiz_result);
- result.setTextColor(Color.parseColor("#228b22"));
- result.setText("Correct!");
- final Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- public void run() {
- final Intent nextQuiz = new Intent(getApplicationContext(), OverheardQuiz.class);
- startActivity(nextQuiz);
- result.setText("");
- finish();
- }
- }, 2500);
- } else {
- final TextView result = (TextView)findViewById(R.id.quiz_result);
- result.setTextColor(Color.parseColor("#ff0000"));
- result.setText("Nope, that's not it! Try again.");
- final Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- public void run() {
- selected.setChecked(false);
- result.setText("");
- }
- }, 2000);
- }
- }
- });
正如JavaAWT或Swing的傳統(tǒng)GUI編程,你能添加事件監(jiān)聽。例子中,我們就正在添加一 個 OnCheckedChangeListener 到RadioGroup。當用戶選擇一個RadioButton時,監(jiān)聽代碼會執(zhí)行。如果用戶選擇 正確,新實例OverheardQuizActivity會顯示一個新單詞。用戶也可以在TextView中得到友好的‘正確’的綠色文本信息。
如果用戶選擇錯誤,信息文本是紅色的,Activity的UI會重置讓用戶再次嘗試。
也要注意兩個Handler實例延遲啟動線程,一個是因為要新建一個Intent啟動,另一個是UI重置。
你也需要刪除在Listing 1 中定義的測驗布局的默認的文本。只需回到XML文件并且從ID為quiz_result的TextView中刪除
android:text=”Result”?,F(xiàn)在啟動你的app并檢測!
Android中的Bundles
接著我們要做的事情是給測驗視圖右下角的計數(shù)器添加一些邏輯(看Figure 3)。對于測驗從用戶的角度觀察,我們想要展示他們的進度給他或她, 像還有多少問題。為了我們的計數(shù)器能工作,我們需要一個保持計數(shù)器的狀態(tài)(例如它之前是2,且現(xiàn)在它應(yīng)該是3),這就需要我們能從一個activity到 另一個之間傳遞數(shù)據(jù),我們用Bundles來做這個任務(wù)。
如果你已經(jīng)讀了一段時間這個系列,那么你已經(jīng)接觸過Bundles了,因為一個Bundles實例是每個Activity的onCreate方法的長參數(shù)。
Bundles必須是一個鍵-值對的Maps。你能通過方法調(diào)用鍵的值檢索一致的值類型。除了Activity實例外,Bundles也被包括在 Intents中,實際上,如果你放一個新Activity鍵-值對到Intent關(guān)聯(lián)關(guān)系中,你就能通過那個Intent檢索Bundle得到創(chuàng)建的那 個Activity。這些聽起來可能很繞,但親自做一次就理解了。
創(chuàng)建計數(shù)器
首先,我們更新創(chuàng)建新的OverheardQuizActivity的Handler,添加一個遞增的計數(shù)器,如下Listing 8所示:
Listing 8. Adding an incremented counter
- final Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- public void run() {
- final Intent nextQuiz = new Intent(getApplicationContext(), OverheardQuiz.class);
- nextQuiz.putExtra(QUIZ_NUM, ++quizNumber);
- startActivity(nextQuiz);
- result.setText("");
- finish();
- }
- }, 2500);
putExtra 方法關(guān)聯(lián)著quizNumber增加的鍵值QUIZ_NUM,兩個都是OverheardQuiz類的成員變量。
下一步,我們更新OverheardQuiz類的onCreate方法從啟動的那個Intent獲取這個值。(如果onCreate沒找到值,指定 它是1。原因是OverheardWordActivity***次被啟動時候沒有設(shè)置QUIZ_NUM的值。如果這個值比10大,序列會重置,并且我們會 發(fā)送給用戶一個慶祝的信息(通過Toast)。
最終,我們將更新計數(shù)器的TextView為新的計數(shù)。全部代碼如下所示Listing 9.
Listing 9. Updating a TextView with the current count
- final Bundle previous = getIntent().getExtras();
- quizNumber = (previous != null) ? previous.getInt(QUIZ_NUM) : 1;
- if (quizNumber > 10) {
- quizNumber = 1;
- CharSequence text = "Great Job! You made it through 10 questions. Here's another 10!";
- Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
- }
- final TextView quizCounter = (TextView)findViewById(R.id.quiz_number);
- quizCounter.setText(quizNumber + " of 10");
現(xiàn)在 讓我們啟動最終版Overheard Word來看看它怎么樣。
Figure 4. Overheard Word is munificent!
這樣,我認為我們已經(jīng)成功親手做了個app:它能夠提供有價值的服務(wù)(快速內(nèi)部詞匯量構(gòu)建),直觀的接口,甚至現(xiàn)在它有個能反復(fù)拉回用戶的有趣的測驗特性。UI可以更有趣(白底黑字比較討厭),但現(xiàn)在到了我們簽名并把它發(fā)到市場的時候了。
給你的Android app簽名
如果你想讓你的app能運行在所有人的設(shè)備上,你需要把它部署到一個公共的app 商店。然而有許多可選,其中Android兩個***的app商店 是Google Play和Amazon Appstore(看 資料)。處于安全原因,兩個商店規(guī)定它們的apps都必須被作者簽名。
當你注冊一個app,你創(chuàng)建一個你私有的證書。然后你用這個數(shù)字證書給你的代碼簽名。簽名是一種發(fā)布由你簽名而非某未知的、潛在惡意的一方的特別二 進制的方法,Android設(shè)備不會安裝沒有簽名的app應(yīng)用,所以如果你想要成為一個Android開發(fā)者,你需要知道如何給你app簽名。
我之前展示的幾篇文章在Android設(shè)備上《如何安裝一個測試app》,實際上你用測試證書簽名。測試證書對測試來說很好用,但公共的app商店不接受這個。這時你需要學習如何通過Eclipse生產(chǎn)一個真正的證書。
為了簽名這個app,去Eclipse ADT的項目菜單中,選擇‘Android Tools’ -> ‘Export Signed Application Package’。會彈出生成新證書向?qū)А?/p>
Figure 5. The Eclipse ADT certificate wizard
這個想到會指導你通過對話框一步步說明你個人信息。如果你曾買過網(wǎng)站ssl證書,你會通過相似的過程。還有Android中沒有證書授權(quán)的過程。因此App簽名比SSL證書更容易,但是它欺詐的可能性也高;如,沒人能阻止我說我是來自美國銀行或可口可樂的人員。
Figure 6. Identifying information for the certificate
在你完成創(chuàng)建證書和私鑰存儲(存儲你的私人密鑰),你就能導出一個.apk擴展名的app包,如下所示Figure 7.
Figure 7. OverheardWord.apk
一旦你的app有了數(shù)字簽名,至少從技術(shù)角度你可以自由的分發(fā)它到全世界。然而要發(fā)到公共的app商店只有簽名還不夠。你也需要提升它。
提升你的App
公共app商店只是提供一個全球市場的平臺,但app市場中聰明的客戶不會下載舊的東西。就像你需要知道在構(gòu)建apps中包如何提供有價值的功能和 樂趣,你能誘惑用戶讓他嘗試。當你上傳(或發(fā)布)一個Google Play或Amazon Appstore的Android app時,你可以做一下 這些:
- 定義你的app的主要特性
- 長短適中且?guī)в袠擞浀拿枋瞿鉧pp
- 提供關(guān)鍵字來幫助提升被搜索幾率
- 上傳不同尺寸的你的app截圖
- 提供你的app操作視頻
當然,你也需要決定是否你的應(yīng)用要免費還是付費;并且你需要設(shè)置一個適當?shù)膬r格、市場利率。的確,你可能發(fā)現(xiàn)構(gòu)建和簽名你的應(yīng)用是容易的部分,而在這個蓬勃發(fā)展、競爭激烈的市場中推銷移動app是一個很難的工作。
總結(jié)
正如你所知,本地的Android開發(fā)只是產(chǎn)生移動appe的一個點。這個系列下一篇首先看看如何用HTML5來構(gòu)建移動網(wǎng)絡(luò)app。以網(wǎng)絡(luò)為基礎(chǔ) 的移動app提供一些比本地apps優(yōu)勢–像實際上你能發(fā)布它們到任何地方!但是基于網(wǎng)絡(luò)的apps不能像本地app那樣華麗而且未必能達到本地app相 同的表現(xiàn)。當我們深入到HTML5中,我們會發(fā)現(xiàn)移動Web app的樂趣和約束。