閱讀434 返回首頁    go 阿裏雲 go 技術社區[雲棲]


麵向大眾的移動技術:簽名,封裝和發布Android app


麵向大從的移動打樁其它四篇文章地址(校對添加):

(一)、android簡介

(二)、輕輕一劃,在android中為手勢編碼

(三)、Android 應用程序生命周期中的活動與圖標

(四)、Overheard Word 的單詞和手勢

添加一個多選擇quiz到你的Android手機app,然後用一個安全數字證書簽名

用網絡邏輯,內容為王。但是對與手機用戶來說,交互規則才是王道。對移動app靜態信息設計在減少,並且遊戲化正在增多。這個月Andrew Glover決定通過將一個多選擇的quiz特性加入到一個示例app(Overheard Word,前一篇介紹的。) 中來介紹Android移動開發。之後他將展示給你如何生成一個數字證書和如何發布和如何提升你的在Google Play或者Amazon Appstore上已經簽名的app。

目前為止在這個Mobil for the masses係列中,我們已經使用Android作為學習怎樣做移動開發的一個實例,其中包括《Android應用程序生命周期》教程,在你的Android apps中實現《手勢滑動功能》。並且《與第三方庫工作》來簡化開發並且增強app功能。當我不確定做Android時候,我對瀏覽其它的手機環境和技術感興趣。所以這個月我們來 通過添加一個quiz特性到Overheard Word示例app 總結我們的 Android-intensive(加強安卓)文章 並且準備部署它到兩個流行的Adroid app stores:Google Play和Amazon Appstore。所有的這些將是下一節的基礎:HTML5侵襲移動開發!

遊戲化我的app

在我們簽名Overheard Word並且把它推送到Google Play和Amazon Appstore的Android市場同數百萬的其它apps競爭前,我想要確定它是最好的Overheard Word app(對我們的Overheard Word app不熟悉麼?回顧下介紹這個 示例 的文章)。如你所知,遊戲是當前推動移動生態係統的強勁動力,並且一係列的apps都被期望有高效的交互。移動apps即使當他們的目標是提供信息價值的時候,點燃好奇心和獲勝的欲望的移動應用也做的很好。那也是為什麼Overheard Word 不僅僅隻是一個頁麵上的單詞列表;相反,它被設計用來煽動讀者來挖掘詞匯量,接著獎勵他們來堅持學習它!(順便說下Gamification 遊戲化 是一個正在開始流行的設計技術的術語)
在我們簽名Overheard Word並且把它推送到Google Play和Amazon Appstore的Android市場同數百萬的其它apps競爭前,我想要確定它是最好的Overheard Word app(對我們的Overheard Word app不熟悉麼?回顧下介紹這個 示例 的文章)。如你所知,遊戲是當前推動移動生態係統的強勁動力,並且一係列的apps都被期望有高效的交互。移動apps即使當他們的目標是提供信息價值的時候,點燃好奇心和獲勝的欲望的移動應用也做的很好。那也是為什麼Overheard Word 不僅僅隻是一個頁麵上的單詞列表;相反,它被設計用來煽動讀者來挖掘詞匯量,接著獎勵他們來堅持學習它!(順便說下Gamification 遊戲化 是一個正在開始流行的設計技術的術語)
在我們簽名Overheard Word並且把它推送到Google Play和Amazon Appstore的Android市場同數百萬的其它apps競爭前,我想要確定它是最好的Overheard Word app(對我們的Overheard Word app不熟悉麼?回顧下介紹這個 示例 的文章)。如你所知,遊戲是當前推動移動生態係統的強勁動力,並且一係列的apps都被期望有高效的交互。移動apps即使當他們的目標是提供信息價值的時候,點燃好奇心和獲勝的欲望的移動應用也做的很好。那也是為什麼Overheard Word 不僅僅隻是一個頁麵上的單詞列表;相反,它被設計用來煽動讀者來挖掘詞匯量,接著獎勵他們來堅持學習它!(順便說下Gamification 遊戲化 是一個正在開始流行的設計技術的術語)

Overheard Word探詢

