Flutter: cameraパッケージで初回の許可ダイアログ表示後にエラー

※当サイトは、アフィリエイト広告を利用しています

結論:permission_handlerを使い、initializeする前段階で許可ダイアログを出す

2022/12/10 Flutter エラー・バグ日記

 

「camera」パッケージを使ったアプリをAndroidでビルドすると、初回起動時に表示されるカメラアクセス許可のダイアログ(下図)が表示された後、エラーになった。

 

Androidの許可ダイアログ

  

エラーの内容は以下。

 

CameraException(Uninitialized CameraController, getMaxZoomLevel() was called on an uninitialized CameraController.)
・・(略)・・
E/AndroidRuntime( 6867): FATAL EXCEPTION: CameraBackground
E/AndroidRuntime( 6867): Process: com.XXXX.YYYY, PID: 6867
E/AndroidRuntime( 6867): java.lang.NullPointerException: Attempt to invoke virtual method 'void android.hardware.camera2.CameraCaptureSession.close()' on a null object reference
E/AndroidRuntime( 6867): at io.flutter.plugins.camera.Camera.closeCaptureSession(Camera.java:1164)
・・(略)・・

 

本記事はライトな日記思考で書いているので、詳細説明はしておらず、基本、テキストのみで画像とかはあまり載せておりません。。m(_ _)m

解説記事ではないため、解決していない内容や、その時々の間違った解釈を述べてしまっている可能性が大いにありますので、何卒、ご了承ください。

 

該当箇所のコードは以下のとおり。エラー文を見る限り、コントローラーを「initialize」メソッドで初期化後、カメラのズームレベルを取得する所でエラーになっているので、初期化処理がうまく行っていない模様。

 

// クラス名、メソッド名、プロパティ名(変数名)について、筆者が作成したもの(名前変更可のもの)
// の名前の末尾には、大文字のオー「O」をつけています
// ※ライブラリ(パッケージ)で予め決められているもの(名前の変更不可のもの)と、
//  自分で作成したもの(名前の変更可のもの)の区別をしやすくするため

    // cameraControllerOの初期化メソッド ※cameraControllerOはCameraControllerのインスタンス
    await cameraControllerO!.initialize();
    
    // ズームレベルの取得 ↓↓たぶんココでエラーになっている
    await cameraControllerO!
        .getMaxZoomLevel()
        .then((double valueO) => maxAvailableZoomO = valueO);
    await cameraControllerO!
        .getMinZoomLevel()
        .then((double valueO) => minAvailableZoomO = valueO);

 

エラーは初回起動時のみで、アプリを再起動すると、Cameraのプレビューが表示され、問題なく動作する。

 

しかし、必ず1回はアプリが落ちる、という状況は避けたい。。

 

実行環境

各種バージョンは、Flutter 3.3.4、Dart 2.18.2、camera: ^0.10.0+4。

 

エラーは、Android10(SDK30)、Android12(SDK31)、Android13(SDK33)いずれのエミュレーターでも発生する。

 

実機でも発生するので、エミュレーターの問題ではなさそう。

 

「許可ダイアログ表示後にエラーが出る」という内容でググると、以下の報告が見つかったが、Android13のみで発生する内容のため、自分の状況とは異なる。

 

 

参考にしたコード

パッケージのサンプルコードも参考にしつつ、メインは、下記Flutter公式サイトのサンプルコードを参考にしていた。

 

 

このサンプルコードのままであれば問題ないが、これをベースに改変を加えた結果、エラーが生じる状況になってしまった。

 

コードが肥大化しているため、全体像を掲載することは難しいが、基本的な骨組みはサンプルコードと同じはずで、

 

  • 「availableCameras」メソッドでカメラ情報を取得
  • 「CameraController」のインスタンス(コントローラー)を作成
  • 「initialize」メソッドでコントローラーを初期化
  • 「CameraPreview」クラスでプレビュー画面を表示

 

の順番で実装している。

 

パッケージのReadmeにある「build.gradle」の設定や、ライフサイクルの実装も対応している。

 

Camera.javaの修正が必要”だった”らしい

さらに調べると、2019年の記事だが、全く同じエラーについて解説があった。

 

 

Cameraパッケージの「Camera.java」ファイルに、

 

if (pictureImageReader != null)

 

という一文を追記して、エラーを回避するとのこと。

 

しかし、「Camera.java」の中身を確認すると、ほぼ同じ趣旨と思われる

 

if (pictureImageReader == null || pictureImageReader.getSurface() == null) return;

 

という一文が既に入っており、解消済の様子だった(上記2019年の記事で使用されている「camera」パッケージのバージョンは「0.5.5+1」なので、現在よりもかなり古い)。

 

ライフサイクル関連が怪しい

コード内各所にprint文を入れてチェックしたところ、許可ダイアログが出ると、アプリのライフサイクルが「非アクティブ」状態になり、「didChangeAppLifecycleState」メソッドが発動されているとが分かった。

 

前述のとおり、ライフサイクル対応は、Readmeのサンプルコードに従って実装したが、うまく機能していない模様。

 

「Future.delayed」で待ち時間を作ると解消する

そもそも許可ダイアログはどこで発動するのか?をprint文を入れて確認したところ、コントローラーを初期化する「initialize」メソッドの所だと分った。

 

そこで、試しに「initialize」メソッドの後に、以下のように3秒ほど待ち時間を入れてみた。

 

    await cameraControllerO!.initialize();
    Future.delayed(Duration(seconds: 3));

 

すると、エラーが解消された。

 

ということは、、許可ダイアログが表示されると、アプリが非アクティブ状態になるので、

 

許可取得→初期化処理→ズームデータの取得

 

を順番どおりに処理できないのかもしれない(この点、ライフサイクルへの理解が乏しく、不確かではあるが、、)。

 

permission_handlerで事前に許可取得することにした

「Future.delayed」は、無駄な待ち時間が発生し、かつエラーを確実に回避できる保証も無いため、できれば採用したくない。。

 

そこで、許可ダイアログと「initialize」メソッドを同時に発動させないように修正することにした。

 

具体的には、アプリが起動した直後に、先に許可ダイアログを出して、カメラへのアクセス許可を取得しておき、それから離れたタイミングで、「initialize」メソッドを実行するようにした。

 

具体的には、「permission_handler」パッケージを導入し、以下のように、カメラ情報を取得する前の段階で、許可ダイアログを表示させるようにした。

 

   // permission_handlerによるカメラアクセス許可ダイアログの表示
  await Permission.camera.request();

  // デバイスで使用可能なカメラのリストを取得
  final camerasO = await availableCameras();

  // コントローラーの設定や初期化はこの後で実行

 

これにより、「initialize」メソッドの所では許可ダイアログが表示されないようになり、無事、エラーも解消された。

 

根本原因にたどり着けず、今ひとつスッキリしない感はあるが、、調べているとキリが無さそうなので、いったん区切りを付けることにする。

 

\一般的なエラー対処法をまとめた記事はこちら/

 

リリースしたアプリ(全てFlutterで開発)

 

個人アプリ開発で役立ったもの

おすすめの学習教材

超初心者向けでオススメな元Udemyの講座/

 

 \キャンペーン時を狙えば安価で網羅的な内容が学べる(日本語訳あり)/

 

\Gitの基礎について無料で学べる/

 

おすすめの学習書籍

実用的image_pickerに関してかなり助けられた/

 

Dartの基礎文法を素早くインプットできる/


Dart入門 - Dartの要点をつかむためのクイックツアー

タイトルとURLをコピーしました