2012年8月16日 星期四

Android 開發筆記 - 更新 ListView 之 AndroidRuntime FATAL EXCEPTION 之 UI Thread 問題

一直以來都知道更新 UI 時,要使用 UI Thread 來進行,有幾種方式:



  • 使用 Handler.post

  • 使用 YourActivity.this.runOnUiThread

  • 使用 mListView.post 


然而今天練習時卻一直蹦出錯誤訊息:


AndroidRuntime FATAL EXCEPTION: main
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.


因為我在背景跑了 Thread 來更新資料,更新完資料在請 Handler 或 runOnUiThread 來更新 ListView,卻依舊碰到上述的錯誤訊息,最後才發現...那就是 ListView 的 Adapter 一開始在初始化時,有指定一個 ListAdapter 給他,那時有綁定一個 List/ArrayList 結構來記錄要顯示的資料(在此簡稱 mItemList),我一直以為只要 mSimpleAdapter.notifyDataSetChanged 、 mListView.requestLayout 在 UIThread 做就好,沒想到那個 mItemList 的增減也必須在 UIThread 做才行。這個 bug 不是每次都會出現,但出現的比例不低啦 :P


總之,解法就把 mItemList 的更新也都在 UIThread 做就行了 :P


申請個人或公司型 Android Developer 開發者帳號

Android Developer Console


摸了 Android app 大概有快一年半吧,一直以來都沒接觸過 Google Play (Android Market) 這塊東西,如今有幸幫公司處理,就來筆記一下啦!購買開發者帳號需要一張有效的信用卡,並透過 Google Wallet 來使用。首先從 Android Developer 官網連入,從右上角選單可以看到 Google Play Developer Console ,點選後進行登入,如果還未註冊為 Android 開發者時,就會顯示註冊畫面,輸入完"開發人員名稱/公司名稱"、"電子郵件地址"、"網站網址"、"電話號碼" 後,就可以點選繼續進行。


Android Market 開發人員發佈協議


接著同意一下條款後,接著就會導到 Google wallet 來進行付款的部份。


註冊成為開發人員


如果要報公司帳,應該要用公司信用卡才行?接著填一填信用卡資料後送出後,驗證完信用卡就算完成吧,並且可以查看 Google wallet 的購買清單狀態或是直接切回 Google Play Developer Console,而 Google Play Developer Console 一開始會顯示資料處理中,但似乎不影響軟體上架的設定:


進行中


過一陣子後,就會顯示已完成的狀態


處理完


如此一來就可以慢慢把玩啦!接著就從右下角的 Upload Application 後,就可以上傳 APK 檔案,並進行後續的設定囉(似乎離軟體上架還有段距離要設定...)。


至於怎樣算是公司型開發者帳號呢,其實就只要修改 Profile 把顯示的名稱改成  XXX Inc. 就差不多了 XD 比 iOS 公司型開發者要容易許多啊啊,並且可以新增無限多的帳號來使用開發者權限。


最後一提,如果公司沒有信用卡並使用個人信用卡去綁公司指定的 Gmail 帳號的話,那用完後可以連 Google Wallet 帳號,在付款方式那邊可以刪掉已綁定的信用卡唷,如此一來就不用擔心自己的信用卡一直綁在公司帳號啦。


2012年8月13日 星期一

[Android] Galaxy Nexus 刷機筆記(重返 Google 親生子?) @ Mac OS X 10.8

七月多推薦周邊親人買 Galaxy Nexus 來用,那時洽逢 SAMSUNG 與 Apple 智慧財產官司的關係,花了好大一把功夫才買到一台(空機價約13.5k附近),雖然不是最便宜的(美國官網公訂價$399美金,台灣有機會可以找到 13k 一台),但看在缺貨情況已經很滿意了。使用上除了拍照有聲音外,還都不錯用,不致於想要 root 它啦,不過,讓我想更新它的主因是...直到 2012-08-11 時,尚未接收到 Android 4.1 OTA 更新通知,查了一下原來這隻手機似乎是港台機?代號是 yakjuzs 並且屬於 SAMSUNG 系列的?也就是系統更新是 SAMSUNG 在維護的,與其等到 SAMSUNG  推出 Android 4.1 更新檔,還不如自己更新吧 :P 於是就下海刷一下吧!


雖然過去工作常編 AOSP 甚至把一些手機 root 研究,但本身使用 Android 手機時,並不太想要 root 或刷機的 :P 例如刷機刷用的檔案有沒被加料等等。故這次更新我主要採用 Google 官方提供的 Factory Images for Nexus Devices 來源,這也是我專挑 Google Nexus 原生手機的主因之一,可以方便地處理一些事務。


