2016年10月11日 星期二

Android: 如何在Gradle建立Build Variants及更換APK檔名

前言

  • Build Variants
    • 當Android開發者在建立APK時,有時會遇到需要建立不同環境的APK,而這些APK很有可能只是應用程式連接的資源或是URL不同。過去可能利用一個Config或Constant檔案,藉由旗標的判斷決定要使用哪些資源,再一個個切換並重新建立應用程式的APK,這實在非常麻煩。自從Android從Eclipse轉換至Android Studio後,藉由Gradle的設定,可以讓我們將應用程式做分類,這個分類可以在我們建立APK階段前,切換不同環境的應用程式,再連結對應的資源,這就是Build variants的好用之處。
  • 更換APK名稱
    • 因為本人在實務開發上,常常需要在釋出給QA團隊測試的APK附上版本及環境的特定資訊,但是這些手動建置實在耗時又擾人,所以我們也能利用Gradle來建置我們想要的APK名稱格式。

Build Variants的建立步驟

  • 使用productFlavors
    • 該如何在Gradle使用productFlavors?我們可以在Gradle檔案中採用以下結構來產生三個環境,分別是Development version,,Beta Version及Production Version,如下圖。

  • 使用buildConfigField
    • 加入之後,就可以連接我們所需的資源。例如我有一個URL,這個URL在這三種環境中,都有各自對應的內容,而這些資源的宣告可以透過buildConfigField來建立。
      • 例如:buildConfigField 'String', 'URL', '"http://www.yahoo.com.tw"'
      • 這段的意思就是宣告有個名為URL的變數,其型態為字串,而賦值為"http://www.yahoo.com.tw"。
      • 完整的內容如下圖。

      • 這時我們可以檢查Android Studio中的Build Variants,看看是否每個環境分別有Debug和Release版本,Debug和Release的差異就在Release版本會Sign過Release Key,這部分就不贅述,如下圖。

  • 測試
    • 我們可以利用BuildConfig來檢視三種環境的URL是否正確切換,如下圖。
      • DevEnv

      • BetaEnv

      • ProEnv

    • 檢查建立出來的APK是否也有相對應的名稱

    • 利用Terminal來建立APK
      • 除了利用Android Studio的介面來切換環境建立APK,也可以透過Terminal來下Gradle的指令建立這三個環境的應用程式。
      • DevEnv
        • gradle assembleDevEnvDebug / assembleDevEnvRelease
      • BetaEnv
        • gradle assembleBetaEnvDebug / assembleBetaEnvRelease
      • ProEnv
        • gradle assembleProEnvDebug / assembleProEnvRelease
      • 全部的Debug/Release APK
        • gradle assembleDebug / assembleRelease
      • 透過這些指令,也能產生上述環境的APK,如下圖。

更換APK名稱的建立步驟

  • 首先,我們先來制定所需的格式,這邊我定的APK檔名格式為 "專案縮寫名稱-正式版本名稱#內部測試版本名稱-環境-debug|release.apk",這是我期望釋出APK後,QA或PM從檔名中就能得知帶有什麼資訊。
    • 例如:該專案名稱為Build-Variants-Sample(BVS),versionName為2.0.3,internalReleaseCode為#15,環境為DevEnv,版本為debug,所以建置出來的APK檔名如下所示。
      • BVS-v2.0.3#15-DevEnv-debug.apk

  • 利用Gradle的ext,宣告一個名為internalReleseCode的變數名稱,如下圖。

  • 在defaultConfig為internalReleseCode變數賦值,這邊為#15,如下圖。

  • 使用Gradle的setProterty建立archivesBaseName,如下圖。

  • 建立APK檢視檔名,如下圖。

2016年10月10日 星期一

Android: 利用Firebase實作使用者間的產品分享機制

前言

Firebase是一個即時的雲端資料庫,原本是一間提供雲端服務的公司,主要供開發人員快速建立網頁與行動裝置,後來被Google收購後,整合了許多新的功能與SDK,像是Analytics, Auth, Storage, Hosting, Test Lab, Crash Reporting, Notifications, Remote Config, Dynamic Links與AdMob等服務。這些服務可以供行動開發者無須依賴後台開發人員的人力,而且這個資料庫也有別於關聯式資料庫,是以Json為資料結構的資料庫,自己便能輕鬆實作出仰賴資料庫的應用程式,也可以看看影片瞭解更多關於Firebase的介紹,在本篇不多著墨,若是對於上述的服務有更大的興趣,更可以利用Firebase Help Center來協助您。而在這個實作中,我們會利用Firebase的Realtime Database, Remote Config與Notifications。