我們將啟動正定義的一個新的布局來使Overheard Word的測試視圖保持一致,接下來我們定義一個來展示布局的Activity。正如前文所說,我正使用Eclipse的ADT作為我的開發環境,我假設你也是使用它。

我們將啟動正定義的一個新的布局來使Overheard Word的測試視圖保持一致,接下來我們定義一個來展示布局的Activity。正如前文所說,我正使用Eclipse的ADT作為我的開發環境,我假設你也是使用它。

Figure 1. Creating a new layout in Eclipse

fig1

我們將啟動正定義的一個新的布局來使Overheard Word的測試視圖保持一致,接下來我們定義一個來展示布局的Activity。正如前文所說,我正使用Eclipse的ADT作為我的開發環境,我假設你也是使用它。

下一步,拷貝下麵的XML到你的新文件。

Listing 1. Quiz layout for Overheard Word
<RelativeLayout xmlns:andro
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".OverheardWord" >

    <LinearLayout
        android:
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="20dp"
        android:orientation="vertical" >

        <TextView
            android:
            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:
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="17dp" >

            <RadioButton
                android:
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Answer 1" />

            <RadioButton
                android:
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Answer 2" />

            <RadioButton
                android:
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Answer 3" />
        </RadioGroup>

        <TextView
            android:
            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:
            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將立即通知一個事件並相應,也有通過這個小測試跟蹤用戶進程的計數器。

我們下一步是創建一個新的Activity類。這個類應該繼承Activity類且提供一個onCreate方法,如Listing2所示。(注意, setContentView指定那個Listing1創建的新布局文件)

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的測試布局。不僅僅創建一個可選菜單項,讓我們嚐試用手勢滑動功能來啟動一個測試。稍後我們也會把app菜單加到測試中。

Intents編程

正如我們之前討論的,一個Android設備極其受電池時間周期、執行能力和內存的約束,大多數個人設備也有許多像電話和文本特性的apps,所以同時他們有多個激活的程序運行。Android平台通過給作為開發者的你在任何時間使你的app應用處於某些約束中來管理程序。例如,你不能強迫一個Activity類啟動並且顯示它的視圖。而是,你要通過給activity發送給平台一個intent來啟動。平台會在能啟動Activity的時候啟動它。

想象一個Intent是一個異步執行任務的機製。從用戶角度看,你建議平台應該做一些事情並且它能做的時候它立刻做了。

所以不能僅僅啟動我們的Activity,我們需要把它包裝在一個Intent裏。之後Android將為我們啟動Activity。由於我們正在用手勢啟動一個新的測試Activity,我們將把intent插到一個GestureDetector實例。(這係列中更多關於GestureDetector看 前兩篇文章)在isUpSwip條件中不是隻能返回false,我們會發送那樣的一個新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實例的方法。

試一試,啟動你的模擬器實例,當顯示一個單詞時候向上滑。你應該看到一個新Activity,如Figure2。

Figure 2. A quiz is born!

fig2

試一試,啟動你的模擬器實例,當顯示一個單詞時候向上滑。你應該看到一個新Activity出現在Figure2那樣

上滑,下滑

我們首先要做的就是添加一個手勢檢測以便能夠讓用戶離開這測試並回到學習單詞。由於上滑是讓用戶進入到測驗,憑直覺下滑是離開測驗比較好。我們調用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中實現了一個操作欄,用來我們退出app。這裏我們重用那段代碼來創建新的測驗退出函數。

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,隻是離開測驗;所以我們更新了操作欄文本‘退出測試’而不是‘退出’。

單詞和定義

下一步,我們設定一個有一個正確答案和兩個錯誤答案的定義。由於我們使用Thingamejig來處理邏輯,我們唯一需要做的就是輸入相關字段,如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方法先構建我們提供的基礎視圖。下一步,初始化手勢。就像原來學習Activity那樣工作,一個WordTestEngine(而不是WordStudyEngine)被實例化且鏈接到Overheard Word的單詞存儲(這個例子中是JSON文件)。possibleAnswersForm方法返回一個有三個定義其中一個正確定義的列表,為了使測驗不是一直顯示相同順序WordTestEngine實例會打亂定義順序。

