在介紹如何 debug 之前,首先必須知道的是,目前的系統到底有沒有記憶體洩漏?以下先介紹幾個常用的記憶體觀察法:

  • cat /proc/meminfo      // 粗略的觀察目前記憶體的種類以及各個種類的大小
  • ps                             // 觀察每個 process 目前記憶體使用的情況 
  • top                            // 看看前幾個記憶體使用最多的 process 是哪些
  • dumpsys meminfo      // Android 特有的指令,有更多的資訊







方法1 Memory Analyzer :

這個方法必須配合 Eclipse 使用,因為 Memory Analyzer (MAT) 是 Eclipse 的一個 Plugin。
  1. 第一個步驟當然是安裝 MAT,安裝得方法有許多種,你可以下載MemoryAnalyzer-1.0.1.201008091353.zip,解開到Plugins,或者在Eclipse上點選 Help --> Install New Software... --> Add,然後在 Name 輸入 Memory Analyzer ,在 Location 輸入 http://download.eclipse.org/mat/1.0/update-site/ ,如圖所示:
    按下 OK,之後要選擇安裝 Plugin 完成安裝程序。

  2. 一如往常的執行你想要 debug 的 Android 程式,然後透過進入 shell 裡面檢查 /data/misc/ 這個目錄是否有可讀寫的權限,因為我們等一下會 dump file 到這個目錄。另外,也可以簡單的把他的權限設成 777:
    adb shell
    chmod 777 /data/misc/
    

  3. 接下來執行程式到你認為會產生 leak 的地方,來回多執行幾次,然後利用 kill 這個指令送信號 SIGUSR1 (-10) 給你的 process (可以透過 ps 查看 Process ID),假設你要 debug "com.test.debug" 這個 process :
    ps
    ...
    PID UID       VSZ  Stat Command
    100 1000   255255    S  com.test.debug
    ...
    
    kill -10 100
    

  4. 你可以透過 logcat 來確認你的 process 有沒有收到 signal:
    logcat &
    

    如果看到類似如下的訊息表示有收到:
    I/dalvikvm(  236): threadid=3: reacting to signal 10
    I/dalvikvm(  236): SIGUSR1 forcing GC and HPROF dump
    I/dalvikvm(  236): hprof: dumping VM heap to "/data/misc/heap-dump-tm1291081439-pid100.hprof-hptemp".
    I/dalvikvm(  236): hprof: dumping heap strings to "/data/misc/heap-dump-tm1291081439-pid100.hprof".
    I/dalvikvm(  236): hprof: heap dump completed, temp file removed
    D/dalvikvm(  236): GC_HPROF_DUMP_HEAP freed 585 objects / 60000 bytes in 10595ms
    

    查看一下 /data/misc/ 目錄應該可以看到 heap-dump-tm1291081439-pid100.hprof:
    ls /data/misc/
    ...
    heap-dump-tm1291081439-pid100.hprof
    ...
    

  5. 離開 shell
    exit
    

    透過 adb pull 把這個檔案抓下來。
    adb pull /data/misc/heap-dump-tm1291081439-pid100.hprof
    

  6. 透過 hprof-conv 把剛剛抓下來的檔案轉換成 Memory Analyzer 認識的格式:
    hprof-conv heap-dump-tm1291081439-pid100.hprof debug.hprof
    

  7. 開啟 Eclipse,點選 Window --> Open Perspective --> Other... ,然後選擇 Memory Analyer。

  8. 點選 File --> Open Heap Dump... ,然後選擇剛剛轉換過的檔案 (此例為 debug.hprof)。接著會出現一個對話框,詢問你想產生什麼樣的報告,如下圖:
    此時我們的目的是偵測 memory leak,所以自然是選擇 Leak Suspects Report,按下 Finish 後,就會產生一個報告,告訴你可疑的地方。是不是很棒呢!


方法2 libc_debug.so : 

這個方法基本上是將 libc.so 換成 libc_debug.so 利用 libc_debug.so 提供多項偵錯的功能來查看記事體是否被不當的使用。

  1. 將 /system/lib/libc.so 替換成 /system/lib/libc_debug.so
  2. 重啟系統 adb shell reboot 
  3. adb shell setprop libc.debug.malloc 1" (or 5, or 10 for slightly different behaviors)
  4. 新增 "native=true" 到你的 ~/.android/ddms.cfg 檔案中
  5. 啟動 stand-alone 版的 DDMS. 接著你應該會看到有 "Native Heap" tab 出現,切到這個頁面後就可以 native memory 的使用情況。


