I have a very strange bug. It only happens in the Emulator. I tested it on multiple Android phones and the Acer Tablet, it works fine there.
My program has a loop that loads in Bitmaps into a Bitmap[] bitCards. The Array is set up for 14 elements by bitCards = new Bitmap[14].
Now it loops 12 times to put a Bitmap into the Array as follows:
bitCards[i] = BitmapFactory.decodeStream(inputStream);
When i = 8 it crashes at this statement.
If i replace it with
bitCards[0] = BitmapFactory.decodeStream(inputStream);
it does not crash, I thought maybe somehow the Array was not big enough so I did the following
bitCards[8]=BitmapFactory.decodeStream(inputStream); // Still did not crash.
The only thing that makes sense is that when I have
bitCards[i] = BitmapFactory.decodeStream(inputStream);
It is relasing the old memory and putting in a new object, thus only memory for one object is created, but.... the exception does not go off, shouldn't I get some kind of an error?
Here is my full code:
void Shuffle()
{
Random generator;
generator = new Random();
int[] iCardsUsed;
iCardsUsed = new int[55];
for(int i=0;i<55;i++)
iCardsUsed[i]=0;
try {
bitCards = new Bitmap[14];
iCards = new int[14];
iTurnOver = new int[14];
for (int i = 0; i < 12; i++)
{
iTurnOver[i]=0;
int cardId;
do {
cardId = generator.nextInt(50);
} while( iCardsUsed[cardId] ==1);
iCardsUsed[cardId] =1;
iCards[i]=cardId;
iCards[i]=i;
String fName=new String("card");
fName+=Integer.toString(iCards[i]+1);
fName+=".jpg";
AssetManager assetManager= ctx.getAssets();
InputStream inputStream;
inputStream = assetManager.open(fName);
// this is where it crashes
bitCards[i]=BitmapFactory.decodeStream(inputStream);
inputStream.close();
}
} catch( IOException e)
{
gi++;
}
// update screen
invalidate();
}
Since you have provided no error message, I am taking a shot in the dark and assuming it is going OOM.
You say that it stops after running for a few times ( when i = 8) , I believe that you are not freeing the resources. Bitmaps can sometime take up a lot of space and if you are persisting them in the memory, I would not be surprised if the device goes OutOfMemory. Different devices have different specs for memory and after a few runs it is filling up the memory.
So, my suggestion would be to clear the Bitmaps, using mBitmap.recycle(), and the other storage that you are using for temporary purposes.
Also, have a look at this question!
Related
Sometimes randomly Volley crashes my app upon startup, it crashes in the application class and a user would not be able to open the app again until they go into settings and clear app data
java.lang.OutOfMemoryError
at com.android.volley.toolbox.DiskBasedCache.streamToBytes(DiskBasedCache.java:316)
at com.android.volley.toolbox.DiskBasedCache.readString(DiskBasedCache.java:526)
at com.android.volley.toolbox.DiskBasedCache.readStringStringMap(DiskBasedCache.java:549)
at com.android.volley.toolbox.DiskBasedCache$CacheHeader.readHeader(DiskBasedCache.java:392)
at com.android.volley.toolbox.DiskBasedCache.initialize(DiskBasedCache.java:155)
at com.android.volley.CacheDispatcher.run(CacheDispatcher.java:84)
The "diskbasedbache" tries to allocate over 1 gigabyte of memory, for no obvious reason
how would I make this not happen? It seems to be an issue with Volley, or maybe an issue with a custom disk based cache but I don't immediately see (from the stack trace) how to 'clear' this cache or do a conditional check or handle this exception
Insight appreciated
In the streamToBytes(), first it will new bytes by the cache file length, does your cache file was too large than application maximum heap size ?
private static byte[] streamToBytes(InputStream in, int length) throws IOException {
byte[] bytes = new byte[length];
...
}
public synchronized Entry get(String key) {
CacheHeader entry = mEntries.get(key);
File file = getFileForKey(key);
byte[] data = streamToBytes(..., file.length());
}
If you want to clear the cache, you could keep the DiskBasedCache reference, after clear time's came, use ClearCacheRequest and pass that cache instance in :
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
DiskBasedCache cache = new DiskBasedCache(cacheDir);
RequestQueue queue = new RequestQueue(cache, network);
queue.start();
// clear all volley caches.
queue.add(new ClearCacheRequest(cache, null));
this way will clear all caches, so I suggest you use it carefully. of course, you can doing conditional check, just iterating the cacheDir files, estimate which was too large then remove it.
for (File cacheFile : cacheDir.listFiles()) {
if (cacheFile.isFile() && cacheFile.length() > 10000000) cacheFile.delete();
}
Volley wasn't design as a big data cache solution, it's common request cache, don't storing large data anytime.
------------- Update at 2014-07-17 -------------
In fact, clear all caches is final way, also isn't wise way, we should suppressing these large request use cache when we sure it would be, and if not sure? we still can determine the response data size whether large or not, then call setShouldCache(false) to disable it.
public class TheRequest extends Request {
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
// if response data was too large, disable caching is still time.
if (response.data.length > 10000) setShouldCache(false);
...
}
}
I experienced the same issue.
We knew we didn't have files that were GBs in size on initialization of the cache. It also occurred when reading header strings, which should never be GBs in length.
So it looked like the length was being read incorrectly by readLong.
We had two apps with roughly identical setups, except that one app had two independent processes created on start up. The main application process and a 'SyncAdapter' process following the sync adapter pattern. Only the app with two processes would crash.
These two processes would independently initialize the cache.
However, the DiskBasedCache uses the same physical location for both processes. We eventually concluded that concurrent initializations were resulting in concurrent reads and writes of the same files, leading to bad reads of the size parameter.
I don't have a full proof that this is the issue, but I'm planning to work on a test app to verify.
In the short term, we've just caught the overly large byte allocation in streamToBytes, and throw an IOException so that Volley catches the exception and just deletes the file.
However, it would probably be better to use a separate disk cache for each process.
private static byte[] streamToBytes(InputStream in, int length) throws IOException {
byte[] bytes;
// this try-catch is a change added by us to handle a possible multi-process issue when reading cache files
try {
bytes = new byte[length];
} catch (OutOfMemoryError e) {
throw new IOException("Couldn't allocate " + length + " bytes to stream. May have parsed the stream length incorrectly");
}
int count;
int pos = 0;
while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) {
pos += count;
}
if (pos != length) {
throw new IOException("Expected " + length + " bytes, read " + pos + " bytes");
}
return bytes;
}
Once the problem occurs, it seems to recur on every subsequent initialization, pointing to an invalid cached header.
Fortunately, this issue has been fixed in the official Volley repo:
https://github.com/google/volley/issues/12
See related issues in the android-volley mirror:
https://github.com/mcxiaoke/android-volley/issues/141
https://github.com/mcxiaoke/android-volley/issues/61
https://github.com/mcxiaoke/android-volley/issues/37
Here's another question for you :)
Basically i made a realtime streaming service, sending multiple jpegs to my android app, that decodes them as soon as he receives them.
// dIn is DataInputStream
// videoFeed is an ImageView
// bitmap is Bitmap
// hand is an Handler of the main thread
//CODE EXECUTED IN ANOTHER THERAD
byte[] inBuff = new byte[8];
byte[] imgBuff;
String inMsg;
while(socket.isConnected()) {
dIn.readFully(inBuff);
inMsg = new String(inBuff, "ASCII").trim();
int size = Integer.parseInt(inMsg);
imgBuff = new byte[size];
dIn.readFully(imgBuff);
out.write("SEND-NEXT-JPEG".getBytes("ASCII"));
bitmap = BitmapFactory.decodeByteArray(imgBuff, 0, size);
hand.post(setImage);
}
}
private Runnable setImage = new Runnable() {
#Override
public void run() {
videoFeed.setImageBitmap(bitmap);
}
};
The problem is that after about 10 or 20 jpegs are perfectly decoded in realtime, the app freezes for 400ms or so and then it continues to decode other 10/20 jpegs before another freeze...
I know that sending multiple jpegs it's not a good way for streaming video but i can only change the client (android app), not the server.
Do you have any idea for get a fluid video and avoid freezes? thanks!
Right now, you are using the three-parameter version of decodeByteArray(). Instead, switch to the four-parameter version, passing in a BitmapFactory.Options as the last value. On there, set inBitmap to be a Bitmap object that can be reused.
This requires you to maintain a small Bitmap object pool. It could be as simple as two Bitmap instances: the one that is presently being displayed and the one that you are preparing for the next "frame" of the video.
The catch is that, for API Level 18 and below, the Bitmap needs to be the same resolution (height and width in pixels). In your case, that's probably not a problem, as I would imagine that each of your bitmaps have the same resolution.
I am trying to create a thumbnail from video. I use the following line:
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(selectedVideoPath, MediaStore.Images.Thumbnails.MICRO_KIND);
It works great when I select an existing video from the gallery, but returns NULL when recording a new video and then trying to get the thumbnail, although the path is valid (/storage/emulated/0/airImagePicker/1394007123308.3gp).
I am on HTC One Android 4.2.2.
Thanks!
I faced the same problem and noticed that it worked when there was a delay between taking the video and creating the bitmap. A workaround that worked for me was to retry creating the bitmap with busy waiting until it wasn't null( it took a few seconds). It's clearly not a clean solution but it seems to do the job.
example of use (in c# xamarin android)
try {
Bitmap bitmap = null;
for (int time = 0; time < 6000; time += timeInterval) {
bitmap = ThumbnailUtils.CreateVideoThumbnail (videoFile.Path, ThumbnailKind.MiniKind);
if (bitmap != null)
break;
await Task.Delay (timeInterval);
}
return bitmap;
I hope it helps.
My current android applications main functionality is to pull the frames (10fps) from a recorded video using the built in camera. Whenever the user selects the video I call my class "FrameCollector" which loops through the video and pulls the frames out and stores them into an ArrayList of Bitmaps. Having this work well for a number of days had me thinking that I was on the right track, but now I'm getting the dreaded "java.lang.OutofMemoryError"
My code is as follows:
Here it is setting the MediaMetaDataRetriever to select 10 frames per second from the video path which was passed from the main class
public class FrameCollector {
MediaMetadataRetriever _mmr;
double _fps;
double _duration;
long _counter = 0;
long _incrementer;
public FrameCollector(String path, Context context)
{
try
{
_mmr = new MediaMetadataRetriever();
_mmr.setDataSource(path);
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
String fpsString = pref.getString("prefFPS", "10");
_fps = Double.parseDouble(fpsString);
_incrementer = (long) (1000000 / _fps);
String stringDuration = _mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
_duration = Double.parseDouble(stringDuration) * 1000;
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
The below method is then adding the frame at that time to a bitmap and adding the bitmap to an arraylist of bitmaps "bitFrames"
public ArrayList<Bitmap> getBitmaps()
{
try
{
ArrayList<Bitmap> bitFrames = new ArrayList<Bitmap>();
Bitmap b = _mmr.getFrameAtTime(_counter);
while (_counter < _duration && b != null)
{
bitFrames.add(b);
_counter += _incrementer;
b = _mmr.getFrameAtTime(_counter);
}
return bitFrames;
}
catch (Exception ex)
{
ex.printStackTrace();
return null;
}
}
I'm thinking my issue lies with in this method. I believe I need to decode the bitmaps before I store them into the ArrayList but I'm unsure how as
Bitmap b = BitmapFactory.decode____(_mmr.getFrameAtTime(_counter));
-- Whether it be decodeStream, decodeResource, decodeFile all bring errors.
Any help would be much appreciated
Many Thanks,
Never store Bitmaps in an ArrayList unless the number of bitmaps are very small. Bitmaps take up large amounts of memory.
I've not worked with video recording before, but I would suggest you try to get a native codec library like ffmpeg to do it for you.
If you have to grab the frames yourself, however, I suggest storing them in a fixed size buffer in memory from the camera and have a background thread which pulls bitmaps from the buffer and stores it to the disk in parallel.
It's not good to store ArrayList of Bitmaps in RAM.
Anyway if you need more than default heap size to store bitmaps in ArrayList, you can try this hack.
http://habrahabr.ru/post/139717/
It's in Russian, but I guess code is quite clear to understand.
The problem
Hi there,
I'm developing an application where the user specifies some pictures and how long they are going to be on the screen.So sometimes he wants to create something like a small animation or viewing the images for a small amount of time.The problem is that after some time the images are not previewed when they should and we have a few ms of error.In the application that i'm developing time matters so I would like some help on what the problem might be.
The code
So let me explain how it works.I take the pictures from my web app and then I save them in a HashMap
Bitmap image = ImageOperations(url,String.valueOf(frameNum) + ".jpg");
ImageMap.put(String.valueOf(frameNum), image);
where the mathod ImageOperations is like that:
private Bitmap ImageOperations(String url, String saveFilename) {
try {
Display display = getWindowManager().getDefaultDisplay();
InputStream is = (InputStream) this.fetch(url);
Bitmap theImage = BitmapFactory.decodeStream(is);
if (theImage.getHeight() >= 700 || theImage.getWidth() >= 700) {
theImage = Bitmap.createScaledBitmap(theImage,
display.getWidth(), display.getHeight() - 140, true);
}
return theImage;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
So later I run a thread that updates the UI when the user specified.The method that updates it is this one.
public void setPictures(int NumOfFrame) {
if (frameArray.get(NumOfFrame - 1).frame_pic.contains("n/a") != true) {
ImagePlace.setImageBitmap(ImageMap.get(String.valueOf(NumOfFrame)));
} else {
ImagePlace.setImageDrawable(null);
}
}
After we update the image we put the thread for sleep and when runs again it updates the thread.Is there something that creates the problem?Does it have to do with Garbage collection?
Thank you in advance
Probably the issue is in increasing heap size when it loads additional images. I would suggest You to do some profiling so things will be much clearer and You'll get full picture of timings for the app.
First you are missing a null check at here:
ImageMap.get(String.valueOf(NumOfFrame))
And you do not recycle the old bitmap at here:
theImage.recycle(); // missing line
theImage = Bitmap.createScaledBitmap(theImage,
display.getWidth(), display.getHeight() - 140, true);
It may lead to outofmemory exceptions, with is most likely from your description of the problem.
Also I am not sure if BitmapFactory.decodeStream throw exception when he fails. You need to add a null point check there too.