刷機有風險,需自行承擔手機失去保固或變成磚頭


手上這隻手機在 "設定" > "關於手機" 可以看到 "基頻版本" 為 I9250XXK1 和 "版本號碼" 有 "ITL41F.I9250ZSKKB" 字樣,並且透過 Android-SDK 來取得 adb/fastboot 等指令來驗證系統,並把手機接上後,也將手機開啟開發者設定,使用 adb devices 和 adb shell 確認手機狀態:


$ adb devices
XXXXXXX

$ adb shell cat /system/build.prop
...
ro.build.id=ITL41F
ro.build.display.id=ITL41F.I9250ZSKKB
ro.build.version.incremental=I9250ZSKKB
...
ro.build.version.release=4.0.1
...
ro.product.model=Galaxy Nexus
ro.product.brand=samsung
ro.product.name=yakjuzs
ro.product.device=maguro
... 


進入刷機前,請先手動備份需要的資料,實在是過程會清掉系統資料,就像重灌電腦一樣。備份資料嘛,例如手機內的照片、簡訊、鈴聲等等,其中簡訊用 SMS Backup & Restore 這套 Android app 軟體,可以輕鬆地把簡訊匯出 XML 檔案,並支援匯入(若有草稿需先清掉才能正常備份)。


接著,把手機關機後,同時按 "Volume + 鍵" 、 "Volume - 鍵" 和 "Powert 鍵" 一會兒,手機就會進入 fastboot mode:


fastboot_mode


透過指令進行 bootloader unlock (此步將清除手機資料,請詳看 unlock bootloader 訊息),打完就可以看到手機畫面切換為詢問 unlock bootloader ,並透過 Volume +/- 鍵進行選擇切換,並用 Power 鍵確定:


unlock_bootloader


$ fastboot oem unlock
...
OKAY [ 23.924s]
finished. total time: 23.924s


選 YES 後,則將進行 unlock bootloader 並且在 fastboot mode 最下一行顯示紅色的 "LOCK STATE - UNLOCKED" ,代表可以自行安裝(刷機)指定的 OS 啦:


fastboot_unlock


接著就將下載好的 Factory Images for Nexus Devices 解壓縮並擺好位置(在此使用 Factory Images "yakju" for Galaxy Nexus "maguro" (GSM/HSPA+) - 4.1.1 (JRO03C) ,請確認自己手機的狀況),透過一連串的 fastboot 指令進行更新:


$ fastboot flash bootloader /path/yakju-jro03c/bootloader-maguro-primelc03.img
sending 'bootloader' (2308 KB)...
OKAY [ 0.315s]
writing 'bootloader'...
OKAY [ 0.300s]
finished. total time: 0.615s
$ fastboot reboot-bootloader
rebooting into bootloader...
OKAY [ 0.007s]
finished. total time: 0.007s
$ fastboot flash radio /path/yakju-jro03c/radio-maguro-i9250xxlf1.img
sending 'radio' (12288 KB)...
OKAY [ 1.720s]
writing 'radio'...
OKAY [ 1.369s]
finished. total time: 3.088s
$ fastboot reboot-bootloader
rebooting into bootloader...
OKAY [ 0.007s]
finished. total time: 0.007s
$ fastboot -w update /path/yakju-jro03c/image-yakju-jro03c.zip
archive does not contain 'boot.sig'
archive does not contain 'recovery.sig'
archive does not contain 'system.sig'
--------------------------------------------
Bootloader Version...: PRIMELC03
Baseband Version.....: I9250XXLF1
Serial Number........: #################
--------------------------------------------
checking product...
OKAY [ 0.007s]
checking version-bootloader...
OKAY [ 0.008s]
checking version-baseband...
OKAY [ 0.008s]
sending 'boot' (4366 KB)...
OKAY [ 0.589s]
writing 'boot'...
OKAY [ 0.431s]
sending 'recovery' (4708 KB)...
OKAY [ 0.629s]
writing 'recovery'...
OKAY [ 0.500s]
sending 'system' (396675 KB)...
OKAY [ 43.758s]
writing 'system'...
OKAY [ 38.104s]
erasing 'userdata'...
OKAY [ 0.292s]
formatting 'userdata' partition...
Creating filesystem with parameters:
        Size: 14539534336
        Block size: 4096
        Blocks per group: 32768
        Inodes per group: 8144
        Inode size: 256
        Journal blocks: 32768
        Label:
        Blocks: 3549691
        Block groups: 109
        Reserved block group size: 871
