2017年10月31日 星期二

記憶體使用量 private bytes, working set 和 virtual bytes 的意義

使用 procexp 查看 process 時會顯示 private bytes 跟 working set,在這邊記錄一下這些跟記憶體使用量有關的 term

Private Bytes

private bytes 是 process 要求的記憶體,不是 process 實際使用到的記憶體,private 的意思是它不包含 shared DLLs 之類的 memory-mapped files,private bytes 也不一定等於 physical memory 用量,它可以包含被 paged 到 disk 的頁面

Working Set

working set 表示被 process 使用到的 physical 記憶體使用量,但是跟 private bytes 不同,working set 包含 memory-mapped file 這個值跟工作管理員理看到的 memory usage 是一樣的

有一點可能會讓人困惑,standby page 雖然也是 physical 但是並沒有包含在 working set 中,所以會發現程式最小化之後 memory usage 突然下降,是因為那些消失的 memory 頁面變成 standby page 了

Virtual Bytes

virtual bytes 是 process 使用的所有 virtual address space 用量,就等於是 working set 加上 standby page 加上被 paged out 到 disk 的 memory pages

2017年10月30日 星期一

Android ListView onCheckedChange 非預期的呼叫

最近在 ListView item 中放入 checkbox,然後用 notifyDataSetChanged 改變 ListView,但是發現 checkbox 的 onCheckedChange 會非預期的被呼叫到

例如在第三個 item 將 checkbox 勾選,然後將儲存在 Adapter 中的 data set 更新,更新後的第三個 item checkbox 預設是沒勾選的狀態,則在呼叫 notifyDataSetChanged 時就會 call 到 第三個 item checkbox 的 onCheckedChange

調查後發現,我使用 setViewBinder 來初始化 checkbox 的勾選狀態,但是在 setChecked 之前先用 setOnCheckedChangeListener 設定了 OnCheckedChangeListener 
@Override
public boolean setViewValue(View view, Object o, String s) {
    // Not correct!!
    AppCompatCheckBox cb = (AppCompatCheckBox)view;
    cb.setOnCheckedChangeListener(listener);
    cb.setChecked((boolean)o);

    // Correct
    cb.setOnCheckedChangeListener(null);
    AppCompatCheckBox cb = (AppCompatCheckBox)view;
    cb.setChecked((boolean)o);
    cb.setOnCheckedChangeListener(listener);
}

notifyDataSetChanged API 的行為應該會保存 item 之前的資料,只對需要的部分作改變,所以第三個 item checkbox 狀態會從 check 到 uncheck,如果有註冊了 listener,則理應通知 listener

那解法就是在初始化 checkbox 時,先 remove 掉 listener 在設定 checkbox,然後才註冊 listener,這樣就避免了不必要的 onCheckedChange 呼叫

此外,還有一個額外需要注意的點是,之前提過,用 notifyDataSetChanged 來更新 ListView,ListView item 之前的狀態都會被保存,所以原本被勾選的 item 更新後沒有被勾選的話會有 checkbox 被取消的動畫效果

如果不想要這個效果的話,則不應該用 notifyDataSetChanged,可以改用 setAdapter 重新設定 adapter 給 ListView,這樣 ListView 就會將整個內容銷毀再更新,就不會有 checkbox 效果,當然這樣會有效能流失,可以感覺到 GUI 的更新速度稍微慢了一點

2017年10月23日 星期一

Android 7.0 中文開發語言坑

Android 系統支援多國語言,若開發 app 時想要支援不同語系的話,會將 default language string resource 放在 values 目錄中,其他想支援的語系放在 values-XX 目錄中,例如我的 app 想支援英文跟繁體中文,可以將英文放在 values 目錄,繁體中文放在 values-zh 目錄

如此,使用者不管是選擇大陸地區或是香港地區,app 都會顯示繁體中文,這是因為 android 在搜尋 string resource 時,只要是 zh-* 的 locale,找不到 resource 時都會 fallback 到 zh 去,所以 zh-HK 或 zh-CN locale 最後會使用到 values-zh 目錄中的 resource,多年來 app 照著這樣的 locale resource search rule 開發,一直運作的很好,直到 Android N 的出現

在 Android N 上面,若是 app 只在 values-zh 中放入中文,在 values 放入英文,手機語系選擇台灣地區會發現 app 顯示的是英文