言歸正傳,由於本人目前在電商公司服務,都是開發與電商有關的應用程式,有些想法想記錄下來,於是,今天想利用Firebase的即時資料庫,來做個簡單使用者間的商品分享。咦!提到這裡,其實很多手機應用程式都有分享的功能,我何必在自己的應用程式加入?在分享的功能中,往往都是透過選擇器使用第三方的應用程式來分享,像是Line, Facebook, Email...等等,但我覺得這個有點不太方便,加上使用者也必須跳離我們的應用程式才能得知這些訊息,假如能讓安裝我們應用程式的使用者加入其他會員,讓使用者選擇欲分享產品的對象,這樣除了可以讓使用者無須借助第三方應用程式,接收方也能及時得知推薦商品的資訊。廢話太多,我們就開始著手利用Firebase來實作使用者間的商品分享功能吧!以下是實作的主要概念與實作方式。

雛形(Prototype)





實作細節

  • 註冊Firebase/Google帳號
    • 首先我們需要一個Google帳號,這個帳號將來可以讓你進行後台管理與設定,但是大家應該都有Google帳號,所以這個部分就不多做說明。若沒有帳號的人,請至Google網頁申請。
  • 開啟Android新專案
    • 透過Android Studio建立一個名為Product-Sharing-Notification新專案,如下圖。

    • 選擇開發平台與SDK,如下圖。

    • 加入Activity,如下圖。

  • 加入Firebase libs
    • 當專案建立好後,我們需要讓專案與Firebase相連結。點選Android Studio工具列的Tools -> Firebase,如下圖。

    • 此時Android Studio會出現Firebase的側邊選單,裡面會列出Firebase提供的服務,我們可先選擇Realtime Database -> Save and retrieve data -> Connect your app to Firebase,如下圖。

    • 這時候你也能透過瀏覽器登入Firebase帳號,進入後台檢查是否專案建置成功,成功則會有個與你於Android Studio建置相同的專案名稱,如下圖。

    • 此時我們點選Connect your app to Firebase的按鈕,如下圖。

    • 若沒有直接在Firebase後台建立資料庫,可以直接透過Create new Firebase project的選項,由Android Studio為你建立,如果你已經在後台建置過,也可以透過Choose an existing Firebase or Google project的選項連結,如下圖。

    • 連結成功後,Connect to Firebase的按鈕則會變成綠燈,顯示Connected,如下圖。

    • 接下來點選Add the Realtime Database to your app按鈕,這部分是將Google services的服務與Firebase database lib加入Gradle內,可以透過自動建置,也能自行在Gradle加入,如下圖。


    • 加入成功後,也同樣會變成綠燈,顯示Connected,如下圖。

    • 除了Realtime Database的服務,我們也會利用Cloud Messaging(FCM)和的服務來完成需求,所以可以利用同樣方式,將這個服務加入至Gradle中,如下圖。

    • 如果你順利與Firebase後台連接成功,Project中也會多了一個google-services.json的檔案,這個檔案則是記錄了你的專案與Firebase所需的相關資料,若沒有這個檔案,也可以在Firebase下載,如下圖。

  • 其他Lib的準備
    • 這邊可以視平常開發的習慣,選擇你需使用的Lib。這邊我會利用到網路存取、Json剖析以及圖片存取的服務,所以加入了以下Lib至Gradle,如下圖。
      • 網路存取: Volley
      • 圖片存取: Glide和Picasso
      • Jason剖析: Gson

  • 介面呈現
    • 前置作業準備好了,可以開始按照Prototype來刻我們的Layout。我們需要註冊一個會員ID,來識別每個會員,同時也需要傳送一個會員ID作為分享的對象,以下是Android實際的Layout,如下圖。

  • 實作ID註冊
    • 接下來就可以串接程式了,先完成註冊的功能,讓Firebase資料庫可以儲存會員ID的資料,以及其他所需的資料。
    • Firebase的後台要發送訊息,是如何識別每支手機?在過去實作GCM時,當你註冊GCM的同時,會獲得一個Device Token,Firebase也須做相同的事達到同樣目的,所以我們需要利用FirebaseInstanceId的類別取得相對應的Token,其使用方式為FirebaseInstanceId.getInstance().getToken()。
    • 這個Token並不是永久相同的,當你把應用程式移除並重新安裝後,Token同時會更新,這時我們要在更新時取得最新的Token,並將它儲存起來,你可以使用Preference或是其他方式儲存。如何得知Token已經更新?我們需要繼承FirebaseInstanceIdService這個類別,並且覆寫onTokenRefresh()方法,這個方法會在Token更新時,進行回呼,這時你就可以取得最新的Token資料,並將它儲存,如下圖。

    • 實作完成後,請記得在AndroidManifest中加入這個Service,如下圖。

    • 再來,資料該如何寫入Firebase?可透過Firebase提供的DatabaseReference物件存取,它是用來與Firebase資料庫做資料相關操作的服務。例如:我想要在Firebase資料庫儲存一筆會員資料,該會員資料除了會員ID外,還儲存了該手機的Token(識別碼),以及使用平台,資料結構如下圖。

  • 接下來,我們可以利用DatabaseReference.的child()方法來新增一個名為users的欄位,在users的欄位下還有一個會員Id,同樣也可以用child()來完成,如下圖。

  • 最後該在何時新增DeviceList欄位底下的資料?首先我們需建立一個記錄Device與Token的資料物件,這邊我取名為DeviceData,其結構如下圖。
  • 再透過DatabaseReference的addListenerForSingleValueEvent()方法,實作當中的onDataChange()方法,這個方法是當資料更新時,可以讓我們讀取最新的資料,這邊提供我們先做一些資料的判斷,再透過setValue()的方法將該資料結構儲存至Firebase中即可,如下圖。
  • 實作推播功能
    • 實作推播只需要幾個步驟
    • 首先,準備好要推播的內容,以這個功能來說,推播的內容有產品名稱以及訊息,接著取你想分享對象的Token,這樣推播才能知道該送給哪個裝置。
      • 該如何取得欲分享對象的Token?先前註冊的功能已經幫我們把每個會員的Token,皆已存入Firebase中,這時同樣可透過DatabaseReference取得。

      • 先前我們透過DatabaseReference的addListenerForSingleValueEvent()方法,會要求我們傳入一個ValueEventListener物件,這個物件要實作onDataChange()與onCancelled()兩個方法,當中的onDataChange()會有個DataSnapshot的物件傳入,我們便可以透過欲傳送推播對象的ID,再由該物件取得其當時註冊的Token,如下圖。
    • 準備好送出的推播格式,透過網路存取機制送出。
      • 取到傳送對象的Token後,可以準備你要推播的格式,這邊的格式需要回傳Json格式,如下圖。

    • 這個格式共有Target與Payload兩部分,Traget需要有你推播對象的Token,Payload也就是傳送的內容。至於這些內容可以詳細參考文件,客製化你所需的內容,這邊就已能達到我們目的即可,如下圖。




    • 準備好傳送格式後,我們需透過網路存取的Post方式傳送推播,需要準備Header內容,這個Header包含這個專案的金鑰(可透過Firebase查詢到)以及Content-Type,如下圖。




    • 最後則是Post的URL:https://fcm.googleapis.com/fcm/send
    • 準備好後,則可以傳送推播了。

  • 最後實作FirebaseMessagingService,透過這個服務,可取得手機接收訊息後,供你顯示資料內容。
    • 實作時,會要求覆寫onMessageReceived()方法,這邊就可以實作當你接收到訊息後,你該怎麼顯示。

    • 顯示部分則是產生Notification物件,其實跟以前的GCM沒什麼差異,其方式如下圖。

    • 記得,當你實作FirebaseMessagingService後,請記得將該Service加至AndroidManifest.xml中,如下圖。

    • 實際測試


