Permalink
Browse files

ExampleViewerFragment: Use Picasso to load (and cache) images.

This seems to be much faster, even on the first load from the network,
compared to the simple AsyncTask doing a BitmapFactory.decodeStream().
  • Loading branch information...
murraycu committed Nov 28, 2014
1 parent f4fdd69 commit 009ddd077968f1a2feac4de196ec814a74017c62
Showing with 34 additions and 73 deletions.
  1. +1 −0 app/app.iml
  2. +1 −0 app/build.gradle
  3. +32 −73 app/src/main/java/com/murrayc/galaxyzoo/app/ExampleViewerFragment.java
@@ -91,6 +91,7 @@
<orderEntry type="library" exported="" scope="TEST" name="okhttp-2.0.0" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="mockwebserver-2.0.0" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-21.0.2" level="project" />
<orderEntry type="library" exported="" name="picasso-2.4.0" level="project" />
<orderEntry type="library" exported="" name="library-1.0.8" level="project" />
</component>
<component name="org.twodividedbyzero.idea.findbugs">
@@ -29,5 +29,6 @@ dependencies {
compile 'com.android.support:appcompat-v7:21.0.2'
compile 'com.android.support:recyclerview-v7:21.0.0'
compile 'com.mcxiaoke.volley:library:1.0.8'
compile 'com.squareup.picasso:picasso:2.4.0'
androidTestCompile 'com.squareup.okhttp:mockwebserver:2.0.0'
}
@@ -29,6 +29,9 @@
import android.view.ViewGroup;
import android.widget.ImageView;

import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@@ -44,6 +47,7 @@
private View mLoadingView;
private View mRootView;
private String mUriStr = null;
private ImageView mImageView = null;

public ExampleViewerFragment() {
// Required empty public constructor
@@ -59,8 +63,21 @@ public ExampleViewerFragment() {
*/
private void loadBitmap(final String strUri, ImageView imageView) {
showLoadingView(true);
final BitmapWorkerTask task = new BitmapWorkerTask(imageView, this);
task.execute(strUri);

//Note: We call cancelRequest in onPause() to avoid a leak,
//as vaguely suggested by the into() documentation.
Picasso.with(getActivity()).load(strUri).into(imageView, new Callback() {
@Override
public void onSuccess() {
showLoadingView(false);
}

@Override
public void onError() {
showLoadingView(false);
Log.error("ExampleViewerFragment.loadBitmap.onError().");
}
});
}

@Override
@@ -73,91 +90,33 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
}

mRootView = inflater.inflate(R.layout.fragment_example_viewer, container, false);
mImageView = (ImageView) mRootView.findViewById(R.id.imageView);

update();

return mRootView;
}

@Override
public void onPause() {
super.onPause();

//Picasso's load() documentation tells us to use cancelRequest() to avoid a leak,
//though it doesn't suggest where/when to call it:
//http://square.github.io/picasso/javadoc/com/squareup/picasso/RequestCreator.html#into-android.widget.ImageView-com.squareup.picasso.Callback-
Picasso.with(getActivity()).cancelRequest(mImageView);
}

public void update() {
final ImageView imageView = (ImageView) mRootView.findViewById(R.id.imageView);
if (imageView != null) {
loadBitmap(mUriStr, imageView);
if (mImageView != null) {
loadBitmap(mUriStr, mImageView);
}
}

public void setExampleUrl(final String uriStr) {
mUriStr = uriStr;
}

//See http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
private static class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private final WeakReference<ExampleViewerFragment> fragmentReference;

private String strUri = null;

public BitmapWorkerTask(final ImageView imageView, final ExampleViewerFragment fragment) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<>(imageView);

// Use a WeakReference to ensure the ImageView can be garbage collected
fragment.showLoadingView(true);
fragmentReference = new WeakReference<>(fragment);
}

// Decode image in background.
@Override
protected Bitmap doInBackground(String... params) {
strUri = params[0];

URLConnection connection;
try {
final URL url = new URL(strUri);
connection = url.openConnection();
} catch (IOException e) {
Log.error("doInBackground(): ExampleViewerFragment.BitmapWorkerTask.doInBackground: exception while opening connection", e);
return null;
}

InputStream stream = null;
try {
stream = connection.getInputStream();
return BitmapFactory.decodeStream(stream);
} catch (IOException e) {
Log.error("doInBackground(): ExampleViewerFragment.BitmapWorkerTask.doInBackground: exception while using stream", e);
return null;
} finally {
if (stream != null) {
try {
stream.close();
} catch (final IOException e) {
Log.error("doInBackground(): Exception while closing stream.");
}
}
}
}

// Once complete, see if ImageView is still around and set bitmap.
// This avoids calling the ImageView methods in the non-main thread.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}

if (fragmentReference != null) {
final ExampleViewerFragment fragment = fragmentReference.get();
if (fragment != null) {
fragment.showLoadingView(false);
}
}
}
}

private void showLoadingView(boolean show) {
if (mLoadingView == null) {
mLoadingView = mRootView.findViewById(R.id.loading_spinner);

0 comments on commit 009ddd0

Please sign in to comment.