Camera使用指南(二)

接著上一篇接口介紹繼續(xù),這一片主要是看一下代碼結(jié)構(gòu),本身自己缺乏這方面的能力,借著看zxing調(diào)用Camera組件的源碼,也寫了一個小demo,下面介紹一下代碼中如何調(diào)用Camera進行拍照。最后會貼上demo地址。
目錄:

  1. 功能介紹
  2. 根據(jù)功能設(shè)計類
  3. 流程分析

1. 功能介紹

這個demo主要實現(xiàn)了調(diào)用Camera組件進行拍照,并保存圖片的功能。一下詳細的功能拆分:

  1. 調(diào)用相機預(yù)覽;
  2. 預(yù)覽過程中相機自動對焦;
  3. 調(diào)用相機拍照;
  4. 保存圖片;
  5. 退出Activity,釋放相機。

2. 根據(jù)功能設(shè)計類

  1. 顯示界面——CameraActivity,用于相機預(yù)覽,根據(jù)上一篇。。。我們知道,相機預(yù)覽需要使用到SurfaceView,所以,CameraActivity中會有一個SurfaceView變量。同時,我們需要監(jiān)聽SurfaceView中的Surface的生命周期,保證在其生命周期之內(nèi),調(diào)用Camera組件的接口setPreviewDisplay(holder)(設(shè)置顯示預(yù)覽界面的Surface),startPreview()(開啟預(yù)覽),stopPreview()(停止預(yù)覽)進行相應(yīng)的操作,所以CameraActivity還需要實現(xiàn)SurfaceHolder.Callback接口。
  2. 管理Camera的各個接口——CameraManager,上一篇。。。介紹過調(diào)用Camera的接口,CameraManager需要對Camera接口進行封裝,提供給Activity使用,主要包括一下成員函數(shù):
    openDriver(SurfaceHolder holder):連接相機,并設(shè)置相機基礎(chǔ)參數(shù),保證預(yù)覽畫面正常。
    closeDriver():釋放相機。
    setFlashLight(boolean open):打開或關(guān)閉閃關(guān)燈。
    startPreview():開始預(yù)覽,告訴相機硬件將預(yù)覽幀數(shù)據(jù)顯示到屏幕上。
    stopPreview():停止預(yù)覽,告訴相機硬件停止繪制預(yù)覽幀數(shù)據(jù)。
    requestPreviewFrame(Handler handler, int message):獲取下一次預(yù)覽幀數(shù)據(jù),為什么說“下一次”呢,因為預(yù)覽數(shù)據(jù)是在不斷更新的,這樣才能保證我們在移動手機的時候,屏幕能顯示你移動之后畫面。這里調(diào)用的是mCamera.setOneShotPreviewCallback(PreviewCallback),這里只是設(shè)置了一個回調(diào)接口PreviewCallback,當我們調(diào)用這個方法設(shè)置回調(diào)接口之后,下一次預(yù)覽幀數(shù)據(jù)顯示到屏幕上的時候,就會回調(diào)PreviewCallback接口的onPreviewFrame(byte[] data, Camera camera)方法,我們會獲取到一個byte[]類型的數(shù)據(jù),這就是預(yù)覽的幀數(shù)據(jù)。為什么會傳入Handler和Message呢,就是為了在回調(diào)接口被調(diào)用的時候,用這里傳入的Handler處理byte[]數(shù)據(jù)。
    requestAutoFocus(Handler handler, int message):自動對焦,這里需要調(diào)用Camera的 autoFocus(AutoFocusCallback cb)方法,該方法會設(shè)置自動對焦的回調(diào)接口AutoFocusCallback,然后調(diào)用native_autoFocus(),告訴相機硬件自動對焦。底層硬件對焦之后,會回調(diào)AutoFocusCallback接口的onAutoFocus(boolean success, Camera camera)方法,返回對焦知否成功,這時傳入Handler就可以針對對焦狀態(tài)進行下一步操作。需要注意的是,調(diào)用 autoFocus(AutoFocusCallback cb)方法才會自動對焦一次,但是我們經(jīng)常會移動手機,改變物體和攝像頭之間的距離,所以需要隔一段時間請求一次自動對焦,這樣才能保證相機一直都能自動對焦。當然,也可以提供手動對角的方法。
    requestCapture(Handler handler, int message):告訴相機硬件,要拍照,這里會調(diào)用takePicture(ShutterCallback shutter, PictureCallback raw,PictureCallback jpeg)方法,ShutterCallback接口會在拍照的瞬間回調(diào),可以在這里設(shè)置拍照的聲音,第二個參數(shù)PictureCallback接口的回調(diào)方法,會返回未壓縮處理的原生數(shù)據(jù)byte[],可以在這里對照片的原生數(shù)據(jù)進行壓縮處理,轉(zhuǎn)換成我們需要的格式,第三個參數(shù)也是PictureCallback類型,這里的回調(diào)接口返回的數(shù)據(jù)也是byte[]類型的,但是將原生的數(shù)據(jù)壓縮成了jepg格式。這里我們主要關(guān)注第三個參數(shù),傳入的Handler和Message可以在PictureCallback的回調(diào)方法onPictureTaken(byte[] data, Camera camera)進行下一步處理。
  3. 預(yù)覽回調(diào)類——PreviewCallback,需要實現(xiàn)接口Camera.PreviewCallback,當然,如果程序中不需要對預(yù)覽數(shù)據(jù)進一步操作,就不用自定義這個回調(diào)接口了。如果需要對預(yù)覽數(shù)據(jù)進行處理(比如掃描識別二維碼,就是獲取預(yù)覽幀數(shù)據(jù)進行識別),就可以創(chuàng)建自己的預(yù)覽回調(diào)類,實現(xiàn)函數(shù)onPreviewFrame(byte[] data, Camera camera),在這里獲取data進行處理。
  4. 自動對焦的回調(diào)類——AutoFocusCallback,實現(xiàn)接口Camera.AutoFocusCallback,可以在onAutoFocus(boolean success, Camera camera)方法中獲取對焦的結(jié)果。
  5. 拍照的回調(diào)類——PictureCallback,實現(xiàn)接口Camera.PictureCallback,然后在方法onPictureTaken(byte[] data, Camera camera)中獲取拍照的圖片數(shù)據(jù),將其保存成圖片。
  6. 輔助Activity,處理各個回調(diào)方法返回的結(jié)果——CaptureHandler,以上幾個回調(diào)接口的結(jié)果會交有CaptureHandler進行處理,這樣CameraActivity就可以只負責界面顯示,CaptureHandler可以處理業(yè)務(wù)邏輯。