2016年3月24日 星期四

DM/ML: 預備知識:資料型態

**# 資料**

- 資料型態
    - 資料物件
        - 一筆記錄,由許多屬性(亦可稱為維度)組合而成。
    - 資料集
        - 多筆資料物件的集合

- 資料特性
    - Qualitative(定性)/Categorical(類別型):無法計算
        - 名目(差異性)
        - 順序(順序性)
            - 門牌號碼
            - 金屬硬度
            - 等級
    - Quantitative(定量)/Numenical(數字型):可以計算
        - 區間(加和減)
            - 日期
        - 比例(乘和除)
            - 電子現金
    - Discrete Data(離散型資料):經由計數的方法取得資料
        - 班上有多少人
        - 目前有多少產品
        - 產品的不合格品有多少
    - Continuous Data(連續型資料):經由量測的方法取得資料
        - 溫度
        - 長度
        - 寬度

- 資料集型態
    - 紀錄資料
        - 記錄資料
        - 交易資料
        - 資料矩陣
    - 圖形資料
        - 物件間的關係
        - 物件本身即為圖形
    - 順序資料
        - 時序性
            - 具有時間關聯
        - 序列性
            - 具有順序關聯
        - 時間序列
            - 時序性的特例,本身是一段時間測量的結果
        - 空間資料
            - 具有空間屬性,例如包含某個區域的特性

