Wednesday, December 28, 2016

Deprecated methods

每次 android API 升級, 除左加入新野之後, 都會有 D 舊野會被清理.
萬一你用左 D 會被清走既舊野, 就要準備改 code 了.

Google 既做法, 係會先提醒你某 D 野就快死我, 今次比你過, 下次就唔包了.
如果唔想出事至急急腳搵解藥, 可以叫佢每次都 check 埋有邊 D 野就快死.

好簡單, 只要在 build.gradle 加入下面幾行就得了.
為左唔想影響咁大, 改 Module:app 自己既 build.gradle 好了, 將呢幾句加落最尾就得,

tasks.withType(JavaCompile) {
    options.compilerArgs << "-Xlint:deprecation"}

之後再  build APK, 就會出唔到了, 講埋邊度唔得, 原來係 SmsMessage.createFromPdu 出事:
E:\SourceTree\WhereAreYou\app\src\main\java\org\super169\findlocation\FindLocationActivity.java:189: warning: [deprecation] createFromPdu(byte[]) in SmsMessage has been deprecated                    SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[i]);
又要上網搵笨法解決了. (1) 最簡單既方法, 咪跟佢既 syntax 比埋個  format 佢就得  (API Level 23) String format = extras.getString("format"); SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[i], format);
世事總係無咁好既, 呢個 syntax 係 API Level 23 開始至用得, 即係要改  minSdkVersion 去到 23 或以上, 即係 Android 6.0 Marshmallow 之後既機至用到, 太過份了. 




(2) 用  Telephony 先攞哂 d msg 再慢慢睇  (API Level 19)



SmsMessage[] msgs = msgs = android.provider.Telephony.Sms.Intents.getMessagesFromIntent(intent);
for (int i = 0; i < pdus.length; i++) {
    SmsMessage message = msgs[i];
今次好 D, API Level 19 就有了 (即係 Android 4.4 KitKat 開始).  所以, 針對 SmsMessage.createFromPdu 可以有以下幾個方案:
  1. 唔鬼理佢, 反正家陣仲用得, 可以 keep 住 midSdkVersion 18
  2. 轉用 Telephony 去讀 message, 而 midSdkVersion 只係升上 19, 影響唔算大
  3. 用 SmsMessage.createFromPdu 既新 Syntax, 但 midSdkVersion 就要上 23 至得

Google Play Services

最後一項, 就係升級 Google Play Services.
本來以為只係改番個 version, 由 8.4.0 改成 10.0.1 就得, 點知出左 error.

(1) IAmHere

先由簡單既做起, IAmHere 無咁多野, 只係讀 location, 點解會出事?
如果直接改成 10.0.1, build 無問題, 出 APK 就會咁:

Error:Execution failed for task ':app:transformClassesWithDexForDebug'.
> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536
網上搵下, 大致係因為係 DexIndex 上限係預設左 65535, 當你個 program 大多野加入去, 就會爆.
之前懶慣左, 成套 Google Play Services 加哂入去, 而 Google 又越加越多功能, 咪爆左囉.
解決方法, 一係加大佢, 一係就用少 D.

其實, Google play service 可以拆開黎加, 佢既 Setup Guide 有資料講有幾多舊既.
唔好咁懶了, 用左邊個咪加邊個囉.
就以 IAmHere 為例, 用得幾多 service?  咪就係一個 location service  咁大把, 唔駛成套加哂.
所以, 只要改成咁就得了:


  • compile 'com.google.android.gms:play-services-location:10.0.1'


(2) WhereAreYou

同樣, WhereAreYou 都係用左 location service 咁大把, 當然又係改番指定 location 就夠.
不過, 世事就係無咁完美既, 轉成 com.google.android.gms:play-services-location:8.4.0 無問題,
但一改成 10.0.1 就出錯了.
注意, Build Project 係無 error 既, 但係出 APK 就死得.

首先, 係會無哂 maps 既 service.  呢樣少事, google 將 maps 由 location 入面分左出黎, 加番呢句就得:
  • compile 'com.google.android.gms:play-services-maps:10.0.1'

不過, 之後就真係唔得了, 在 FindLocationActivity.jav 入面用到既 getMap() 既 method 無左了.
網上睇家陣要改用 getMapAsync(), 但 async mode 既做法又唔同了.

首先, getMapAsync 係 Async call, 咁本來放佢後面既 code, 就要拆出黎, 放去一個 callback method 度.
而 getMapAsync 需要一個 implement 左 OnMapReadyCallback 既 class, 咁最簡單就係將原本個 activity class (即係 FindLocationActivity) 再 implement 埋佢.

public class FindLocationActivity extends FragmentActivity
implements ConnectionCallbacks,
OnConnectionFailedListener,
LocationListener,
OnMyLocationButtonClickListener,
OnMapReadyCallback { .....
之後, 當然係加番有關既 method 喇, 而 OnMapReadyCallback 只係需要一個 OnMapReady 既 method, 而佢就會收到一個 GoogleMap 既 parameter, 順理成章, 就係將原本做完 getMap() 既 code, 放入去 呢個 method 就得了.

@Override
public void onMapReady(GoogleMap map) {
    mMap = map;
    if (mMap != null) {
        mMap.setMyLocationEnabled(true);
        mMap.setOnMyLocationButtonClickListener(this);
        mMap.getUiSettings().setZoomControlsEnabled(false);
    }
}

改完呢度, 基本上可以行得了.


一點手尾.....

不過, 其實 Build APK 果陣, 出左句野:
Note: E:\SourceTree\WhereAreYou\app\src\main\java\org\super169\findlocation\FindLocationActivity.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
呢個就同 getMap 一樣, 今次仲可以用, 遲下就會無.
之後會拆解埋佢, 咁就可以玩得耐 D.



又有野就黎唔用得, 同 getMap 之前一樣, 今次仲可以用, 遲下就會無.
懶既可以唔理佢, 反正今水仲用得, 有時間就睇下發生乜事.

由於 default 係比你過既, 所以唔知邊度死, 跟佢講 Recompile with -Xlint:deprecation for details.
打開 build.gradle, 加呢段野落最尾就得:

tasks.withType(JavaCompile) {
    options.compilerArgs << "-Xlint:deprecation"}

之後再  build APK, 就會出唔到了, 講埋邊度唔得, 原來係 SmsMessage.createFromPdu 出事:

E:\SourceTree\WhereAreYou\app\src\main\java\org\super169\findlocation\FindLocationActivity.java:189: warning: [deprecation] createFromPdu(byte[]) in SmsMessage has been deprecated
                    SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[i]);
又要上網搵笨法解決了.

本來, 最簡單既方法, 咪跟佢既 syntax 比埋個  format 佢就得:
String format = extras.getString("format");
SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[i], format);
世事總係無咁好既, 呢個 syntax 係 API Level 23 開始至用得, 即係要改  minSdkVersion 去到 23 或以上, 即係 Android 6.0 Marshmallow 之後既機至用到, 太過份了.

