顯示具有 android 標籤的文章。 顯示所有文章
顯示具有 android 標籤的文章。 顯示所有文章

用 RxPermissions 處理 Android 6.0 Permission

Android 6(API level 23) 之後,開發者會被要求者處理 APP 權限問題(針對 Run),程式碼要撰寫相關邏輯:檢查有沒有權限、處理取得權限後的行為...等。官方範例碼大概就是畫面跳轉類似用 Listener Pattern 的方式來處理,另外還要處理當使用者選取"不要再跳出權限對話框"的特殊狀況。總覺得應該就是一個 yes 或 no 就該解決的事情,但每次寫出來都不一樣,各別提不同人寫出來的流程都是天差地遠了。
這次改用 RxPermissions 來重寫,RxPermissions 封裝邏輯後寫出來的程式碼順序很直覺:

  • 路徑1:要求權限 > 都有權限 > 執行需要權限的程式碼。
  • 路徑2:要求權限 > 有權限被拒絕會自動跳出對話框 > 還是有權限被拒絕(對話框沒勾選不再詢問) > 再重新要求權限 或 執行當沒有權限還是可以執行的程式碼。
  • 路徑3:要求權限 > 有權限被拒絕而且曾在對話框勾選不再訊問 > 提示有用訊息並引導使用者到設定頁面打開權限 > onActivityResult 處理設定頁面跳轉回來的事件再重新要求權限。
廢話不多說,上扣!

Error:Android Dex: [xxx] Failed to load dx.jar

前陣子把開發環境升到 Android 8.0 就是26這個版本,今天開舊的專案發現全部爛掉不能 build,命令列會吐以下 log:

Error:Android Dex: [xxx] Failed to load dx.jar

比對一下這些舊專案的特徵,都不是用 gradle 而都是用 Intellij 15 來建構,而建構的預設行為上網找是會去找最新版的 build-tools 來用(如下)。

By default, the Android SDK uses the most recent downloaded version of the Build Tools.
嘗試過在 project.properties 裡頭加上 sdk.buildtools 17.0.0,嘗試要指定比較舊版本的 build tool,但完全無用,看起來 Intellij 15 沒有用到專案下(非 gradle 專案)的 ant 相關檔案,就根據專案設定的 Java SDK + Android SDK 就自己用預設值做建構了。

解決方式如下:

方法一
利用 SDK Manager 將 Android SDK Build-Tools 移除26.x.x版本,改用25.0.3。有確認過 25.0.3 和 26.0.2 的這些項目:
  • build-tools/25.0.3/dx > 檔案有存在該路徑
  • build-tools/26.0.2/dx > 檔案有存在該路徑,且內容與25.0.3一模一樣
  • build-tools/25.0.3/lib/dx.jar > 檔案有存在該路徑
  • build-tools/26.0.2/lib/dx.jar > 檔案有存在該路徑,但檔案大小與26.0.2不一樣

方法二(官方作法)
若 Android 專案是走 gradle 架構,可以在 build.gradle 裡指定要該專案要使用的 Build-Tools 版本。


另外測試過以下排列組合:
  • Android SDK 17
    • Java SDK 1.7 + Build-Tools 25.0.3 > Error:Android Dex: [xxx] Exception in thread "main" java.lang.UnsupportedClassVersionError: com/android/dx/command/dexer/Main : Unsupported major.minor version 52.0
    • Java SDK 1.8 + Build-Tools 25.0.3 > 沒問題
    • Java SDK 1.7 + Build-Tools 26.0.2 > Error:Android Dex: [xxx] Failed to load dx.jar
    • Java SDK 1.8 + Build-Tools 26.0.2 > Error:Android Dex: [xxx] Failed to load dx.jar
  • Android SDK 26
    • Java SDK 1.7 + Build-Tools 25.0.3 > Error:Android Dex: [xxx] Exception in thread "main" java.lang.UnsupportedClassVersionError: com/android/dx/command/dexer/Main : Unsupported major.minor version 52.0
    • Java SDK 1.8 + Build-Tools 25.0.3 > 沒問題
    • Java SDK 1.7 + Build-Tools 26.0.2 > Error:Android Dex: [xxx] Failed to load dx.jar
    • Java SDK 1.8 + Build-Tools 26.0.2 > Error:Android Dex: [xxx] Failed to load dx.jar

用程式碼介紹 RxJava

前言

RxJava 也強迫自己用兩個月了,除了自己的專案外,工作的專案也拿來改寫原本很難看懂的邏輯。這篇建議就當作一篇引導文或是心得文看看,有興趣繼續研究的朋友,網路上已經有很多中/英文資源,不怕沒地方學只怕你不學。

安裝

要導入 RxJava 很簡單,gradle 下一行即可,沒用 gradle 也只要抓一個 rxjava.jar 檔案,再引入專案就可以了。
compile 'io.reactivex:rxjava:x.y.z'