這是因為從 Android N 開始,locale system 與 resource search rule 都改變了,但其實改變了也就算了,最坑的地方是這個改變不會向前相容,就算你 app 的 targetSdkVersion 設定是 N 之前的系統也沒用,大概絕大多數中文 app 恐怕都要更新才能在 N 上面顯示中文了,真不知 google 是忘了做向下相容還是真的想要讓許多的 app 做更新來適應這個變化

回來看這次 Android N 的改變,照 google 在 issue tracker 的回應,他覺得所有 zh-* 的 locale 最後都 fallback 到 values-zh 是不對的行為,Android 裡面有演算法會自動找出他覺得最適合的 resource

根據 CLDR,Android N 上,zh 會被自動轉為 zh-Hans,zh-CN 會被轉為 zh-Hans-CN,zh-TW 會被轉為 zh-Hant-TW
 
所以我猜想原本 values-zh 應該會被轉為 values-zh-Hans,也就是 values-zh 預設已經變成簡體中文,而台灣地區的 locale 為 zh-Hant-TW,Android N 找不到 zh-Hant-TW 的 resource就會往上找 zh-Hant,還是找不到就找 default resource 了

這樣看來在 Android N 之後,繁體中文跟簡體中文 resource 的 search tree 為不同的兩個分支,彼此間不會有交集

Reference
https://litotom.com/2017/05/02/android7-locale-language/
http://blog.30sparks.com/android-7-0-locales-chinese-problem/
https://issuetracker.google.com/issues/37102249
https://issuetracker.google.com/issues/37093759

2017年10月16日 星期一

.NET library SharpSSH socket read fail

SharpSSH 實際上 depends on Org.Mentalis.Security.dll 來作 ssh 的加解密部分,假如執行目錄下沒有 Org.Mentalis.Security.dll 的話,會發現出現 exception,不過這個問題有點難發現到底是甚麼問題,因為它丟出來的是 IOException,而不是 dll not found 之類的 exception

2017年10月4日 星期三

Android studio 找不到 support library version 26

原本在 Android studio 中要使用 support library 是很簡單的,只要在 gradle 的 dependencies block 中加入要使用的 library 就好,這是因為 Android studio 會自動去 jcenter 這個 maven 倉庫 中去找 support library 下來加入到專案,現在 Android stdio 新建專案後,jcenter 都已經會自動的加入到 gradle 的 repositories block,在這邊可看到更詳細的說明

但是從 support library 26 後,會發現 Android studio 回報找不到 support library 的錯誤,因為 google 已經將最新的 support library 搬到自己的 maven 倉庫,所以要在 dependencies block 中宣告 google 的 maven 倉庫才能下載到 support library 26,在這邊可看到範例,詳細說明可看官網

Google issue tracker for Android

Google 將自己內部使用的 issue tacker system open 出來讓外部使用者也可以使用,例如讓 android 開發者可以 submit bug 到 google issue tracker,感覺是個好東西,沒事可以上去看看大家提交了那些 android 的 bug,尤其是 android 最新的版本,可能你遇到的 bug 還沒甚麼人知道,在 stackoverflow 之類的地方找不到解答,就可以上去 google issue tracker 上找找看有沒有人提交這個 bug

reference:
https://developers.google.com/issue-tracker/
https://android-developers.googleblog.com/2017/04/a-new-issue-tracker-for-our-aosp.html?hl=zh_TW

Android 權限動態請求

Android 6.0 (API level 23) 開始,app 權限區分為兩類,normal 跟 dangerous,normal 的權限跟以前一樣在 app 安裝時給予,但 dangerous 的權限在安裝時不會給,app 必須要在執行時動態的請求 user 給予

例如存取 external storage 的權限,只在 manifest 宣告 android.permission.WRITE_EXTERNAL_STORAGE 權限是不夠的,它是屬於 dangerous 的權限,必須要執行時跟使用者要求,並且使用者同意後才可以

開發的時候需將所有需要的權限透過 API 傳給系統,系統會跳出視窗讓使用者選擇是否同意,但視窗上只會顯示該權限的 group,所以 user 同意後是會將整個 group 的權限給予,之後 app 再要求同一 group 的其他權限,系統將不會跳出視窗並且立刻回覆 app 已擁有權限

詳細可看官網說明