Created filesystem with 11/887696 inodes and 97200/3549691 blocks
sending 'userdata' (137559 KB)...
writing 'userdata'...
OKAY [ 28.911s]
erasing 'cache'...
OKAY [ 0.013s]
formatting 'cache' partition...
Creating filesystem with parameters:
Size: 452984832
Block size: 4096
Blocks per group: 32768
Inodes per group: 6912
Inode size: 256
Journal blocks: 1728
Label:
Blocks: 110592
Block groups: 4
Reserved block group size: 31
Created filesystem with 11/27648 inodes and 3566/110592 blocks
sending 'cache' (8832 KB)...
writing 'cache'...
OKAY [ 2.715s]
rebooting...


finished. total time: 116.000s


如此一來,手機重開機後,就會是 Android 4.1.1 囉!先測試一下打電話、接電話的基本功能吧!


最後,我還是習慣把手機的 bootloader lock 起來 XD 一樣關機後,同時按住 "Volume + 鍵" 、 "Volume - 鍵" 和 "Powert 鍵" 一會兒,進入 fastboot mode 後,透過指令進行:


$ fastboot oem lock
...
OKAY [ 0.165s]
finished. total time: 0.165s


收工。


一開始只是單純想要手動升到 Android 4.1.1 ,後來發現連拍照聲音也都解決了 XD 只能說剛好買到的 Galaxy Nexus 可能要拿去賣給亞洲(日本?)吧,所以有些地區有規定手機拍照一定要有聲音(防偷拍)。透過這次的系統更新,之後應該可以等 Google 第一波作業系統就升級了吧,不再需要等 SAMSUNG 囉~


2012年8月10日 星期五

[CSS] 使用 CSS Multi-column Layout Module 實作自動分頁

CSS Multi-column Layout Module (html pagination)


兩年前在 Monocle EPUB Reader 看到的作法,今天被朋友問了一下分頁作法,我一時之間也想不起來 XD 只好翻翻以前寫的 HTML EPUB Reader,順便做個筆記吧!


概念是透過 CSS Multi-column Layout Module http://www.w3.org/TR/css3-multicol/ 實作的,假設定一個 DIV 並在裡頭塞滿字,透過設定 [-moz- | -webkit-]column-width = 150px 話,假設瀏覽器視窗為 600px ,那就會一頁看到 4 個 columns 囉。


於是,分頁的作法就是在指定的顯示範圍內,切換指定的 column 而已。


範例:


<html>
       <head>
              <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
              <style type="text/css">
                     #show {
                            position:absolute;
                            height:100%;width:100%;
                            margin: 0;
                            z-index:100;
                            overflow: hidden;
                     }
                     #content {
                            position:absolute;
                            font-size:20px;
                            -webkit-column-gap: 0pt; -webkit-column-width: 1000px;
                            -moz-column-gap: 0pt; -moz-column-width: 1000px;
                            z-index:100;
                     }
              </style>
              <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.0.min.js"></script>
              <script type="text/javascript">
                     function windowResize() {
                            var window_width = $(window).width() - 20;
                            var window_height = $(window).height() - 50;
                            window_width += 'px';
                            window_height += 'px';
                            $( '#show' ).width( window_width).height(window_height);
                            $( '#content' ).width(window_width).height(window_height)
                            .css( '-moz-column-width' , window_width ).css( '-webkit-column-width' , window_width );
                     }
                     $( window ).resize( function() {
                            windowResize();
                     } );
                     $( document ).ready( function() {
                            windowResize();
                            $('#prev').click( function() {
                                   //console.log('prev');
                                   var obj = document.getElementById('show');
                                   if( obj.scrollLeft > 0 )
                                          obj.scrollLeft -= $('#show').width();
                                   else
                                          obj.scrollLeft = 0;
                            } );
                            $('#next').click( function() {
                                   //console.log('next');
                                   var obj = document.getElementById('show');
                                   if( obj.scrollLeft < $('#show').width() )
                                          obj.scrollLeft += $('#show').width();
                                   } );
                                   setTimeout(function() { window.scrollTo(0, 1) }, 100);
                     } );
              </script>
       </head>
       <body>
              <button id="prev">Prev</button><button id="next">Next</button>
              <div id="show">
                     <div id="content">
                            <p>大量文字</p>
                     </div>
              </div>
       </body>
</html>


[Linux] 使用 Dropbox Command Line @ Ubuntu 12.04 server