不用綁定 JVM 版本,不限定是 Android 專案還是純 Java 專案,只要一個 jar 檔案就能使用 RxJava。

什麼是 RxJava

直接舉幾個例子,來說明什麼是 RxJava,又為什麼我要用 RxJava,看了程式碼應該比用文字描述來的有感覺。這些例子也是我在專案中反覆用到的程式碼來改寫的。

Android 中需要更新 GUI 的耗時工作

取代 AsyncTask 在 Android 上呼叫 耗時工作,呼叫前更新 GUI,呼叫完再更新 GUI。

對於耗時工作回傳的結果,在 RxJava 中還能做進一步處理,就像工廠的流水線,從頭到尾能看到做哪些處理。想想如果要用第一個耗時工作的結果,去呼叫第二的耗時工作,在 RxJava 就是單純再多一個站點(下面程式碼中的 map()),如果沒有 RxJava 這段程式碼要怎麼寫?寫出來的程式碼會不會很難看?
Observable.just("input")
    .doOnSubscribe(new Action0()
    {
        @Override
        public void call()
        {
            // 在觸發耗時工作前更新 GUI
            // 比如顯示進度條
        }
    })
    .subscribeOn(Schedulers.newThread()) // 設定耗時工作在新的 Thread 上執行
    .map(new Func1<String, String>()
    {
        @Override
        public String call(String input) 
        {
            // 執行耗時工作
            // 比如是一個 Web API 呼叫
            return new WebApi(input);
       }
    })
    .map(new Func1<String, User>()
    {
        @Override
        public User call(String input) 
        {
            // 處理耗時工作回傳的結果
            // 比如 Web API 回傳的 Json 字串轉成 POJO
            return new Gson().fromJson(input, User.class);
       }
    })
    .observeOn(AndroidSchedulers.mainThread()) // 設定非同步工作完成後回到 Android GUI Thread 上更新 GUI
    .subscribe(new Observer<User>() 
    {
        @Override
        public void onNext(User user) 
        {
            // 更新 GUI
            // 比如在 TextView 上顯示 User 的名稱
        }

        @Override
        public void onCompleted()
        {
           // 如果沒有發生例外
           // 比如關掉進度條
        }

        @Override
        public void onError(Throwable error)
        {
            // 如果發生例外
            // 比如關掉進度條後跳出警告視窗
        }
    }); 

移除會造成 Callback Hell 的程式風格

延續上個範例提到的,如果要用第一個耗時工作的結果(用帳號密碼呼叫 Web API 得到 token),去呼叫第二的耗時工作(用 token 呼叫得到使用者的訊息清單),最後印出訊息清單。

在 Java 中考慮到呼叫 Web API 是一個耗時工作,所以大部分設計的呼叫參數常會多帶一個 Handler(Observer Pattern),被呼叫端做完耗時工再後,再利用 Handler 讓呼叫端知道該做什麼,程式碼大致會長成這樣:
webApi.getToken("user", "password", new DefaultHandler()
{
    // 第一個 Web API 呼叫成功
    @Override
    public void onSuccess(String response)
    {
        webApi.getMessageList(response, new DefaultHandler()
        {   
            // 第二個 Web API 呼叫成功
            @Override
            public void onSuccess(List<String> response)
            {
                // 呼叫成功
                for (String message : response)
                {
                    // 印出每個訊息
                }
            }

            // 第二個 Web API 呼叫失敗
            @Override
            public void onFailure(Exception exception)
            {
                // 印出失敗訊息
            }
        });
    }

    // 第一個 Web API 呼叫失敗
    @Override
    public void onFailure(Exception exception)
    {
        // 印出失敗訊息
    }
});

有沒有看到程式碼一直往內凹,這個就是 Callback Hell,雖然程式碼還是可以動,但就是不夠簡單不夠整齊。如果今天採用 RxJava 來設計,RxJava 本來就是 Observer Pattern,前一個方法呼叫完成,才會進入下一個方法,程式碼大致會長成這樣:
Observable.just(webApi.getToken("user", "password"))
    .flatMap(new Func1<String, Observable<String>>()
    {
        // 第一個 Web API 呼叫成功
        @Override
        public Observable<String> call(String token)
        {
            try
            {
                // 第二個 Web API 呼叫成功
                return Observable.from(webApi.getMessageList(token));
            }
            catch (Exception e)
            {
                // 第二個 Web API 呼叫失敗
                // 將錯誤丟出來
                throw Exceptions.propagate(e);
            }
        }
    })
    .subscribe(new Observer<String>() 
    {
        @Override
        public void onNext(String message) 
        {
            // 印出每個訊息
        }

        @Override
        public void onCompleted()
        {
            // 如果沒有發生例外
        }

        @Override
        public void onError(Throwable error)
        {
            // 第一個 Web API 呼叫失敗
            // 第二個 Web API 呼叫失敗
        }
    });