以下各個類之間的靜態(tài)結(jié)構(gòu):


CameraDemo類圖.png

3. 流程分析

為了幫助理解,我們看一下類之間的調(diào)用流程:


CameraDemo時序圖.png

最后看一下代碼中可能需要注意的幾個地方吧。

1. 在CameraActivity的onResume中初始化相機。

protected void onResume() {
    super.onResume();
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
        //初始化Camera
        initCamera();
    } else {
        ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CAMERA }, REQUEST_PERMISSIONS);
    }
}

這里主要是,申請權(quán)限,然后初始化相機。下面看一下initCamera().

private void initCamera() {
    //1:初始化SurfaceView
    if (mSurfaceView == null) {
        mSurfaceView = new SurfaceView(this);
        mSurfaceView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        mContainer.addView(mSurfaceView, 0);
    }
    //2.獲取SurfaceHolder,判斷Surface是否可用
    SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
    Surface surface = surfaceHolder.getSurface();
    boolean isValid = surface == null ? false:surface.isValid();
    if (mHasSurface && isValid) {
        //2.1 SurfaceV可用,直接調(diào)用initCamera(surfaceHolder)初始化相機
        initCamera(surfaceHolder);
    } else {
        //2.2 Surface不可用,通過SurfaceHolder.Callback監(jiān)聽Surface的生命周期
        surfaceHolder.addCallback(this);
        //設(shè)置Surface的type,該參數(shù)表示,由Camera為Surface提供幀數(shù)據(jù)。
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
}
  1. 首先,初始化SurfaceViewSurfaceView在這里可以當做一個普通View進行初始化。
  2. 獲取SurfaceHolder,目的是判斷Surface是否可用,如果可用,直接調(diào)用initCamera(surfaceHolder)初始化相機,如果不可用,需要通過SurfaceHolder.Callback監(jiān)聽Surface的生命周期,確保在surfaceCreated(SurfaceHolder holder)surfaceDestroyed(SurfaceHolder holder)之內(nèi),使用Surface,這里如果不清楚的,可以看上一篇 Camera使用指南(一)。這里的mHasSurfaceonCreate()中設(shè)置成false,在surfaceCreated(SurfaceHolder holder)中設(shè)置為true,在surfaceDestroyed(SurfaceHolder holder)設(shè)置成false,這樣就不需要用isValid來判斷了,這里其實比較多余。
    這里看一下initCamera(surfaceHolder):
private void initCamera(SurfaceHolder surfaceHolder) {
    try {
        //1. 連接相機
        if (!CameraManager.get().openDriver(surfaceHolder)) {
            Log.d(TAG, "Camera被占用");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    if (mCaptureHandler == null) {
        mCaptureHandler = new CaptureHandler(this);
    }
}

這里主要步驟就是調(diào)用CameraManager連接相機,一般情況下不會有什么問題,但是在多個調(diào)用相機的應(yīng)用切換的時候,就很有可能會出問題,但是你的應(yīng)用里可能也不會出現(xiàn)多個調(diào)用相機應(yīng)用切換的時候,比較有的時候,切換是需要先按home鍵或者back鍵返回的,所以這里我沒有進行處理。這里有兩個需要注意的地方,可能會用到:

  1. 多個調(diào)用Camera的進程切換,爭搶Camera,導(dǎo)致應(yīng)用預(yù)覽失敗,預(yù)覽界面黑屏/定屏。
    這種情況可以在調(diào)用openDriver(surfaceHolder)之后,判斷結(jié)果,結(jié)果為false,一般就是openDriver()內(nèi)部發(fā)生了異常,這時,可以等待2S左右,類似這樣:
long oldTime = System.currentTimeMillis();
boolean isRunning = !CameraManager.get().openDriver(surfaceHolder);
while (isRunning && (System.currentTimeMillis() - oldTime) < 2500) {
    try {
        Thread.sleep(50);
    } catch (Exception e) {

    }
    isRunning = !CameraManager.get().openDriver(surfaceHolder);
}
if (isRunning)
    showToast("相機被占用");
  1. 此外,連接相機是一個耗時操作,可以放到子線程中進行,這樣的話,釋放相機也最好放到子線程中。
2. 在CameraActivity的onPause中釋放相機。
protected void onPause() {
    super.onPause();
    //斷開Camera連接
    if (mCaptureHandler != null) {
        try {
            //停止預(yù)覽
            mCaptureHandler.quitSynchronously();
            mCaptureHandler = null;//下次進入重新初始化
            //釋放相機
            CameraManager.get().closeDriver();
        } catch (Exception e) {
            //關(guān)閉攝像頭失敗情況下,最好退出Activity,否則下次初始化相機的時候,會提示相機占用
            finish();
        }
    }
}

其它的內(nèi)容,可以看一下demo,地址如下,CameraDemo.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內(nèi)容

  • 上一篇介紹了如何使用系統(tǒng)相機簡單、快速的進行拍照,本篇將介紹如何使用框架提供的API直接控制攝像機硬件。 你還在為...
    Xiao_Mai閱讀 7,210評論 4 18
  • 這篇文章本來上個月就要寫的,后來一直沒寫,正好在新年寫下,對之前的工作進行總結(jié)。 之前工作中需要自定義相機,網(wǎng)上看...
    zero_sr閱讀 4,879評論 0 11
  • 不怕跌倒,所以飛翔 最近觀看視頻學習的時候,看見慕課網(wǎng)上又關(guān)于Camera實現(xiàn)自定義拍照的功能,特此寫下筆記記錄一...
    筆墨Android閱讀 3,065評論 0 0
  • 現(xiàn)在的工作需要用到camera模塊,所以打算分析Zxing中的camera實現(xiàn),來了解android的camera...
    暴風雨1024閱讀 3,998評論 1 5
  • 她 “你說我是穿裙子好還是穿褲子好?琪琪”女孩早早六點半就起來,她要去車站接他。這次是她們從相戀到六個月的第一次見...
    zpenGl閱讀 230評論 0 0