最終,循環輸入匹配所給定義的單詞到我們的RadioButtons。如果你啟動app實例,你將會看到一個漂亮的 有著一個單詞定義和三個單詞選項的表單測驗接口。

Figure 3. The quiz UI with words and a definition

fig3

還有注意在屏幕底部有個靜態Result按鈕和一個顯示所有的測驗問題數量的計數器,截圖中10個問題已經使用了1個。

 

目前為止,我們使用Thingamejig和一些原來的Activity邏輯快速創建了測驗的樣本邏輯。接下來我們將手動處理測驗的單選框的按鈕部分。我們回調到單選按鈕組的行為並且監聽什麼時候選項被選,而不需要用戶額外多點擊按鈕(類似提交)來表明選中選項。這種方式用戶不需要額外操作就立即得到反饋。

處理事件調度

我提到我想要基於他們的單詞選項來提供立即反饋。還有,我還想程序Overheard Word 對於反饋可以短暫的暫停。如果用戶給了一個正確的回應,我想他或她在進入下一題前有幾秒中來體會這種感覺。如果回答錯誤,我應該讓他們再試或者保持一兩秒後再顯示正確答案。任何方式,用戶都有機會了解他們在測驗中的操作。

 

但是記住,我們不僅做一些我們喜歡的Android app;沒有強迫app隨時睡眠或啟動線程。(在Android app中啟動線程是可能的,當然,但是各種相關因素是不受控製的。)就像我們使用Intents來告訴Android app 我們意圖讓它有怎樣的行為,我們用Handlers來指示我們渴望推遲或立即執行;Android怎樣處理請求是Android的事了。

請暫停

在Android中,你使用Handlers來處理事件調度,並且也通過線程間傳遞信息。這種情況,我們將一直用Handlers調度一個事件,它將是一個密封的Runnable實例。

在響應用戶選中一個單詞的事件中,我們將創建一個Handler實例,在Runnable內部傳遞某寫邏輯(如展示一個新的Activity)並且設置幾秒延遲。

我們實現onCheckedChangeListener監聽附加到RadioGroup上來回調敲擊單選按鈕,如下所示: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的傳統GUI編程,你能添加事件監聽。例子中,我們就正在添加一個 OnCheckedChangeListener 到RadioGroup。當用戶選擇一個RadioButton時,監聽代碼會執行。如果用戶選擇正確,新實例OverheardQuizActivity會顯示一個新單詞。用戶也可以在TextView中得到友好的‘正確’的綠色文本信息。

如果用戶選擇錯誤,信息文本是紅色的,Activity的UI會重置讓用戶再次嚐試。

也要注意兩個Handler實例延遲啟動線程,一個是因為要新建一個Intent啟動,另一個是UI重置。

你也需要刪除在Listing 1 中定義的測驗布局的默認的文本。隻需回到XML文件並且從ID為quiz_result的TextView中刪除

android:text=”Result”。現在啟動你的app並檢測!

Android中的Bundles

接著我們要做的事情是給測驗視圖右下角的計數器添加一些邏輯(看Figure 3)。對於測驗從用戶的角度觀察,我們想要展示他們的進度給他或她,像還有多少問題。為了我們的計數器能工作,我們需要一個保持計數器的狀態(例如它之前是2,且現在它應該是3),這就需要我們能從一個activity到另一個之間傳遞數據,我們用Bundles來做這個任務。

如果你已經讀了一段時間這個係列,那麼你已經接觸過Bundles了,因為一個Bundles實例是每個Activity的onCreate方法的長參數。

Bundles必須是一個鍵-值對的Maps。你能通過方法調用鍵的值檢索一致的值類型。除了Activity實例外,Bundles也被包括在Intents中,實際上,如果你放一個新Activity鍵-值對到Intent關聯關係中,你就能通過那個Intent檢索Bundle得到創建的那個Activity。這些聽起來可能很繞,但親自做一次就理解了。

創建計數器

首先,我們更新創建新的OverheardQuizActivity的Handler,添加一個遞增的計數器,如下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 方法關聯著quizNumber增加的鍵值QUIZ_NUM,兩個都是OverheardQuiz類的成員變量。

