Reputation: 2440
I have been trying hard to create an activity which allows a user to take a photo and email this image using intents. I create the file name and store it in a string, but it eventually gets it value taken away. When it was being reset to null I ended up creating another string variable to hold its value so I could ensure nothing had access to changing its value, but the second string also ends up as null. I use the value of the string and pass it to the intent in order to add the just taken image as an attachment. Can anyone see why and how this variable is having its value changed?
Here is my class, with comments:
public class ImproveActivity extends Activity implements OnClickListener{
private static final String JPEG_FILE_PREFIX = "Improve_";
private static final String JPEG_FILE_SUFFIX = ".jpg";
private Bitmap mImageBitmap;
private ImageView mImageView;
private File storageDir;
private String mCurrentPhotoPath;
Button share, capture;
private String _path;
private File f;
//This variable holds file name
private String imageFileName;
//I am storing the file name variable here to keep its value
private String temp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.improve_layout);
mImageView = (ImageView)findViewById(R.id.imgview);
capture = (Button)findViewById(R.id.capture);
share = (Button)findViewById(R.id.share);
capture.setOnClickListener(this);
share.setOnClickListener(this);
}
private void dispatchTakePictureIntent() {
if(isIntentAvailable(getApplicationContext(), MediaStore.ACTION_IMAGE_CAPTURE)){
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
;
try {
//f is the image
f = createImageFile();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
startActivityForResult(takePictureIntent, 1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(getApplicationContext(), "error creating image", Toast.LENGTH_LONG).show();
}
}
else{
Toast.makeText(getApplicationContext(), "no camera available", Toast.LENGTH_LONG).show();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent){
Toast.makeText(getApplicationContext(), "returning", Toast.LENGTH_LONG).show();
if (requestCode == 1) {
mImageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath);
if (mImageBitmap == null) {
// bitmap still null
} else {
// set bitmap in imageview
mImageView.setImageBitmap(mImageBitmap);
}
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp =
new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
//display the current image file name
Toast.makeText(getApplicationContext(), imageFileName, Toast.LENGTH_LONG).show();
File file = new File(getAlbumDir().getAbsolutePath());
file.mkdirs();
Toast.makeText(getApplicationContext(), imageFileName, Toast.LENGTH_LONG).show();
File image = File.createTempFile(
imageFileName,
JPEG_FILE_SUFFIX
);
mCurrentPhotoPath = image.getAbsolutePath();
temp = imageFileName;
return image;
}
private File getAlbumDir() {
return storageDir = new File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM
),
"Camera"
);
}
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.capture :
dispatchTakePictureIntent();
//displaying the value of the string imageFileName, it is what it should be, the file name
Toast.makeText(getApplicationContext(), imageFileName, Toast.LENGTH_LONG).show();
//displaying the file name in temp at this point it is still the file name I need
Toast.makeText(getApplicationContext(), "temp is " +temp, Toast.LENGTH_LONG).show();
break;
//Once the image is saved, clicking share to open chooser
case R.id.share:
//displaying the temp string the file name is gone it is now null ????
Toast.makeText(getApplicationContext(), "temp is " +temp, Toast.LENGTH_LONG).show();
Intent picMessageIntent = new Intent(android.content.Intent.ACTION_SEND);
picMessageIntent.setType("image/jpg");
File downloadedPic = new File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM),
"Camera/"+ temp+"jpg"); //I want to use the file name string to send the image via email
picMessageIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(downloadedPic));
startActivity(Intent.createChooser(picMessageIntent, "Send your picture using:"));
break;
}
}
If anyone could shed some light on how this is happening I would be grateful.
Here is the logcat, I open the activity, take a photo, save it, then it goes back to the activity view, I press share and the toast displays the value of temp while the chooser opens.
11-25 17:56:34.573: D/dalvikvm(12037): GC_EXTERNAL_ALLOC freed 1176 objects / 87312 bytes in 53ms
11-25 17:56:39.313: W/IInputConnectionWrapper(12037): showStatusIcon on inactive InputConnection
11-25 17:56:49.803: W/System.err(12037): java.lang.NullPointerException: Argument must not be null
11-25 17:56:49.808: W/System.err(12037): at java.io.FileInputStream.<init>(FileInputStream.java:78)
11-25 17:56:49.808: W/System.err(12037): at java.io.FileInputStream.<init>(FileInputStream.java:134)
11-25 17:56:49.808: W/System.err(12037): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:349)
11-25 17:56:49.808: W/System.err(12037): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:399)
11-25 17:56:49.808: W/System.err(12037): at com.seweb.app.ImproveActivity.onActivityResult(ImproveActivity.java:94)
11-25 17:56:49.808: W/System.err(12037): at android.app.Activity.dispatchActivityResult(Activity.java:3890)
11-25 17:56:49.808: W/System.err(12037): at android.app.ActivityThread.deliverResults(ActivityThread.java:3511)
11-25 17:56:49.808: W/System.err(12037): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3115)
11-25 17:56:49.808: W/System.err(12037): at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3143)
11-25 17:56:49.813: W/System.err(12037): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2684)
11-25 17:56:49.813: W/System.err(12037): at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3815)
11-25 17:56:49.813: W/System.err(12037): at android.app.ActivityThread.access$2400(ActivityThread.java:125)
11-25 17:56:49.813: W/System.err(12037): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2037)
11-25 17:56:49.818: W/System.err(12037): at android.os.Handler.dispatchMessage(Handler.java:99)
11-25 17:56:49.818: W/System.err(12037): at android.os.Looper.loop(Looper.java:123)
11-25 17:56:49.818: W/System.err(12037): at android.app.ActivityThread.main(ActivityThread.java:4627)
11-25 17:56:49.818: W/System.err(12037): at java.lang.reflect.Method.invokeNative(Native Method)
11-25 17:56:49.818: W/System.err(12037): at java.lang.reflect.Method.invoke(Method.java:521)
11-25 17:56:49.823: W/System.err(12037): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
11-25 17:56:49.823: W/System.err(12037): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
11-25 17:56:49.823: W/System.err(12037): at dalvik.system.NativeStart.main(Native Method)
11-25 17:56:50.273: D/dalvikvm(12037): GC_EXTERNAL_ALLOC freed 1920 objects / 108152 bytes in 136ms
Upvotes: 0
Views: 1166
Reputation: 667
I suggest instead of saving it in shareprefrences save it in savedInstanceState.
you can call to store:
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putAll(getIntent().getExtras());
savedInstanceState.putString("currentPhotoPath", mCurrentPhotoPath);
super.onSaveInstanceState(savedInstanceState);
}
and in onCreate call to restore:
if (savedInstanceState != null) {
mCurrentPhotoPath = savedInstanceState.getString("currentPhotoPath", mCurrentPhotoPath);
}
So this is what happens:
You can also check these pages to get better explenation:
https://developer.android.com/training/basics/activity-lifecycle/recreating.html
https://developer.android.com/training/camera/photobasics.html
Upvotes: 0
Reputation: 86948
You should permanently save your Strings in a SharedPreferences, a database, or a generic file.
When you leave your Activity to take a photo with the built-in camera app, your app goes into the background. Any app in the background runs the risk of being killed by the OS if extra memory is needed. If this happens all of your variables are reset to their default null values...
Addition
From your LogCat:
java.lang.NullPointerException: Argument must not be null
...
at com.seweb.app.ImproveActivity.onActivityResult(ImproveActivity.java:94)
I'm guessing that line 94 is:
mImageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath);
which makes sense if the app is destroyed and restarted behind the scenes. When the app is completely restarted like this, only onCreate()
is called, not createImageFile()
or any other methods.
Save mCurrentPhotoPath
in the SharedPreferences as well imageFileName
and restore these values in onActivityResult()
whenever mCurrentPhotoPath
is null.
Using SharedPreferences
Add these methods to your Activity:
private void savePreferences(){
// We need an Editor object to make preference changes.
SharedPreferences.Editor editor = getPreferences(MODE_PRIVATE).edit();
editor.putString("imageFileName", imageFileName);
editor.putString("mCurrentPhotoPath", mCurrentPhotoPath);
// Commit the edits!
editor.commit();
}
private void restorePreferences() {
SharedPreferences settings = getPreferences(MODE_PRIVATE);
imageFileName = settings.getString("imageFileName", "");
mCurrentPhotoPath = settings.getString("mCurrentPhotoPath", "");
}
I ignored temp
, as you said it was just a duplicate of imageFileName
while you were debugging.
Now we need to call these methods at appropriate times. First lets save both Strings just before leaving the Activity to take a picture:
try {
savePreferences();
...
startActivityForResult(takePictureIntent, 1);
}
Then check if you need to reload these values in onActivityResult()
:
if (requestCode == 1) {
if(mCurrentPhotoPath == null)
restorePreferences();
mImageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath);
...
Upvotes: 4
Reputation: 9870
Yes initialize your variable prevent to get a NullPointerException, but the question is, why is it null at this point. Are you sure, that a photo is taken if you get a null-string? Please show stacktrace, maybe something is going wrong with Your CreateImageFile-Method, does this method sometomes throws an Exception? If yes, then this String couldn´t be initialized.
Upvotes: 0