方法3 c/c++ debugger : 

利用c/c++ debugger 來協助 debug c/c++ 層的 memory leak,接下的例子會使用 dmalloc 來 debug JNI native library.

  1. 先到 dmalloc 的官方網站 http://www.dmalloc.com 下載 source code。
  2. 交叉編譯 dmalloc,我個人是偏好用 Android NDK 來編譯,不過在這之前必須要先產生原本的 Makefile 然後照著 Makefile 來產生 Android.mk
    1. 執行 configure
      ./configure --prefix=/arm-linux/ --enable-cxx --enable-threads
      
      其中/arm-linux/是隨便指定的路徑,反正我們只是想要參考 Makefile。
    2. 參考 Makefile,轉換成 Android.mk
    3. 執行 ndk-build 來編譯 dmalloc
  3. 因為我們是要 debug JNI 的 native library 所以沒辦法透過一般的設定環境變倏地方法來指定 debug options:
    DMALLOC_OPTIONS=debug=0x4e48503,inter=100,log=/sdcard/dmalloc.%p
    export DMALLOC_OPTIONS
    

    必須自行在程式進入點指定 debug option,以 JNI 為例,可以利用 JNI_OnLoad() 當作是進入點,此外,離開的時候也必須關閉:
    #include "dmalloc.h"
    
    JNIEXPORT jint JNICALL JNI_OnLoad (
    ) {
        dmalloc_debug_setup ("debug=0x4e48503,inter=100,log=/sdcard/dmalloc.%p");
    }
    
    JNIEXPORT void JNICALL JNI_OnUnLoad (
    ) {
        dmalloc_shutdown ();
    }
    

    然後利用 dmalloc_mark() 與 dmalloc_log_changed() 來標記要偵錯的區段,下面我們以一個 JNI function 為例,故意造成 memory leak:
    unsigned long mark;
    
    JNIEXPORT void JNICALL JNI_com_example_debug_Test (
        JNIEnv* env,
        jobject thisArg
    ) {
        // Mark the start of debug
        mark = dmalloc_mark();
    
        // Leak memory 1000 bytes.
        char* leak = (char*) malloc (1000);
    
        // log unfreed pointers that have been added to
        // the heap since mark
        dmalloc_log_changed (
            mark,
            1,    // log unfreed pointers
            0,    // do not log freed pointers
            1     // log each pnt otherwise summary
        );
    }
    

    一旦這個 function 被呼叫,就會在 /sdcard/ 目錄下產生類似 dmalloc.1000 (副檔名是這個process 的 PID),然後裡面就會告訴你那一行發生 memory leak了。
    948436802: 1: Dmalloc version '5.5.2' from 'http://dmalloc.com/'
    948436802: 1: flags = 0x4e48503, logfile 'logfile'
    948436802: 1: interval = 100, addr = 0, seen # = 0, limit = 0
    948436802: 1: starting time = 948436802
    948436802: 1: process pid = 1000
    ...
    

<有背景音樂,請打開喇叭,音樂檔很大,請耐心等候>

好久以前曾經幫同事 Roger 做過婚禮記錄,但這一些記錄也隨著之前發表過的網站關閉而消失了,前陣子 Roger 父親的事情讓我感慨良多,藉著今天是 Roger 的生日,也順便把這些照片再做一番整理,也對希望 Roger 的父親能夠在天堂裡保佑我們,以馬內利。

出發去迎娶之前先拜祭家裡的祖先,祖先們啊, Roger 已經長大囉!今天就要娶媳婦過門了,請你們要好好保佑他...



傳統的習俗還是要遵守的...



Roger 拿著漂亮的捧花要準備出發囉!ㄟㄟ...少年耶,你笑得很僵耶!是很緊張吧!有我在免驚啦!...呵呵...^^



出發前郵差先生還送來一封信,有幸福的預感喔!...^^



Roger 的人生大事,父親不敢大意,在臨行前不忘再三叮嚀...




