out of memory in thread repeatition? - android

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/

Related

Tracking Android device in space using device camera

I am looking to get device position (x, y, and z) in space in the most efficient way possible. Currently I am using ArSceneView (ArCore) to get the cameras pose as it updates.
The problem is that ArSceneView or the class it extends from seems to have a memory leak that eventually crash my app. I have tried forcing garbage collection and increasing the heap size but the app is still crashing.
I do not need view to display the camera stream so a class with a view is not necessary, I just need a way to get the mobiles x y and z coordinates. Does anyone know of a way to get these using MLKit or pure ArCore (no view)?
Side note: my app does not crash when the android studio profiler is on... anyone know why?
WHERE I START AND STOP AR MODULE:
public void initializeSession() {
if (sessionInitializationFailed) {
return;
}
UnavailableException sessionException;
try {
session = new Session(context);
Config config = new Config(session);
config.setUpdateMode(Config.UpdateMode.LATEST_CAMERA_IMAGE);
setupSession(session);
session.configure(config);
return;
} catch (Exception e) {
e.printStackTrace();
sessionException = new UnavailableException();
sessionException.initCause(e);
}
sessionInitializationFailed = true;
}
void start() {
System.gc();
if (isStarted) {
return;
}
isStarted = true;
try {
initializeSession();
this.resume();
} catch (CameraNotAvailableException ex) {
sessionInitializationFailed = true;
}
}
void stop() {
if (!isStarted) {
return;
}
isStarted = false;
this.pause();
this.stop();
System.gc();
}
WHERE I LISTEN FOR CHANGES:
public void onUpdate(FrameTime frameTime) {
if (arModule == null) {
return;
}
Frame frame = arModule.getArFrame();
if (frame == null) {
return;
}
Camera camera = frame.getCamera();
Pose pose = camera.getPose();
TrackingState trackingState = camera.getTrackingState();
if (trackingState == TrackingState.TRACKING) {
float x = pose.tx();
float y = pose.ty();
float z = pose.tz();
}
}
In the HelloAR sample app, you can clearly see that they are getting the pose of the camera. Their app does not crash, so it is not a bug. It's hard to tell from your code whether you are getting a memory leak somewhere else. Just add the bit about storing camera pose inside the HelloAR sample app.

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
}

How do u ensure that a method call(JNI Call)inside a thread inside OnPreviewFrame() is finished before being called again

This question is related to Android multi-threading, OpenCV and JNI. The JNI call made inside onCameraFrame is kind of an expensive image-processing operation, hence the camera-preview frame rate gets very slow (a lot of frame lag). When, the native method - 'FindSquare' is called in background from a new thread, the performance improves a little but not very much.
Please suggest the most efficient way to do what I intend to do below, so as to improve the frame-rate.
boolean isCallFinished = false;
public Mat onCameraFrame(Mat inputFrame) {
Size originalSize = inputFrame.size();
Imgproc.cvtColor(inputFrame, mDstImg, Imgproc.COLOR_RGBA2BGR);
Imgproc.resize(inputFrame, mDstImg, new Size(mScreenWidth,
mScreenHeight)); // 2048, 1536
new Thread(new Runnable() {
public void run() {
Message msg = new Message();
if(!isCallFinished) {
msg.arg1 = FindSquares(mDstImg.getNativeObjAddr()); // JNI call
isCallFinished = true;
messageHandler.sendMessage(msg);
}
}
}).start();
if (mDraw == 1) {
Imgproc.resize(mDstImg, inputFrame, originalSize);
}
return inputFrame;
}
A rapid optimization on a first glance would be to avoid creating a new thread for each frame.
Creating a new thread is very expensive.
Try the following approach :
Create a worker thread (keep reference to it)
Create a synchronized queue to communicate with the worker thread
Add objects to the worker thread queue and process them as a bunch
I'll try to write down the concept bellow , and adapt it to your needs (please note that the code wasn't tested in a proper environment).
public class MyWorkerThread extends Thread
{
ArrayList<FrameEvent> frameList;
boolean runWorkerThread;
public WorkerThread
{
super("FrameEventsWorkerThread");
runWorkerThread=true;
frameList= new ArrayList<FrameEvent>();
}
public synchronized AddFrameEvent(FrameEvent aEvent)
{
frameList.add(aEvent);
}
public void kill ()
{
runWorkerThread=true;
}
public void run()
{
while (true && runWorkerThread)
{
synchronized(this)
{
if (frameList.size()>0)
{
for (int i=0;i<frameList.size();i++)
{
Message msg = new Message();
if (!isCallFinished) { // don't quite understand what you want with this
FrameEvent evt = frameList.get(i);
msg.arg1 = FindSquares(evt.dstImg.getNativeObjAddr());
isCallFinished=true;
messageHandler.sendMessage(msg);
}
}
frameList.clear();
}
}
try {
Thread.sleep(10);
} catch (Exception e)
{
}
}
}
}
Your new function should look something like this :
boolean isCallFinished = false;
MyWorkerThread theThread;
public Mat onCameraFrame(Mat inputFrame) {
Size originalSize = inputFrame.size();
Imgproc.cvtColor(inputFrame, mDstImg, Imgproc.COLOR_RGBA2BGR);
Imgproc.resize(inputFrame, mDstImg, new Size(mScreenWidth,
mScreenHeight)); // 2048, 1536
FrameEvent newEvt = new FrameEvent();
newEvt.dstImg = mDstImg;
theThread.AddFrameEvent(newEvt); // avoid creating new threads
if (mDraw == 1) {
Imgproc.resize(mDstImg, inputFrame, originalSize);
}
return inputFrame;
}

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

problem is in ASyncTask class in 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.

Categories

Resources