當要串的 Web API 越多的時候,帶 Handler 產生的巢狀架構,會讓程式碼越來越複雜不容易看懂,而用 RxJava 寫法的程式碼只會變長。

執行固定次數的動作

有時想要執行某個動作10次,一般都是要寫個 for 迴圈,現在不用羨慕 Ruby/Python 有很簡單的寫法,現在我們也有了。
Observable.range(0, 10).subscribe(new Action1<Integer>() // 0, 1, 2, 3...9
{
    @Override
    public void call(Integer item)
    {
        // 執行動作
        System.out.println(string);
    }
});

幹,好像也沒有比 for 迴圈少打幾個字,這個就必須要你有學好 lamda 了。
Observable.range(0, 10).subscribe(item -> System.out.println(item)); // 0, 1, 2, 3...9

資料集合處理

寫後台寫邏輯常常會需要處理資料集合,今天如果我們有個字串集合,要做以下處理(依序):
  • 只取出有帶 "boy" 的字串
  • 取出字串中的數字
  • 將數字轉成英文(1變成 "one")
  • 只取最前面兩個符合條件的英文
廢話不多說,直接上程式碼最有感覺。
public class HelloRxJava
{
    public static void main(String[] args)
    {
        List<String> list = new ArrayList<String>(Arrays.asList("boy_4", "boy_1", "girl_1", "boy_2", "boy_3", "girl_2"));


        // 傳統寫法:
        int take = 2;
        int i = 1;
        for (String string : list)
        {
            if (isBoy(string))
            {
                if (take < i)
                {
                    return; // Jump!
                }

                System.out.println(numberToEnglishNumber(getNumber(string)));
                i++;
            }
        }


        // RxJava 沒有 lamda 的寫法:
        Observable.from(list)
            .filter(new Func1<String, Boolean>()
            {
                @Override
                public Boolean call(String string) 
                {
                    return isBoy(string);
                }
            })
            .map(new Func1<String, String>()
            {
                @Override
                public String call(String string) 
                {
                    return getNumber(string);
                }
            })
            .map(new Func1<String, String>()
            {
                @Override
                public String call(String string) 
                {
                    return numberToEnglishNumber(string);
                }
            })
            .take(2)
            .subscribe(new Action1<String>()
            {
                @Override
                public void call(String string) 
                {
                    System.out.println(string);
                }
            });


        // RxJava 有 lamda 的寫法:
        Observable.from(list)
            .filter(string -> isBoy(string) == true)
            .map(string -> getNumber(string))
            .map(string -> numberToEnglishNumber(string))
            .take(2)
            .subscribe(string -> System.out.println(string));
    }




    /**
     * 將阿拉伯數字轉為英文字
     */
    private static String numberToEnglishNumber(String string)
    {
        if (string.equals("1")) return "one";

        if (string.equals("2")) return "two";

        if (string.equals("3")) return "three";

        return "oops!";
    }

    /**
     * 取得字串裡的數字
     */
    private static String getNumber(String string)
    {
        return string.substring(string.indexOf("_") + 1);
    }

    /**
     * 字串是否包含 boy
     */
    private static boolean isBoy(String string)
    {
        return string.startsWith("boy");
    }
}

為求範例簡潔,一些邏輯都抽取成 private 方法。可以發現傳統寫法雖然不長,但有巢狀 if 的關係,需要花一點時間才能了解程式碼的意圖,而用 RxJava 的寫法,基本上根本就是把剛剛條列的動作依序執行,而用 lamda 的方式寫的程式碼更是很容易就能看出程式碼的意圖。

小結

本文說明了:
  • 無論是單純的 Java 環境,還是在 Android 上,只要一個 jar 就能開始用 RxJava。
  • RxJava 雖然讓程式碼變長了,但讓程式碼的意圖變得比較好理解。
  • 如果你會用 lamda,你的程式碼就不會那麼長。
  • RxJava 提供的多種運算子,讓我們很容易就能做出"取出最前面兩個元素"、"Retry 10次"之類的邏輯。
我有把我學習過程中寫的範例碼都放上 github,有興趣的人可以拉下來跑,基本上都是一些釐清觀念的 Code。另外推薦這篇 RxJava 文檔中文版,除了介紹 RxJava 的概念外,也針對各種運算子有很詳細的說明。

JBoss 的推播方案 - Unified Push Server(現在叫 AeroGearPush)

前言

2016年獨立開發者最慘痛的第一個消息,大概就是臉書併購的 Parse 要收起來不玩了,我手上的專案用到 Parse 的推播功能也中鏢了,想到要自己把 GCM 接起來真的是一個頭兩個大。

簡單說明一下 Parse 是一個 BaaS(Back end as Service) 服務,可以想成臉書幫你養了一台提供資料庫/會員註冊/排程工作/推播的雲端主機,對開發者來說省去了架設/部署的工作,能夠專注於開發。

