problem is in ASyncTask class in Android - android

basically i am reading image bytes from file i have a loop which has to read almost 20 images from file and in this loop i have making the object of my class which extends ASyncTask class and i am reading bytes and drawing on canvas in doItBackground() method and setting my image on onPostExecute() method. so basically Asynchronously 20 threads starts execution in background. now problem is it is not showing any image.
anybody have any idea how to handle multiple AsyncTask class? here is my code.
#Override
protected Void doInBackground(Void... params)
{
// Log.i("start background thread", "start background thread");
Set<Integer> keys;
keys = iTileListOffsets.keySet();
boolean found = false;
Object key[] = keys.toArray();
int k = 0;
for (int i=0; i < key.length && found==false ; i++)
{
k = (Integer) key[i];
MapTileHeader tileHeader = iTileListOffsets.get(k);
if (tileHeader.equalsWith(mapTile.getiMapTileHeader()) )
{
found = true;
}
}
if(found)
{
try
{
synchronized (MapMaker.getSingletonObject())
{
fc.position(0);
input.skipBytes(k);
int tSizeOfTile = input.readInt();
mapTile.loadTileWithFileHeader(input, tSizeOfTile, headerBytesArray);
}
mapMaker.getBufferedTilesDataList().add(mapTile);
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
byte imageTile[] = mapTile.getImageTilesBytes(); //bufferedTile.getImageTilesBytes();
if(mapTile.getImageTilesBytes() != null)
{
try
{
Bitmap tileBitmap = BitmapFactory.decodeByteArray(imageTile, 0, imageTile.length);
canvas.drawBitmap(tileBitmap, rect.left, rect.top, null);
tileBitmap = null;
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
// Log.i("ENDDDD background thread", "ENDDD background thread");
return null;
}
#Override
protected void onPostExecute(Void result)
{
// TODO Auto-generated method stub
super.onPostExecute(result);
Log.i("start POSTECEXCUTE thread", "start POSTECEXCUTE thread");
MyRect mapRect = aBufferArea.GetScreenRectangle(buffer, aScrnArea);
Bitmap smallBitmap = Bitmap.createBitmap(bitmap, mapRect.left, mapRect.top, mapRect.width, mapRect.height);
image.setImageBitmap(smallBitmap);
Log.i("ENDDDDD POSTECEXCUTE thread", "ENDDDD POSTECEXCUTE thread");
}
thanks

I dont think you can update the UI directly from doInBackground, as the code in this method is run in a backgound thread and does not have direct access to the IU thread. You can update the UI either from onPostExecute or from onProgressUpdate. You can call the latter of these from doInBackground by calling publishProgress.
There is a simple example in the documentation for AsyncTask, and this one is a little more complex.

I'd like to suggest a slightly different way of thinking about the problem. Instead of having a thing in the background which both downloads and draws the bitmaps, you might try making the doInBackground method decode and return the Bitmap, then draw it onto the canvas in onPostExecute. This arrangement should prevent you from having contention on the canvas.
Looking at the code you've posted, the loaded bitmaps get drawn onto canvas. Then bitmap gets displayed in image. I can't see, in the code you posted, anywhere that the loaded bitmaps canvas gets sent to the screen.
It might be worth double-checking that the image you're drawing to has a large enough height and width.

Related

Android: Should I use AsyncTask for these two operations?

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
});
}
})

Android: Issues with images downloading