另一個方法, 就係用  Telephony, 先攞哂 d msg 再慢慢睇.

SmsMessage[] msgs = msgs = android.provider.Telephony.Sms.Intents.getMessagesFromIntent(intent);
for (int i = 0; i < msgs.length; i++) {
    SmsMessage message = msgs[i];


今次好 D, API Level 19 就有了 (即係 4.4 KitKat 開始).

所以, 針對 SmsMessage.createFromPdu 可以有以下幾個方案:


  1. 唔鬼理佢, 反正家陣仲用得, 可以 keep 住 midSdkVersion 18
  2. 轉用 Telephony 去讀 message, 而 midSdkVersion 只係升上 19, 影響唔算大
  3. 用 SmsMessage.createFromPdu 既新 Syntax, 但 midSdkVersion 就要上 23 至得
最後, 我暫時用第 2 個方案, 費事下次就要改.  



啟用 Google Maps API

如果只係 upgrade Android Studio, 以下既野唔需要做, 因為應該已經做左.

不過, 如果裝左部新機, 又或開左個新 project  想用 Google API 就要了.


(1) 要搵出自己部機既 SHA1 key (唔係跟機的, 重裝 Android studio 都會變既)

"C:\Program Files\Java\jre1.8.0_111\bin\keytool.exe" -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android


記住 將上面  SHA1 果串野, 備用

(2) 打開 Google APIs Manager , 睇 D API Projects



(3) 去 Credentials 果頁, 搵番 FindLocation



(4) 條 key 應該已經加左入 AndroidManifest.xml 入面, 唔放心可以 check 下先.


(5) 如果想當新  project, 可以 create 條新 key 比佢, 不過今次唔需要.

(6) 如果係新機, 就要加番部機入去.  安全點, 就算 upgrade 完都  check check.
     Click 落 FindLocation 果度, 可以入去睇下有乜野機加左.
     對番上面 (1) 既 SHA1, 如果本身有就唔駛做野, 無就加番落去.


   
       


dependencies version

Android project 既 dependencies 既 version, 又係有點煩既野.
當然, 如果你唔升級, 其實一般唔改都無問題.
不過, 一如以往, 有新就玩新, 我都係貪新既人, 每次都會嘗試升級.

在 IAmHere 同 WhereAreU 入面, 基本上只有兩個 dependencies


  • compile 'com.android.support:appcompat-v7:25.1.0'
  • compile 'com.google.android.gms:play-services:10.0.1'


呢兩件野, 其實我都唔記得邊度用左, 而且, 最煩係唔多識 check 個 version 要點填.
之前好鐘意用 + 結尾, 即係大過呢個就得.
不過,  佢會話咁樣唔係咁好, 叫你最好指定一個.


咁點樣搵家陣最新邊個呢, 我都係網上睇, 估估下.


Support Library
  • compile 'com.android.support:appcompat-v7:<version>'
可以去呢度睇最新既 version: Recent Support Library Revisions


Google API Service:
  • compile 'com.google.android.gms:play-services:<version>'
可以去呢度睇最新既 version:  Google APIs for Android - Release Notes



世事往往無咁順利既, 當改完個 Google API Service 既 version, 再 compile 出 APK, 就會出事.

在 WhereAreU 中, Clean Project 都 build 到, 但係 Build APK 就會出 Error:

  • Error:(233, 21) error: cannot find symbol method getMap()
getMap 既 method, 已經 out 左, 網上教要改用 getMapAsyc(), 但係 getMapAsync 要比 OnMapReadyCallback.  我之前係用 sync mode 去寫既, 要睇下可唔可以直接將後尾既野放入 callback 以 async  mode 去做.  暫時都係唔改住.

其實, 除左 getMap() 之外, 仲有少系問題既, 安全起見, 都係攪到舊野 compile 到先.
暫時唔升級 Google API Service, 之後會嘗試改番既.
之前係用 8.4.0 既, 所以改番轉頭就算, 過左海就神仙.

  • compile 'com.google.android.gms:play-services:8.4.0'
記住, 每次改完 build.gradle, 都要按下右上既 "Sync Now" 等佢 sync 一野.

改完再 Clean Project, 會見到佢提你 getMap() 已經 out 左, 不過 8.4.0 仲比你用到, 頂住先.


Tuesday, December 20, 2016

又再 Android

來來回回, 呢樣寫下, 果陣又寫下, 最近又想寫番 Android App.
對上一次, 已經係 2 年前既事, 當時係用 Android Studio 1.5.1, 家陣都去到 2.2.3.0 了.

再玩番既原因, 係因為之前個  IAmHere 同 WhereAreYou 用左一段日子後, 發覺有好多地方可以改進.  而且, Android 又已經升左幾個版本, 希望花少少時間研究下, 幫自己個 Apps 升級.

無寫 2 年, 以我呢條老野, 仲鬼記得入面 D 野咩, 又要再學過.

首先當然係裝番 Android Stuio, 再試下當年既 Apps 家陣可唔可以直接 Compile 到.

之前轉上 1.5.1 都寫左兩篇野, 記低有乜野要改, 今次準備上 2.2, 都會做番類似既筆記.


不過, 今次可能會嘗試認真再睇下 Android 有乜新野合用, 可能唔再係改原本既 code, 而係重新寫一次.  Anyway, 都會先嘗試 migrate 左個舊 project 上 2.2. 先, 當係一個開始吧.

Google 既野都轉得好快, 有時想搵番 D 舊野都唔易, 今次先 mark 低 相關既下載, 第時唔駛搵.
(最好記低自己當初用邊個版本做既, 有乜事都容易即刻試番, 以免唔同版本有野變左影響結果.)

Google 下載都有樣好, so far 佢 D 舊野好似唔鏟既, 留番條 link 就得.

如果有舊版本, 我都唔知升級好定係重裝好.  升級既可能好多野唔駛攪, 但係  Android 會 keep 左好多無用既舊版本 sdk, 要自己去清理.  重裝就可能要自己 set 番 d 野.  各有各好, 自己諗喇.

由 1.5.1 轉 2.2.3 都算幾順暢, 首先因為 2.2.3 跟黎既  API  唔同哂, 更係要執番 D version.

(1) compileSdkVersion & buildToolsVersion

呢個可以參考 build.gradle 入面唔同既 version 點樣搵番家陣既 version 改番佢.


(2) dependencies

跟手就可以參考 dependencies version, 睇下升級埋 dependence 入面既  version


(3) application version

最後就更係一 D 自家既 version,

build.gradle 入面, defaultConfig 既 versionName 用黎顯示在畫面上, 方便自己 check.
今次升 2.2.3, versionName 亦由 1.1.0 升上 1.2.0.

注意, 唔好同 res\values\strings.xml 入面既 app_data_version 撈亂.  呢個係資料格式既 version, 係自己定義, 用黎分辨 data 既 format 的.  唔同既 app version, 都可以用番同一套 data format 的, 雖然有關既 logic 未寫, 家陣會 ignore 左佢, 但將來一定會 implement 番的.  所以唔好亂改, 呢次只係升級 android studio, 無改過資料格式, keep 番 1.1 就可以了.  

之後既更加簡單, 都係 default 比佢改就得, build 兩 build 就可以出到個 APK 了.


(4) Google Maps API

Compile 出到 APK 都唔代表一定得, WhereAreU 有可以無野睇的.
Why?  因為地圖係用左 Google Maps API 去顯示, 佢需要  Developer 部機登記左至得.
如果重新裝過 Android Studio, 仲 key 都會變既, 只要再登記番就的.
詳情可以參考 啟用 Google Maps API.

build.gradle 入面唔同既 version

每次再玩番 android program, 都會遇到 version 變左既問題.
先唔講話某 d API version 入面既功能唔同 (呢樣都好正常), 最煩係 build.gradle 入面既 version.

可能小弟唔係成日玩, 次次開番, 如果重裝左 Android studio, 直開舊 program, d version 多數唔 work.

最常出問題就係:

  • compileSdkVersion
  • buildToolsVersion

點解?  因為呢兩個係跟 SDK 既, 而無玩一排, 鏟左重裝, SDK 更新哂.
再開個舊 program, 原本設定既版本都唔會預裝, 自然無左.

解決方案, 唔係裝番舊既 SDK tools, 就係更新個 build.gradle.
人都係向前看好, 既然有新既 SDK, 除非唔夾, 否則都係改番個 build.gradle 用新喇.

有樣野比較煩, 次次都唔係好肯定, compileSdkVersion 同 buildToolsVersion 可以點搵.

首先, compileSdkVersion, 就係話係用邊個版本既 skd 去 compile, 注意, 呢個同你將來可以執行既版本又唔同既, 只係用黎 compile.  只要佢唔係早過你將來用黎執行既版本就可以.
比如你諗住部野要在 5.0 Lollipop 既機行, 你就唔可能用 4.4 KitKat 既 SDK 去 compile 喇.

另一樣要注意, SdkVersion 唔係 android 既 verison, 佢要既係對應既 API Level (唉...一時 version, 一時 level, google 真係亂).

要搵你可以用既 compileSdkVersion, 就要打開 Android SDK 了.

就咁睇, 未必可以直接見到可以用邊個 API Level, 要 check 右下 "Show Package Details" 至可以睇到.

呢度見到, Android SDK Platform 25 係 Installed 既, 即係 compileSdkVersion 可以用 25 了.

之後再搵埋 buildToolsVersion, 就要轉去睇 SDK Tools 果版了.

嘩...好多數字都係 Installed 既,  25.0.3 定係 25.2.4 呢?  兩個都唔係, 家陣要搵既係 buildToolsVersion, 更係睇 Build-Tools 喇...咦, 無 version 既...又要 "Show Package Details".

見到了, 有 23.0.1 又有 23.0.2....不過, 呢度同 API Level 既顯示唔同, 佢係舊既排頂的, 所以一定要拉落去睇下.

最新已經裝左 25.0.2 了.

既然有新就用新, 基於以上既圖, 結論係:

    compileSdkVersion 25
    buildToolsVersion "25.0.2"

至於 minSdkVersion 同 targetSdkVersion, 就要睇你想比乜野機用, 再根據番 Android 版本對應既 API Level 去 set 番就得了.