-2

I am a super beginner of android studio. I downloaded a source code in github and encountered error running it. My android version is 3.2 and 9.0 pie.

https://github.com/pangguoming/android-camera-color-picker

The error is like below. Bold lines are errors.


10/24 09:07:41: Launching app $ adb shell am start -n "com.pangguoming.whatcolor/com.pangguoming.whatcolor.CameraActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER Client not ready yet..Waiting for process to come online Connected to process 16649 on device sm_g950n-ce031713a52c54930c Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page. I/vndksupport: sphal namespace is not configured for this process. Loading /vendor/lib64/egl/libGLES_mali.so from the current namespace instead. I/InstantRun: starting instant run server: is main process D/libEGL: loaded /vendor/lib64/egl/libGLES_mali.so I/yanzi: Camera open.... I/yanzi: Camera open over.... I/DisplayUtil: Screen---Width = 1080 Height = 2009 densityDpi = 480

E/AndroidRuntime: FATAL EXCEPTION: Thread-7 Process: com.pangguoming.whatcolor, PID: 16649 java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.SurfaceHolder com.pangguoming.whatcolor.CameraSurfaceView.getSurfaceHolder()' on a null object reference at com.pangguoming.whatcolor.CameraActivity.cameraHasOpened(CameraActivity.java:77) at com.pangguoming.whatcolor.CameraInterface.doOpenCamera(CameraInterface.java:51) at com.pangguoming.whatcolor.CameraActivity$1.run(CameraActivity.java:36) I/DisplayUtil: Screen---Width = 1080 Height = 2009 densityDpi = 480 D/OpenGLRenderer: HWUI GL Pipeline D/ViewRootImpl@1927bfe[CameraActivity]: setView = DecorView@19a5f5f[CameraActivity] TM=true MM=false D/ViewRootImpl@1927bfe[CameraActivity]: dispatchAttachedToWindow D/ViewRootImpl@1927bfe[CameraActivity]: Relayout returned: old=[0,0][0,0] new=[0,0][1080,2009] result=0x1 surface={valid=false 0} changed=false E/ViewRootImpl@1927bfe[CameraActivity]: Surface is not valid. D/ViewRootImpl@1927bfe[CameraActivity]: Relayout returned: old=[0,0][1080,2009] new=[0,0][1080,2009] result=0x1 surface={valid=false 0} changed=false D/ViewRootImpl@1927bfe[CameraActivity]: dispatchDetachedFromWindow D/InputEventReceiver: channel 'b2fa8d0 com.pangguoming.whatcolor/com.pangguoming.whatcolor.CameraActivity (client)' ~ Disposing input event receiver. channel 'b2fa8d0 com.pangguoming.whatcolor/com.pangguoming.whatcolor.CameraActivity (client)' ~NativeInputEventReceiver. Application terminated.


How can I fix it? Help me!

Sagar
  • 23,903
  • 4
  • 62
  • 62
hyunseo kim
  • 1
  • 1
  • 2

1 Answers1

0

Looks like a Race Condition.

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Thread openThread = new Thread(){
        @Override
        public void run() {
            // TODO Auto-generated method stub
            CameraInterface.getInstance().doOpenCamera(CameraActivity.this);
        }
    };
    openThread.start();
    setContentView(R.layout.activity_camera);
    initUI();
    initViewParams();

    shutterBtn.setOnClickListener(new BtnListeners());
}

This code is problematic. It runs doOpenCamera() before running initUI(). doOpenCamera() takes an instance of CamOpenOverCallback as an argument, which the CameraActivity implements.

When doOpenCamera() completes, it calls cameraHasOpened() on the CamOpenOverCallback instance it was passed. However, the code in cameraHasOpened() makes a call on the global surfaceView variable, and surfaceView is initialized under initUI().

Since doOpenCamera() is performed on a separate Thread (I'm honestly not sure why, since one of the reasons for the use of a callback is to avoid using Threads), it's possible that initUI() will complete before doOpenCamera() does, however it's not guaranteed.

cameraHasOpened() may be invoked before initUI(), meaning surfaceView is null at that point, leading to your NPE.

The code in that sample project seems kind of sloppy to be honest, and I wouldn't recommend using it. If you need a quick fix, though, some reordering will work:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_camera);
    initUI();
    initViewParams();

    shutterBtn.setOnClickListener(new BtnListeners());

    Thread openThread = new Thread(){ //make the Thread invocation the LAST thing in onCreate()
        @Override
        public void run() {
            // TODO Auto-generated method stub
            CameraInterface.getInstance().doOpenCamera(CameraActivity.this);
        }
    };
    openThread.start();
}

To really fix this issue, though, the Thread itself should be moved to CameraInterface.

Currently, doOpenCamera() looks like this:

public void doOpenCamera(CamOpenOverCallback callback){
    Log.i(TAG, "Camera open....");
    mCamera = Camera.open();
    Log.i(TAG, "Camera open over....");
    callback.cameraHasOpened();
}

It should look like this:

public void doOpenCamera(CamOpenOverCallback callback) {
    new Thread(new Runnable() {
        @Override
        public void run() {  
            Log.i(TAG, "Camera open....");
            mCamera = Camera.open();
            Log.i(TAG, "Camera open over....");
            callback.cameraHasOpened();
        }
    }).start();
}

And onCreate() in CameraActivity should look like this:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_camera);
    initUI();
    initViewParams();

    shutterBtn.setOnClickListener(new BtnListeners());

    CameraInterface.getInstance().doOpenCamera(CameraActivity.this); //remove the wrapped Thread
}

Keep in mind that this project uses the deprecated Camera API. Any project targeting devices running Lollipop (API 21) or later should be using the Camera2 API, while still implementing the deprecated Camera API to provide compatibility to older devices.

TheWanderer
  • 16,775
  • 6
  • 49
  • 63