I have an app in which i am downloading and displaying images in a thread. Everything is working fine. But when the images are getting loaded same image is getting loaded in two to three places and when we scroll the screen images are loaded correctly. Please can any one of you can give any suggestions to this?
code:
try{
holder.progress.setVisibility(View.VISIBLE);
DownLoadImageInAThreadHandler(Item, holder);
}
catch(Exception e)
{
System.out.println("Exception in Downloading image : " + e.getMessage());
}
return convertView;
}
//This code is outside the getview method
public void DownLoadImageInAThreadHandler(final CategoryData Item, final ViewHolder holder)
{
nImageDownLoads++;
System.out.println("The images being downloaded :" + nImageDownLoads);
final Handler handler = new Handler()
{
#Override public void handleMessage(Message message)
{
holder.imgitem.setImageDrawable((Drawable) message.obj);
holder.imgitem.setVisibility(View.VISIBLE);
holder.progress.setVisibility(View.GONE);
}
};
//Thread for downloading the images
Thread t = new Thread()
{
public void run()
{
try
{
Item.bImageDownLoaded = 2;
System.out.println("Downloading image :"+Item.ImageUrl);
drawable=getDrawableFromUrl(Item.ImageUrl);
nImageDownLoads--;
System.out.println("Downloaded image :" + Item.ImageUrl);
System.out.println("Remaining images for downloading: " + nImageDownLoads);
if(drawable != null)
{
Item.bImageDownLoaded = 1;
//Send the message to the handler
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
else{
int idNoImage = R.drawable.giftsuggestionsnoimage;
Drawable dwgNoImg = getParent().getResources().getDrawable(idNoImage);
//Send the message to the handler
Message message = handler.obtainMessage(1, dwgNoImg);
handler.sendMessage(message);
}
}
catch(Exception exp)
{
System.out.println("Exception in DownLoadImageInAThread : " + exp.getMessage());
}
}
private Drawable getDrawableFromUrl(String imageUrl) throws IOException
{
try
{
image = DownloadDrawable(imageUrl, "src");
//bmp = readBitmapFromNetwork(new URL(imageUrl));
}
catch (IOException e)
{
e.printStackTrace();
}
return image;
//return bmp;
}
};
t.start();
}
Drawable DownloadDrawable(String url, String src_name) throws java.io.IOException
{
return Drawable.createFromStream(((java.io.InputStream) new java.net.URL(url).getContent()), src_name);
}
Are you using a ListView? if so, is it possible that the view is already recycled by ListView (reused to display another list item) by the time that you write the image in it?
Problem happens because you have written the code to load the images in getView() method.
And that method get called every time you scroll the list.
So that's a poor and not good idea at all.
Remove that code from there..
Load images and in other thread or AsyncTask
And then call notifyDataSetChanged() of you Adapter class object.
Just steps for what I indicated to do, Please read carefully and try to implement, if cant understand anything feel free to ask..sorry couldnt paste any code snippet..
ListView adapter's getView() method
show default icon if array doesn't contain bitmap at that position
if bitmap is there show bitmap else show default icon
AsyncTask to Load Data
do in background
load data and show in listview
AsyncTask to Load Images
do in background
download images one bye one from the urls got in first AsyncTask
in onPostExecute call adapter's notifyDataSetChanged() to show the bitmaps too
you can also call onProgressUpdate periodically means..after 2-3 image download
call publishProgress in doInBackground which will call onProgressUpdate and
in that method also write adpater.notifyDataSetChanged() to
reflect currently downloaded/available bitmaps in array

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]
}
}

A little help with trying to time an async task