- 資料集特性
    - 維度
    - 稀疏性
    - 解析度

- 資料品質的議題
    - 資料品質的主要處理步驟
        - 資料清理
        - 使用演算法容忍資料品質不佳的問題
    - 資料品質的問題
        - 測量誤差和資料搜集誤差
        - 雜訊以及人為疏失
        - 精確度、偏誤及正確性
        - 離群值
        - 遺漏值
            - 刪除資料物件或屬性
            - 遺漏值估計
                - 使用相似的樣本來估計
                - 最近鄰近點的平均值來估計
                - 最常出現的屬性值取代
            - 忽略
        - 不一致
        - 重複性

- 資料前處理
    - 聚合
        - 例如減少變數,365天縮減為12個月
    - 抽樣
        - 隨機抽樣
            - 抽樣後放回
            - 抽樣後不放回
        - 分層抽樣
            - 不同類型的資料都有相同的抽樣率
            - 不同類型的資料依照資料比例來決定抽樣數量
        - 漸進式抽樣
            - 一開始樣本較少,然後慢慢增加至獲得足夠樣本為止。
    - 維度縮減:亦即將舊有的屬性結合成新的屬性,主要作法為特徵選取
        - 維度的問題
            - 維度增加可能增加稀疏性
        - 維度的縮減
            - 線性代數
                - 主成份分析(Principal components analysis, PCA)
                - 奇異值分解(Singular value decomposition, SVD)
            - 特徵選取:將一些重複及無關的屬性過濾,選取條件則是特徵權重。像是SVM或是計算Cosine相似度後的物件。
                - 嵌入法
                    - 決定自己所要用的屬性或是需忽略的屬性
                - 過濾法
                    - 可選擇相關度較低的屬性
                - 包裝法
                    - 找出最好的屬性,但不會處理可能的特徵組合
            - 二元化或離散化
                - 二元化:將類別值指定至整數區間
                    - 將個整數轉成二元值,則需要個二元屬性來表示,
                - 離散化:通常用在分類或是關聯式分析
                    - 監督式離散化
                        - 純度(Purity)
                        - 亂度(Entropy)
                    - 非監督式離散化
                        - K-means
            - 變數轉換
                - 正規化或標準化

- 資料物件的相似度、不相似度與相關度
    - 定義:
        - 相似度
            - 為資料物件間的相同程度,相似度越高,則越相似,而其值僅會介於0~1之間,不為負值;
        - 不相似度
            - 和距離則是同義詞,距離越大,則不相似度越高。
        - 相關度
            - 具有二元或是連續屬性的二個物件,可以用線性函式來計算相關性,也可稱為相似度。
    - 距離計算
        - 歐式距離
        - 曼哈頓距離
        - 切比雪夫距離
        - 閔可夫斯基距離
        - 標準化歐式距離
        - 馬氏距離
        - 漢名距離
    - 相似度計算
        - 夾角餘弦(Cosine相似度)
        - 簡單配對係數(Simple matching coefficient, SMC)
        - Jaccard係數
        - Extended Jaccard係數(Tanimoto係數)
    - 相關度計算
        - 皮爾森相關係數
        - Bregman分散度