Parse 預計明年一月要收掉,整個 Parse 也已經開源出來,如果你本身也熟悉 Node.js 的開發生態,基本上可以整包拿下來架站。我最終選擇是改用 JBoss 提供的推播方案,下文大概介紹一下架構、功能,詳細實作的程式碼因為會隨著改版而失效,這邊就不會寫上程式碼。

JBoss Unified Push 架構

JBoss 的推播方案一開始叫做 Unified Push,現在就整進 AeroGearPush 裡頭(算是其中一個子專案),整個程式碼也有開源出來,其實是一個跑在 JAVA EE Server 上的 war 檔。

因為我只有實作 Android Push 這一塊,以下就用 GCM 為例說明。Push Server 封裝了 GCM,如果沒有 Push Server,我們就必須自己去管理手機從 GCM 取得的註冊資訊。當有跨手機平台的推播需求的話,我們也只需與 Push Server 介接,Push Server 負責實作推播至不同手機平台的功能。

以下說明上圖每個元件的作用:

  • Push Server:收集所有手機傳回的註冊資訊。發送訊息給各平台的推播系統,再推播至手機上。
  • Application Backend Server:實作 APP 的後台邏輯,當有推播需求(ex:商品上架、有新事件)時透過 SDK 將訊息傳給 Push Server。
  • Push Networks:就是指 Android / iOS 原生的推播系統。
  • 手機:除了接收訊息外,最重要的將從原生推播系統取得註冊資訊傳給 Push Server。以 Android 來說,透過 SDK 將從 GCM 取得的 token 傳給 Push Server。


環境架設

除了把 Push Server 架起來外,上面可以看到手機 APP 和後台 Server 還會需要用到以下 SDK:
  • aerogear-android-push (用來跟 PushServer 註冊裝置)
  • unifiedpush-java-client (包裹 Web API 用來將要傳送的訊息交給 PushServer 推播)。
很多程式碼,名字變來變去,版號很多,這就代表有很多雷可以踩,建議先裝好 Push Server,裝好以後在 Web GUI 把推播相關的參數設定好,在 Web GUI 上有自動產生樣本程式碼的功能,這時候再來去抓最新 SDK,如果找不到類別或錯誤就降一版 SDK 試看看。我也是花了一點時間才找出來可以動的組合。

建議的步驟如下:

  1. 安裝 Push Server。
  2. 在 Push Server 上建立 Application。
  3. Application 下設定 variant,variant 主要用來介接 iOS/Android 推播系統,介接的資料欄位需要從 iOS/Android 推播系統後台得到。
  4. 此時,你 Push Server 上應該有一個 Application,一個以上的 variant。
  5. 利用 Push Server > Application > variant 產生回傳註冊資訊至 Push Server 的範例程式碼,整合至手機 APP 程式碼,看看與下載的 aerogear-android-push 相不相容,不相容就找別的版本。
    1. 開啟手機 APP,利用 Push Server 的 WEB GUI 觀察手機有無註冊成功。
    2. 利用 Push Server 的 WEB GUI 發送訊息,驗證 Push Server 可以將訊息推到手機上。
  6. 利用 Push Server > Application 產生推播訊息至 Push Server 的範例程式碼,整合 APP 後台程式碼,看看與下載的 unifiedpush-java-client 相不相容,不相容就找別的版本。
    1. APP 後台發訊息給 Push Server,驗證可以透過 Push Server 將訊息推到手機上。


其他

關於 Push Server 的註冊部分,看到的寫法是每次打開 APP 或是某個頁面就重新註冊,這部分觀察資料庫,除非重新安裝 APP 否則上傳的註冊資料(GCM token)都是一樣的。解除註冊部分,看到的寫法可能寫在使用者勾選的開關上(讓使用者決定要不要收到推播),或是離開某個頁面就解除註冊。以 GCM 來說,唯一知道這裝置已經解除 GCM 註冊的時機點是在推播的時候,然後如果是自己寫的 Push Server 就可以將這些裝置清掉,但 JBoss (我用的版本是1.0.0 beta 2)的做法是沒寫這塊邏輯,所以必須自己處理,我自己的做法是定時下 SQL 去清除一個月以上沒有向 Push Server 回傳註冊資訊的裝置。

最後還是想唸一下,紅帽的文件不是寫不好寫不夠多,是整理的很沒系統,這次找文件找 SDK 的時間大概比寫扣的時間還要多,只能說免費的最貴。

解譯 Android APK

前言

最近有個需求需要看一下別的 APK 是怎麼做的,原本以為有現成的工具應該蠻簡單的,沒想到命令列苦手跟一些不熟悉的技術問題還是把我弄的很慘,寫個筆記記錄一下。

透過本文,可以知道我用了哪些工具跟這些工具的功用,最後我把所有步驟寫成一個 Groovy Script ,我會簡單介紹用法,透過這個 Script 最終可以得到 APK 所有的資源檔案、java 檔案、或是一包 jar 檔案。