今天看到一篇文章談論關於 team 合作的模式,其中提及 Dropbox 的地位,例如大家組隊參加比賽,在比賽過程中有不少工作的委派跟追蹤,這時候該如何進行?其中有一個微小卻很重要的就是檔案的交換,這時候使用 dropbox 共享某個目錄就顯得很方便,甚至大家可以自定 filename 或 dirname 當做一個 notification,經由 dropbox 同步功能、通知功能就方便達成啦!


講了那麼多,我只是要用在虛擬機器上 XDD 這樣機器掛了,至少一些工作的檔案或記錄,可以有多幾版本 sync 到 dropbox 啦,以免哪天機器突然下線欲哭無淚。申請 dropbox 免費帳號 2GB 空間實在很夠用了!更可每一台虛擬機器申請一個帳號(可避免關鍵帳密擺在虛擬機器上),接著常用帳號分享目錄給各台的功能。此外,搭配 GMail 又可以放大絕(YourAccount+VM1@gmail.com)。另外,在工作站上,又可以直接開很多個 user 跑 dropbox 喔。


設定 Dropbox @ Ubuntu 12.04 64bit server:


$ wget -O dropbox.x86.tar.gz "http://www.dropbox.com/download/?plat=lnx.x86"
$ tar -xvf dropbox.x86.tar.gz
$ mv .dropbox-dist/ ~/
$ ~/.dropbox-dist/dropboxd
/home/user/.dropbox-dist/dropboxd: 10: exec: /home/user/.dropbox-dist/dropbox: not found
$ file ~/.dropbox-dist/dropbox
/home/user/.dropbox-dist/dropbox: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.8, stripped
$ sudo apt-get install ia32-libs
$ ~/.dropbox-dist/dropboxd
This client is not linked to any account...
Please visit https://www.dropbox.com/cli_link?host_id=xxxxxxxxxxxxxxxxx&cl=en_US to link this machine.



Client successfully linked, Welcome Bbs!


如果 OS 環境也是 64Bit 的,那就跟我一樣安裝一下 ia32-libs 即可,當執行 ~/.dropbox-dist/dropbox 時,顯示 This client is not linked to any account... 並給予一支 url 時,就只需開瀏覽器去瀏覽那個 url ,並用你將綁定在機器的帳號登入即可,如此一來,成功後會顯示 Client successfully linked, Welcome Bbs! 訊息。


經由上述流程後,已經設定好 dropbox command line 的使用了,在家目錄可以看到 ~/Dropbox 跟 ~/.dropbox 兩個目錄,其中 ~/Dropbox 就是用來同步的目錄,而 ~/.dropbox 則是綁定帳號的相關資訊,如果要用其他帳號來綁定的話,請刪掉這兩個目錄吧。


設定一下開機自動啟動:


網路上一堆人參考 http://wiki.dropbox.com/TipsAndTricks/TextBasedLinuxInstall/UbuntuStartup 和 http://wiki.dropbox.com/TipsAndTricks/TextBasedLinuxInstall 等等,但我現在都連不上無法看原始的 script


故暫時參考 modified script from here http://wiki.dropbox.com/TipsAndTricks/TextBasedLinuxInstall/UbuntuStartup,這 script 做的事就是找出有哪些 user 的家目錄中有 ~/.dropbox-dist/dropbox ,找到後把它跑起來,如此而已。而這個修改的版本嘛,還滿通用的,只需設定一個 group 名為 dropbox ,並把機器上使用的帳號加入此 group 即可,無需修改 script 囉。


$ sudo cp dropbox-script /etc/init.d/dropbox
$ sudo chmod +x /etc/init.d/dropbox
$ sudo /etc/init.d/dropbox status
Status of dropbox ...
$ sudo update-rc.d dropbox defaults 
Adding system startup for /etc/init.d/dropbox ...
/etc/rc0.d/K20dropbox -> ../init.d/dropbox
/etc/rc1.d/K20dropbox -> ../init.d/dropbox
/etc/rc6.d/K20dropbox -> ../init.d/dropbox
/etc/rc2.d/S20dropbox -> ../init.d/dropbox
/etc/rc3.d/S20dropbox -> ../init.d/dropbox
/etc/rc4.d/S20dropbox -> ../init.d/dropbox
/etc/rc5.d/S20dropbox -> ../init.d/dropbox
$ sudo /etc/init.d/dropbox start
Starting dropbox...
$ sudo /etc/init.d/dropbox status
Status of dropbox ...
dropboxd for USER username: running (pid #####)


最後,如需要更豐富的 dropbox 運行資訊,可以試試 http://www.dropbox.com/download?dl=packages/dropbox.py 這隻程式吧。