Android: Should I use AsyncTask for these two operations? - android

I have these next two methods, takescreenshots and saveScreenshot:
public static Bitmap takeScreenshot(View oView)
{
Bitmap oBitmap = null;
if(oView!=null) {
if (oView.getWidth()>0 & oView.getHeight()>0) {
oBitmap = Bitmap.createBitmap(oView.getWidth(),
oView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(oBitmap);
try{
oView.draw(canvas);
}catch (Exception ex){
//log or whatever
}
}
}
return oBitmap;
}
public static File saveScreenshot(Bitmap oBitmap)
{
String mPath = ApplicationContext.getActivity().getCacheDir().getAbsolutePath() + "/artandwordsshared_img.jpg";
File imageFile = new File(mPath);
try{
boolean operationOK = true;
if (imageFile.exists()){
operationOK = imageFile.delete();
}
if (operationOK) {
operationOK = imageFile.createNewFile();
}
if (operationOK) {
FileOutputStream outputStream = new FileOutputStream(imageFile);
int quality = 75;
oBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
outputStream.flush();
outputStream.close();
}
}catch(java.io.IOException ex){
ex.printStackTrace();
}
return imageFile;
}
I use this "takeScreenshot" method to capture a view (I use it in some parts of my app) and the other one to save screenshot on device, and I'd like to know if I must/should (regarding good practices) call them through an AsyncTask so they run on background, or if it is not necessary at all. I'm currently using AsyncTask for both and all it's working just fine, but not sure if it is really a must, like it is, for example, for network operations.
Thank you in advance.
Edit 1: Adding the AsyncTask "TakeScreenshot" class.
class TakeScreenshot extends AsyncTask<String, Void, ImageProcessingResult>
{
private WeakReference<View> view2Capture;
private ImageListeners listener;
private String asyncTaskCaller;
TakeScreenshot(View view2Capture, ImageListeners listener, String asyncTaskCaller)
{
this.listener = listener;
this.view2Capture = new WeakReference<>(view2Capture);
this.asyncTaskCaller = asyncTaskCaller;
}
protected ImageProcessingResult doInBackground(String... urls)
{
Bitmap bitmap = null;
String result = Enum.Result.KO;
if(view2Capture.get()!=null)
{
bitmap = ImageHelper.takeScreenshot(view2Capture.get());
result = ImageHelper.saveBitmap(bitmap);
}
return new ImageProcessingResult(bitmap, result);
}
protected void onPostExecute(ImageProcessingResult ipr)
{
listener.onScreenCaptureFinished(ipr, asyncTaskCaller);
}
}
BTW, as for now takeScreenshot method called from AsyncTask is working just fine. Just trying to use good practices, and that's why my post.

I'm not sure you will be able to call first function takeScreenshot in the background Thread. Because you are performing operation with UI Draw. In any case, it makes no sense to put this small implementation to the background.
Next function saveScreenshot must be defined in the background Thread for sure. You need to eliminate the jank on UI which you would have because of using in in the foreground. Maybe you will not feel difference on new devices, but in some condition/platforms you will.
UPDATE
Seems like you new to the Android. Of course you can use AsyncTask, but people prefer other tools. AsyncTask is very old and nowadays there are bunch of alternatives. Just try to search for it.
On another hand. AsyncTask based on Java Executors(which includes ThreadPool, "Handler", MessageQueue, etc). For simple actions like yours, you can use just Thread. Clean and simple.
//Just make it once
private final Handler ui = new Handler(
Looper.getMainLooper());
//Whenever you need just call next call
new Thread("Tag").start(() -> {
//Background Work
//Whenever you need to submit something to UI
ui.post(() -> {
// Foreground Work
});
}
})

Related

How to get WeakReference in doInBackground() of AsyncTask