工具

透過我走過的流程順便帶出我使用的工具,APK 轉換的結果,對我來說重要的就是資源檔案(layout、語系...)和程式碼,我嘗試過的三個流程如下(最後當然是用最後一個流程):

APK -> 透過 Apktool 解出來 -> 可以取得資源檔案,但只有 Smali(VM 上的虛擬語言,語法介於 Java 和 組合語言之間) 可以看但要花很多時間看。

APK -> 透過 dex2jar 直接把 APK 轉成 jar -> 沒有資源檔案,當 APK 裡頭有不只一個 dex 檔案時它只會解第一個 dex 出來,代表原始碼會缺少。

APK -> 改副檔名為 zip 解壓縮 -> 可以取得資源檔案和 dex -> 透過 dex2jar 把所有 dex 檔轉成 jar -> 解壓縮所有 jar 檔會得到所有的 .class 檔案 -> 把所有 .class 組成一個單一 jar 檔就可以用 JD-GUI 來看原始碼,或是透過 JAD 將所有 .class 轉成 .java。

怎麼使用我的 Script 

問我為何不寫 linux script 就好,我是有寫一版 linux script,但條件判斷和檔案搜尋結果再利用在 linux script 下真的是很難拼出來,所以我讓 groovy 做他擅長的部分,但執行指令還是會用下指令的方式。為什麼不用 python,因為我主力寫 Java,Groovy 跟 Java 語法相容。

Script 放在 github,所有會用到的工具就直接包含在專案裡了,使用環境是在 MAC 上,當然要先裝好 groovy 環境,然後修改 script 中 APK 的指定路徑,執行完後所有結果都會放在桌面上的 output 資料夾。

資源檔可以去 output/apktool 下找到。最終完成的單一 jar 檔可以去 output/onejar 下找到,用 JD-GUI 直接開這個 jar 就能看到原始碼。所有轉出來的 .java 放在 output/jad 下,這樣就能用 IDE 或文字編輯器來看。

解開 APP 百萬下載次數的成就


2015-09-09 08.14.30

最近某支 APP 達到一百萬人次下載了,個人第一個想法是如果下載次數可以換成錢就好了,目前大概除了每個月廣告的零用錢,和履歷上可以多寫這一項成就以外,大概就沒啥屁用了。避免本篇走向流於無意義的自我感覺良好閃光文,寫一些心得給想寫 APP 賺錢的人分享。

先說本人的背景,主力 Java,寫過韌體(CP / Merge 也算的話),早期用 Swing 寫桌面 APP,SQL 會用但不到 DBA 等級,網頁幼幼班(CSS/HTML/JS 看得懂可以改從無到有就有點強人所難),後台比較熟 Java EE 相關(Tomcat/JBoss 7/Wildfly),雲端服務看到會接看看(Amazon/Google/Openshift/Parse/Memcache...反正需要什麼就接什麼),SA 技術也是幼幼班(Linux 會 ps 會 ls 真的要用還是要查鳥杯杯)。技術講完講其他,有結婚有小孩有一份正職,所以只能用六點下班後的時間搞這些阿沙布魯的東西,以前很愛畫圖,所以美工自己來,勉勉強強可用,然後前台到後台都有碰過,所以基本上整個 APP 開發所需要的知識都算有涵蓋到。

因為技術點上 Java 點的比較多,所以理所當然就是開發 Android APP,需要的成本除了時間以外(個人是覺得時間最貴啦),開發者帳號終身約臺幣800元(如果你沒被停帳號的話,後面會提),其他的花費不一定,但基本上我花過的大致有雲端服務的費用(例如:Amazon S3 儲存檔案),買素材的費用(例如:圖示),下廣告的費用(例如:臉書廣告)。

做 APP 不考慮到人力成本,一定是做遊戲要不就是搞出一個社群然後這群人會生內容出來(例如:臉書)。個人覺得寫遊戲的物理一定要很好,另外企劃和對遊戲系統的平衡都要很有料,真的不是買一本書,弄個飛機射子彈還是青蛙過街就有人會買單了。再來是社群的東西,一來要經營社群,二來後台未來也不是隨便一台 PC 搞定,三來該有的社群也都有了,人家為什麼要用你的 APP。考慮以上,果然還是跟 Sam Lu 大講的差不多,只有工具類 APP 的規模和後續維護的心力比較符合我能投入的時間。

至於 APP 的主題,我自己是開發前問自己三個問題,如果都是正向的,才會花時間下去弄,寫出來給各位參考一下:

  1. 使用者是誰?
  2. 需要每天用嗎?
  3. 一定得用手機嗎? 
基本上就是強迫練習自己找問題來用技術解決,而不是看到新技術或是想練技術才找題目。成效如何?如果一萬人次下載就算成功,做了10+個 APP 有五個 APP 達標,所以這三個問題基本上應該還算可以刪掉一些無用的 APP 點子吧!

