7 Jul 2010, 12:17am
Android 電腦
by lachesis

leave a comment

[筆記]讓Android上的browser用你寫的app開啟連結

真是落落長的標題,不過因為沒有要仔細討論 intent filter 還是不要用什麼『極祕!Android的 intent filter萬萬強』這類聳動的 title 比較好,免得點進來一看大失所望。
當網站上有 market://xxx.ooo 這樣的連結出現,用 Android 瀏覽器點下去,就會自動打開 market下載應用程式,相當便利。如果連結是 tel:012345678 就會叫出撥號畫面,而且號碼已經帶好了,Google Map 則是 Geo:經緯度 這種形式。
我們自己開發的 Android 程式,要怎麼支援這種功能?比如說我的網站上提供這樣的連結

tarutaru://dance

希望點下去就會帶出我們的app,出現塔魯跳舞動畫..

方法:
1. 請打開 AndroidManifest.xml
2. 找到塔魯跳舞的Activity
3. 請參考下面的寫法









這樣不管你是 tarutaru:dance 還是 tarutaru://dance 還是 tarutaru:///dance 都會叫出目標activity
如果 data 這行寫成:


這樣就只有 tarutaru://mysite/xxx 才會對應到你的跳舞Activity. 也可以利用這樣來把連結導到不同Activity。
比方說 tarutaru://dance/xxx, tarutaru://panic/ooo 就可以分別對應到不同兩個Activities。
其他更多細節就請自行參考官方文件啦age ヽ( ̄∀ ̄)ノ

9 Mar 2010, 11:46am
Android
by lachesis

3 comments

[筆記]讓Android emulator連intranet

先說明,這算是某種特殊情況,一般Android開發者不見得會遇到同樣情形。總之在公司內部使用Android emu時,打開browser可以使用內部IP連上內部網站,但如果是使用domain name就會失敗,說是無法解析DN。試過使用官方說明的emulator下 -dns-server參數,但直接就在開模擬器時收到無法解析的錯誤了,當然browser一樣不認得內部網站的名稱。強者小Y同事建議改 hosts 檔看看,不過會得到 "failed to copy 'hosts' to '/system/etc/hosts': Read-only file system" 這樣的訊息。找到一篇blog文章提供的解法:Configure hosts File in Android。一開始嘗試是失敗的,後來小Y大人又給了些建議之後成功了。事實上,直接參考該文的做法對 Android 1.5, 1.6 的模擬器都是有效的,但偏偏我剛好正在用來測的是2.1。直接把作法寫在下面:

[通用部分]
如果開了一個以上的模擬器的話請自行加adb參數
0a. 先adb pull /system/etc/hosts hosts抓回local來修改。
0b. 在原本的 127.0.0.1 localhost 加上新的行,寫入你需要的對應如 192.168.1.15 mytestsite,存檔。
1. adb remount (才不會發生Read-only file system錯誤)
2. adb push hosts /system/etc/hosts

[2.0.1, 2.1 only]
這兩個版本會在這邊丟出錯誤訊息,分別為
failed to copy 'hosts' to '/system/etc/hosts': No space left on device
failed to copy 'hosts' to '/system/etc/hosts': Out of memory
解決方法是,開啟模擬器時不要直接從 AVD Manager介面開,請下指令:
$ emulator -avd youravdname -partition-size 128
接著再對這個模擬器使用上面的 hosts修改大法就不會出現錯誤了。

partition size的單位是MB,預設似乎是64。從adb shell 用 df 指令觀察的結果,/system 和 /data 都會被這個設定成這邊指定的值。而1.5, 1.6 的 image 因為剛好沒超過 64MB,還剩下一點空間,所以 hosts 還寫得進去,而 2.x 的 image 有 七萬多K,mount之後 /system 這個 partition 看起來就是全滿,剩下空間 0K。因此 hosts變肥後無法寫入,真的是因為partition爆了。事實上正常的手機確實也不需要留空間給user hack…模擬器還讓我們調參數已經很偷笑了,可惜AVD Manager 的 Start 沒地方可以設定 partition size。若有此需求的話只好麻煩一點手動下指定開emulator囉。

24 Feb 2010, 2:50pm
Android
by lachesis

leave a comment

[筆記]Android emulator常用指令與技巧

1. 切換 Layout為 Landscape or Protrait: Ctrl + F11 or Ctrl + F12

2. 模擬網路 ON/OFF: F8

3. 模擬有電話打進來的情形: 開兩個模擬器即可互打,電話號碼就是模擬器上的 5554, 5556 etc。

4. 把檔案放到 emu 的 sdcard 或系統目錄
adb push my_song.mp3 /sdcard/my_song.mp3

5. 從 emu 把檔案 copy出來
adb pull /data/data/com.example.android.notepad/databases/note_pad.db note_pad.db