I have a class named RetrieveCoverImageTask. It retrieves the actual cover image from the URL in a Track instance:
private class RetrieveCoverImageTask extends AsyncTask<Track, Void, Void> {
private WeakReference<Context> context;
RetrieveCoverImageTask(Context context) {
this.context = new WeakReference<>(context);
}
// V1
#Override
protected Void doInBackground(Track... tracks) {
Context context = this.context.get();
if (context != null) {
for (Track track : tracks) {
try {
Bitmap bmp = Picasso.with(context).load(track.getCoverUrl()).get();
if (bmp != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
track.setCoverImage(stream.toByteArray());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
// V2
#Override
protected Void doInBackground(Track... tracks) {
for (Track track : tracks) {
try {
Context context = this.context.get();
if (context != null) {
Bitmap bmp = Picasso.with(context).load(track.getCoverUrl()).get();
if (bmp != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
track.setCoverImage(stream.toByteArray());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
An example call from an Activity is as below:
new RetrieveCoverImageTask(context).execute(track1, track2, track3);
First, I did V1. Then, I thought that the image retrieval with Picasso takes time and the calling Activity may become null in the meanwhile, therefore, I have to get context at the beginning of each iteration of the for loop. That's how I implemented V2.
Which version of doInBackground() is efficient and error-resistant? V1 or V2?
Efficiency is probably not the biggest issue here. V2 is safer, but the whole thing is not very "elegant". It would be much easier to set something up with RxJava. You would not need WeakReferences. The key would be to dispose of your subscription when the context becomes invalid (e.g. onDestroy()) and you would save the check for null.
Also you might want to use an approach to getting the bmp that does not involve picasso and thus does not require context. Or you use the application context which exists as long as your application is running, so you do not need the null check
The WeakReference.get is correct.
If you want to use Picasso, you can do it directly on the View (probably onBindViewholder) cause it already do it in background.
If you want to use a task you need to download the stream instead with a URLConnection to be efficiently, or OkHttp (latest version, cause its embedded as the URLConnection)
There is no point of using Picasso in async task because Picasso anyway loads image asynchronously on a different thread. Also, you are storing bitmaps in variables which will cost you a lot of memory because bitmaps are heavy and all of them will be stored in the ram even when they are not required. A better approach would be to directly use Picasso in your ViewHolder as pointed by Marcos Vasconcelos.

Glide Image request : Does downloadOnly check the cache before downloading only?

I am making a request like below, but i want to know if downloadOnly checks the cache for the image first?
FutureTarget<File> future = Glide.with(applicationContext)
.load(yourUrl)
.downloadOnly(500, 500);
File cacheFile = future.get();
My main issue is that the image in yourUrl is already loaded in the cache, and i need a synchronous way to retrieve the image from the cache in a background thread.
The code above works, but i need to know if there is a cache check before downloadOnly. Thanks.
After turning on verbose logging for Glide, I was able to see exactly what was going on when the image was being called, while DecodeJob mostly fetched from cache in less than 10ms, at times it fetched the data again, not sure maybe from disk or over the wire.
So ultimately i had to check only the cache with a custom StreamModelLoader that throws an exception if trying to go over the wire, and on a cache MISS then use the default flow.
private final StreamModelLoader<String> cacheOnlyStreamLoader = new StreamModelLoader<String>() {
#Override
public DataFetcher<InputStream> getResourceFetcher(final String model, int i, int i1) {
return new DataFetcher<InputStream>() {
#Override
public InputStream loadData(Priority priority) throws Exception {
throw new IOException();
}
#Override
public void cleanup() {
}
#Override
public String getId() {
return model;
}
#Override
public void cancel() {
}
};
}
};
FutureTarget<File> future = Glide.with(progressBar.getContext())
.using(cacheOnlyStreamLoader)
.load(url).downloadOnly(width, height);
File cacheFile = null;
try {
cacheFile = future.get();
} catch(Exception ex) {
ex.printStackTrace(); //exception thrown if image not in cache
}
if(cacheFile == null || cacheFile.length() < 1) {
//didn't find the image in cache
future = Glide.with(progressBar.getContext())
.load(url).downloadOnly(width, height);
cacheFile = future.get(3, TimeUnit.SECONDS); //wait 3 seconds to retrieve the image
}

Running code in main thread in background thread while blocking background thread

I am facing some problems trying to call main thread in my background thread.
Based on this post: Running code in main thread from another thread
The solution should be:
private void runOnMainThread() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Do something
ottoBus.post(new MyObjectEvent(mMyObject));
// End do something
mMyObject = null;
}
});
}
However my background thread is still being able to read Bluetooth socket data between "Do something" and "End do something"
What am I missing here? Is it possible to lock the background thread, while "Do something" is being executed?
My code for reading socket data is following:
InputStream stream = null;
InputStreamReader reader = null;
BufferedReader bufferedReader = null;
String data = "";
try {
stream = mSocket.getInputStream();
byte[] bytes = new byte[20];
int numRead = 0;
while ((numRead = stream.read(bytes)) >= 0) {
String s = new String(bytes, 0, numRead);
if (mMyObject != null) {
fillData(s); // Can cause NPE
} else {
mMyObject = new MyObject();
fillData(s);
}
// This should be synchronised call
runOnMainThread();
Thanks.
You will need to use a Java pattern called wait/notify. Simply put: it defines two threads,
a producer and a consumer, so that the consumer, after initiating the producer, stops and waits until the producer thread has completed.
It goes like this:
static final object uiActionMonitor = new Object();
transient boolean uiCompleted;
void network_thread_run() {
int numRead = 0;
while ((numRead = stream.read(bytes)) >= 0) {
String s = new String(bytes, 0, numRead);
// This should be synchronised call
uiCompleted = false;
runOnMainThread();
synchronized(uiActionMonitor) { //<---------- wait for UI to complete
while (!uiCompleted) {
uiActionMonitor.wait();
}
}
}
And the UI code:
private void runOnMainThread() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Do something
// End do something
uiCompleted = true;
synchronized(uiActionMonitor) { //<---------- release networking thread
uiActionMonitor.notifyAll();
}
}
});
}
Copy the synchronization logic exactly as is. This is where many developers get it wrong.
I must admit I fail to see why you need to block your networking thread while the UI thread is handling your message...
I find CountDownLatch to be the simplest way to accomplish this sort of thing. Here's a reusable method for running Runnables on the main thread and blocking on their completion:
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
private static void runOnMainThreadBlocking(Runnable runnable) throws InterruptedException {
CountDownLatch completionSignal = new CountDownLatch(1);
mainHandler.post(new Runnable() {
#Override public void run() {
runnable.run();
completionSignal.countDown();
}
});
completionSignal.await();
}
I think you need to use locks or synchronized blocs. You can take a look into the java concurency documentation and more specificaly this and this part.
This way you can guaranty that on portion of the code won't be executed muliple times in parallel.

Requesting filesize and MIME type for a list of URLs in Android

In an Android application, I have a list of image URLs like this:
List<String> urls = new ArrayList<String>(100);
urls.add("http://www.example.org/1.jpg");
urls.add("http://www.example.org/2.png");
urls.add("http://www.example.org/3.jpg");
urls.add("http://www.example.org/4.jpg");
urls.add("http://www.example.org/5.png");
urls.add("http://www.example.org/6.png");
urls.add("http://www.example.org/7.png");
urls.add("http://www.example.org/8.jpg");
urls.add("http://www.example.org/9.jpg");
urls.add("http://www.example.org/10.gif");
...
urls.add("http://www.example.org/100.jpg");
Now I have to get the filesize and MIME type for all of these URLs, and this should be done as fast as possible, of course.
What I did is the following:
for (String url : urls) {
int fileSize;
try {
URLConnection urlConnection;
urlConnection = new URL(url).openConnection();
urlConnection.connect();
final String mimeType = urlConnection.getContentType();
final int fileSize = urlConnection.getContentLength();
// do something with those two pieces of information
}
catch (MalformedURLException e) {
continue;
}
catch (IOException e) {
// some special handling
}
}
But this is terribly slow. This is because it is using a single thread and requesting the URLs one by one, while a web browser would always access multiple files at a time, isn't it?
So how can I make it faster?
For HttpClient, I've read that you should re-use instances and there are some ways to use them in a multi-threaded environment.
But how would I do this with URLConnection or any other class that gives you filesize and MIME type?
Edit:
The images are not all on the same host, but spread across only a few servers, say 100 images spread across 5 host names.
Can you use a few threads or run several AsynTasks at once that do the job? Is there anything you have to pay attention to, such as recycling URLConnection objects or so?
I'm not quite sure how to use multiple threads to share task list (100 image files) and merge the results (MIME types and file sizes) afterwards. Can you help?
Split your work up into smaller peaces and let a worker Thread handle it:
The worker Thread:
public class Downloader extends Thread {
private final String mUrl;
private String mMimeType;
private int mFileSize;
public Downloader(String url) {
mUrl = url;
}
#Override
public void run() {
try {
URLConnection urlConnection;
urlConnection = new URL(mUrl).openConnection();
urlConnection.connect();
mMimeType = urlConnection.getContentType();
mFileSize = urlConnection.getContentLength();
} catch (IOException e) {
e.printStackTrace();
}
}
public String getMimeType() {
return mMimeType;
}
public int getFileSize() {
return mFileSize;
}
}
Instantiate, run and wait for the worker:
ArrayList<String> urls = new ArrayList<String>(10);
// ...
ArrayList<Thread> threads = new ArrayList<Thread>(10);
for (String url : urls) {
Thread t = new Downloader(url);
threads.add(t);
t.start();
}
for (Thread t : threads) {
try {
// do not wait for other threads in main UI thread!
t.join();
//((Downloader) t).getMimeType();
//((Downloader) t).getFileSize();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Make sure to note wait for the worker Thread in your UI Thread.
The answer above should only be used for a small set of URLs. A ThreadPool may not be necessary because the Threads will wait for IO most of the time.
Here is your requested answer with a ThreadPool.
It's using the same Downloader class as the above example with only one change:
Spawning Threads is done by the ThreadPool and the single tasks don't need to be a real Thread anymore. So let the Downloader implement a Runnable instead of extending a Thread:
public class Downloader implements Runnable {
Hopefully it's what you are looking for.
public class ThreadedDownloader {
private static final int KEEP_ALIVE_TIME = 1;
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
private LinkedBlockingQueue<Runnable> mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
private ThreadPoolExecutor mDecodeThreadPool = new ThreadPoolExecutor(NUMBER_OF_CORES,
NUMBER_OF_CORES, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, mDecodeWorkQueue) {
#Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Downloader d = (Downloader) r;
// do something with finished Downloader d
// like saving it's result to some sort of list
// d.getMimeType();
// d.getFileSize();
if (mDecodeWorkQueue.isEmpty()) {
onAllDownloadsFinised();
}
}
};
/** Download a list of urls and check it's mime time and file size. */
public void download(List<String> urls) {
for (String url : urls) {
mDecodeThreadPool.execute(new Downloader(url));
}
}
/** Calles when all downloads have finished. */
private void onAllDownloadsFinised() {
// do whatever you want here
// update UI or something
}
}
I don't have example Code, but the HTTP verb HEAD is what you're looking for. It retrieves the headers (including mime and content-length) without transferring the content body.
This answer goes into more detail about what HEAD does.
I think you have most, if not all of the pieces to your solution in the answers already submitted.
Here's what I'd do:
1) make the head requests using Executors.newCachedThreadPool();
2) store the responses in a Map
3) create a payload Map
4) load the real images using AsyncTask instances
5) when each bitmap load completes, store the results in the payload map
Just my thoughts.

How do I cancel an AsyncTask running BitmapFactory.decodeFile() and clean-up

In my interface, the user selects from a variable number of songs, and when a song is selected, I need to display the relevant background image.
The user needs to keep control of the interface while the images are loading, and still be able to change song.
The way I currently do this is using an AsyncTask.
I am executing it using:
if (LoadBG!=null&&!LoadBG.isCancelled())
LoadBG.cancel(false);
LoadBG = new loadBG();
LoadBG.execute((Object) diff.BGPath);
attempting to cancel the previous task if it is still running and creating it anew.
The task code does the bitmap loading:
protected Boolean doInBackground(Object... param) {
String pathName = param[0].toString();
if (!pathName.equals(gfxStore.currentBGPath)) {
currentBGLoaded = false;
while(overlayOpacity!=255)
Thread.yield();
//set current bg
if (this.isCancelled())
return true;
Bitmap d;
try
{
d = gfxStore.factory.decodeFile(pathName,gfxStore.opts);
}
catch (OutOfMemoryError e)
{
System.gc();
return true;
}
if (this.isCancelled())
{
d.recycle();
d = null;
System.gc();
return true;
}
Bitmap s;
try
{
s = gfxStore.scaleImageForCanvas(canvasWidth, canvasHeight,d );
}
catch (OutOfMemoryError e)
{
//XXX uuuugh
System.gc();
return true;
}
if (this.isCancelled())
{
d.recycle();
d=null;
s.recycle();
s=null;
System.gc();
return true;
}
d.recycle();
d=null;
System.gc();
gfxStore.currentBG = s;
gfxStore.currentBGPath = pathName;
wasChange = true;
}
else
wasChange=false;
return true;
}
I've made quite a mess of recycling, nulling, running GC, all attempting to cancel the current task so that the newly created one will have enough memory for available for allocation,but whatever I try, I always get outofmemory exceptions when attempting to run too many too soon (about 4/5 times)
The images are 1024x768 jpg files, and ask for 1.5mb memory allocation, i use the bitmapfactory options:
opts.inPreferredConfig = Bitmap.Config.RGB_565;
opts.inPurgeable = true;
opts.inSampleSize = 1;
Absolutely -any- advice would be appreciated, I've searched to no end about recycling bitmaps, nulling, GCing, attempting to use purgeable bitmaps.
Can you try to serialize the calls with a Mutex, so that only one operation (even if it is being cancelled for some reason) executes? See a very rudimentary approach below. Obviously you could do more selective locking within the doInBackground method, but you get the picture.
static class DecodeLock extends Object {
}
static public DecodeLock lockObject = new DecodeLock();
// ...
protected Boolean doInBackground(Object... param) {
synchronized (lockObject) {
String pathName = param[0].toString();
if (!pathName.equals(gfxStore.currentBGPath)) {
//[..snip]
}
}

Categories

Resources