如果真的有幸,APP 做出來了,也上架了,我自己推廣的方式大概就是到 PTT / Mobile 01 寫推薦文,如果有相關的論壇也會上去推推文。付費的話有玩過 Google AdWords / Facebook。不管免費或者是付費,我自己的經驗效果都不會是很好,PTT / Mobile 01 頂多就是當天會有些人下載,隔天就沒了。付費廣告則是瀏覽量一直都有,但實際安裝人數並沒有上來,按點擊收費看起來也是只要點到 Google Play 就算你錢了,實際上從 Google Play 點安裝的轉換率並不會是100%。個人碰過比較有效的方式,一個是部落客名人/媒體推薦,(但這種可遇也不可求),第二個是 APP 搜尋結果/排行在很前面,新上架一個月有進“最新熱門項目”前三名,那以後就輕鬆得多,搜尋結果至少要排進第二名,不過現在 Google 會管你描述裡是不是有塞垃圾敘述,一不小心又會被下架,等等會提到。

再來提到違規了,說到違規我應該可以說是專家,個人違規有發生過以下項目:
  1. 被人檢舉內容不符合普級(但明明三點不露,同類型 APP 也是普級)。
  2. APP 描述頁面有重複詞彙(我不是亂塞,就是寫個“關鍵字:xxx,yyy,zzz”這種等級)。
  3. 侵犯智慧財產權(當時一堆人都做進擊的巨人也都還活著)。
  4. APP 內容裡頭有品牌名稱被說意圖假冒是別人做的 APP。
不說心得,直接說說要注意的事項,一個帳號只要犯三次,整個帳號會被終止,連同帳號底下其他的 APP 都會被下架,所以最好的方法是一個產品一個帳號,免得被拖累。再來基本上申訴也沒屁用,建議犯規一次就可以開新帳號準備了。

最後,回答大家可能最關心的問題,到底有沒有辦法賺錢,看我還在上班就知道沒有辦法賺到大錢,個人手上 APP 所有的使用者大概是45萬人左右,然後都是工具類,使用者大概點開來看個10分鐘就了不起了(抱歉我 APP 沒塞 GA 所以沒有詳細數據不是我不提供),這樣子的前提下,一個月的零用錢大概是5000~9000新台幣的水準,給各位參考一下。

當然啦我沒有辦法全心全意寫出並且經營一個很大很棒的點子,不過最前面有介紹我的個人背景,看官可以比對一下,也許你單身你技術強你有人脈你有錢請人,那我相信一定可以賺比我多很多啦!最後大家點一下小弟全家合力經營的 APP 作品集一作品集二(Google Play 連結無毒),看看有沒有什麼剛好合用的 APP,下載或是推薦,小弟感恩不盡!

最後的最後,想當初讓一隻超爛手機(記得是台哥大的 A3)能超順瀏覽小海嚴選的美女圖,真的是超爽的。牛頭不對馬尾的舉例,其實我只是想說,下班以後寫程式這件事本身就是要有愛才做得到的,如果你夠幸運你會賺到大錢,如果不幸只有幾百人用,想想看有幾百人在用你寫的東西,不也是很爽的事情嗎!

Facebook 買廣告

推廣 APP 是件麻煩事,目前看到大家的做法(我們自己也是)貼 PTT 貼 Mobile 01 貼論壇,有關係就請部落客推薦一下,有錢就買 Google 廣告,這次我買的是 Facebook 廣告,不專業筆記一下,分享給看到的獨立開發者。

螢幕快照 2015-05-18 上午11.26.10

怕爆預算的話,基本上你可以設定每天費用上限,或是替整個廣告播放期間設一個費用上限。

費用的計算分成,別人有點擊廣告才算錢,或是按曝光次數來算錢,不過我建立廣告時行銷活動目標是選“增加應用程式安裝次數”,就只能選擇前者。

它後台報表可以直接看到 APP 安裝的次數,不過我的一直顯示為零,仔細去看文件,這部分應該是要後台去註冊我是這 APP 的擁有者,加上 APP 裡頭要包 Facebook 的 SDK,才能偵測到 APP 安裝的次數。

再來就看看成效了,講一下自己的心得了。

廣告我買了五天

  • 花費了我30.38刀(約942新台幣)
  • 曝光了14,278次
  • 臉書後台顯示點擊了369次(有點去 Google Play 看)
  • Google Play 後台顯示安裝了111次(真的有下載來安裝的)
所以大概就等於
  • 每天大約有22人下載(姑且先假設都是看到臉書廣告點的)
  • 一個人下載我需要花8.5新台幣
APP 有上到該分類最新熱門免費項目的第八名,也有上該分類的上升速度最快的榜單。就申請難易度較低,費用門檻也不高。

