Browse Source

Dropping async task for bonsai in image downloading

master
anthony restaino 7 years ago
parent
commit
3c133748e9
  1. 2
      app/build.gradle
  2. 3
      app/src/main/java/acr/browser/lightning/app/AppComponent.java
  3. 53
      app/src/main/java/acr/browser/lightning/async/AsyncExecutor.java
  4. 37
      app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java
  5. 142
      app/src/main/java/acr/browser/lightning/view/ImageDownloader.java

2
app/build.gradle

@ -66,7 +66,7 @@ dexcount {
dependencies { dependencies {
// support libraries // support libraries
def supportLibVersion = '25.3.0' def supportLibVersion = '25.3.1'
compile "com.android.support:palette-v7:$supportLibVersion" compile "com.android.support:palette-v7:$supportLibVersion"
compile "com.android.support:appcompat-v7:$supportLibVersion" compile "com.android.support:appcompat-v7:$supportLibVersion"
compile "com.android.support:design:$supportLibVersion" compile "com.android.support:design:$supportLibVersion"

3
app/src/main/java/acr/browser/lightning/app/AppComponent.java

@ -7,6 +7,7 @@ import acr.browser.lightning.activity.ReadingActivity;
import acr.browser.lightning.activity.TabsManager; import acr.browser.lightning.activity.TabsManager;
import acr.browser.lightning.activity.ThemableBrowserActivity; import acr.browser.lightning.activity.ThemableBrowserActivity;
import acr.browser.lightning.activity.ThemableSettingsActivity; import acr.browser.lightning.activity.ThemableSettingsActivity;
import acr.browser.lightning.view.ImageDownloader;
import acr.browser.lightning.browser.BrowserPresenter; import acr.browser.lightning.browser.BrowserPresenter;
import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.BookmarkPage;
import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.HistoryPage;
@ -76,4 +77,6 @@ public interface AppComponent {
void inject(SuggestionsAdapter suggestionsAdapter); void inject(SuggestionsAdapter suggestionsAdapter);
void inject(ImageDownloader imageDownloader);
} }

53
app/src/main/java/acr/browser/lightning/async/AsyncExecutor.java

@ -1,53 +0,0 @@
package acr.browser.lightning.async;
import android.support.annotation.NonNull;
import android.util.Log;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
/**
* Created 9/27/2015 Anthony Restaino
*/
public class AsyncExecutor implements Executor {
private static final String TAG = AsyncExecutor.class.getSimpleName();
private static final AsyncExecutor INSTANCE = new AsyncExecutor();
private final Queue<Runnable> mQueue = new ArrayDeque<>(1);
private final ExecutorService mExecutor = Executors.newFixedThreadPool(4);
private AsyncExecutor() {}
@NonNull
public static AsyncExecutor getInstance() {
return INSTANCE;
}
public synchronized void notifyThreadFinish() {
if (mQueue.isEmpty()) {
return;
}
Runnable runnable = mQueue.remove();
execute(runnable);
}
@Override
protected void finalize() throws Throwable {
mExecutor.shutdownNow();
super.finalize();
}
@Override
public void execute(@NonNull Runnable command) {
try {
mExecutor.execute(command);
} catch (RejectedExecutionException ignored) {
mQueue.add(command);
Log.d(TAG, "Thread was enqueued");
}
}
}

37
app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java

@ -35,6 +35,7 @@ import com.anthonycr.bonsai.SingleSubscriber;
import com.squareup.otto.Bus; import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe; import com.squareup.otto.Subscribe;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -44,8 +45,7 @@ import acr.browser.lightning.R;
import acr.browser.lightning.activity.ReadingActivity; import acr.browser.lightning.activity.ReadingActivity;
import acr.browser.lightning.activity.TabsManager; import acr.browser.lightning.activity.TabsManager;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.async.AsyncExecutor; import acr.browser.lightning.view.ImageDownloader;
import acr.browser.lightning.async.ImageDownloadTask;
import acr.browser.lightning.browser.BookmarksView; import acr.browser.lightning.browser.BookmarksView;
import acr.browser.lightning.bus.BookmarkEvents; import acr.browser.lightning.bus.BookmarkEvents;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
@ -74,6 +74,8 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
@Inject PreferenceManager mPreferenceManager; @Inject PreferenceManager mPreferenceManager;
private ImageDownloader mImageDownloader;
private TabsManager mTabsManager; private TabsManager mTabsManager;
private UIController mUiController; private UIController mUiController;
@ -124,7 +126,9 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
mWebpageBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme); mWebpageBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme);
mFolderBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_folder, darkTheme); mFolderBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_folder, darkTheme);
mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(context) : mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(context) :
ThemeUtils.getIconLightThemeColor(context); ThemeUtils.getIconLightThemeColor(context);
mImageDownloader = new ImageDownloader(mWebpageBitmap);
} }
private TabsManager getTabsManager() { private TabsManager getTabsManager() {
@ -222,7 +226,9 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
mWebpageBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_webpage, darkTheme); mWebpageBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_webpage, darkTheme);
mFolderBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, darkTheme); mFolderBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, darkTheme);
mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(activity) : mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(activity) :
ThemeUtils.getIconLightThemeColor(activity); ThemeUtils.getIconLightThemeColor(activity);
mImageDownloader = new ImageDownloader(mWebpageBitmap);
} }
private void updateBookmarkIndicator(final String url) { private void updateBookmarkIndicator(final String url) {
@ -389,14 +395,31 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
ViewCompat.jumpDrawablesToCurrentState(row); ViewCompat.jumpDrawablesToCurrentState(row);
HistoryItem web = mBookmarks.get(position); final HistoryItem web = mBookmarks.get(position);
holder.txtTitle.setText(web.getTitle()); holder.txtTitle.setText(web.getTitle());
if (web.isFolder()) { if (web.isFolder()) {
holder.favicon.setImageBitmap(mFolderBitmap); holder.favicon.setImageBitmap(mFolderBitmap);
} else if (web.getBitmap() == null) { } else if (web.getBitmap() == null) {
holder.favicon.setImageBitmap(mWebpageBitmap); holder.favicon.setImageBitmap(mWebpageBitmap);
new ImageDownloadTask(holder.favicon, web, mWebpageBitmap, BrowserApp.get(context)) holder.favicon.setTag(web.getUrl().hashCode());
.executeOnExecutor(AsyncExecutor.getInstance());
final String url = web.getUrl();
final WeakReference<ImageView> imageViewReference = new WeakReference<>(holder.favicon);
mImageDownloader.newImageRequest(url)
.subscribeOn(Schedulers.worker())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<Bitmap>() {
@Override
public void onItem(@Nullable Bitmap item) {
ImageView imageView = imageViewReference.get();
Object tag = imageView != null ? imageView.getTag() : null;
if (tag != null && tag.equals(url.hashCode())) {
imageView.setImageBitmap(item);
}
web.setBitmap(item);
}
});
} else { } else {
holder.favicon.setImageBitmap(web.getBitmap()); holder.favicon.setImageBitmap(web.getBitmap());
} }

142
app/src/main/java/acr/browser/lightning/async/ImageDownloadTask.java → app/src/main/java/acr/browser/lightning/view/ImageDownloader.java

@ -1,69 +1,91 @@
package acr.browser.lightning.async; package acr.browser.lightning.view;
import android.app.Application; import android.app.Application;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.widget.ImageView;
import com.anthonycr.bonsai.Schedulers; import com.anthonycr.bonsai.Single;
import com.anthonycr.bonsai.SingleAction;
import com.anthonycr.bonsai.SingleSubscriber;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import javax.inject.Inject;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.Utils;
public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> { /**
* An ImageDownloader that creates image
private static final String TAG = ImageDownloadTask.class.getSimpleName(); * loading requests on demand.
@NonNull private final WeakReference<ImageView> mFaviconImage; */
@NonNull private final Application mContext; public class ImageDownloader {
@NonNull private final HistoryItem mWeb;
private final String mUrl; private static final String TAG = "ImageDownloader";
@NonNull private final Bitmap mDefaultBitmap;
@Inject Application mApp;
public ImageDownloadTask(@NonNull ImageView bmImage,
@NonNull HistoryItem web, @NonNull private Bitmap mDefaultBitmap;
@NonNull Bitmap defaultBitmap,
@NonNull Application context) { public ImageDownloader(@NonNull Bitmap defaultBitmap) {
// Set a tag on the ImageView so we know if the view mDefaultBitmap = defaultBitmap;
// has gone out of scope and should not be used BrowserApp.getAppComponent().inject(this);
bmImage.setTag(web.getUrl().hashCode()); }
this.mFaviconImage = new WeakReference<>(bmImage);
this.mWeb = web; /**
this.mUrl = web.getUrl(); * Creates a new image request for the given url.
this.mDefaultBitmap = defaultBitmap; * Emits the bitmap associated with that url, or
this.mContext = context; * the default bitmap if none was found.
*
* @param url the url for which to retrieve the bitmap.
* @return a single that emits the bitmap that was found.
*/
@NonNull
public Single<Bitmap> newImageRequest(@Nullable final String url) {
return Single.create(new SingleAction<Bitmap>() {
@Override
public void onSubscribe(@NonNull SingleSubscriber<Bitmap> subscriber) {
Bitmap favicon = retrieveBitmap(mApp, mDefaultBitmap, url);
Bitmap paddedFavicon = Utils.padFavicon(favicon);
subscriber.onItem(paddedFavicon);
subscriber.onComplete();
}
});
} }
@NonNull @NonNull
@Override private static Bitmap retrieveBitmap(@NonNull Application app,
protected Bitmap doInBackground(Void... params) { @NonNull Bitmap defaultBitmap,
Bitmap mIcon = null; @Nullable String url) {
// unique path for each url that is bookmarked. // unique path for each url that is bookmarked.
if (mUrl == null) { if (url == null) {
return mDefaultBitmap; return defaultBitmap;
} }
File cache = mContext.getCacheDir();
final Uri uri = Uri.parse(mUrl); Bitmap icon = null;
if (uri.getHost() == null || uri.getScheme() == null) { File cache = app.getCacheDir();
return mDefaultBitmap; final Uri uri = Uri.parse(url);
if (uri.getHost() == null || uri.getScheme() == null || Constants.FILE.startsWith(uri.getScheme())) {
return defaultBitmap;
} }
final String hash = String.valueOf(uri.getHost().hashCode()); final String hash = String.valueOf(uri.getHost().hashCode());
final File image = new File(cache, hash + ".png"); final File image = new File(cache, hash + ".png");
final String urlDisplay = uri.getScheme() + "://" + uri.getHost() + "/favicon.ico"; final String urlDisplay = uri.getScheme() + "://" + uri.getHost() + "/favicon.ico";
if (Constants.FILE.startsWith(uri.getScheme())) {
return mDefaultBitmap;
}
// checks to see if the image exists // checks to see if the image exists
if (!image.exists()) { if (!image.exists()) {
FileOutputStream fos = null; FileOutputStream fos = null;
@ -79,12 +101,12 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
in = connection.getInputStream(); in = connection.getInputStream();
if (in != null) { if (in != null) {
mIcon = BitmapFactory.decodeStream(in); icon = BitmapFactory.decodeStream(in);
} }
// ...and cache it // ...and cache it
if (mIcon != null) { if (icon != null) {
fos = new FileOutputStream(image); fos = new FileOutputStream(image);
mIcon.compress(Bitmap.CompressFormat.PNG, 100, fos); icon.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush(); fos.flush();
Log.d(Constants.TAG, "Downloaded: " + urlDisplay); Log.d(Constants.TAG, "Downloaded: " + urlDisplay);
} }
@ -97,9 +119,10 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
} }
} else { } else {
// if it exists, retrieve it from the cache // if it exists, retrieve it from the cache
mIcon = BitmapFactory.decodeFile(image.getPath()); icon = BitmapFactory.decodeFile(image.getPath());
} }
if (mIcon == null) {
if (icon == null) {
InputStream in = null; InputStream in = null;
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
@ -113,12 +136,12 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
in = connection.getInputStream(); in = connection.getInputStream();
if (in != null) { if (in != null) {
mIcon = BitmapFactory.decodeStream(in); icon = BitmapFactory.decodeStream(in);
} }
// ...and cache it // ...and cache it
if (mIcon != null) { if (icon != null) {
fos = new FileOutputStream(image); fos = new FileOutputStream(image);
mIcon.compress(Bitmap.CompressFormat.PNG, 100, fos); icon.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush(); fos.flush();
} }
@ -129,28 +152,11 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
Utils.close(fos); Utils.close(fos);
} }
} }
if (mIcon == null) {
return mDefaultBitmap;
} else {
return mIcon;
}
}
@Override if (icon == null) {
protected void onPostExecute(Bitmap bitmap) { return defaultBitmap;
super.onPostExecute(bitmap); } else {
AsyncExecutor.getInstance().notifyThreadFinish(); return icon;
final Bitmap fav = Utils.padFavicon(bitmap);
final ImageView view = mFaviconImage.get();
if (view != null && view.getTag().equals(mWeb.getUrl().hashCode())) {
Schedulers.main().execute(new Runnable() {
@Override
public void run() {
view.setImageBitmap(fav);
}
});
} }
mWeb.setBitmap(fav);
} }
} }
Loading…
Cancel
Save