This crash is very random. So when you call adjustCameraParameters() it crashes either when you set Flash or set Focus or sometime when you set Preview.
Its a null pointer exception
Attempt to write to field 'java.util.LinkedHashMap$LinkedEntry java.util.LinkedHashMap$LinkedEntry.prv' on a null object reference.
I imported this as a library so have the latest changes.
Does this occur when you startup the activity containing the camera and/or around the time you call CameraView.start()? If so, you may want to look at a similar issue that has been marked as closed (https://github.com/gogopop/CameraKit-Android/issues/49).
The issue likely has something to do with threading in the CameraView class. Both CameraView.start() and CameraView.setFacing() are called on new threads, and in certain circumstances ( when CameraView.start() is called shortly after the CameraView constructor is called ), two instances of the same methods may be running at once. This could cause various types of modification errors, including ConcurrentModificationException and NullPointerException.
Upon further inspection, I believe the issue is a result of the TextureView's inconsistent callback time.
Shortly after being created, a TextureView executes the callback function onSurfaceTextureAvailable(), which, in this case, calls dispatchSurfaceChanged() and onSurfaceChanged() in the parent PreviewImpl object.
The onSurfaceChanged callback method for the PreviewImpl object is defined in Camera1.java, as seen below:
preview.setCallback(new PreviewImpl.Callback() {
@Override
public void onSurfaceChanged() {
if (mCamera != null) {
setupPreview();
adjustCameraParameters();
}
}
});
The Exception arises because the method adjustCameraParameters() can also be called from the openCamera() ( which is called from the start() method ):
private void openCamera() {
if (mCamera != null) {
releaseCamera();
}
mCamera = Camera.open(mCameraId);
mCameraParameters = mCamera.getParameters();
adjustCameraParameters();
mCamera.setDisplayOrientation(
calculateCameraRotation(mDisplayOrientation)
);
mCameraListener.onCameraOpened();
}
When adjustCameraParameters() is run in the PreviewImpl's callback method, the code runs on the main thread, since the object "preview" is originally created on the main thread in the CameraView constructor.
However, when adjustCameraParameters() is run in openCamera(), it is run on a background thread, since CameraView's start method runs mCameraImpl.start() in a new thread ( As seen below )
public void start() {
...
new Thread(new Runnable() {
@Override
public void run() {
mCameraImpl.start();
}
}).start();
}
In most cases, this causes no problems. If the onSurfaceChanged() callback is called before CameraView.start() is called, then the (mCamera != null) condition in the callback is false since the camera has not been initialized, and nothing occurs. Furthermore, if onSurfaceChanged() is called after the mCameraImpl.start() method ends, then setupPreview() and adjustCameraParameters() on the background thread do not conflict with code on the main thread ( since the adjustment code has already finished running ).
However, in the specific case that the callback is fired while the adjustCameraParameters() method in the background thread has just started, the (mCamera != null) condition will be true, since mCamera is initialized in the openCamera() method before adjustCameraParameters() ( See openCamera() code block above ). As a result, adjustCameraParameters() will begin to run on the main thread as well as the background thread — which, as mentioned in the comment above, causes various types of exceptions.
As mentioned in Issue #49, running mCameraImpl.start() on the main thread instead of a new thread should solve this problem.
try using these overrides to allow things to catch up before starting cameraview:
Thread delayThread0;
Thread delayThread1;
@Override
public void onResume() {
super.onResume();
if(getUserVisibleHint()) {
new Thread(new Runnable() {
@Override
public void run() {
delayThread0 = Thread.currentThread();
try {
delayThread0.sleep(500);
CameraActivity.this.cameraView.start();
}catch(Exception e){
e.printStackTrace();
}
}
}).start();
}else{
if(delayThread0 != null) {
if (delayThread0.isAlive()) {
delayThread0.interrupt();
}
}
if(delayThread1 != null) {
if (delayThread1.isAlive()) {
delayThread1.interrupt();
}
}
cameraView.stop();
}
}
@Override
public void onPause() {
if(delayThread0 != null) {
if (delayThread0.isAlive()) {
delayThread0.interrupt();
}
}
if(delayThread1 != null) {
if (delayThread1.isAlive()) {
delayThread1.interrupt();
}
}
cameraView.stop();
super.onPause();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(cameraView != null) {
if (!isVisibleToUser) {
if(delayThread0 != null) {
if (delayThread0.isAlive()) {
delayThread0.interrupt();
}
}
if(delayThread1 != null) {
if (delayThread1.isAlive()) {
delayThread1.interrupt();
}
}
cameraView.stop();
}
if (isVisibleToUser) {
if(delayThread0 != null){
if(!delayThread0.isAlive()){
new Thread(new Runnable() {
@Override
public void run() {
delayThread1 = Thread.currentThread();
try {
delayThread1.sleep(500);
CameraActivity.this.cameraView.start();
}catch(Exception e){
e.printStackTrace();
}
}
}).start();
}else{
//let thread 0 start camera
}
}else{
new Thread(new Runnable() {
@Override
public void run() {
delayThread1 = Thread.currentThread();
try {
delayThread1.sleep(500);
CameraActivity.this.cameraView.start();
}catch(Exception e){
e.printStackTrace();
}
}
}).start();
}
}
}
}
Device: Honor 8 Lite
OS: EMUI5.0(Android 7.0)
From where do you get getUserVisibleHint?
This should be fixed now with the latest release. Can you update your build.gradle to reference com.wonderkiln:camerakit:0.11.1. If the issue persists can you please re-open.
Thanks!
This issue still exists even in version 0.12.0
Most helpful comment
try using these overrides to allow things to catch up before starting cameraview: