把之前開發(fā)遇到的一些問題總結(jié)一下, 由于很多東西都是和具體平臺(高通/MKT)相關的, 本來有更多的內(nèi)容可以總結(jié), 但由于不在之前公司了,一些源碼和環(huán)境都沒了, 只能寫一下記得比較清楚的東西了.
dumpsys
dumpsys
是Android系統(tǒng)中一個調(diào)試神器, 可以看內(nèi)存信息, 電池信息, 相機參數(shù)等等, 基本Android中每一個大的模塊, 都能通過dumpsys
來查看. 調(diào)試Camera我們通常需要知道一些參數(shù)信息, 默認參數(shù), 實際設置參數(shù), 支持的參數(shù), 這些信息都可以通過dumpsys media.camera
來查看, 具體用法如下:
//執(zhí)行這個adb命令會打印所有默認參數(shù)和當前使用參數(shù)
adb shell dumpsys media.camera
//查看某一項參數(shù),使用過濾命令grep(linux), windows下可使用findstr來過濾
adb shell dumpsys media.camera |grep picture-size
//輸出
picture-size: 3264x2448
picture-size-values:
5520x4140,5984x3366,3840x2160,3264x2448,2048x1536,1920x1080,1280x720,640x480,480x320,320x240
其中參數(shù)后面帶values(比如上面picture-size-values)表示這個參數(shù)可設置的值,需要注意的是, 如果當前沒有打開Camera, 執(zhí)行命令也會有相應輸出, 此時輸出的值是默認值(打開camera不設置任何參數(shù)).
上面說的是針對HAL1的參數(shù), 如果是HAL3參數(shù), 輸出內(nèi)容就不一樣了,如下:
adb shell dumpsys media.camera
// HAL3部分參數(shù)輸出
android.control.aeLockAvailable (10024): byte[1]
[TRUE ]
android.control.awbLockAvailable (10025): byte[1]
[TRUE ]
android.control.availableModes (10026): byte[2]
[1 2 ]
android.shading.availableModes (100002): byte[2]
[1 2 ]
android.statistics.info.availableLensShadingMapModes (120007): byte[1]
[0 ]
android.sensor.info.preCorrectionActiveArraySize (f000a): int32[4]
可以看到, HAL1和HAL3差別很大, HAL3參數(shù)格式和Android Camera API2是對應的,如果要看HAL3的相關參數(shù), 直接使用grep命令存在問題, 因為grep只會輸出有關鍵字的那一行,但HAL3參數(shù)的Key和Values一般都不在一行, 所以要把輸出保存到文件中,然后搜索關鍵字
adb shell dumpsys media.camera > camera.txt
No Display Mode
通常情況下,正常使用Camera的流程是:
- 打開Camera
- 設置參數(shù)
- 設置預覽Surface
- 開啟預覽:startPreview()
- 拍照:takePicture()
但有些情況下, 我們希望不設置預覽Surface就能拍照(默認情況下不設置預覽進行拍照會Crash), 常見的應用場景是雙攝項目, 打開了兩個Camera,但副攝預覽不需要用戶看到, 這樣做也可以減少系統(tǒng)資源占用, 高通和MTK平臺都提供了不設置預覽也可以拍照的功能, 使用方法如下:
- 高通平臺
高通平臺可以在ZSL開啟的情況下, 通過設置參數(shù)Parameters.set("no-display-mode", "1");
來達到不用設置Surface也能拍照的效果(參數(shù)必須在startPreview()之前設置). - MTK平臺
MTK平臺要更簡單些, 保證ZSD開啟的情況下, 直接不用設置Surface, 進行startPreview()和takePicture()即可.
Camera內(nèi)存占用問題
我們都知道, Camera App和CameraService是兩個進程, App內(nèi)存占用大家一般用Android Studio就能看到, 但如果我們在HAL層加入了一些修改, 要看內(nèi)存占用, 這個時候就需要看CameraService的內(nèi)存占用了, CameraService調(diào)用HAL層接口, 所以HAL層內(nèi)存占用就體現(xiàn)在CameraService中, 要找到CameraService的進程, 可通過如下方法:
Android 7.1及以下版本
adb shell ps |grep -i camera
//高通Android 7.1.1, 輸出如下:
cameraserver 371 1 20456 2240 binder_thr a84b396c S /system/bin/cameraserver
camera 401 1 73188 3404 poll_sched ab1a8a6c S /system/bin/mm-qcamera-daemon
可以看到和Camera相關的進程有兩個, 一個是我們要找的CameraService,名稱為cameraserver
另一個就高通的Camera守護進程qcamera-daemon
找到進程后, 查看內(nèi)存就簡單了,也是通過dumpsys來查看(meminfo后面也可以使用pid作為參數(shù)):
$ adb shell dumpsys meminfo cameraserver
Applications Memory Usage (in Kilobytes):
Uptime: 24402928 Realtime: 83208095
Pss Private Private Swap Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 196 196 0 40 0 0 0
Dalvik Heap 0 0 0 0 0 0 0
Stack 32 32 0 64
Other dev 5 0 4 0
.so mmap 470 204 176 508
Other mmap 14 8 4 8
Unknown 88 88 0 88
TOTAL 805 528 184 708 0 0 0
App Summary
Pss(KB)
------
Java Heap: 0
Native Heap: 196
Code: 380
Stack: 32
Graphics: 0
Private Other: 104
System: 93
TOTAL: 805 TOTAL SWAP (KB): 708
Android 8.0及以上版本
在Android 8.0上, Google推出了一項Project Treble計劃,旨在規(guī)范HAL接口, 簡化Android的版本升級,其中一個重要改變就是, Framework層和HAL層也是通過Binder(此Binder和我們通常使用Binder不是同一個)通信, 因此HAL層內(nèi)存占用并不在CameraService中, 而是在名為android.hardware.camera.provider@xx
(xx表示版本號)中,比如SDM450平臺就是android.hardware.camera.provider@2.4
,我們找到這個進程也是通過 adb shell ps |grep -i camera
, 然后通過pid(名字可能會重復)來查看內(nèi)存即可, 基本方法和上面一樣, 只是進程不是CameraService.
API和HAL版本
Camera由于其復雜的特點, Android在發(fā)展過程中, Camera API也進行了更新, 在Android 5.0上, Google推出了Camera2 API, 同樣HAL版本中API1和API2對應的版本是HAL1和HAL3. 但由于一些原因, HAL3和API2普及并不理想, 到目前為止, 中低端手機基本都是API1+HAL1, 對于開發(fā)者來說,我們可能想知道一個App用的API和HAL的版本,這個時候可以通過過濾Log方式來實現(xiàn)(高通和MTK都通用):
$ adb logcat |grep CameraService
04-07 14:07:43.174 371 4415 I CameraService: CameraService::connect call (PID -1 "com.smewise.camera2", camera ID 0) for HAL version default and Camera API version 1
可以看到Log打印的信息有App API的版本和HAL版本, API版本只有API1 和API2, HAL版本一般有三個結(jié)果: 256, 768, default.
256代表使用HAL1, 768為HAL3, default一般表示調(diào)用的是沒有特別指定版本, 根據(jù)平臺配置來決定,這種情況如果想知道到底使用的哪個版本, 需要看平臺代碼或者一些Log.
注:如果平臺只支持HAL1, App通過API2去調(diào)用Camera的話, Framework層會將API2的調(diào)用轉(zhuǎn)為用API1去調(diào)用, 也就是說雖然App使用的API2, 單對于CameraService來說使用的還是API1.
高通平臺副攝可見
做過雙攝項目的一般都知道, 副攝(一般id為2)對上層App是不可見的, 但我們在開發(fā)過程用需要對副攝進行一些測試, 因此是需要能打開副攝的,高通平臺隱藏副攝是在Framework層做的處理, 代碼如下:
API1 代碼:
frameworks/base/core/java/android/hardware/Camera.java
public static int getNumberOfCameras() {
boolean exposeAuxCamera = false;
String packageName = ActivityThread.currentOpPackageName();
/* Force to expose only two cameras
* if the package name does not falls in this bucket
*/
String packageList = SystemProperties.get("camera.aux.packagelist");
if (packageList.length() > 0) {
TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
splitter.setString(packageList);
for (String str : splitter) {
if (packageName.equals(str)) {
exposeAuxCamera = true;
break;
}
}
}
int numberOfCameras = _getNumberOfCameras();
if (exposeAuxCamera == false && (numberOfCameras > 2)) {
numberOfCameras = 2;
}
return numberOfCameras;
}
API2 代碼:
frameworks/base/core/java/android/hardware/camera2/CameraManager.java
private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
// 部分代碼省略
try {
numCameras = cameraService.getNumberOfCameras(CAMERA_TYPE_ALL);
/* Force to expose only two cameras
* if the package name does not falls in this bucket
*/
boolean exposeAuxCamera = false;
String packageName = ActivityThread.currentOpPackageName();
String packageList = SystemProperties.get("camera.aux.packagelist");
if (packageList.length() > 0) {
TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
splitter.setString(packageList);
for (String str : splitter) {
if (packageName.equals(str)) {
exposeAuxCamera = true;
break;
}
}
}
if (exposeAuxCamera == false && (numCameras > 2)) {
numCameras = 2;
}
//部分代碼省略
可以看到,這段代碼邏輯是如果App包名在camera.aux.packagelist
這個屬性中,則可以打開副攝, 否則不行.因此打開副攝有兩種方法:
- 設置
camera.aux.packagelist
這個屬性, 把要打開的App包名添加進去 - 刪除這部分限制Camera個數(shù)的代碼(不推薦)
SnapdragonCamera高級設置
高通平臺調(diào)試Camera一般用的就是SnapdragonCamera這個App, 這個App默認的設置參數(shù)比較有限, 有些我們想調(diào)節(jié)的參數(shù)沒有, 比如anti-banding, 其實高通埋了個彩蛋在設置中的, 只要瘋狂點擊設置菜單中的Reduce Red Eye(減少紅眼)選項, 然后重新進入設置菜單, 就能看到額外的設置選項了, 內(nèi)容非常多, 比較有用, 控制這個額外菜單選項(DeveloperMenu)的代碼如下:
packages/apps/SnapdragonCamera/src/com/android/camera/PhotoMenu.java
public void onPreferenceClicked(ListPreference pref, int y) {
if (!mActivity.isDeveloperMenuEnabled()) {
if (pref.getKey().equals(CameraSettings.KEY_REDEYE_REDUCTION)) {
privateCounter++;
if (privateCounter >= DEVELOPER_MENU_TOUCH_COUNT) {
mActivity.enableDeveloperMenu();
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(mActivity);
prefs.edit().putBoolean(CameraSettings.KEY_DEVELOPER_MENU, true).apply();
RotateTextToast.makeText(mActivity,
"Camera developer option is enabled now", Toast.LENGTH_SHORT).show();
}
} else {
privateCounter = 0;
}
}
//其他代碼省略
CameraService啟動流程
CameraService是在開機的的時候啟動的, 并且不管有沒有App使用Camera, 進程會一直存在, 在開機時,CameraService會發(fā)起一次對底層Camera信息的查詢, 這個操作主要是確定Camera的個數(shù)和Camera基本信息的, 也就是說如果在HAL層及以下, 修改了Camera個數(shù), 需要重啟手機才會生效.
另外由于CameraService開機啟動, 如果HAL層修改東西存在問題, 可能會導致手機無法開機, 但據(jù)我所知, 高通平臺Android 7.1.1及以后的版本, CameraService無法啟動應該是不會影響整個系統(tǒng)的啟動.