2

I'm using camera in my application. So in onCreate() I get camera instance, in onPause() I release the camera, in onResume() I get the camera instance if variable is null. Then I added to my app another activity. From this second activity I wanted to close all activities... close the application. I used this approach:

Intent intent = new Intent(getApplicationContext(), FirstActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("EXIT", true);
startActivity(intent);

and

if (getIntent().getBooleanExtra("EXIT", false)) {
    finish();
}

It worked fine. The application closed. But then when I wanted to start the app again I got force close message. I used step by step debugging and my code was failing on line maCamera = getCameraInstance();:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ...

    maCamera = getCameraInstance();

    ...

}

The error log:

11-05 00:48:02.289: E/AndroidRuntime(8257): FATAL EXCEPTION: main
11-05 00:48:02.289: E/AndroidRuntime(8257): java.lang.RuntimeException: Unable to start activity        ComponentInfo{com.blabla/com.blabla.YoMainActivity}: java.lang.NullPointerException
11-05 00:48:02.289: E/AndroidRuntime(8257):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1651)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at android.app.ActivityThread.access$1500(ActivityThread.java:117)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at android.os.Looper.loop(Looper.java:130)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at android.app.ActivityThread.main(ActivityThread.java:3687)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at java.lang.reflect.Method.invokeNative(Native Method)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at java.lang.reflect.Method.invoke(Method.java:507)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at dalvik.system.NativeStart.main(Native Method)
11-05 00:48:02.289: E/AndroidRuntime(8257): Caused by: java.lang.NullPointerException
11-05 00:48:02.289: E/AndroidRuntime(8257):     at com.blabla.YoCameraPreview.<init>(YoCameraPreview.java:26)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at com.blabla.YoMainActivity.onCreate(YoMainActivity.java:82)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
11-05 00:48:02.289: E/AndroidRuntime(8257):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1615)
11-05 00:48:02.289: E/AndroidRuntime(8257):     ... 11 more

I figured that it has something to do with releasing the camera. So I tried to call camera.release() in onDestroy() and it worked. No more force close messages.

So my question is: Is this normal behavior? Why isn't calling release in onPause() enough?

p.s. I can post some more code if it is not usual behavior and the problem is probably in my code.

UPDATE:

Methods in my code:

@Override
protected void onDestroy(){
    super.onDestroy();
    maPreview = null;
    releaseCamera();   
}

@Override
protected void onResume(){
    super.onResume();
    if (maCamera == null) {
        maCamera = getCameraInstance();
    }
    if (maPreview == null) {
        maPreview = new YoCameraPreview(this, maCamera);
        maLayoutPreview.removeAllViews();
        maLayoutPreview.addView(maPreview);
    }
}

@Override
protected void onPause() {
    super.onPause();
    maPreview = null;
    releaseCamera();
}

private void releaseCamera(){
    if (maCamera != null){
        maCamera.release();
        maCamera = null;
    }
}

I don't take pictures with this app. I only use the camera preview, therefore I have no previewCallback set.

Will Neithan
  • 1,190
  • 9
  • 12
  • What does "close all activities" achieve? Maybe, you will be better served by `System.exit()`? – Alex Cohn Nov 05 '14 at 03:51
  • It closes (quits) the application. That was my intention. To close my application on click of a button from activity that is not main (root) activity. And, well I was searching the internet and it is said that one should avoid using `System.exit()` and use `finish()` instead. – Will Neithan Nov 05 '14 at 11:46
  • Internet in general, and SO [_in particular_](http://stackoverflow.com/questions/2033914/quitting-an-application-is-that-frowned-upon), does warn against `System.exit()`, but there are some cases when this way to quit is the most appropriate. `finish()` for all activities does not guarantee that the process will be gone, or that all services will shut down, or all system resources will be released (e.g. Camera instance), and how quickly. How do you know if some activity in your app was ever launched? What is its process affinity, task affinity, etc? – Alex Cohn Nov 05 '14 at 13:02
  • Ok, I've read the post you linked to. And I'm not trying to terminate my app. I'm totally cool with the set system. I finish all the activities in my app and it is not shown in task manager as running. I don't care if it is still hibernating somewhere. That is the OS problem not mime. I'm just simply trying to understand **why my app crashes when I comment out my** `onDestroy()` **method**. But I learned something new from the link you posted, thanks. – Will Neithan Nov 05 '14 at 13:30
  • Ok, so I figured out what the problem is. I stumbled upon [this](http://stackoverflow.com/questions/8282569/oncreate-flow-continues-after-finish) post. And because I was calling `finish()` in `onCreate()` method it immediately calls `onDestroy()` skipping all the other methods as `onStart()`, `onResume()`, `onPause()`, etc. – Will Neithan Nov 05 '14 at 14:04
  • On the face of it, you may find it interesting to learn more about affinity attributes. If you want to tune the Activity stack, they are designed exactly for that: http://developer.android.com/guide/components/tasks-and-back-stack.html. – Alex Cohn Nov 05 '14 at 14:38

2 Answers2

0

Firstly, you must understand the android activity lifecycle (http://developer.android.com/reference/android/app/Activity.html). It goes to onPauses(), and release the camera. After that, if you enter the activity again, it will enter the onResum() instead of oncreate() and cause NPE because the camera is released.

Unless your activity is destroyed, your activity will go to onCreate() again. So to solve your problem, you can either check and get camera instance in onResume() and release in onPause() or create camera onCreate() and release onDestroy() (like your finding). It means you do not need to release on both onPause() and onDestroy(), however, the first approach is recommended.

T D Nguyen
  • 7,054
  • 4
  • 51
  • 71
  • Sorry I think I wasn't clear enough. I understand the activity lifecycle. I had a fully functioning application using camera preview. I have defined all the usual stuff. Release `onPause()`, getting back camera instance `onResume()`. It is all there. The problems occurred after playing around when trying to close all activities at once. Which I fixed by releasing camera instance `onDestroy()`. And that is what I don't understand, because `onResume()` should have been called before and release the camera but clearly it didn't. – Will Neithan Nov 05 '14 at 01:26
  • What do you means close all activities at once? May be it is the problem. I donot think activity can bypass onResume(). – T D Nguyen Nov 05 '14 at 01:32
  • I explained this issue on the beginning of my post. I posted also sample of pseudo code I used. That is why I'm confused. – Will Neithan Nov 05 '14 at 01:43
0

So this post explains my question.

Because, I was catching intent extras inside onCreate() method and I also get camera instance here:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ...

    if (getIntent().getBooleanExtra("EXIT", false)) {
        finish();
    }

    ...

    maCamera = getCameraInstance();

    ...

}

From Android documentation:

You can call finish() from within this function, in which case onDestroy() will be immediately called without any of the rest of the activity lifecycle (onStart(), onResume(), onPause(), etc) executing.

So onCreate() method runs, which gets camera instance and right after onCreate() is done activity jumps right onto onDestroy(). And because I released camera instance only in onPause(), camera was never released. Fixed by adding return right after finish() so it skips getting camera instance.

finish();
return;
Community
  • 1
  • 1
Will Neithan
  • 1,190
  • 9
  • 12
  • but you said you only take Camera in onResume(), not onCreate() – Alex Cohn Nov 05 '14 at 14:29
  • I forgot to mention that I create camera instance also in `onCreate()` and in `onResume()` I have `if` statement to get camera only when it is `null`. My bad, I'll update my question so it is clear. – Will Neithan Nov 05 '14 at 15:15