想見到新娘哪有這麼容易啊?好姊妹當然要考考這位新郎官啦!Roger 說時遲那時快立刻掏出放在褲襠的..."紅包"...嘿嘿...還好我事先有準備,這下應該搞定了吧!



嗯...紅包這招是不錯,不過,為了姊妹的「性福」,體能考驗還是要來一下啦!不是猛龍不過江,看我 Roger 輕鬆過關,嘿嘿...俺可是有練過的喔!...^^



通過重重的考驗終於見到新娘啦! Rita 看起來好高興喔!不過我在一旁觀察到 Rita 父親的神情似乎有一些感傷,人家說:「女兒是父親前世的情人」真的一點也沒錯...女兒要嫁人總是捨不得的。



拜別女方家長






現場瀰漫著一種既喜氣又感傷的氣氛,我從 Rita 跟他父親的互動雖然不多但是卻可以看出他們的感情很好...



這時候 Roger 機警的安撫丈母娘的情緒,這一招果然讓丈母娘放心許多...「師奶殺手」Roger 果然不是浪得虛名喔!呵呵...^^



Rita 看著窗外的神情好美啊!就像一個美麗的「天使」(這也是我選這首背景音樂的原因)。看著窗外似乎也看到了未來,而 Roger 你應該知道吧!你就是她的未來...



傳統的習俗是新娘出發前要把扇子丟掉...



到新郎家囉!下車前要先摸個橘子,有吉利的意思喔!



過火盆跟採瓦片也是一種招福的習俗。



祭祖,啟稟祖先家裡今後將多一個成員囉,請祖先好好保佑葉家的媳婦。



新娘給長輩奉茶啦!這也是古禮之一喔!



看長輩笑得合不攏嘴...呵呵...



來場景換到了新竹的勝利堂,接下來就是基督教的結婚儀式了。



這位風度翩翩的帥哥就是新郎耶,一度以為是瓊瑤劇裡面的男主角呢...帥氣指數破表...呵呵...



花童、伴娘跟新娘依序出場...



牧師正在祝福這對新人,牧師口才很好,現場氣氛很歡樂喔...



底下的新人聚精會神的再聆聽。



交換戒指



掀頭紗



祝福



說完新郎可以吻新娘了,接下來就是這種激情狂吻了...真令人臉紅心跳啊...



詩歌祝福



Rita 給媽媽一個深深的擁抱,媽媽...謝謝您。



入洞房囉...接下來兒童不宜,所以就不貼囉...呵呵...其實接下來我也沒拍到啦!THE END!



不知道大家知不知道 meld 這個 visual diff viewer 呢!?個人覺得在 Linux 的環境底下這可能是最好的 viewer 了。有機會再跟各位詳細介紹吧!

如果沒有安裝過不妨先安裝一下:

sudo apt-get install meld

接著設定 GIT 讓他使用 Meld :
git config --global diff.external meld

然後像往常一樣比較某一個檔案:
git diff HEAD^ filename
其中 HEAD^ 是指上一個版本。

Meld 會被叫起來,但是會說參數錯誤 "Wrong number of arguments (Got 7)"。 原因是 GIT 會送 7 個參數給 Meld ,但是 Meld 只需要兩個參數,兩個需要比較的檔名。所以不能直接用 Meld ,必須要做一點小修改:

在自己的的目錄下建立一個 git-meld.sh 的 script:
vi ~/git-meld.sh

加入以下內容:
#!/bin/sh
meld $2 $5

改變檔案的屬性:
chmod 777 ~/git-meld.sh

然後把 external diff 改成這個 shell script :
git config --global diff.external ~/git-meld.sh

大功告成。

Ubuntu 9.04 之前的 gcc 和 g++ 的版本都是 4.3 的,但是 Ubuntu 9.10 以後卻換成了 4.4 的。 而 4.4 除了檢查更加嚴格外也改了某些函數的回傳型別,把 char* 改成了更嚴謹的 const char* 其用意是讓這些函數更安全。

由於修改 android source code 可是一大工程,而且 source code 隨著 google 的修改版本一直在 update ,除非決定自己 maintain 否則修改可不是一個合理的行為。所以把 gcc 和 g++ 還原成 4.3 版是一個比較可行的辦法。