6. 安裝 apk 到 emu 上
adb install c:/android-apk/myapp.apk

7. 進入 emu 的 shell ,可執行 ls, rm 等動作,有root權
adb shell

8. 進入sqlite3 shell,先進入 shell之後
# sqlite3 /data/data/com.example.android.notepad/databases/note_pad.db

以上指令如果在開啟了超過一個模擬器的狀態下,必須指定要針對哪個模擬器
adb -s emulator-5554 shell
列出所有模擬器代號:
adb devices

附註: SQLite資料檔可以使用 push pull 拉到 emu外來使用輔助工具 SQLite Database Browser 來瀏覽或管理,對開發很有幫助。

11 Dec 2009, 1:45pm
Android
by lachesis

leave a comment

[筆記] Android 使用httpclient對self-signed certificate網站進行SSL連線

Android SDK 在進行 https 連線時,對於自簽署的憑證是會拒絕連線的,會得到 Not trusted server certificate 的例外。如果使用 HttpsURLConnection 來連線,網路上可以找到一些破解方法,在此不多談。使用 apache httpclient 其實執行效率比較差一點,但是他最大的好處就是有內建的機制儲存cookie,並且也可以跟隨 server 作自動轉址。網路上資料比較多的是 httpclient 3.x版,Android 使用 httpclient 4 (而且還有些實作被拿掉) 唯一找到比較可信的來源是 apache httpclient 官方的 example。節錄重點段落如下:

        KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream instream = new FileInputStream(new File("my.keystore"));
        try {
            trustStore.load(instream, "nopassword".toCharArray());
        } finally {
            instream.close();
        }

        SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore);
        Scheme sch = new Scheme("https", socketFactory, 443);
        httpclient.getConnectionManager().getSchemeRegistry().register(sch);

直接把這段拿去用當然只有一個死字。本來看討論以為是要製造一個假的空憑證騙過 httpclient,從 Android 檔案系統有點微妙開始改來改去,一連串不同的例外或直接crash就不多談了。解決了檔案路徑,到底有沒有建立等等方面的問題之後才終於發現,假憑證是不行的…

1. 所以首先,開啟你PC或Mac上的瀏覽器連上目標網站,從憑證管理的地方把該網站的憑證匯出。每個瀏覽器做法都不同,請各位發揮正常工程師的水準做完這件事。以Firefox為例,找到site名後選匯出,多半是會得到一個檔名為 your_site_name.crt 的 X509(PEM) 憑證檔。為了之後使用方便,先把這檔案複製一份並把檔名改為 your_site_name.pem。

2. 將這個憑證格式從 PEM 轉為 BKS 格式。 這是關鍵性的一步啊。

KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());

這行中的 getDefaultType 到底會 get到什麼 type 呢?答案:在 Android 中是 BKS。有 J2ME 經驗的人會想說,我看多半是跟 J2ME 一樣的 Sun JKS 格式吧,而且,我不要用 getDefaultType 就好了,我可以自己指定為 PEM, JKS格式啊。是的,你可以。只是你會得到錯誤訊息說 KeyStore JKS implementation not found。測了半天,看起來 Android 的 httpclient 只吃 BKS 就對了,其他都沒實作。

3. 那格式要怎麼轉?根據網路上找到的資料,可以使用 KeyTool IUI 這個Java工具。打開後從介面選 create → KeyStore,格式選 BKS,自己命名一下,要不要設密碼都可。接著選 import → Keystore’s entry → Trusted certificate → Regular certificate。 Source 的部分選 PEM 並選到剛剛瀏覽器得到的那個檔,Target 當然選 BKS 和剛 create 出來的檔。按OK之後會跳出Entries視窗,下方提示你enter new alias,隨便取個名字後ok連打,順利的話你應該就會有一個 your_keystore.bks 之類的檔案了。

4. 要把憑證檔放在 Android app 能讀到的地方,做法有兩種,放在 SD card 中,或直接放在 resources 中。

[方案一] 放在 SD card 的情形:首先模擬器要掛上SD card,這在 Eclipse 用 AVD 工具產生 avd 時就可以順便指定SD了。如果習慣用指令的話,也可以用如下指令產生 image 並 mount 上:

$ mksdcard 512M my_sdcard.so
$ emulator -sdcard ./my_sdcard.so

再來要把憑證檔 copy 到 SD card 中,目前只知道指令的做法:

$ adb push file_path/your_keystore.bks /sdcard

如果有顯示類似 ftp 傳輸速度之類的訊息應該就是成功了。
接著把 android 程式碼中檔案部分改一改

        FileInputStream instream = new FileInputStream(new File("/sdcard/your_keystore.bks"));
        ...
            trustStore.load(instream, null);

如果沒設密碼,可以把 keystore load 的第二個密碼參數改成 null,有設的話當然就把 “nopassword” 改成你的密碼。
基本上這樣應該就大功告成了。