廣告播放能限定使用者組群/使用設備/使用網路,目前我是指定28歲~43歲,住在台灣,使用 Android 手機,使用 Wi-Fi,以上條件的情況下才播放廣告,這些篩選條件至少可以排除用 iOS 手機然後點擊(我要被收錢)然後發現是 Android APP 很囧。

比照之前洗版請親友推薦,或是上網自我推薦,雖然要出錢,但的確下載數據是有往上跑,而且費用跟下載是正相關,不至於是肉包子打狗。在初期階段想要衝榜,在沒人氣,又沒想用網路上所謂衝榜的服務(開發者應該常常收到類似的廣告信),這次的經驗是還蠻不錯的,如果有人有更便宜又更有效的廣告途徑,歡迎或來信留言討論。

Build Android apk 流程

最快產生 apk 的方式當然是用 IDE 匯出就好,但因為我要用客製化的 android.jar 來編譯,還要讓 jenkins Server 能自己 build apk,所以必須要了解 build apk 的流程。

在 Andoid 專案下看到 build.xml 那就八九不離十是用 ants 來做這件事,點進去看大概的思考邏輯就是:

  • 會 import Android SDK 的 build.xml,檔案位置會在 ${android_sdk}/tools/ant/build.xml
  • 可以利用 ant 命令列帶參數覆寫 property
  • 可以建立 ${你的專案}/ant.properties 來覆寫 property
  • 覆寫 Android SDK 的 build.xml 裏的 target
  • 新增 Android SDK 的 build.xml 裏的 target
到 ${android_sdk}/tools/ant/build.xml 最下面可以看到定義好的所有 target,很明顯要包 apk 需要執行 release 這個 target,執行成功可以看到以下 target 被執行的過程為:
  1. -set-mode-check:
  2. -set-release-mode:
  3. -release-obfuscation-check:
  4. -pre-build:
  5. -check-env:
  6. -setup:
  7. -build-setup:
  8. -code-gen:
  9. -pre-compile:
  10. -compile:
  11. -post-compile:
  12. -obfuscate:
  13. -dex:
  14. -crunch:
  15. -package-resources:
  16. -package:
  17. -post-package:
  18. -release-prompt-for-password:
  19. -release-nosign:
  20. -release-sign:
  21. -post-build:
  22. release
這些都搞定之後,就可以根據自己的需求來 修改 property 或是 覆寫 target,這裏才是地獄的開始,Good Luck!

PS:利用指令自動產生專案跟 ants 相關的檔案 > android update project -p ${你的專案}

參考:

Nexus 4 倒地不起

我的四兒子在 2014 過年的時候陣亡了,行動上網上到一半死當,然後重開機後就卡死在四個小球的開機畫面(原生的 Android 4.4.2),當初嘗試了:

  • 刷原廠 Android 4.4.2 > 卡在開機畫面
  • 刷原廠 Android 4.3 > 卡在開機畫面
  • 刷原廠 Android 4.2.2 > 開機有點久但可以開機
四兒子是活過來了,但手機訊號一直是零格,點進去看發現 baseband 版本是 unknown,IMEI 也變成 unknown 了,爬了國外跟對岸的文,發現也有人跟我有一樣狀況,但都沒有解答,唯二看到比較有可能回復的,一個是掏寶有把一個 box 可以重新刷板子,另外 XDA 有篇要自己改 ROM / Script 。難度太高我就放棄了。基本上這台就變成 Nexus Touch 了...

最近手機又掛了,爬文依舊沒有解答,但有問題的人變多了,但 Google 屌,完全沒有要處理的態度,又嘗試了以下的排列組合:
  • 刷原廠 Android 4.4.4 > 卡在開機畫面
  • 刷原廠 Android 4.4.3 > 卡在開機畫面
  • 刷原廠 Android 4.4.2 > 卡在開機畫面
  • 刷原廠 Android 4.3 > 卡在開機畫面
  • 刷原廠 Android 4.4.4 +Baseband 2.0.1700.48 > 卡在開機畫面
  • 刷原廠 Android 4.4.3 +Baseband 2.0.1700.48 > 卡在開機畫面
  • 刷原廠 Android 4.4.2 +Baseband 2.0.1700.48 > 卡在開機畫面
  • 刷原廠 Android 4.3 +Baseband 2.0.1700.48 > 卡在開機畫面
  • 刷 cyanogenmod 11 SNAPSHOT M8 > 卡在開機畫面
  • 刷 paranoidandroid 4.42 > 卡在開機畫面
  • 刷 paranoidandroid 4.2 > 卡在開機畫面

最後還是刷原廠 4.2.2 就又活了,但還是沒有訊號...

寫了文件如下,等到之後有新的原廠 ROM 再來試看看...

AllJoyn Peer Group Manager Module

關於 AllJoyn 請參考這篇