首先安裝 4.3 版的 gcc 和 g++ :


sudo apt-get install gcc-4.3 g++-4.3 g++-4.3-multilib


接著刪掉原本的 link :


sudo rm /usr/bin/gcc
sudo rm /usr/bin/g++


產生指到成舊版的 link :


sudo ln -s /usr/bin/gcc-4.3 /usr/bin/gcc
sudo ln -s /usr/bin/g++-4.3 /usr/bin/g++


補充:在 Ubuntu 10.10 之後有一種稱為 fastjar 的工具, 是以 c 語言寫成的工具,比原本 java 版本的快了 100 倍. 但是目前有一些穩定性的疑慮,所以如果在 Ubuntu 10.10 發現類似 "unable to access file: corrupted zip file" 這樣的錯誤,可以試著執行:

sudo update-alternatives --config jar


然後會出現:
There are 2 choices for the alternative jar (providing /usr/bin/jar).

Selection Path Priority Status
------------------------------------------------------------
* 0 /usr/bin/fastjar 100 auto mode
1 /usr/bin/fastjar 100 manual mode
2 /usr/lib/jvm/java-6-sun/bin/jar 63 manual mode

Press enter to keep the current choice[*], or type selection number:


輸入 2, 選擇 java 版的就可以了。

自從家裡有個小搗蛋出生之後,對這種防過敏的東西就特別有興趣。因為我本身有過敏的體質,所以很希望這種不好的過敏現象不要發生在下一代身上。關於對抗過敏基本上可以分成兩派,死豬不怕水燙派跟預防派,死豬派是認為基本上環境不是個人小小的力量可以改變的,就算是改變了自家的環境,到了外面依然污染嚴重,既然如此就應該讓小孩從小習慣污染,以後才能適應惡劣的環境。另一派的說法是從過敏發生的原因來著手,過敏的原因其實就是身體對抗污染環境的一種保護機制,也是對抗體對自身發出的一種警告。抗體的本意是好的,他盡忠職守的阻止過敏原進入人體,所以才會一直打噴嚏流鼻水。然而更深入研究會發現,這些抗體其實本來是不認識這些過敏原的,當過敏原與抗體接觸後,抗體才會產生記憶來記住這些過敏原,等下次再發現這些過敏原,抗體就會起來對抗。這也就是為什麼花粉症通常再搬到某個地方的第二年以後才會發生。

基於我是預防派的,所以對486強烈推薦的「大王空氣清淨機」一直有著濃厚的興趣。終於在一次過敏又來襲的時候,敗給了心中的小惡魔..../_\
有興趣的人請看請看486的文章:
http://blog.yam.com/kiss486/article/26499887
http://blog.yam.com/kiss486/article/29637297

廢話不多說,直接開箱吧!


箱子超大的...

打開箱子終於看到廬山真面目了,真的很大一台,甚至快要是除濕機兩倍大了,但是也是這麼大台才稱的上是大王,因為他的濾網也很大。

打開前面的蓋子可以很清楚的看到 HEPA 濾網,這就是可以有效過濾過敏原的關鍵啦!又大又厚呢!家裡的過敏原就靠你來清除了。

喔...幸好有輪子可以在房子裡推來推去,真是貼心的設計。

這次的團購還有附贈10片活性炭濾網以及馬達五年保固,真的很划算,難怪熱銷近600台。

這幾天用下來的結果真的很不錯,機器一啟動,真的可以明顯的感覺到空氣變好了。很神奇的是今天我本來鼻子又不通了,所以給他打開強力風速,咦...開機大概一個小時我的鼻子竟然通了,神奇啊...讚啦!...^^

基本上 Emulator /system 目錄是唯讀的,要解除其唯讀屬性可以透過下列方法:

adb remount
adb push libmy.so /system/lib

但是,在 Android 2.0 (Eclair) 以後,上面的作法會出現這樣的錯誤:
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
接著再對這個模擬器使用上面的方法就不會出現錯誤了。

深究其原因是 partition size 預設是 64MB。從 adb shell 用 df 指令觀察的結果,/system 在2.x 的 image mount 之後剩下空間是 0K。因此即使 remount 之後也無法寫入。

