Search code examples
javaandroidandroid-camera2

Updating MainActivity's TextView in ImageReader's OnImageAvailable


My task is to get preview frames from camera, process them and update a TextView in my layout.I'm referring google's camera2 sample code and have managed to get frames using OnImageAvailableListener's OnImageAvailable() method, but I can't update my TextView's content in OnImageAvailable() definition(App crashes). I'm fairly new to Android programming and java. Any way to update my TextView after getting each frame.

Definition of OnImageAvailable(part of a fragment, not the CameraActivity, like google's sample):

public final ImageReader.OnImageAvailableListener mOnImageAvailableListener
            = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {
            Image image = null;
            try {
                image = reader.acquireLatestImage();
                ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                byte[] imageBytes = new byte[buffer.remaining()];
                buffer.get(imageBytes);
                final Bitmap bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
                textView.append("a"); // crashes here
            } finally {
                if (image != null) {
                    image.close();
                }
            }
        }
    };

Crash log from Android Monitor:

03-19 13:14:12.384 13895-14107/com.example.android.camera2basic E/AndroidRuntime: FATAL EXCEPTION: CameraBackground
                                                                                  Process: com.example.android.camera2basic, PID: 13895
                                                                                  android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                                                                                      at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6462)
                                                                                      at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:932)
                                                                                      at android.view.ViewGroup.invalidateChild(ViewGroup.java:4692)
                                                                                      at android.view.View.invalidateInternal(View.java:11806)
                                                                                      at android.view.View.invalidate(View.java:11770)
                                                                                      at android.view.View.invalidate(View.java:11754)
                                                                                      at android.widget.TextView.checkForRelayout(TextView.java:6867)
                                                                                      at android.widget.TextView.setText(TextView.java:4063)
                                                                                      at android.widget.TextView.setText(TextView.java:3921)
                                                                                      at android.widget.TextView.append(TextView.java:3627)
                                                                                      at android.widget.TextView.append(TextView.java:3617)
                                                                                      at com.example.android.camera2basic.Camera2BasicFragment$6.onImageAvailable(Camera2BasicFragment.java:760)
                                                                                      at android.media.ImageReader$ListenerHandler.handleMessage(ImageReader.java:548)
                                                                                      at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                      at android.os.Looper.loop(Looper.java:135)
                                                                                      at android.os.HandlerThread.run(HandlerThread.java:61)

Solution

  • You need to put it in a runnable/thread. I'm not really versed with it but maybe this might help, but if it doesn't, I'm more than sure it's pointing in the right direction to a solution

    public final ImageReader.OnImageAvailableListener mOnImageAvailableListener
                = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(final ImageReader reader) {
                Image image = null;
                try {
                    image = reader.acquireLatestImage();
                    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                    byte[] imageBytes = new byte[buffer.remaining()];
                    buffer.get(imageBytes);
                    final Bitmap bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
    
                    YourClassNameHere.this.runOnUIThread(new Runnable() {
                        @Override
                        public void run() {
                           textView.append("a"); // crashes here
                    });
    
                } finally {
                    if (image != null) {
                        image.close();
                    }
                }
                }
    
        }
        };