下一步,我們更新OverheardQuiz類的onCreate方法從啟動的那個Intent獲取這個值。(如果onCreate沒找到值,指定它是1。原因是OverheardWordActivity第一次被啟動時候沒有設置QUIZ_NUM的值。如果這個值比10大,序列會重置,並且我們會發送給用戶一個慶祝的信息(通過Toast)。

最終,我們將更新計數器的TextView為新的計數。全部代碼如下所示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");

現在 讓我們啟動最終版Overheard Word來看看它怎麼樣。

Figure 4. Overheard Word is munificent!

fig4

這樣,我認為我們已經成功親手做了個app:它能夠提供有價值的服務(快速內部詞匯量構建),直觀的接口,甚至現在它有個能反複拉回用戶的有趣的測驗特性。UI可以更有趣(白底黑字比較討厭),但現在到了我們簽名並把它發到市場的時候了。

給你的Android app簽名

如果你想讓你的app能運行在所有人的設備上,你需要把它部署到一個公共的app 商店。然而有許多可選,其中Android兩個最大的app商店是Google Play和Amazon Appstore(看 資料)。處於安全原因,兩個商店規定它們的apps都必須被作者簽名。

當你注冊一個app,你創建一個你私有的證書。然後你用這個數字證書給你的代碼簽名。簽名是一種發布由你簽名而非某未知的、潛在惡意的一方的特別二進製的方法,Android設備不會安裝沒有簽名的app應用,所以如果你想要成為一個Android開發者,你需要知道如何給你app簽名。

我之前展示的幾篇文章在Android設備上《如何安裝一個測試app》,實際上你用測試證書簽名。測試證書對測試來說很好用,但公共的app商店不接受這個。這時你需要學習如何通過Eclipse生產一個真正的證書。

為了簽名這個app,去Eclipse ADT的項目菜單中,選擇‘Android Tools’ -> ‘Export Signed Application Package’。會彈出生成新證書向導。

Figure 5. The Eclipse ADT certificate wizard

fig5

這個想到會指導你通過對話框一步步說明你個人信息。如果你曾買過網站ssl證書,你會通過相似的過程。還有Android中沒有證書授權的過程。因此App簽名比SSL證書更容易,但是它欺詐的可能性也高;如,沒人能阻止我說我是來自美國銀行或可口可樂的人員。

Figure 6. Identifying information for the certificate

fig6

在你完成創建證書和私鑰存儲(存儲你的私人密鑰),你就能導出一個.apk擴展名的app包,如下所示Figure 7.

Figure 7. OverheardWord.apk

fig7

一旦你的app有了數字簽名,至少從技術角度你可以自由的分發它到全世界。然而要發到公共的app商店隻有簽名還不夠。你也需要提升它。

提升你的App

公共app商店隻是提供一個全球市場的平台,但app市場中聰明的客戶不會下載舊的東西。就像你需要知道在構建apps中包如何提供有價值的功能和樂趣,你能誘惑用戶讓他嚐試。當你上傳(或發布)一個Google Play或Amazon Appstore的Android app時,你可以做一下這些:

  • 定義你的app的主要特性
  • 長短適中且帶有標記的描述你app
  • 提供關鍵字來幫助提升被搜索幾率
  • 上傳不同尺寸的你的app截圖
  • 提供你的app操作視頻

 

當然,你也需要決定是否你的應用要免費還是付費;並且你需要設置一個適當的價格、市場利率。的確,你可能發現構建和簽名你的應用是容易的部分,而在這個蓬勃發展、競爭激烈的市場中推銷移動app是一個很難的工作。

總結

正如你所知,本地的Android開發隻是產生移動appe的一個點。這個係列下一篇首先看看如何用HTML5來構建移動網絡app。以網絡為基礎的移動app提供一些比本地apps優勢–像實際上你能發布它們到任何地方!但是基於網絡的apps不能像本地app那樣華麗而且未必能達到本地app相同的表現。當我們深入到HTML5中,我們會發現移動Web app的樂趣和約束。

最後更新:2017-05-23 16:33:19

  上一篇:go  Java網絡教程之Socket
  下一篇:go  女神節 | 科學界的巾幗英雄:致敬十位中國青年女科學家