本篇簡單說明 AllJoyn 傳輸檔案的 Peer Group Manager Module,架構在原本 AllJoyn 之上,大大簡化了 AllJoyn 的概念和所需要寫的程式碼。

網路上中文的技術文件比較少,在此分享一下跟工作無關的部分。

AllJoyn File Transfer Module

關於 AllJoyn 請參考這篇

本篇簡單說明 AllJoyn 傳輸檔案的 File Transfer Module,架構在原本 AllJoyn 之上,目前支援三種不同的傳檔情境。

網路上中文的技術文件比較少,在此分享一下跟工作無關的部分。

AllJoyn 開發筆記

AllJoyn 是高通的開源專案,AllJoyn 會自己處理底層的連線問題(支援 WiFi、藍芽),讓不同作業系統的設備透過 AllJoyn 來連線傳輸資料。

網路上中文的技術文件比較少,在此分享一下跟工作無關的部分。

Google 的第四個兒子入手 – Nexus 4

寫這篇才發現,我自己真正自己在用的手機好像都沒有好好寫文。上一支手機是三星的 Note 1,一上市沒多久就買了,用了一年半下來,其實硬體效能還是能勝任我生活上的需求,會想換機的理由大概就是 1) 拍照對焦速度慢了,臨時要拍小孩很窘, 2) 最近有寫 Android 的 APP,就買支原生安卓手機,至少兩年內都還可以升到最新版,才不會落到明明發布了新的 API,卻只能用模擬器跑的慘況。

20130706_102904

Galaxy Nexus (第三代 Google 手機),雖然二手價很便宜(3500元左右),但螢幕又是我眼睛已經看膩的 AMOLED,所以就放棄了。然後 Nexus 4 台灣沒有上市,雖然已經賣了快半年了,但完全沒有看到便宜的二手機,最後透過代買以一萬一千多入手 8G 的白色,同時附贈一個 Bumper (原廠的保護套),入手的隔天就看到白色停產的新聞…

20130706_103212

包裝跟內容物都相關簡潔,說明書、充電器和傳輸線,然後就沒了,連耳機都沒有啊真的是很簡潔,其他細部照片就請轉看別的大大拍的照片吧。

之前使用 HTC、三星的手機,就大多是使用第三方的 APP。所以原生的安卓介面用起來沒有很痛苦。如果要說 HTC / 三星 的手機跟原生介面差在哪裡,HTC / 三星 就像是套餐,你一買來它都幫你配好了,看起來風格都是一致。原生介面就像是自助餐,每樣菜都是不同人煮的,你要一樣一樣挑自己喜歡的菜。如果沒有很依賴手機廠商提供的介面,比如說 HTC 各種好看的 Widget、Note 針對數位筆開發的繪圖 APP,現在 4.2 的原生介面加上 Google Play 上的 Widget,其實已經蠻好看也夠用了。

總結來說,這支的撞機率很低(台灣沒賣),外型也很特別(背後有閃亮亮的%$#@),如果你不想要手機只升級一次就斷頭,又懶得 ROOT 刷 ROM,這支有 Google 加持的手機值得入手一試。最後附上我最近很愛看的 Zealer 測評 Nexus 4 的影片,看完你會對這支手機的操作介面有更進一步的了解,當然悲劇的白平衡也一樣… T.. T

圖片的縮放參數 android:scaleType

本文內容同步更新到我之前寫的 Android UI 屬性筆記(持續更新)

scaleType 屬性,決定了圖片如何在 ImageView 中被顯示,下方用兩張圖說明。為了避免圖片跟 ImageView 的顯示範圍搞不清楚,所以我程式碼將

  • ImageView 的長寬設為 match_parent ,也就是填滿上層的 LinerLayout。
  • ImageView 的底色設成橘色。

所以下圖中的橘色也就是 ImageView 會塞滿整個手機顯示範圍。

ScaleType_02

ScaleType_01

觀察後的結論:

  • 填入圖片之前,先決定好 ImageView 的長寬。若 ImageView 長寬設為 wrap_content ,也就是 ImageView 跟圖片一樣大,以小圖就例子,不管你怎麼改 scaleType,圖片都不會有變化,因為 ImageView 已經跟圖片一樣大了。
  • 開發階段,可以設定每個元件的底色,方便 debug。
  • 在確定了 ImageView 的長寬後,若我有可能同時放比 ImageView 大或小的圖片,又希望能完整呈現原本的圖片,大小又都要看起來差不多大小(比如 Line 裡頭選擇聊天圖案的畫面),最好的選擇是使用 fitCenter。

Android UI 屬性筆記(持續更新)

不管是 Swing、HTML/CSS 還是 Android,UI 跟排版相關的屬性總是容易讓人搞混,只要不常寫就忘光光了,比如 layout_gravity、gravity、padding、layout_margin…等。

網路上資料很多,千言萬語還是自己整理一下,用圖說明解釋才比較容易理解。最下方附上原始碼,讓大家自己修改來玩看看。