質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.34%

Xamarin.AndroidでCamera2APIを使用した際、写真撮影2回目にImage is already closedが発生する

受付中

回答 0

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 203

apollo15gou

score 0

前提・実現したいこと

Xamarin初心者です。よろしくお願いいたします。

Xamarin.Androidを用いて、写真撮影を行うアプリケーションを作成しています。
3画面構成で、
メイン画面→写真撮影画面→写真撮影結果表示画面→メイン画面…
に遷移します。
写真撮影結果表示画面からメイン画面に遷移する際は
写真撮影結果表示画面・写真撮影画面には戻れないよう、

intent.AddFlags(ActivityFlags.ClearTop);
intent.AddFlags(ActivityFlags.SingleTop);


を行い遷移します。

Camera2Basicを参考に、Camera2BasicFragment.csをActivityに移植し、
インカメラを用いて写真撮影をする機能を実装しましたが、
ImageAvailableListener.csの

ByteBuffer buffer = mImage.GetPlanes()[0].Buffer;


にブレークポイントを設定すると、1回目のブレークヒットでは例外が発生せず、
2回目にブレークヒットした際に以下のエラーメッセージが発生しました。
また、上記箇所にブレークポイントを設定せずに実機に配置すると、
問題なく、2回目以降の写真の撮影が行えます。

発生している問題・エラーメッセージ

Java.Lang.IllegalStateException
  Message=Image is already closed


例外が発生する理由、例外を解消する方法がわかりません。
Camera2Basicでは同様の挙動にならないため、自身の実装箇所によるものです。
しかし、原因がわからないため、上記例外に対して対応ができていません。

該当のソースコード

using Android.Media;
using Java.IO;
using Java.Lang;
using Java.Nio;
using System;

namespace WorkTimeInputTabletApp.Listeners
{
    public class ImageAvailableListener : Java.Lang.Object, ImageReader.IOnImageAvailableListener
    {
        private readonly EmployeeNumInput owner;
        public ImageAvailableListener(EmployeeNumInput owner)
        {
            if (owner == null)
                throw new System.ArgumentNullException("owner");

            this.owner = owner;
        }

        //public File File { get; private set; }
        //public Camera2BasicFragment Owner { get; private set; }

        public void OnImageAvailable(ImageReader reader)
        {
            owner.mBackgroundHandler.Post(new ImageSaver(reader.AcquireNextImage(), owner));
        }

        // Saves a JPEG {@link Image} into the specified {@link File}.
        private class ImageSaver : Java.Lang.Object, IRunnable
        {
            // The JPEG image
            private Image mImage;

            // The file we save the image into.
            private File mFile;

            private EmployeeNumInput mOwner;

            public ImageSaver(Image image, EmployeeNumInput owner)
            {
                if (image == null)
                    throw new System.ArgumentNullException("image");
                if (owner == null)
                    throw new System.ArgumentNullException("owner");

                mImage = image;
                mOwner = owner;

            }

            public void Run()
            {
                ByteBuffer buffer = mImage.GetPlanes()[0].Buffer;
                byte[] bytes = new byte[buffer.Remaining()];
                buffer.Get(bytes);

                try
                {
                    //フォーマット
                    var dateFormat = "yyyy年MM月dd日";
                    var timeFormat = "HH:mm:ss";

                    var workDate = DateTime.ParseExact(mOwner.Work.WorkDate, dateFormat, null);
                    var workTime = DateTime.ParseExact(mOwner.Work.WorkTime, timeFormat, null);
                    string fileName = mOwner.EmployeeNum.Text + "_" + workDate.ToString("MMdd") + workTime.ToString("HHmm") + ".jpg";
                    mFile = new File(mOwner.GetExternalFilesDir(Android.OS.Environment.DirectoryPictures), fileName);
                }
                catch (System.Exception)
                {
                    DisplayAlert("==DBG==", "ImageAvailableListener ファイル名生成時に例外発生");
                }
                using (var output = new FileOutputStream(mFile))
                {
                    try
                    {
                        output.Write(bytes);
                    }
                    catch (IOException e)
                    {
                        e.PrintStackTrace();
                    }
                    finally
                    {
                        mImage.Close();
                    }
                }
            }

            private void DisplayAlert(string v1, string v2)
            {
                throw new NotImplementedException();
            }
        }
    }
}

試したこと

Message=Image is already closed
なので、初期化処理などが行えていないかと思い、
写真撮影処理(TakePicture())に遷移する前に

Thread.Sleep(3000);


を追加するなどしましたが、
Java.Lang.IllegalStateException
Message=Image is not initialized
という別の例外が発生します。

補足情報(FW/ツールのバージョンなど)

試している実機デバイス HUAWEI P10 lite
Androidバージョン 8.0-API26
Microsoft Visual Studio Community 2019 Version 16.8.2
Xamarin   16.8.000.255

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る