DM/ML: 預備知識:何謂資料探勘?

# **資料探勘**

- 定義
    - 能在大量資料自動化發現一些有用的資訊的流程,可以用來處理大量資料,亦可推論與預測。

- 包含
    - 統計學、估計和假設檢定
    - 應用數學、線性代數與離散數學
    - 人工智慧、機器學習及圖形辨識

- 工作
    - 預測工作
    - 敘述性工作
    - 預測模式
        - 分類模式:離散型資料
        - 回歸模式:連續型資料
    - 關聯規則
        - 具有高度關聯的特徵屬性
    - 分群分析
        - 相似特徵的觀察結果
    - 自動化偵測
        - 偵測異常值或離群值

2016年1月18日 星期一

Testing: 測試自動化(三): 實作與Demo

介紹完Cucumber與Calabash的基本認識與流程,這個章節則進入尾聲,也就是實際看看該如何從安裝、撰寫測試案例到運行。這個章節共分為環境安裝、撰寫測試案例、執行並產生測試報告與Demo。(以下的測試是以Android應用程式為範例,然而Calabash也可以支援IOS,該領域的測試自動化流程可以參考[Calabash-IOS](https://github.com/calabash/calabash-ios)的相關教學,在此不多做說明。)

# 環境安裝
首先是環境安裝,先前的章節有提到Calabash是基於Ruby on Rails,想當然爾我們需要安裝Ruby。以下為Windows環境,Mac環境請參考Ruby on Rails實戰聖經Calabash-android的教學。

1. Rubuy Installer
    - 下載RubyInstaller
    - 安裝過程
        - 勾選名為"Add Ruby executables to your PATH"的選項
        - 測試
            - 開啟Command Line Window,並輸入"ruby -v"來測試
            - 若顯示"ruby 2.0.0p353 (2013-11-22) [x64-mingw32]"則是成功安裝

2. DevKit
    - 下載DevLit:
    - 下載至你欲儲存的儲存位置,如: D:\DevKit
    - 開啟Command Line Window,導航至你儲存DevKit的路徑,並輸入"ruby de.rb init"。
    - 檢查名為"config.yml"的檔案是否存在
    - 在Command Line Window下輸入"ruby dk.rb install"。
    - 待安裝完成

3. Rails及Calabash-Android
    - 開啟Command Line Window
        - 輸入"gem update --system"
    - 安裝Rails
        - 輸入"gem install rails --no-ri --no-rdoc"
    - 測試
        - 輸入"rails –v"
        - 若顯示"Rails 4.0.2"則是成功安裝      
    - 安裝Calabash-android
        - 開啟Command Line Window,輸入"gem install calabash-android"
        - 待安裝完成


# 撰寫測試案例
待安裝環境後,接下來該如何利用Cucumber來撰寫測試案例?這邊分為三個步驟: 產生Cucumber Skeleton、撰寫測試案例與情境、實作Ruby程式:

## 1. 產生Cucumber Skeleton

產生Cucumber Skeleton的目的是,我們需要產生一個名為"featrues"的資料夾,當中會存放測試相關的內容,主要包含了測試案例(*.feature)及Ruby執行的檔案(*.rb),可以讓開發者或測試工程師存放自己撰寫的內容。我們可以開啟Command Line Window,使用"calabash-android gen"的指令來產生Cucumber Skeleton,其結構如下圖所示:

428343_08

## 2. 撰寫測試案例與情境

在建立Cucumber Skeleton後,我們可以在Cucumber Skeleton的資料夾中建立即將測試的檔案,下圖為實際的測試案例,測試目標為"Android應用程式- friDay購物"的內容,分為欲測試的功能與情境描述,下列皆以該案例陳述。

428343_09


    - 測試功能: 包含目的與測試角色。
        1. 目的: 測試使用者使用該應用程式時,可以順利從首頁瀏覽商品,並且選擇一件商品。
        2. 測試角色: friDay購物的會員(需申請一個已知的帳號進行測試,並且成功驗證註冊信後成為friDay購物的會員之一。) 
        
    - 情境描述: 可區分為情境描述與步驟。
        1. 情境描述: 使用者可以從首頁Viewpager的GridView內容選擇一個商品。
        2. 步驟: 步驟的描述可以包含三大元素: Given、When與Then、另外兩個非必要元素: And和But。
            - Given: 描述測試的前置條件,以上圖為例: 使用者需直接關閉使用者導覽進入首頁。
            - When: 描述關鍵的行為或判斷的觸發行為,以上圖為例: 當使用者登入時,需做後續的三個步驟。
            - Then: 描述預期的結果或是斷言,類似Unit Test的斷言,你預期會有甚麼樣的行為產生,以上圖為例: Then I enter "商品資訊" page.,使用者則會進入商品資訊頁面。
            - And和But: 當有多個Given、When或是Then的時後併用,如同自然語言的"以及..."和"但是..."。

## 3. 實作Ruby程式

在建立撰寫測試案例與情境後,便是我們將欲測試的步驟給實現出來的時候,而除了Ruby本身的語法結構外,實際上我們也需要Calabash-android提供的API讓我們達成測試的實作。以下為基本的操作API:
    
    - Query(uiquery, *args): 找出可視範圍的View(視圖)。我們可以透過該API找出手機可視範圍的View,以及應用程是物件,讓測試者可以斷言結果或是從中得到相關資訊,其回傳結果會為一個陣列。以下圖為例: 下圖則是想找出與變數"pageName"相同文字內容的textView物件,回傳結果如下所示。

428343_10

回傳結果: 
[{"class"=>"android.widget.TextView", "tag"=>nil, "description"=>"android.widget.TextView{3d05290a V.ED.... ........ 408,28-672,116}", "id"=>nil, "text"=>"商品資訊", "visible"=>true, "rect"=>{"height"=>88, "width"=>264, "y"=>103, "x"=>408, "center_x"=>540, "center_y"=>147}, "enabled"=>true, "contentDescription"=>nil}]

    - touch(uiquery, options={}): 在測試的過程中,操作上往往我們會運用點擊的事件,該如何在Calabash-android中模擬點擊?便是使用touch這個API。我們可以透過touch API中的第一個參數,先查詢所需點擊的UI物件,便可完成欲點擊的物件行為。以下圖為例: 下圖是以查詢符合變數"buttonName"所帶之文字內容的物件,若該物件存在,則點擊它。 

428343_11

    - enter_text(uiquery, text, options={}): 輸入文字也是常用的操作行為,有時測試人員常需要進行文字輸入,對某些欄位輸入特定字串,可以使用該API。以下圖為例: 下圖是在在Webview內的"account"欄位與"password"欄位輸入帳密。

428343_12

    - tap_when_element_exists(uiquery, options={}): tap_when_element_exists API與touch類似,該API可以在點擊之前等待該物件出現,在該誤鍵出現後才進行操作。 

428343_13

    - scroll_down and scroll_up: 滑動捲軸也是常用的事件之一,當一個可視範圍超出螢幕時,便可使用該操作,如下圖所示。

428343_14

# 執行並產生測試報告
當上述步驟都完成後,則進入最後的階段 - 執行。我們可以開啟Command Line Window,導航至Cucumber Skeleton產生的features內的同一層目錄中,並將測試的APK放至其內,接著輸入"calabash-android run apkName.apk –format html –out reports.html" ,便可產生測試報告。該指令是讓測試者可以運行欲測試的APK後,產生reports.html這個檔案作為輸出內容,測試者則可以利用這份檔案對測試案例的成功與否進行檢視。

428343_15

# Demo
上述步驟皆完成,也表示我們順利完成一個Android應用程式的自動化測試流程,本篇的分享也告一段落,但在此有些建議,我們應該要釐清測試目的為何?如果開發者或是測試人員僅是將操作流程自動化,其實這樣的測試與[Monkey Test](http://developer.android.com/intl/zh-tw/tools/help/monkey.html)類似,既然我們需要透過測試案例的方式來確認我們的功能是否運行正常,就並非只是僅僅將操作透過自動化方式來實現,而是在每個測試案例的操作行為上,去斷言這些操作行為後,測試人員將預期的結果為何?這樣最後的測試報告所賦予的實質意義才會遠比僅僅將每個操作流程自動化來得大,我們也可以確實收斂和釐清每個測試環節失敗的原因為何。最後就讓我們來看看實際的運行結果,以下影片則記錄該測試案例的運行過程。