Situation: I would like to pull a network resource (a file), display it for 30 seconds, then load the next file. While displaying the initial file for 30 seconds, I'd like to preload the next file.
Question: Should I be using concurrent threads with a lock (like a linked blocking queue?) or a handler? Or something I'm missing?
Currently I have the onCreate call an asynctask that has a for loop which iterates through an array of paths for the files one at a time, which then calls the next asynctask (inside the for loop) which downloads the file from that path and then in the onPostExecute assigns the file to the UI view.
This works but I am unable to setup timing where the first file loads in the UI view then 30 seconds later the next file loads in the UI view and so on... Right now it loads the files fine but the 1st may take 40 seconds to display and the 2nd 25 seconds and the 3rd 60.
(FYI, the files are uniform size and only take 5~15 sec to load)
Here's a general example (non compile-able):
onCreate(){
new firstAsynchTask().execute(filePaths[]);
}
private class firstAsynchTask extends AsyncTask<String[], Void, Void> {
protected Void doInBackground(String[]... x) {
try {
for(int i = 0; x[0] != null && i < x.length; i++)
{
long startT = System.currentTimeMillis();
if(x[0][i].isFile())
{
SmbFile g = new SmbFile(x[0][i].getPath());
new secondAsynchTask().execute(g);
long TimeNow = (System.currentTimeMillis() - 30000);
if(startT > TimeNow)
{
try {
Thread.sleep(startT - TimeNow);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();}
}
}
if(i == x.length - 1)
{
i = 0;
}
}
return null;
} catch (IOException e) {
// TODO Auto-generated catch block
return null;
}
}
private class SecondAsyncTask extends AsyncTask<File, Integer, Drawable> {
protected File doInBackground(File... x) {
SomeType FileTemp = new File();
try {
long startT = System.currentTimeMillis();
FileTemp = (get streamed file);
long TimeNow = (System.currentTimeMillis() - 30000);
if(startT > TimeNow)
{
Thread.sleep(startT - TimeNow);
return FileTemp;
}
else
{
return FileTemp;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
protected void onPostExecute(File result) {
if(result != null)
{
FileView centerFile = (FileView) findViewById(R.id.FileView1);
centerFile.setFile(result);
}
}
}
It looks like a simple producer-consumer approach would be sufficient for what you need. I would have an array of URLs that point to your network resource with a synchronised access. A single AsyncTask would be enough to pop a single URL off the queue, download and display it. How you will re-invoke the task depends on you: you can display downloaded resource and at the same time run task, putting downloaded resource to some temporary placeholder (a Drawable member field for example). Have a timer that every 30 seconds just puts temporary resource to the display and runs download/cache task again.
Of course this is a very simple solutions without handling cases when your task haven't finished in 30 seconds time frame, etc.
Another approach would be to have a boolean object that you will set to true every 30 seconds. Your AsyncTask runs, downloads image and on onPostExecute checks if previously mentioned boolean is set to true, if yes - displays image, sets flag to false and finishes. If flag is false then it enters a loop that sleeps for, say, 200ms and checks condition again, etc.

out of memory in thread repeatition?

friends,
i am using following code to display single full screen image in activity using thread problem scenario is from custom image gallery on each thumbnail clicking displays large image on this screen.
Now the problem is the user clicks on image and thread loads image and presses back button to go to previous page user keeps clicking each thumbnail one by one to display full screen image and repeats this scenario.
Finally, application crashes with out of memory error bitmap.
Please guide what mistake am i doing?
public void Move(final String path)
{
if (!isConnected()) {
Constants.DisplayMessage(getApplicationContext(),
Constants.CONNECTION_ERROR_MESSAGE);
return;
}
progressBar = (ProgressBar) findViewById(R.id.progressbar_default);
progressBar.setVisibility(View.VISIBLE);
myThread = new Thread(new Runnable() {
public void run() {
while (serviceData == null) {
serviceData = DisplayLiveImage(path);
callComplete = true;
}
mHandler.post(MoveNow());
}
});
if(myThread.getState() == Thread.State.NEW)
myThread.start();
}
private Runnable MoveNow() {
return new Runnable() {
public void run() {
if (callComplete) {
try
{
if (!serviceData.equals(""))
{
bm = (Bitmap)serviceData;
float ImageHeight = bm.getHeight();
float ImageWidth = bm.getWidth();
float totalHeight = (ImageHeight / ImageWidth ) * CurrentScreenWidth;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
params.width = LayoutParams.FILL_PARENT;
params.height = (int)totalHeight;
img.setLayoutParams(params);
img.setImageBitmap(bm);
}
else
{
// show error
}
}catch(Exception ex)
{
// show error
}finally
{
progressBar.setVisibility(View.GONE);
serviceData = null;
callComplete = false;
}
}
}
};
}
public void stopThread()
{
try
{
if(myThread != null)
myThread.interrupt();
}catch(Exception ex)
{ }
}
#Override
public void onDestroy()
{
if(bm != null)
{
bm.recycle();
bm = null;
}
stopThread();
super.onDestroy();
}
Bitmaps are the most annoying thing to manage with Android.
To avoid most of the OOM exception I use :
//Try to free up some memory
System.gc();
System.runFinalization();
System.gc();
Yes you read right 2 system.gc...Go figure :s
Just before trying to decode it. So in your case it would be before the setImageBitmap I presume. But look into your logcats and put it just before the line that makes your app crashes.
If it still happen try to open a smaller version of your bitmap by using bitmapOtions.insamplesize when you allocate your bitmap.
Are you running the debugger when the out of memory error occurs? If so, then the debugger might be the source of the problem. If you run the app under the debugger, then any threads created will still be retained by the debugger, even when they're finished running. This leads to memory errors that won't occur when the app is running without the debugger. Try running without the debugger to see if you still get the memory leak.
http://code.google.com/p/android/issues/detail?id=7979
https://android.googlesource.com/platform/dalvik/+/master/docs/debugger.html
If this isn't the case, then try using the Eclipse Memory Analyzer to track down what the memory issue might be. http://www.eclipse.org/mat/

Categories

Resources