把自己的心得跟做過的東西寫下來。重點是不要跟大家複製貼上的內容一樣,要自己做過思考過整理過的東西。 e-Mail:strength0179@gmail.com
2016年9月29日 星期四
2016年7月21日 星期四
【Android】APK Method Count
http://inloop.github.io/apk-method-count/
APK的Method數量有限制。
用這個網頁可以計算。
APK的Method數量有限制。
用這個網頁可以計算。
標籤:
小秘訣,
Android,
Android API
2016年5月10日 星期二
關於ActivityLifeCycleCallback
API文件在此。
這東西很有趣。
如果沒有它,「將共用資料擺在Application」這樣的認知或建議其實不太好用。
不過這是「我」。我認為應該要將Activity模組化後,統一套用「讀取或導入資料」的流程。
但是很多人只是需要「有個地方取用資料」。
將資料存在Application中後,用getApplication()取出即可......
很合理很有效率的想法!
但假設「不合用」或「不夠用」吧!
那我們就需要考慮一下LifeCycleCallback的用法。
這是個介面,裡面有完整的相對應了所有Activity LifeCycle的函數。
每個Activity的LifeCycle函數中,都會先在super()中執行所有被Application註冊的Callback,然後「才是」設計師複寫的物件內容。(LifeCycle不是建構子,其實設計師可以隨意調整super()執行的位置。)
(注意!是所有被註冊了的Callback都會被執行!)
(再注意!不是只有Application有能力註冊Callback,其他類別也有能力;相對的,也不是只有Application有能力反註冊Callback。因此一個Callback運作的開始和結束是很有彈性的!不會比Service差,但又比Service精準!)
所以假設......純假設.......
Callback中會在onResume啟動一個Thread,然後在onStop中將Thread關閉。(偶爾會發生從某一個Activity A返回前一個Activity時,Activity A的onDestroy會在下一個Activity的onResume之後才運作,這種時候可能會產生意外!如果允許Callback內產生多個Thread,那該關閉哪個Thread可能就是個要頭痛一下的問題了!...不是無解,也不複雜。)
這樣就可以達到讓背景任務可以順利跨Activity運行、而且又能對應當下的Activity產生變化。(例如視頻撥放的功能,可以將Activity視為「視頻線上存放點」的切換或記錄器,而下載功能和下載資料緩存則建立在Callback中,等於達到常駐的效果,而不會隨著每個Activity的啟動或關閉而有資源管理的問題。...這只是初步構想,還沒實驗過。)
再假設......純假設.......
每個Callback類別都有對應的Interface可以讓Activity實作,然後在Application中註冊多個Callback,只是每個Callback在執行對應的生命週期函數以前,都會檢查傳入的Activity是否有實作自己對應的Interface...如果沒有就不執行,那就可以大幅簡化Activity要處理的工作。
但對我來說,這個介面真正實用之處在於它讓一個跨「多Activity」甚至「多APP」的Thread可以精準的知道「何時有Activity可以取用」。
舉例...
就假設要設計個最簡單的APP內建有獨立邏輯和功能的計時器吧!
計時流程如果要精準可靠,那就不能因為Activity切換而損失效能或時間失準。(注意!它有獨特邏輯,所以不是直接把手機時間拿出來顯示就好。)所以不能採用「每個Activity獨立啟動Thread」的做法。
但在UI1中,時間顯示在右上角,在UI2中,顯示在正中央,而UI3卻又顯示在左下角...所以上面說要記得設定一個Interface讓每個Activity設定自己顯時間值的方法,如果有這樣做,就可以將這個Thread寫在一個Callback中,讓Application在產生Acitivity前就先運行這個Thread。
研究完用法思考了一下...
凡事要在背景常駐處理的都寫個Service...會不會對系統負擔太大呢!
畢竟Service是個功能完整的Context!但LifeCycleCallback只是個簡化介面的實作。
如此一來,Android預設的幾個主要功能類別其實就越來越模糊了!遵守Activity、Service、BroadCast...這類的類別去設計規劃APP的架構,可能反而會是個阻礙。
但同樣的理由...當一隻APP背景存在數十個額外設定的Callback時,效能是不是一樣會變差?
所以設計一項服務真的要把各個功能切割成多個APP(其中有一個是所謂的Main APP)來完成,這樣每個APP前景背景都會變得很單純穩定!
這東西很有趣。
如果沒有它,「將共用資料擺在Application」這樣的認知或建議其實不太好用。
不過這是「我」。我認為應該要將Activity模組化後,統一套用「讀取或導入資料」的流程。
但是很多人只是需要「有個地方取用資料」。
將資料存在Application中後,用getApplication()取出即可......
很合理很有效率的想法!
但假設「不合用」或「不夠用」吧!
那我們就需要考慮一下LifeCycleCallback的用法。
這是個介面,裡面有完整的相對應了所有Activity LifeCycle的函數。
每個Activity的LifeCycle函數中,都會先在super()中執行所有被Application註冊的Callback,然後「才是」設計師複寫的物件內容。(LifeCycle不是建構子,其實設計師可以隨意調整super()執行的位置。)
(注意!是所有被註冊了的Callback都會被執行!)
(再注意!不是只有Application有能力註冊Callback,其他類別也有能力;相對的,也不是只有Application有能力反註冊Callback。因此一個Callback運作的開始和結束是很有彈性的!不會比Service差,但又比Service精準!)
所以假設......純假設.......
Callback中會在onResume啟動一個Thread,然後在onStop中將Thread關閉。(偶爾會發生從某一個Activity A返回前一個Activity時,Activity A的onDestroy會在下一個Activity的onResume之後才運作,這種時候可能會產生意外!如果允許Callback內產生多個Thread,那該關閉哪個Thread可能就是個要頭痛一下的問題了!...不是無解,也不複雜。)
這樣就可以達到讓背景任務可以順利跨Activity運行、而且又能對應當下的Activity產生變化。(例如視頻撥放的功能,可以將Activity視為「視頻線上存放點」的切換或記錄器,而下載功能和下載資料緩存則建立在Callback中,等於達到常駐的效果,而不會隨著每個Activity的啟動或關閉而有資源管理的問題。...這只是初步構想,還沒實驗過。)
再假設......純假設.......
每個Callback類別都有對應的Interface可以讓Activity實作,然後在Application中註冊多個Callback,只是每個Callback在執行對應的生命週期函數以前,都會檢查傳入的Activity是否有實作自己對應的Interface...如果沒有就不執行,那就可以大幅簡化Activity要處理的工作。
但對我來說,這個介面真正實用之處在於它讓一個跨「多Activity」甚至「多APP」的Thread可以精準的知道「何時有Activity可以取用」。
舉例...
就假設要設計個最簡單的APP內建有獨立邏輯和功能的計時器吧!
計時流程如果要精準可靠,那就不能因為Activity切換而損失效能或時間失準。(注意!它有獨特邏輯,所以不是直接把手機時間拿出來顯示就好。)所以不能採用「每個Activity獨立啟動Thread」的做法。
但在UI1中,時間顯示在右上角,在UI2中,顯示在正中央,而UI3卻又顯示在左下角...所以上面說要記得設定一個Interface讓每個Activity設定自己顯時間值的方法,如果有這樣做,就可以將這個Thread寫在一個Callback中,讓Application在產生Acitivity前就先運行這個Thread。
研究完用法思考了一下...
凡事要在背景常駐處理的都寫個Service...會不會對系統負擔太大呢!
畢竟Service是個功能完整的Context!但LifeCycleCallback只是個簡化介面的實作。
如此一來,Android預設的幾個主要功能類別其實就越來越模糊了!遵守Activity、Service、BroadCast...這類的類別去設計規劃APP的架構,可能反而會是個阻礙。
但同樣的理由...當一隻APP背景存在數十個額外設定的Callback時,效能是不是一樣會變差?
所以設計一項服務真的要把各個功能切割成多個APP(其中有一個是所謂的Main APP)來完成,這樣每個APP前景背景都會變得很單純穩定!
標籤:
小秘訣,
Android,
Android API,
Thread
2016年5月9日 星期一
使用byte矩陣傳輸資料到NFC Tag
這是在Safari的官網上找到關於NFC資料的技術文件。主要是讓使用者可以在Web中經由Safari瀏覽器啟動Android的NFC功能後,再用byte矩陣自組要傳輸到Tag、或Tag Reader中的資料。
但奮戰了整整一天......最後是徒勞無功。在這裡寫下我的心得。
用Android的NdefRecord中的public NdefRecord (short tnf, byte[] type, byte[] id, byte[] payload)函數,雖然很快有基本的Beaming功能,但送出去的訊息ID遲遲都是0...而且是一個0,而不是一串0!(一個長度只有1的byte[]。)
用Android的NdefRecord中的public NdefRecord (short tnf, byte[] type, byte[] id, byte[] payload)函數,雖然很快有基本的Beaming功能,但送出去的訊息ID遲遲都是0...而且是一個0,而不是一串0!(一個長度只有1的byte[]。)
所以就異想天開用public NdefMessage (byte[] data)的方式來組看看,或許ID顯示為0是NdefRecord物件的問題。
NFC的每一筆資料,稱為Message,可以由多組Record組成。
每一組Record又由RecordHeader和RecordPayLoad組成。
每個RecordPayLoad資料長度上限為2的32次方。除此以外沒有限制。(但似乎也不能為0。)
「文件中」說Header由6~9個程度不等的Byte矩陣組成,為何會有6和9的差異?因為其中有所謂ShortRecord和Non-ShortRecord的差異。如果是ShortRecord,則為6,反之則為9。
(為何要強調「文件中」...等一下解釋。先聲明這是文件中非常不精準的地方。)
RecordHeader中的第一個Byte稱為TNF+Flag(Message Flags),我帶入的是217。
因為一個Byte由八個bit組成,在這個欄位中,每個bit都有自己獨特的意義。而我用的是11011001。每個數字代表著一個bite,所以內容只有0或一,換算成十進位後數值是217。
第四個bit正好決定了這筆Record為ShortRecord或Non-ShortRecord,而我帶入的是1,也就表示「True」,所以PayLoadLength的部分只有一個Byte。要等一下計算玩PlayLoad長度後決定數值。
但如果TNF+Flag(Message Flags)是11001001,就會佔去四個Byte,因為第四個數值是0,也就表示這筆資料不是ShortRecord。(Record的長度是四個Byte或一個Byte的差異。)
開頭是{1,1},因為我的Message中只有一組Record,所以都是Begin兼End,如果是Begin,就是10,如果是End就是01。(假設共有三組Record,則第一組10,第二組00,第三組01。)
第三個0/1是Chunk...還是搞不懂這是什麼東西。
第五個0/1是ID Length,如果0,就表示沒有ID,自然也不需要設定ID Length,RecordHead就少了兩個欄位-----設ID Length的欄位和ID本身。
所以上面要強調「文件中」,因為如果ShortRecord為false,而ID Length又為True,則Header的長度上限絕對不只9個Byte。
第六到第八個0/1是TypeNameFormat,是一組的。三個bit可以表示0~7,而Type剛好有八種組合,Empty/Well-Known/URI.......一般都是用WellKnown和URI。(我帶入其他的數值都失敗。)
到此為止,資料很成功送出去,Read很成功的讀取到了我用這方式所寫入的資料。
但Header除了第一個Byte為TNF+Flag(Message Flags)以外,接著還有Type Length(一個Byte),PayLoadLength,(一或四個Byte,視資料是否為ShortRecord而定,)還有IDLength(一個Byte)。
1.TNF+Flag。(長度1)
2.TypeLength。(長度1)
3.PayLoadLength。(長度1)
4.IDLength。(長度1)
這六個Byte之後是所謂的長度浮動的階段,第一個是PayLoadType,它是一個字串轉換成Byte矩陣形式後插入,長度必須要精準計算後寫入TypeLength欄位中,(所以長度大小自然不能超過255。)但PayLoadType的命名邏輯文件中的解釋很模糊,但始終是那八大類,這只是個用來顯示的字串罷了!
接著就是所謂的ID。不管是數值或字串,它一樣也是轉換成Byte矩陣後計算長度插入,長度要寫在IDLength欄位中。
但Header除了第一個Byte為TNF+Flag(Message Flags)以外,接著還有Type Length(一個Byte),PayLoadLength,(一或四個Byte,視資料是否為ShortRecord而定,)還有IDLength(一個Byte)。
1.TNF+Flag。(長度1)
2.TypeLength。(長度1)
3.PayLoadLength。(長度1)
4.IDLength。(長度1)
這六個Byte之後是所謂的長度浮動的階段,第一個是PayLoadType,它是一個字串轉換成Byte矩陣形式後插入,長度必須要精準計算後寫入TypeLength欄位中,(所以長度大小自然不能超過255。)但PayLoadType的命名邏輯文件中的解釋很模糊,但始終是那八大類,這只是個用來顯示的字串罷了!
接著就是所謂的ID。不管是數值或字串,它一樣也是轉換成Byte矩陣後計算長度插入,長度要寫在IDLength欄位中。
標籤:
Android,
Android API,
NFC
Activity無法接觸的非主線呈Thread可主動更新Activity內容
會特地寫這篇心得,是因為Handler Leaking一直困擾我。
在Activity可直接控制的範圍內觸發的Thread,可以用Handler.obj的方式來解決。
但如果是Activity完全接觸不到的Thread內,甚至是「第三方APP」內的Thread,這時要怎麼將Thread工作的結果回傳到Activity中?
在Guide文件「Display a location address」中曾經短暫提到的一個物件「ResultReciever」就是被設計來解決這個問題的工具。
問題是按照Google文件一慣的原則,它們完全不認為這種概念需要獨立成一個章節來介紹,仔細查了一下...確實只有這篇Guide文件有提到過這個物件,剩下的就是要進入個別API說明才會發現了。
這東西其實很簡單...
ResultReciever物件是個Parcelable介面的實作,將它當作參數傳入Intent,再經由Intent傳入獨立Thread或IntentService中,就可以在Thread結束時,(似乎是有能力偵測這個線程結束被關閉,)執行使用者實作的onRecieverResult(int code, Bundle data)中的內容。
(若是Activity可以直接控制影響的範圍,大可以直接啟動,不需要經由Intent。談到Intent,自然就是跨Activity,而且兩個Activity之間無法互通互相影響的情況。)
但是我原始的想法是在ActivityA中設置一個Intent Reciever,(通常是BroadCastReciever,)然後讓Activity接觸不到的Thread將工作結果也用Intent回傳。
如果有這個機制,這個方法就顯得很笨重多餘。
在Activity可直接控制的範圍內觸發的Thread,可以用Handler.obj的方式來解決。
但如果是Activity完全接觸不到的Thread內,甚至是「第三方APP」內的Thread,這時要怎麼將Thread工作的結果回傳到Activity中?
在Guide文件「Display a location address」中曾經短暫提到的一個物件「ResultReciever」就是被設計來解決這個問題的工具。
問題是按照Google文件一慣的原則,它們完全不認為這種概念需要獨立成一個章節來介紹,仔細查了一下...確實只有這篇Guide文件有提到過這個物件,剩下的就是要進入個別API說明才會發現了。
這東西其實很簡單...
ResultReciever物件是個Parcelable介面的實作,將它當作參數傳入Intent,再經由Intent傳入獨立Thread或IntentService中,就可以在Thread結束時,(似乎是有能力偵測這個線程結束被關閉,)執行使用者實作的onRecieverResult(int code, Bundle data)中的內容。
(若是Activity可以直接控制影響的範圍,大可以直接啟動,不需要經由Intent。談到Intent,自然就是跨Activity,而且兩個Activity之間無法互通互相影響的情況。)
但是我原始的想法是在ActivityA中設置一個Intent Reciever,(通常是BroadCastReciever,)然後讓Activity接觸不到的Thread將工作結果也用Intent回傳。
如果有這個機制,這個方法就顯得很笨重多餘。
標籤:
Android,
Android API,
Thread
2016年4月28日 星期四
【Android】開啟第三方APP
讓APP的內容或服務可以被其他APP開啟跟呼叫,在Android上是很重要的一項功能,──雖然沒什麼人真的在做。
因為技術上它有很多Google進階設計時模糊曖昧的地方!API或文件都沒有精準說明,也沒有範例可以參考。
所以下面的筆記只是個人使用心得,離參考文件還有很長的距離...
在AndroidManifest檔中加入的Activity設定值標籤內,可以再加入intent-filter的標籤。
預備要被第三方APP呼叫的Activity中,必須要在intent-filter標籤內加入額外的參數。(會這樣說是因為基本用法中,除了靠點擊啟動的Activity需要加入main和launcher參數以外,其餘的Activity並不需要加入額外的參數,甚至連intent-filter都可以不需要。)
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
SEND跟VIEW似乎只要擇一加入即可,差別只在於Activity中如果有UI要顯示,「使用」這個Activity的Intent中若是傳入SEND,則這個UI不會被顯示。
其實呼叫第三方APP的Activity方法很多,但關鍵應該在於addFlag的動作!
(還沒機會使用到其他型態的Flag,如果有機會用到,會補上結果。)
另外,ComponentName的實體建構子中要傳入兩個參數,一個是第三方APP project名稱,一個則是Activity的完整專案路徑與名稱。
(會強調完整專案路徑與名稱,是因為邏輯上來說,Activity可以分散在多個子project路徑內,例如tw.android.demo2下可以有tw.android.demo2.test,但將Activity放在裏頭,不知道為何AndroidManifest檔內的設定會失敗...一直沒有機會來回頭釐清這一段的正確使用方式和原則。──好像有點本末倒置,不去先釐清更基本的東西,卻在研究比較進階的使用方法。)
另一個奇妙的問題是...當Activity被啟動後,啟動的目的為何?
絕對不是僅僅為了看個UI介面。(有時候會有選單來選擇資料回傳,這時候當然就是要操作UI。但這裡不是指這種情況。)
例如要使用不對外公開的API來跟後台交換資料,這時候就需要建立Thread來完成網路傳輸工作。
奇妙的是...
這種讓第三方啟動的Activity竟然無法在基本生命週期階段中啟動獨立Thread。(寫了個Thread程式,卻無法被啟動,但也沒看到錯誤訊息產生。)
所以這時候就需要使用Service,特別推薦IntentService。(前者可能要等任務完成、關閉Activity後才會跟著關閉,但後者會先在任務完成後關閉自己、同時執行關閉Activity的工作。)
........
怎麼使用IntentService?
別鬧了!去別的地方找資料。
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//!!!關鍵在這個Flag
intent.addCategory(Intent.CATEGORY_DEFAULT);
ComponentName cn = new ComponentName("tw.android.demo2", "tw.android.demo2.MainActivity2");
intent.setComponent(cn);
startActivity(intent);
其實呼叫第三方APP的Activity方法很多,但關鍵應該在於addFlag的動作!
(還沒機會使用到其他型態的Flag,如果有機會用到,會補上結果。)
另外,ComponentName的實體建構子中要傳入兩個參數,一個是第三方APP project名稱,一個則是Activity的完整專案路徑與名稱。
(會強調完整專案路徑與名稱,是因為邏輯上來說,Activity可以分散在多個子project路徑內,例如tw.android.demo2下可以有tw.android.demo2.test,但將Activity放在裏頭,不知道為何AndroidManifest檔內的設定會失敗...一直沒有機會來回頭釐清這一段的正確使用方式和原則。──好像有點本末倒置,不去先釐清更基本的東西,卻在研究比較進階的使用方法。)
另一個奇妙的問題是...當Activity被啟動後,啟動的目的為何?
絕對不是僅僅為了看個UI介面。(有時候會有選單來選擇資料回傳,這時候當然就是要操作UI。但這裡不是指這種情況。)
例如要使用不對外公開的API來跟後台交換資料,這時候就需要建立Thread來完成網路傳輸工作。
奇妙的是...
這種讓第三方啟動的Activity竟然無法在基本生命週期階段中啟動獨立Thread。(寫了個Thread程式,卻無法被啟動,但也沒看到錯誤訊息產生。)
所以這時候就需要使用Service,特別推薦IntentService。(前者可能要等任務完成、關閉Activity後才會跟著關閉,但後者會先在任務完成後關閉自己、同時執行關閉Activity的工作。)
........
怎麼使用IntentService?
別鬧了!去別的地方找資料。
標籤:
Android,
Android API,
Thread
2016年2月22日 星期一
在Android上用座標查詢地址原來這麼簡單!
無意間在StackOverFlow上逛到這篇文章...才知道原來這東西那麼好用!
簡單說:Android預設的物件GeoCoder(點擊可以轉到API)其實就可以幫設計師快速的把這個動作完成!
詭異的是我遲遲沒有找到相關的標準範例!結果原來是因為Google Guide教程中,關於「如何取得GPS座標」的部分已經轉換成使用GoogleApiClient,但後續取得地址的部分...雖然GoogleApiClient也有相關功能,可是在Google Guide中卻還是使用GoeCoder!
(只能說:習慣就好...)
這個物件設計讓我看了要說「好用」的原因...
第一,它直接使用座標,而不是相關系列的物件!
第二,承接第一點,這表示它不需要申請金鑰!在註冊功能上會省去很多手續。(但其實最新的Google Place API For Android在註冊上也很簡單!並沒有多複雜!)
第三,JavaScript的地址查詢API回傳的JSON資料結構非常複雜!
第四,跟Google Place API For Android相比,它可以完全適應自己設計的Thread。因為Google Place API需要使用CallBack,而且如果只是單純的地址查詢,Google Place API並不「精準」,會獲得很多無關的資訊。
補充說明:在Google Place API For Android中獲得PlaceID的方法。
其實就是額外使用JavaScriptAPI去獲得JSON資料而已!
目前這段功能顯然還未有完整物件化。
簡單說:Android預設的物件GeoCoder(點擊可以轉到API)其實就可以幫設計師快速的把這個動作完成!
詭異的是我遲遲沒有找到相關的標準範例!結果原來是因為Google Guide教程中,關於「如何取得GPS座標」的部分已經轉換成使用GoogleApiClient,但後續取得地址的部分...雖然GoogleApiClient也有相關功能,可是在Google Guide中卻還是使用GoeCoder!
(只能說:習慣就好...)
這個物件設計讓我看了要說「好用」的原因...
第一,它直接使用座標,而不是相關系列的物件!
List<Address> addresses = null; try { addresses = geocoder.getFromLocation( location.getLatitude(), location.getLongitude(), 1); } catch (IOException ioException) { // Catch network or other I/O problems. } catch (IllegalArgumentException illegalArgumentException) { } }(其他時候,Google Guide的範例程式碼很繁雜!但這篇的卻是相較之下精簡,而我上面貼的這段是更精簡。因為這個功能並不需要多做無謂的說明...)
第二,承接第一點,這表示它不需要申請金鑰!在註冊功能上會省去很多手續。(但其實最新的Google Place API For Android在註冊上也很簡單!並沒有多複雜!)
第三,JavaScript的地址查詢API回傳的JSON資料結構非常複雜!
第四,跟Google Place API For Android相比,它可以完全適應自己設計的Thread。因為Google Place API需要使用CallBack,而且如果只是單純的地址查詢,Google Place API並不「精準」,會獲得很多無關的資訊。
補充說明:在Google Place API For Android中獲得PlaceID的方法。
其實就是額外使用JavaScriptAPI去獲得JSON資料而已!
目前這段功能顯然還未有完整物件化。
訂閱:
文章 (Atom)