[方案二] 再講講憑證放在 resources 的方法。首先把檔案複製到專案下的 res/raw/your_keystore.bks 。Eclipse 沒有錯誤的話就 ok,否則多半是你檔名不合規格,稍微改改。接著取用方法是把 android 程式碼改成:

InputStream instream = getResources().openRawResource(R.raw.your_keystore);

其它同SD card。

5. 還有一個地方要注意,就是 SSL port。

        Scheme sch = new Scheme("https", socketFactory, 443);

如果你要連的網站不是用 port 443,這邊請記得改掉。

6. 另外如果要使用檔案放在 resources 的做法,getResources() 應該只能在 Activity class 中執行,如果另外包裝連線類別的話請不要直接服用上面的程式碼,要自行從 context 抓到資源再傳過去。

看起來不太複雜的事情我其實還鬼打牆了滿久才解決,希望對各位有點幫助。

10 Dec 2009, 11:19am
Android
by lachesis

leave a comment

[筆記]Android 的 Activity Lifecycle

Activity Lifecycle實驗:1 = Root Activity, 2 = Sub Activity started by 1

Scenario A

  1. 先測單一Activity時,程式啟動,進入主畫面
    onCreate 1
    onStart 1
    onResume 1
  2. 按 BACK,螢幕跑回桌面
    onPause 1
    onStop 1
    onDestroy 1
  3. 選程式icon再進入主畫面
    onCreate 1
    onStart 1
    onResume 1

結論:在 Root 按 BACK 等於結束程式。

Scenario B

  1. 程式啟動進入主畫面
    onCreate 1
    onStart 1
    onResume 1
  2. 按 HOME,螢幕跑回桌面
    onSaveInstanceState 1
    onPause 1
    onStop 1
  3. 選程式icon再進入主畫面
    onRestart 1
    onStart 1
    onResume 1

結論:在 Root 按 HOME 只是把程式放到背景,重新進入後不會執行 onCreate。

Scenario C

  1. 程式啟動進入主畫面
    onCreate 1
    onStart 1
    onResume 1
  2. 按下按鈕,進入畫面2
    onSaveInstanceState 1
    onPause 1
    onCreate 2
    onStart 2
    onResume 2
    onStop 1
  3. 按BACK 返回主畫面
    onPause 2
    onRestart 1
    onStart 1
    onResume 1
    onStop 2
    onDestroy 2

結論:在 Sub 按 BACK,Sub活動會消滅,另外看起來Android滿仔細的,停掉2重開1的流程是有巧妙的安排。

Scenario D

  1. 程式啟動進入主畫面
    onCreate 1
    onStart 1
    onResume 1
  2. 按下按鈕,進入畫面2
    onSaveInstanceState 1
    onPause 1
    onCreate 2
    onStart 2
    onResume 2
    onStop 1
  3. 在畫面2,按HOME 返回桌面
    onSaveInstanceState 2
    onPause 2
    onStop 2
  4. 選程式icon,會直接進入畫面2
    onRestart 2
    onStart 2
    onResume 2

結論:在 Sub活動按 HOME 會暫停Sub (Root早就暫停了),再回到程式是直接 Reactivate Sub畫面

Scenario E

  1. 補充:在 textbox 打字到一半,按POWER鎖住螢幕
    onSaveInstanceState x
    onPause x
  2. 解除鎖定,會直接回app畫面,並且之前的輸入到一半的文字不會消失
    onResume x

Scenario F

  1. 補充:打字到一半,有電話進來
    onSaveInstanceState x
    onPause x
    onStop x
  2. 掛斷電話,畫面及游標回到原來的 textbox,輸入的文字還在
    onRestart x
    onStart x
    onResume x

這裡面有個更讓我在意的事情,就是我在兩個Activity都有overwrite onRestoreInstanceState,並加入message,但是從來都沒有被呼叫過。官方手冊在提到 onSaveInstance 的時候,也是說明這儲存的bundle是讓你在下次onCreate時使用,並沒有提到 onRestoreInstanceState。不論如何,這兩個method並不是在 Lifecycle 中必定會呼叫的程序,不要依賴它們來儲存使用者輸入到一半的資料,照設計目的看來比較像是記住上一次是看到第二頁之類的功能。之後如果有讀到相關說明再補充上來。

上面的實驗雖然 onPause 和 onStop 總是在一起,但根據手冊,Pause 狀態跟 Stop狀態確實是不同。Pause狀態下,使用者不能與這個活動互動,但是仍然看的見它。也就是說如果你產生了一個透明的或不占到全螢幕的子活動,則主活動有可能只會 Pause,而不會 Stop。而Pause狀態對系統來說仍然是alive,只有系統資源真的太低時才會kill它,而Stop的活動相對來說很有機會被殺掉。(不知道 Alert Dialog 與 Parent是不是這種關係)

