diff options
author | Matt Holgate <matt@emobix.co.uk> | 2014-06-20 10:21:40 +0100 |
---|---|---|
committer | Matt Holgate <matt@emobix.co.uk> | 2014-06-20 10:21:40 +0100 |
commit | d7ece4132d6219ee10ba9ed85a9f2a052a6bb92c (patch) | |
tree | 47b751a13321ad9d7fe4fa8261b41ff29587a353 /platform/android/src/com | |
parent | 7ba8c60c5af8ff745336c50c7504bec4f9b22a76 (diff) | |
download | mupdf-d7ece4132d6219ee10ba9ed85a9f2a052a6bb92c.tar.xz |
Improvement which should hopefully help with bug #693607 - MupdfActivity crash when rotating the device.
When cancelling a render async task, we now wait for it to actually finish
before continuing. The benefit of this is that we should be able to guarantee
that its Bitmap becomes eligible for GC before we continue to create any
new bitmaps.
This should hopefully help with the OOM errors seen when rotating
the device and trying to create the new bitmaps.
To prevent the UI thread from being blocked for too long while we're waiting
for the async task to finish, we use a fz_cookie and set the 'abort' flag to
request the render be stopped as soon as possible.
Diffstat (limited to 'platform/android/src/com')
6 files changed, 223 insertions, 68 deletions
diff --git a/platform/android/src/com/artifex/mupdfdemo/CancellableAsyncTask.java b/platform/android/src/com/artifex/mupdfdemo/CancellableAsyncTask.java new file mode 100644 index 00000000..fcb1b744 --- /dev/null +++ b/platform/android/src/com/artifex/mupdfdemo/CancellableAsyncTask.java @@ -0,0 +1,79 @@ +package com.artifex.mupdfdemo; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; + +// Ideally this would be a subclass of AsyncTask, however the cancel() method is final, and cannot +// be overridden. I felt that having two different, but similar cancel methods was a bad idea. +public class CancellableAsyncTask<Params, Result> +{ + private final AsyncTask<Params, Void, Result> asyncTask; + private final CancellableTaskDefinition<Params, Result> ourTask; + + public void onPreExecute() + { + + } + + public void onPostExecute(Result result) + { + + } + + public CancellableAsyncTask(final CancellableTaskDefinition<Params, Result> task) + { + if (task == null) + throw new IllegalArgumentException(); + + this.ourTask = task; + asyncTask = new AsyncTask<Params, Void, Result>() + { + @Override + protected Result doInBackground(Params... params) + { + return task.doInBackground(params); + } + + @Override + protected void onPreExecute() + { + CancellableAsyncTask.this.onPreExecute(); + } + + @Override + protected void onPostExecute(Result result) + { + CancellableAsyncTask.this.onPostExecute(result); + task.doCleanup(); + } + }; + } + + public void cancelAndWait() + { + this.asyncTask.cancel(true); + ourTask.doCancel(); + + try + { + this.asyncTask.get(); + } + catch (InterruptedException e) + { + } + catch (ExecutionException e) + { + } + catch (CancellationException e) + { + } + + ourTask.doCleanup(); + } + + public void execute(Params ... params) + { + asyncTask.execute(params); + } + +} diff --git a/platform/android/src/com/artifex/mupdfdemo/CancellableTaskDefinition.java b/platform/android/src/com/artifex/mupdfdemo/CancellableTaskDefinition.java new file mode 100644 index 00000000..62b04f30 --- /dev/null +++ b/platform/android/src/com/artifex/mupdfdemo/CancellableTaskDefinition.java @@ -0,0 +1,8 @@ +package com.artifex.mupdfdemo; + +public interface CancellableTaskDefinition <Params, Result> +{ + public Result doInBackground(Params ... params); + public void doCancel(); + public void doCleanup(); +} diff --git a/platform/android/src/com/artifex/mupdfdemo/MuPDFCancellableTaskDefinition.java b/platform/android/src/com/artifex/mupdfdemo/MuPDFCancellableTaskDefinition.java new file mode 100644 index 00000000..b95d8e93 --- /dev/null +++ b/platform/android/src/com/artifex/mupdfdemo/MuPDFCancellableTaskDefinition.java @@ -0,0 +1,38 @@ +package com.artifex.mupdfdemo; + +public abstract class MuPDFCancellableTaskDefinition<Params, Result> implements CancellableTaskDefinition<Params, Result> +{ + private MuPDFCore.Cookie cookie; + + public MuPDFCancellableTaskDefinition(MuPDFCore core) + { + this.cookie = core.new Cookie(); + } + + @Override + public void doCancel() + { + if (cookie == null) + return; + + cookie.abort(); + } + + @Override + public void doCleanup() + { + if (cookie == null) + return; + + cookie.destroy(); + cookie = null; + } + + @Override + public final Result doInBackground(Params ... params) + { + return doInBackground(cookie, params); + } + + public abstract Result doInBackground(MuPDFCore.Cookie cookie, Params ... params); +} diff --git a/platform/android/src/com/artifex/mupdfdemo/MuPDFCore.java b/platform/android/src/com/artifex/mupdfdemo/MuPDFCore.java index ecdeccc7..ec35ef7c 100644 --- a/platform/android/src/com/artifex/mupdfdemo/MuPDFCore.java +++ b/platform/android/src/com/artifex/mupdfdemo/MuPDFCore.java @@ -33,12 +33,14 @@ public class MuPDFCore private native void drawPage(Bitmap bitmap, int pageW, int pageH, int patchX, int patchY, - int patchW, int patchH); + int patchW, int patchH, + long cookiePtr); private native void updatePageInternal(Bitmap bitmap, int page, int pageW, int pageH, int patchX, int patchY, - int patchW, int patchH); + int patchW, int patchH, + long cookiePtr); private native RectF[] searchPage(String text); private native TextChar[][][][] text(); private native byte[] textAsHtml(); @@ -69,9 +71,36 @@ public class MuPDFCore private native void destroying(); private native boolean hasChangesInternal(); private native void saveInternal(); + private native long createCookie(); + private native void destroyCookie(long cookie); + private native void abortCookie(long cookie); public native boolean javascriptSupported(); + public class Cookie + { + private final long cookiePtr; + + public Cookie() + { + cookiePtr = createCookie(); + if (cookiePtr == 0) + throw new OutOfMemoryError(); + } + + public void abort() + { + abortCookie(cookiePtr); + } + + public void destroy() + { + // We could do this in finalize, but there's no guarantee that + // a finalize will occur before the muPDF context occurs. + destroyCookie(cookiePtr); + } + } + public MuPDFCore(Context context, String filename) throws Exception { globals = openFile(filename); @@ -152,16 +181,18 @@ public class MuPDFCore public synchronized void drawPage(Bitmap bm, int page, int pageW, int pageH, int patchX, int patchY, - int patchW, int patchH) { + int patchW, int patchH, + MuPDFCore.Cookie cookie) { gotoPage(page); - drawPage(bm, pageW, pageH, patchX, patchY, patchW, patchH); + drawPage(bm, pageW, pageH, patchX, patchY, patchW, patchH, cookie.cookiePtr); } public synchronized void updatePage(Bitmap bm, int page, int pageW, int pageH, int patchX, int patchY, - int patchW, int patchH) { - updatePageInternal(bm, page, pageW, pageH, patchX, patchY, patchW, patchH); + int patchW, int patchH, + MuPDFCore.Cookie cookie) { + updatePageInternal(bm, page, pageW, pageH, patchX, patchY, patchW, patchH, cookie.cookiePtr); } public synchronized PassClickResult passClickEvent(int page, float x, float y) { diff --git a/platform/android/src/com/artifex/mupdfdemo/MuPDFPageView.java b/platform/android/src/com/artifex/mupdfdemo/MuPDFPageView.java index 0d3cc961..c18f44ab 100644 --- a/platform/android/src/com/artifex/mupdfdemo/MuPDFPageView.java +++ b/platform/android/src/com/artifex/mupdfdemo/MuPDFPageView.java @@ -2,6 +2,8 @@ package com.artifex.mupdfdemo; import java.util.ArrayList; +import com.artifex.mupdfdemo.MuPDFCore.Cookie; + import android.annotation.TargetApi; import android.app.AlertDialog; import android.content.ClipData; @@ -550,16 +552,31 @@ public class MuPDFPageView extends PageView implements MuPDFView { return true; } + @Override - protected void drawPage(Bitmap bm, int sizeX, int sizeY, - int patchX, int patchY, int patchWidth, int patchHeight) { - mCore.drawPage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, patchWidth, patchHeight); + protected CancellableTaskDefinition<Void, Void> getDrawPageTask(final Bitmap bm, final int sizeX, final int sizeY, + final int patchX, final int patchY, final int patchWidth, final int patchHeight) { + return new MuPDFCancellableTaskDefinition<Void, Void>(mCore) { + @Override + public Void doInBackground(MuPDFCore.Cookie cookie, Void ... params) { + mCore.drawPage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, patchWidth, patchHeight, cookie); + return null; + } + }; + } - @Override - protected void updatePage(Bitmap bm, int sizeX, int sizeY, - int patchX, int patchY, int patchWidth, int patchHeight) { - mCore.updatePage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, patchWidth, patchHeight); + protected CancellableTaskDefinition<Void, Void> getUpdatePageTask(final Bitmap bm, final int sizeX, final int sizeY, + final int patchX, final int patchY, final int patchWidth, final int patchHeight) + { + return new MuPDFCancellableTaskDefinition<Void, Void>(mCore) { + + @Override + public Void doInBackground(MuPDFCore.Cookie cookie, Void ... params) { + mCore.updatePage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, patchWidth, patchHeight, cookie); + return null; + } + }; } @Override diff --git a/platform/android/src/com/artifex/mupdfdemo/PageView.java b/platform/android/src/com/artifex/mupdfdemo/PageView.java index b7ef580d..c7fecea1 100644 --- a/platform/android/src/com/artifex/mupdfdemo/PageView.java +++ b/platform/android/src/com/artifex/mupdfdemo/PageView.java @@ -21,18 +21,6 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ProgressBar; -class PatchInfo { - public Point patchViewSize; - public Rect patchArea; - public boolean completeRedraw; - - public PatchInfo(Point aPatchViewSize, Rect aPatchArea, boolean aCompleteRedraw) { - patchViewSize = aPatchViewSize; - patchArea = aPatchArea; - completeRedraw = aCompleteRedraw; - } -} - // Make our ImageViews opaque to optimize redraw class OpaqueImageView extends ImageView { @@ -117,13 +105,13 @@ public abstract class PageView extends ViewGroup { private Matrix mEntireMat; private AsyncTask<Void,Void,TextWord[][]> mGetText; private AsyncTask<Void,Void,LinkInfo[]> mGetLinkInfo; - private AsyncTask<Void,Void,Void> mDrawEntire; + private CancellableAsyncTask<Void, Void> mDrawEntire; private Point mPatchViewSize; // View size on the basis of which the patch was created private Rect mPatchArea; private ImageView mPatch; private Bitmap mPatchBm; - private AsyncTask<PatchInfo,Void,PatchInfo> mDrawPatch; + private CancellableAsyncTask<Void,Void> mDrawPatch; private RectF mSearchBoxes[]; protected LinkInfo mLinks[]; private RectF mSelectBox; @@ -147,8 +135,8 @@ public abstract class PageView extends ViewGroup { mEntireMat = new Matrix(); } - protected abstract void drawPage(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight); - protected abstract void updatePage(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight); + protected abstract CancellableTaskDefinition<Void, Void> getDrawPageTask(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight); + protected abstract CancellableTaskDefinition<Void, Void> getUpdatePageTask(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight); protected abstract LinkInfo[] getLinkInfo(); protected abstract TextWord[][] getText(); protected abstract void addMarkup(PointF[] quadPoints, Annotation.Type type); @@ -156,12 +144,12 @@ public abstract class PageView extends ViewGroup { private void reinit() { // Cancel pending render task if (mDrawEntire != null) { - mDrawEntire.cancel(true); + mDrawEntire.cancelAndWait(); mDrawEntire = null; } if (mDrawPatch != null) { - mDrawPatch.cancel(true); + mDrawPatch.cancelAndWait(); mDrawPatch = null; } @@ -233,7 +221,7 @@ public abstract class PageView extends ViewGroup { public void setPage(int page, PointF size) { // Cancel pending render task if (mDrawEntire != null) { - mDrawEntire.cancel(true); + mDrawEntire.cancelAndWait(); mDrawEntire = null; } @@ -274,13 +262,10 @@ public abstract class PageView extends ViewGroup { mGetLinkInfo.execute(); // Render the page in the background - mDrawEntire = new AsyncTask<Void,Void,Void>() { - protected Void doInBackground(Void... v) { - drawPage(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y); - return null; - } + mDrawEntire = new CancellableAsyncTask<Void, Void>(getDrawPageTask(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y)) { - protected void onPreExecute() { + @Override + public void onPreExecute() { setBackgroundColor(BACKGROUND_COLOR); mEntire.setImageBitmap(null); mEntire.invalidate(); @@ -300,12 +285,14 @@ public abstract class PageView extends ViewGroup { } } - protected void onPostExecute(Void v) { + @Override + public void onPostExecute(Void result) { removeView(mBusyIndicator); mBusyIndicator = null; mEntire.setImageBitmap(mEntireBm); mEntire.invalidate(); setBackgroundColor(Color.TRANSPARENT); + } }; @@ -588,8 +575,8 @@ public abstract class PageView extends ViewGroup { mPatch.invalidate(); } } else { - Point patchViewSize = new Point(viewArea.width(), viewArea.height()); - Rect patchArea = new Rect(0, 0, mParentSize.x, mParentSize.y); + final Point patchViewSize = new Point(viewArea.width(), viewArea.height()); + final Rect patchArea = new Rect(0, 0, mParentSize.x, mParentSize.y); // Intersect and test that there is an intersection if (!patchArea.intersect(viewArea)) @@ -608,7 +595,7 @@ public abstract class PageView extends ViewGroup { // Stop the drawing of previous patch if still going if (mDrawPatch != null) { - mDrawPatch.cancel(true); + mDrawPatch.cancelAndWait(); mDrawPatch = null; } @@ -620,24 +607,22 @@ public abstract class PageView extends ViewGroup { mSearchView.bringToFront(); } - mDrawPatch = new AsyncTask<PatchInfo,Void,PatchInfo>() { - protected PatchInfo doInBackground(PatchInfo... v) { - if (v[0].completeRedraw) { - drawPage(mPatchBm, v[0].patchViewSize.x, v[0].patchViewSize.y, - v[0].patchArea.left, v[0].patchArea.top, - v[0].patchArea.width(), v[0].patchArea.height()); - } else { - updatePage(mPatchBm, v[0].patchViewSize.x, v[0].patchViewSize.y, - v[0].patchArea.left, v[0].patchArea.top, - v[0].patchArea.width(), v[0].patchArea.height()); - } + CancellableTaskDefinition<Void, Void> task; - return v[0]; - } + if (completeRedraw) + task = getDrawPageTask(mPatchBm, patchViewSize.x, patchViewSize.y, + patchArea.left, patchArea.top, + patchArea.width(), patchArea.height()); + else + task = getUpdatePageTask(mPatchBm, patchViewSize.x, patchViewSize.y, + patchArea.left, patchArea.top, + patchArea.width(), patchArea.height()); + + mDrawPatch = new CancellableAsyncTask<Void,Void>(task) { - protected void onPostExecute(PatchInfo v) { - mPatchViewSize = v.patchViewSize; - mPatchArea = v.patchArea; + public void onPostExecute(Void result) { + mPatchViewSize = patchViewSize; + mPatchArea = patchArea; mPatch.setImageBitmap(mPatchBm); mPatch.invalidate(); //requestLayout(); @@ -647,30 +632,27 @@ public abstract class PageView extends ViewGroup { } }; - mDrawPatch.execute(new PatchInfo(patchViewSize, patchArea, completeRedraw)); + mDrawPatch.execute(); } } public void update() { // Cancel pending render task if (mDrawEntire != null) { - mDrawEntire.cancel(true); + mDrawEntire.cancelAndWait(); mDrawEntire = null; } if (mDrawPatch != null) { - mDrawPatch.cancel(true); + mDrawPatch.cancelAndWait(); mDrawPatch = null; } + // Render the page in the background - mDrawEntire = new AsyncTask<Void,Void,Void>() { - protected Void doInBackground(Void... v) { - updatePage(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y); - return null; - } + mDrawEntire = new CancellableAsyncTask<Void, Void>(getUpdatePageTask(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y)) { - protected void onPostExecute(Void v) { + public void onPostExecute(Void result) { mEntire.setImageBitmap(mEntireBm); mEntire.invalidate(); } @@ -684,7 +666,7 @@ public abstract class PageView extends ViewGroup { public void removeHq() { // Stop the drawing of the patch if still going if (mDrawPatch != null) { - mDrawPatch.cancel(true); + mDrawPatch.cancelAndWait(); mDrawPatch = null; } |