From 1ef2227bfae3d0daadaeac27dfc8f732c40932e6 Mon Sep 17 00:00:00 2001 From: Angus Kong Date: Fri, 18 Jan 2013 18:11:09 -0800 Subject: Avoid waiting for ContentResolver in main thread. Move ContentResolver insertions to ImageSaver thread instead of main thread. bug:8017753 Change-Id: I42dbb03951eb1bc5736c0b0377c95ff911d1a5cd --- src/com/android/camera/PhotoModule.java | 152 +++++++++++--------------------- src/com/android/camera/Storage.java | 86 ------------------ 2 files changed, 53 insertions(+), 185 deletions(-) diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index 6f88b6aa..1edd35d0 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -114,6 +114,7 @@ public class PhotoModule private static final int START_PREVIEW_DONE = 10; private static final int OPEN_CAMERA_FAIL = 11; private static final int CAMERA_DISABLED = 12; + private static final int UPDATE_SECURE_ALBUM_ITEM = 13; // The subset of parameters we need to update in setCameraParameters(). private static final int UPDATE_PARAM_INITIALIZE = 1; @@ -204,7 +205,7 @@ public class PhotoModule private ImageSaver mImageSaver; // Similarly, we use a thread to generate the name of the picture and insert // it into MediaStore while picture taking is still in progress. - private ImageNamer mImageNamer; + private NamedImages mNamedImages; private Runnable mDoSnapRunnable = new Runnable() { @Override @@ -433,6 +434,11 @@ public class PhotoModule R.string.camera_disabled); break; } + + case UPDATE_SECURE_ALBUM_ITEM: { + mActivity.addSecureAlbumItemIfNeeded(false, (Uri) msg.obj); + break; + } } } } @@ -630,7 +636,7 @@ public class PhotoModule mShutterButton.setVisibility(View.VISIBLE); mImageSaver = new ImageSaver(); - mImageNamer = new ImageNamer(); + mNamedImages = new NamedImages(); mFirstTimeInitialized = true; addIdleHandler(); @@ -668,7 +674,7 @@ public class PhotoModule mLocationManager.recordLocation(recordLocation); mImageSaver = new ImageSaver(); - mImageNamer = new ImageNamer(); + mNamedImages = new NamedImages(); initializeZoom(); keepMediaProviderInstance(); hidePostCaptureAlert(); @@ -967,11 +973,15 @@ public class PhotoModule width = s.height; height = s.width; } - Uri uri = mImageNamer.getUri(); - mActivity.addSecureAlbumItemIfNeeded(false, uri); - String title = mImageNamer.getTitle(); - mImageSaver.addImage(jpegData, uri, title, mLocation, - width, height, orientation); + String title = mNamedImages.getTitle(); + long date = mNamedImages.getDate(); + if (title == null) { + Log.e(TAG, "Unbalanced name/data pair"); + } else { + if (date == -1) date = mCaptureStartTime; + mImageSaver.addImage(jpegData, title, date, mLocation, width, height, + orientation); + } } else { mJpegImageData = jpegData; if (!mQuickCapture) { @@ -1022,8 +1032,8 @@ public class PhotoModule // Each SaveRequest remembers the data needed to save an image. private static class SaveRequest { byte[] data; - Uri uri; String title; + long date; Location loc; int width, height; int orientation; @@ -1057,11 +1067,11 @@ public class PhotoModule } // Runs in main thread - public void addImage(final byte[] data, Uri uri, String title, - Location loc, int width, int height, int orientation) { + public void addImage(final byte[] data, String title, long date, Location loc, + int width, int height, int orientation) { SaveRequest r = new SaveRequest(); r.data = data; - r.uri = uri; + r.date = date; r.title = title; r.loc = (loc == null) ? null : new Location(loc); // make a copy r.width = width; @@ -1102,7 +1112,7 @@ public class PhotoModule } r = mQueue.get(0); } - storeImage(r.data, r.uri, r.title, r.loc, r.width, r.height, + storeImage(r.data, r.title, r.date, r.loc, r.width, r.height, r.orientation); synchronized (this) { mQueue.remove(0); @@ -1139,106 +1149,53 @@ public class PhotoModule } // Runs in saver thread - private void storeImage(final byte[] data, Uri uri, String title, + private void storeImage(final byte[] data, String title, long date, Location loc, int width, int height, int orientation) { - boolean ok = Storage.updateImage(mContentResolver, uri, title, loc, + Uri uri = Storage.addImage(mContentResolver, title, date, loc, orientation, data, width, height); - if (ok) { + if (uri != null) { + mHandler.sendMessage(mHandler.obtainMessage(UPDATE_SECURE_ALBUM_ITEM, uri)); Util.broadcastNewPicture(mActivity, uri); } } } - private static class ImageNamer extends Thread { - private boolean mRequestPending; - private ContentResolver mResolver; - private long mDateTaken; - private int mWidth, mHeight; + private static class NamedImages { + private ArrayList mQueue; private boolean mStop; - private Uri mUri; - private String mTitle; + private NamedEntity mNamedEntity; - // Runs in main thread - public ImageNamer() { - start(); + public NamedImages() { + mQueue = new ArrayList(); } - // Runs in main thread - public synchronized void prepareUri(ContentResolver resolver, - long dateTaken, int width, int height, int rotation) { - if (rotation % 180 != 0) { - int tmp = width; - width = height; - height = tmp; - } - mRequestPending = true; - mResolver = resolver; - mDateTaken = dateTaken; - mWidth = width; - mHeight = height; - notifyAll(); + public void nameNewImage(ContentResolver resolver, long date) { + NamedEntity r = new NamedEntity(); + r.title = Util.createJpegName(date); + r.date = date; + mQueue.add(r); } - // Runs in main thread - public synchronized Uri getUri() { - // wait until the request is done. - while (mRequestPending) { - try { - wait(); - } catch (InterruptedException ex) { - // ignore. - } + public String getTitle() { + if (mQueue.isEmpty()) { + mNamedEntity = null; + return null; } + mNamedEntity = mQueue.get(0); + mQueue.remove(0); - // return the uri generated - Uri uri = mUri; - mUri = null; - return uri; + return mNamedEntity.title; } - // Runs in main thread, should be called after getUri(). - public synchronized String getTitle() { - return mTitle; + // Must be called after getTitle(). + public long getDate() { + if (mNamedEntity == null) return -1; + return mNamedEntity.date; } - // Runs in namer thread - @Override - public synchronized void run() { - while (true) { - if (mStop) break; - if (!mRequestPending) { - try { - wait(); - } catch (InterruptedException ex) { - // ignore. - } - continue; - } - cleanOldUri(); - generateUri(); - mRequestPending = false; - notifyAll(); - } - cleanOldUri(); - } - - // Runs in main thread - public synchronized void finish() { - mStop = true; - notifyAll(); - } - - // Runs in namer thread - private void generateUri() { - mTitle = Util.createJpegName(mDateTaken); - mUri = Storage.newImage(mResolver, mTitle, mDateTaken, mWidth, mHeight); - } - - // Runs in namer thread - private void cleanOldUri() { - if (mUri == null) return; - Storage.deleteImage(mResolver, mUri); - mUri = null; + private static class NamedEntity { + String title; + long date; } } @@ -1303,9 +1260,7 @@ public class PhotoModule animateFlash(); } - Size size = mParameters.getPictureSize(); - mImageNamer.prepareUri(mContentResolver, mCaptureStartTime, - size.width, size.height, mJpegRotation); + mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime); mFaceDetectionStarted = false; setCameraState(SNAPSHOT_IN_PROGRESS); @@ -1745,8 +1700,7 @@ public class PhotoModule if (mImageSaver != null) { mImageSaver.finish(); mImageSaver = null; - mImageNamer.finish(); - mImageNamer = null; + mNamedImages = null; } } diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java index afada236..648fa7d8 100644 --- a/src/com/android/camera/Storage.java +++ b/src/com/android/camera/Storage.java @@ -122,92 +122,6 @@ public class Storage { return uri; } - // newImage() and updateImage() together do the same work as - // addImage. newImage() is the first step, and it inserts the DATE_TAKEN and - // DATA fields into the database. - // - // We also insert hint values for the WIDTH and HEIGHT fields to give - // correct aspect ratio before the real values are updated in updateImage(). - public static Uri newImage(ContentResolver resolver, String title, - long date, int width, int height) { - String path = generateFilepath(title); - - // Insert into MediaStore. - ContentValues values = new ContentValues(4); - values.put(ImageColumns.DATE_TAKEN, date); - values.put(ImageColumns.DATA, path); - - setImageSize(values, width, height); - - Uri uri = null; - try { - uri = resolver.insert(Images.Media.EXTERNAL_CONTENT_URI, values); - } catch (Throwable th) { - // This can happen when the external volume is already mounted, but - // MediaScanner has not notify MediaProvider to add that volume. - // The picture is still safe and MediaScanner will find it and - // insert it into MediaProvider. The only problem is that the user - // cannot click the thumbnail to review the picture. - Log.e(TAG, "Failed to new image" + th); - } - return uri; - } - - // This is the second step. It completes the partial data added by - // newImage. All columns other than DATE_TAKEN and DATA are inserted - // here. This method also save the image data into the file. - // - // Returns true if the update is successful. - public static boolean updateImage(ContentResolver resolver, Uri uri, - String title, Location location, int orientation, byte[] jpeg, - int width, int height) { - // Save the image. - String path = generateFilepath(title); - String tmpPath = path + ".tmp"; - FileOutputStream out = null; - try { - // Write to a temporary file and rename it to the final name. This - // avoids other apps reading incomplete data. - out = new FileOutputStream(tmpPath); - out.write(jpeg); - out.close(); - new File(tmpPath).renameTo(new File(path)); - } catch (Exception e) { - Log.e(TAG, "Failed to write image", e); - return false; - } finally { - try { - out.close(); - } catch (Exception e) { - } - } - - // Insert into MediaStore. - ContentValues values = new ContentValues(9); - values.put(ImageColumns.TITLE, title); - values.put(ImageColumns.DISPLAY_NAME, title + ".jpg"); - values.put(ImageColumns.MIME_TYPE, "image/jpeg"); - // Clockwise rotation in degrees. 0, 90, 180, or 270. - values.put(ImageColumns.ORIENTATION, orientation); - values.put(ImageColumns.SIZE, jpeg.length); - - setImageSize(values, width, height); - - if (location != null) { - values.put(ImageColumns.LATITUDE, location.getLatitude()); - values.put(ImageColumns.LONGITUDE, location.getLongitude()); - } - - try { - resolver.update(uri, values, null, null); - } catch (Throwable th) { - Log.e(TAG, "Failed to update image" + th); - return false; - } - - return true; - } - public static void deleteImage(ContentResolver resolver, Uri uri) { try { resolver.delete(uri, null, null); -- cgit v1.2.3