Filesystem           1k-blocks      Used Available Use% Mounted on
tmpfs                   128276         0    128276   0% /dev
tmpfs                   128276         0    128276   0% /mnt/asec
/dev/block/mtdblock0     77568     77568         0 100% /system
/dev/block/mtdblock1    129152     54988     74164  43% /data
/dev/block/mtdblock2     65536      1156     64380   2% /cache
/dev/block/vold/179:0
                       1044472     65608    978864   6% /mnt/sdcard
/dev/block/vold/179:0
                       1044472     65608    978864   6% /mnt/secure/asec

可惜AVD Manager 沒地方可以設定 partition size。所以目前只能在 console 以手動下指定開emulator囉。

有用過 Android adb shell 的人應該都知道這個 shell 的功能實在是食之無味棄之可惜的雞肋,一些常用的 linux command 幾乎都不支援,所以今天要介紹 busybox 這個工具,它的支援相當豐富,以下是執行的畫面:

$ busybox
BusyBox v1.16.0 (2010-01-27 20:00:00 CET) multi-call binary.
Copyright (C) 1998-2009 Erik Andersen, Rob Landley, Denys Vlasenko
and others. Licensed under GPLv2.
See source distribution for full notice.

Usage: busybox [function] [arguments]...
   or: function [arguments]...

 BusyBox is a multi-call binary that combines many common Unix
 utilities into a single executable.  Most people will create a
 link to busybox for each function they wish to use and BusyBox
 will act like whatever it was invoked as.

