I'm facing a big problem related to Activity
. My scenario is as follows:
I starts activity A,clicked on a button then picked an image from the gallery then in onActivityResult
I called startActivity(B)
.
Now in my Activity B that image selected in A has been shown on ListView
which is showing.
Now on click of a button I called startActivityforResult(Recorder class)
opened a new activity which is a custom video recorder activity.
Now in activity Recorder on clicking stop button mediaRecorder
stops, release and save video and then after setResult
it goes to again activity B into onActivityResult()
.
But here is the problem:
Two different cases I found here:
First one is when I declared my Recorder class as Landscape:
Here on clicking stop button in Recorder class my screen goes black and after few seconds activity A starts.
Second one is when I changed orientation of Recorder class to Portrait mode:
Here on clicking stop button in Recorder class activity B starts (no black screen) but again after few seconds activity A starts.
One more thing which I observed here:
- I replaced
startActivityForResult()
with startActivity()
in activity B and from Recorder Activity i again called startActivity(B)
which shows activity B,
but here something new happens after few seconds the same activity B starts again.
- I am sure this is just because of
MediaRecorder
as when I commented all media recorder code from my Recorder
class then it was fine.
My Code of Recorder class is here:
public class Recorder extends Activity implements SurfaceHolder.Callback{
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mediaRecorder = new MediaRecorder();
setContentView(R.layout.recorde);
initMediaRecorder();
timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if(counter<=10)
counter++;
//Android UI get Updated continouly
else {
// If condition full filled the timer will stop here
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
timer.cancel();
Recorder.this.finish();
Intent i = new Intent(Recorder.this,B.class);
setResult(Activity.RESULT_OK, i.putExtra("returnedVideo", strPath));
}
}
};
timer.schedule(timerTask, 1000, 1000);
SurfaceView myVideoView = (SurfaceView)findViewById(R.id.videoview);
surfaceHolder = myVideoView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
myButton = (Button)findViewById(R.id.mybutton);
myButton.setOnClickListener(myButtonOnClickListener);
}
private Button.OnClickListener myButtonOnClickListener
= new Button.OnClickListener(){
public void onClick(View arg0) {
// TODO Auto-generated method stub
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
timer.cancel();
Recorder.this.finish();
Intent i = new Intent(Recorder.this,B.class);
setResult(Activity.RESULT_OK, i.putExtra("returnedVideo", strPath));
}};
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
prepareMediaRecorder();
}
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
mediaRecorder.stop();
mediaRecorder.release();
//Recorder.this.finish();
}
@Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
mediaRecorder.stop();
mediaRecorder.release();
}
private void initMediaRecorder(){
Random genraotr = new Random();
int n = 10000;
n = genraotr.nextInt(n);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
CamcorderProfile camcorderProfile_HQ = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
mediaRecorder.setProfile(camcorderProfile_HQ);
mediaRecorder.setOutputFile("/sdcard/my_video"+n+".mp4");
strPath = "/sdcard/my_video"+n+".mp4";
mediaRecorder.setMaxDuration(10000); // Set max duration 10 sec.
mediaRecorder.setMaxFileSize(5000000); // Set max file size 5M
}
private void prepareMediaRecorder(){
mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
try {
mediaRecorder.prepare();
mediaRecorder.start();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
It seems there is a problem with your recorder class. Please try once using this code:
public class AndroidVideoCapture extends Activity{
private Camera myCamera;
private MyCameraSurfaceView myCameraSurfaceView;
private MediaRecorder mediaRecorder;
Button myButton;
SurfaceHolder surfaceHolder;
boolean recording;
public static String strPath;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recording = false;
setContentView(R.layout.main);
//Get Camera for preview
myCamera = getCameraInstance();
if(myCamera == null){
Toast.makeText(AndroidVideoCapture.this,
"Fail to get Camera",
Toast.LENGTH_LONG).show();
}
myCameraSurfaceView = new MyCameraSurfaceView(this, myCamera);
FrameLayout myCameraPreview = (FrameLayout)findViewById(R.id.videoview);
myCameraPreview.addView(myCameraSurfaceView);
myButton = (Button)findViewById(R.id.mybutton);
myButton.setOnClickListener(myButtonOnClickListener);
}
Button.OnClickListener myButtonOnClickListener
= new Button.OnClickListener(){
public void onClick(View v) {
// TODO Auto-generated method stub
if(recording){
// stop recording and release camera
mediaRecorder.stop(); // stop the recording
releaseMediaRecorder(); // release the MediaRecorder object
Intent i = new Intent(AndroidVideoCapture.this,TakeAnotherPhoto.class);
i.putExtra("returnedVideo", strPath);
setResult(Activity.RESULT_OK, i);
//Exit after saved
finish();
}else{
//Release Camera before MediaRecorder start
releaseCamera();
if(!prepareMediaRecorder()){
Toast.makeText(AndroidVideoCapture.this,
"Fail in prepareMediaRecorder()!\n - Ended -",
Toast.LENGTH_LONG).show();
finish();
}
mediaRecorder.start();
recording = true;
myButton.setText("STOP");
}
}};
private Camera getCameraInstance(){
// TODO Auto-generated method stub
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
private boolean prepareMediaRecorder(){
Random genraotr = new Random();
int n = 10000;
n = genraotr.nextInt(n);
myCamera = getCameraInstance();
mediaRecorder = new MediaRecorder();
myCamera.unlock();
mediaRecorder.setCamera(myCamera);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
mediaRecorder.setOutputFile("/sdcard/auction_video"+n+".mp4");
strPath = "/sdcard/auction_video"+n+".mp4";
mediaRecorder.setMaxDuration(10000); // Set max duration 10 sec.
mediaRecorder.setMaxFileSize(5000000); // Set max file size 5M
mediaRecorder.setPreviewDisplay(myCameraSurfaceView.getHolder().getSurface());
try {
mediaRecorder.prepare();
} catch (IllegalStateException e) {
releaseMediaRecorder();
return false;
} catch (IOException e) {
releaseMediaRecorder();
return false;
}
return true;
}
protected void onPause() {
super.onPause();
releaseMediaRecorder(); // if you are using MediaRecorder, release it first
releaseCamera(); // release the camera immediately on pause event
}
private void releaseMediaRecorder(){
if (mediaRecorder != null) {
mediaRecorder.reset(); // clear recorder configuration
mediaRecorder.release(); // release the recorder object
mediaRecorder = null;
myCamera.lock(); // lock camera for later use
}
}
private void releaseCamera(){
if (myCamera != null){
myCamera.release(); // release the camera for other applications
myCamera = null;
}
}
public class MyCameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback{
private SurfaceHolder mHolder;
private Camera mCamera;
public MyCameraSurfaceView(Context context, Camera camera) {
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceChanged(SurfaceHolder holder, int format, int weight,
int height) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// make any resize, rotate or reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
}
}
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}
@Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
mediaRecorder.stop(); // stop the recording
releaseMediaRecorder(); // release the MediaRecorder object
//Exit after saved
finish();
}
// public void showMessage(String title, String message, final Context context){
// try
// {
// AlertDialog.Builder alt_bld = new AlertDialog.Builder(context);
// alt_bld.setTitle(title);
// alt_bld.setMessage(message);
// alt_bld.setCancelable(false);
// alt_bld.setNeutralButton("OK", new DialogInterface.OnClickListener() {
//
// public void onClick(DialogInterface dialog, int which) {
// // TODO Auto-generated method stub
// dialog.dismiss();
//
// Intent i = new Intent(AndroidVideoCapture.this,TakeAnotherPhoto.class);
// i.putExtra("returnedVideo", strPath);
// setResult(Activity.RESULT_OK, i);
// }
// });
// alt_bld.show();
// }
// catch (Exception e) {
// // TODO: handle exception
// }
// }
}
and let me know if it works.
Thanks
I do not see any code that would save and restore the Activity instance state. Android may destroy and recreate activities when it puts another activity on the top, also it restarts activities when you reorient the device. You seem having the problems exactly in these two cases.
Android then tries to resume activities but you need to assist by storing and resuming your state. Not much is restored by default, especially your own fields just get null values.
Implement onSavedInstanceState, putting into provided bundle (map) that may be important for your program to continue working. Then check if onCreate parameter savedInstanceState is not null and if so, restore that you think is important from this bundle. Also, implement onRestoreInstanceState.
Here is the code demonstrating the problem. It is a simple application that has one state variable of the type String. Start the program and try to reorient the device from portrait to landscape. Your message text will changed into something like "My state was null but now restored to 13c140ff7e6". Hence create() has been called the second time, the state field go null and must now be restored from the passed bundle. At the same time, the value in the static field most likely will be preserved (while in some cases also might go away), indicating that this is not a full cold restart of the application. This may explain how simple reorientation may crash the app that is unaware about such things. Starting another activity for result may cause the similar effect. The code also demonstrates how the problem should be solved.
package com.example.state;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
String myState;
static String staticString;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView sv = (TextView) findViewById(R.id.main);
if (savedInstanceState != null) {
String state = savedInstanceState.getString("myState");
sv.setText("My state was " + myState + " but now restored to "
+ state+", static "+staticString);
myState = state;
} else {
String state = Long.toHexString(System.currentTimeMillis());
sv.setText("My state: " + myState + ", cold start, now set to "+state);
myState = state;
staticString = state;
}
}
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
myState = savedInstanceState.getString("myState");
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("myState", myState);
}
}
with layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:id="@+id/main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
/>
</RelativeLayout>