關於保存狀態或傳遞資料方面,官方SDK手冊強調,如果需要回傳結果給主活動,不要在 onDestroy 加入 setResult function! ,之前被 google 到的某論壇文章騙了,說可以在 onDestroy setResult,怒。根據手冊說,想要做保存資料,例如 user 輸入之類的事情應該放在 onPause,onDestroy 應該只做釋放資源之類的動作,因為 oPause是唯一一個保證會在系統 kill process 之前呼叫並且等待其return的function。實際上測試的結果,確實在 onDestroy 試圖 setResult 是沒有用的,intent 應該是會隨著此sub Activity 被消滅,onActivityResult 的地方會得到 null intent。一般來說返回鍵表示要放棄現在的動作返回,不保留狀態還算合理,但看了一下Android的通訊錄app,按下返回鍵時,其實還是當作 submit 立刻儲存了打到一半的資料,這可能是為了防止編輯到一半電話進來,資料全被清空的滿腔怒火。另外已經在背景的Activity是有機會被kill process的。萬一發生了這事情,在kill process之前會先 onSaveInstanceState(Bundle),下次再 onCreate(Budle) 時就可以使用這 bundle 的動態暫存資料。

總之,儲存永久性資料的動作可以在 onPause做,onDestroy只能做資源回收,比如 connection close,之後有其他心得會再補充資料。

9 Dec 2009, 11:43am
Android
by lachesis

1 comment

[筆記]Android 多國語言

官網的Hello, L10N,還有 ADT plug-in 都叫大家使用兩個字母的語言代碼,例如 zh, en, ja 來分字串語系檔。圖片則規定要使用語系+地區,例如 zh-rTW。這時候最大的疑惑立刻浮上心頭,那簡體跟繁體字串要怎麼辦(〃*`Д´)。網路上沒有搜到甚麼資料,乾脆自己惡搞看看,直接把/res/values-zh/strings.xml 複製修改成兩份,變成 /res/values-zh-rTW/strings.xml 跟 /res/values-zh-rTW/strings.xml 內容當然分別是簡體跟繁體的。喔! 沒有編譯錯誤,很好。接著開始切換模擬器語系…おおお!還真的會分簡體跟繁體啊!教學幹嘛不寫啊!(`・ω・´)總之姊妹們,有需要的話語系分下去就對了(*´∀`*)
ps. 版本的話,SDK 1.1的模擬器只有德文和英文懶的測,SDK 1.5 和 SDK 2.01 是測試OK的,推論應該是只要手機有支援相關語系都可以。

8 Dec 2009, 1:41pm
Android
by lachesis

2 comments

[筆記]Android的返回鍵

從Main Activity 使用 startActivity 或 startActivityForResult 產生並切換到 sub Activity之後,如果按下返回鍵,會發生甚麼事? 本來很擔心整個sub Activity只是看不見,但仍然蹲在角落畫圈圈,隨著Main Activity一次一次的再度 startActivity 而產生撿不完的屍體。Google之後再測試一下發現好家在,按下返回鍵系統就會呼叫 onDestroy(),結束掉這個 sub Activity。上以前被寫得很爛的J2ME Midlet 嚇過,真的是很害怕屍體山啊,為了愛護大自然大家一定要做好資源回收喔。∑(´д`*)

關於 BACK key,HOME key,官方Dev Guide 的 Application Fundamentals 的 Activities and Tasks 這個 session 講得滿清楚的。你的整個 App 是一個TASK,root Activity 和 sub Activity,包括中間呼叫系統地圖在內,跑在同一個 TASK process,一個 TASK 就是一個 STACK。主活動也就是程式載入的第一個活動一定是在堆疊底端,它產生子活動時,子活動就被加入堆疊。按下BACK key 時,子活動就從堆疊中被拿出來(丟掉)。子活動的子活動當然也是相同。所以BACK key做的事情就是把現在這個活動拿去丟:P (各位兄弟姊妹們應該沒有忘記STACK是LIFO吧) 而HOME key又會做甚麼? 他不丟東西,他會把這整個TASK / STACK 放到背景。等你在別人家玩一圈,再度按下app icon,背景的那整包 STACK / TASK 就被放回前景。如果剛在是從某個子活動按下HOME,現在應該還是看到剛才的那個子活動才對。BACK key則還是負責殺掉目前的子活動,並不會回到你剛玩過的其他app。謎之聲:不知道為什麼忽然開始覺得好險沒有Forward Key。另外補充一點,就是如果離開去外面玩太久,系統會把TASK中的子活動都殺光,只剩下主活動,下次切回來時看起來就好像新開的應用程式一樣。

上面說的是系統預設行為,TASK 和 Activity 的行為是可以由一些參數設定加以改變的,這邊就不深入討論了。有興趣可以繼續看完上面給的連結。