Currently defined functions:
 [, [[, acpid, addgroup, adduser, adjtimex, ar, arp, arping, ash, awk,
 basename, beep, blkid, brctl, bunzip2, bzcat, bzip2, cal, cat, catv,
 chat, chattr, chgrp, chmod, chown, chpasswd, chpst, chroot, chrt, chvt,
 cksum, clear, cmp, comm, cp, cpio, crond, crontab, cryptpw, cttyhack,
 cut, date, dc, dd, deallocvt, delgroup, deluser, depmod, devmem, df,
 dhcprelay, diff, dirname, dmesg, dnsd, dnsdomainname, dos2unix, du,
 dumpkmap, dumpleases, echo, ed, egrep, eject, env, envdir, envuidgid,
 ether-wake, expand, expr, fakeidentd, false, fbset, fbsplash, fdflush,
 fdformat, fdisk, fgrep, find, findfs, flash_eraseall, flash_lock,
 flash_unlock, flashcp, fold, free, freeramdisk, fsck, fsck.minix,
 fsync, ftpd, ftpget, ftpput, fuser, getopt, getty, grep, gunzip, gzip,
 halt, hd, hdparm, head, hexdump, hostid, hostname, httpd, hush,
 hwclock, id, ifconfig, ifdown, ifenslave, ifplugd, ifup, inetd, init,
 inotifyd, insmod, install, ionice, ip, ipaddr, ipcalc, ipcrm, ipcs,
 iplink, iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5,
 klogd, last, length, less, linux32, linux64, linuxrc, ln, loadfont,
 loadkmap, logger, login, logname, logread, losetup, lpd, lpq, lpr, ls,
 lsattr, lsmod, lspci, lsusb, lzmacat, lzop, lzopcat, makedevs,
 makemime, man, md5sum, mdev, mesg, microcom, mkdir, mkdosfs, mke2fs,
 mkfifo, mkfs.ext2, mkfs.minix, mkfs.vfat, mknod, mkpasswd, mkswap,
 mktemp, modprobe, more, mount, mountpoint, msh, mt, mv, nameif, nc,
 netstat, nice, nmeter, nohup, nslookup, ntpd, od, openvt, passwd,
 patch, pgrep, pidof, ping, ping6, pipe_progress, pivot_root, pkill,
 popmaildir, poweroff, printenv, printf, ps, pscan, pwd, raidautorun,
 rdate, rdev, readahead, readlink, readprofile, realpath, reboot,
 reformime, renice, reset, resize, rm, rmdir, rmmod, route, rpm,
 rpm2cpio, rtcwake, run-parts, runlevel, runsv, runsvdir, rx, script,
 scriptreplay, sed, sendmail, seq, setarch, setconsole, setfont,
 setkeycodes, setlogcons, setsid, setuidgid, sh, sha1sum, sha256sum,
 sha512sum, showkey, slattach, sleep, softlimit, sort, split,
 start-stop-daemon, stat, strings, stty, su, sulogin, sum, sv, svlogd,
 swapoff, swapon, switch_root, sync, sysctl, syslogd, tac, tail, tar,
 taskset, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time,
 timeout, top, touch, tr, traceroute, traceroute6, true, tty, ttysize,
 tunctl, tune2fs, udhcpc, udhcpd, udpsvd, umount, uname, uncompress,
 unexpand, uniq, unix2dos, unlzma, unlzop, unzip, uptime, usleep,
 uudecode, uuencode, vconfig, vi, vlock, volname, wall, watch, watchdog,
 wc, wget, which, who, whoami, xargs, yes, zcat, zcip

網路上已經有人幫忙把 busybox comile 成 android 可以用的 binary 檔了,有需要的人可以到這裡下載。

安裝 busybox 的步驟如下:

1. 建立安裝的目錄
adb shell mkdir /data/busybox

2. 複製 busybox 的 binary 檔到前一個步驟所建立的目錄:
adb push busybox /data/busybox/busybox

3. 開始安裝 busybox
adb shell
cd /data/busybox
./busybox --install. 
然後你可以檢查 /data/busybox 目錄,裡面應該有許多links.
但是預設的路徑並沒有被指定到 /data/busybox 為了讓用起來方便一點,可以修改 init.rc 多加進去 busybox 的安裝路徑,但如果是在 emulator 上 run 那就要去修改 ramdisk.img 才能達到一勞永逸的效果,否則每次開啟 emulator 後 init.rc 會被覆蓋掉。

修改 ramdisk.img 的步驟:

2.6內核開始,initrd.img採用cpio壓縮,ramdisk.img也一樣,使用gunzip解壓縮,然後再使用 cpio解包。

1. 將 ramdisk.img 從 android sdk 的 platroms\android-xx 目錄下複製到 linux 的作業環境其他目錄(如果已經在 linux 下則此一步驟省略)

2. 名稱改為 ramdisk.img.gz 並用 gunzip 解壓
mv ramdisk.img ramdisk.img.gz
gunzip ramdisk.img.gz

3. 新建一個文件夾 ramdisk 並進入此目錄後用 cpio 解開
mkdir ramdisk
cd ramdisk
cpio -i -F ../ramdisk.img
此時現在目錄已經是解開後的 ramdisk 檔案目錄了

4. 修改 init.rc 在 PATH 中加上 /data/busybox 路徑
## Global environment setup
##
env {
#其中, /data/busybox 為busybox安裝路徑,bash也是放在其中
PATH /data/busybox:/sbin:/system/sbin:/system/bin
...
}

5. 重新打包 ramdisk
find . | cpio -o -H newc -O ../new_ramdisk.img
gzip ../new_ramdisk.img

6. 把 new_ramdisk.img.gz 改成 ramdisk.img 並複製到 emulator 原來放置 ramdisk.img 的目錄覆蓋掉。大功告成...^^

應該很多人都知道 Android Dalvik 的 Maximum Heap Size 一般都是 16M,如果你的程式非常龐大可能這 16M 連跑都跑不起來,這時就要把 Heap Size 放寬才行,那麼該如何放寬限制呢?以下提供幾種方法把 heap size 從 16M 改成 64M:

1. 第一種方法是提供給想在實機上執行程式並且有能力修改 android framework 的朋友,直接到 android source code 目錄,打開 frameworks\base\core\jni\AndroidRuntime.cpp 找到有關建字"16m"這一行,改成"64m":

property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "64m");
重新產生 android image 就大功告成了。

2. 第二種方法是提供給想在 Emulator 上執行程式的朋友,請修改 config.ini 並加入這一行(註1):
vm.heapSize=64

3. 如果上述兩種辦法都不可行,試試看第三種方法,在 /data 目錄下產生 local.prop 並修改檔案內容,加入 dalvik.vm.heapsize=64m ,例如在 shell 模式下輸入:
cd /data
echo dalvik.vm.heapsize=64m > local.prop


註1:
如果在你是 Windows 底下開發的話,這個檔案一般會放在 "C:\Documents and Settings\YourAccount\.android\avd\YourAVD\"

其實 Android 製作 .apk 的 Private Key 是放在 \build\target\product\security 這個目錄下,這裡有好幾組 Key 而其主要的差異如下:

testkey -- a generic key for packages that do not otherwise specify a key.
platform -- a test key for packages that are part of the core platform.
shared -- a test key for things that are shared in the home/contacts process.
media -- a test key for packages that are part of the media/download system.

因此,如果你希望在 Android 建制 Framework 的過程中把你寫的 Application 改成你所指定的 Private Key ,基本上你需要改動的就是 testkey ,這個目錄下還有個 mkkey.sh 來協助你作這樣的工作, mkkey.sh 的內容如下:

if ["$1" == ""]; then
 echo "Create a test certificate key."
 echo "Usage: $0 NAME"
 echo "Will generate NAME.pk8 and NAME.x509.pem"
 echo "  /C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com"
 return
fi

openssl genrsa -3 -out $1.pem 2048

openssl req -new -x509 -key $1.pem -out $1.x509.pem -days 10000 \
    -subj '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'

openssl pkcs8 -in $1.pem -topk8 -outform DER -out $1.pk8 -nocrypt

說穿了,就是利用 openssl 來產生新的 Private Key,所以更改的方法很簡單,你可以把 openssl req -new ... 這一行改成你所需要的格式, 接著你只要執行 mkkey.sh 就可以產生新的 Private Key 了! 讚吧!

上一個方法是把 Private Key 跟 Certificate 直接換掉,如果你已經有現成的 Certificate 可以使用,令一個比較簡單的方法是更改 Android.mk 的設定,加一行:


LOCAL_CERTIFICATE := vendor/example/certs/app

如此,在 build system 的時候就可以使用指定的 Certificate 了。

可是這樣還離完美差一點點,你也許想把這個 Private Key 放到 Java 的 .keystore 檔案去,關於這個問題可以參考這篇文章http://www.agentbob.info/agentbob/79-AB.html,裡面有很詳細的說明。

OK, 到這裡為止一切就算是大功告成囉!

<有背景音樂,請打開喇叭,音樂檔很大,請耐心等候>

好久以前曾經寫過同事 Catherine 的婚禮紀錄,後來寫紀錄的網站關了,紀錄也就跟著消失了...嗚...好想哭啊...~>_<~ 現在只好重寫了, 話說美麗的 Catherine 小姐要結婚了,作為同事兼攝影社社長的我能給予最大的祝福,莫過是盡心幫她完成一個美好的婚禮紀錄了,看到她那幸福的模樣讓我也覺得很幸福。

在化妝室的時候幫 Catherine 拍了幾張,看得出來有一點小緊張喔!呵呵...







同事 Phil 的小孩來充當小花童,是個小帥哥呢!



小妹妹也很討喜...真可愛...呵呵...so cute...



新郎新娘進場囉!大家掌聲鼓勵鼓勵...





Catherine 發巧克力給大家,為了捕捉現場的氣氛我衝到人群裡近拍,差一點跌個四腳朝天,終於能體會新聞記者有多辛苦了



切蛋糕儀式...



終於到了大家期待的敬酒儀式,嘿嘿...有仇報仇啦...



「得即高歌失即休,多愁多恨亦悠悠;今朝有酒今朝醉,明日愁來明日愁」...大家一起舉杯祝福這對新人吧!乾啦!ㄏㄡ搭啦!



怎麼可以放過這個千載難逢的機會,讓這對新人來個「愛的親親」...嘻嘻...





哇...這個火辣的熱吻真是太激情了...可以確定這對新人增產報國的戰鬥力應該也不在我之下呢...呵呵...



透過酒杯人的表情變得很有趣,有哈哈鏡的效果...^^



這張也是,呵呵...只不過苦主變成新娘了...



新娘的飾品很美麗,我特地拍了幾張...



送客囉!呷甜甜生豪生...



終於結束了,有一個傳說是:「人一生下來就只有一半,於是每個人窮其一生都在找尋那失落的另一半」。如今你找到了妳的另一半,完美了他也完美了妳。看到你們幸福的模樣真替妳感到高興,恭喜ㄋㄟ!一定要永遠幸福下去喔!