I am building a music app with images in it and I am using Picasso to load the images. The target I'm using with Picasso is the following:
Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
try {
if (artNormal != null) {
artNormal.recycle();
artNormal = null;
}
artNormal = Bitmap.createBitmap(bitmap);
TransitionDrawable td = null;
if (upNextShowing) {
Bitmap scaled = Bitmap.createScaledBitmap(artNormal, 10, 10, true);
td = new TransitionDrawable(new Drawable[]{
playerArt.getDrawable(),
new BitmapDrawable(context.getResources(), scaled)
});
// Updated Part -- Updated Part
if(scaled!=null){
scaled.recycle();
scaled = null;
}
} else {
td = new TransitionDrawable(new Drawable[]{
playerArt.getDrawable(),
new BitmapDrawable(context.getResources(), bitmap)
});
}
td.setCrossFadeEnabled(true);
playerArt.setImageDrawable(td);
td.startTransition(1000);
new GetBlurredAlbumArt(artNormal).execute();
} catch (Exception e) {
e.printStackTrace();
Log.e("LargePlayer", "Error :" + e.getLocalizedMessage());
Log.e("LargePlayer", "Error :" + e.getCause());
}
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
Log.e("LargePlayer", "Picasso Load Failed");
TransitionDrawable td = null;
if (artNormal != null) {
artNormal.recycle();
artNormal = null;
}
artNormal = BitmapFactory.decodeResource(context.getResources(),
R.drawable.album_art_large);
artNormal = Bitmap.createScaledBitmap(artNormal, 800, 800, true);
if (upNextShowing) {
Bitmap scaled = Bitmap.createScaledBitmap(artNormal, 10, 10, true);
td = new TransitionDrawable(new Drawable[]{
playerArt.getDrawable(),
new BitmapDrawable(context.getResources(), scaled)
});
// Updated Part -- Updated Part
if(scaled!=null){
scaled.recycle();
scaled = null;
}
} else {
td = new TransitionDrawable(new Drawable[]{
playerArt.getDrawable(),
new BitmapDrawable(context.getResources(), BitmapFactory.decodeResource(context.getResources(),
R.drawable.album_art_large))
});
}
td.setCrossFadeEnabled(true);
playerArt.setImageDrawable(td);
td.startTransition(1000);
new GetBlurredAlbumArt(artNormal).execute();
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
class GetBlurredAlbumArt extends AsyncTask<Void, Void, Bitmap> {
Bitmap bitmap;
public GetBlurredAlbumArt(Bitmap bitmap) {
this.bitmap = bitmap;
}
#Override
protected Bitmap doInBackground(Void... params) {
bitmap = Bitmap.createScaledBitmap(bitmap, 300, 300, true);
bitmap = ImageUtilties.fastblur(bitmap, 100);
try {
return bitmap;
} catch (Exception e) {
Log.e("LargePlayer", "Error :" + e.getLocalizedMessage());
Log.e("LargePlayer", "Error :" + e.getCause());
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
TransitionDrawable td = new TransitionDrawable(new Drawable[]{
blurredColors.getDrawable() != null ? blurredColors.getDrawable() : new ColorDrawable(Color.WHITE),
new BitmapDrawable(context.getResources(), bitmap)
});
td.setCrossFadeEnabled(true);
blurredColors.setImageDrawable(td);
td.startTransition(1000);
}
}
Every time the image is changed memory consumption is increased by 2-3 MB. Memory usage could increase upto 200 MB (which is surely not acceptable). There are only 2 imageviews that change in the activity. Image sizes are nearly upto 1200x1200. I know they are pretty big but I am recycling the bitmap every time before setting but still the Memory usage increases like anything. After that application crashes. It also sometimes gives the error trying to use a recycled Bitmap. Also tried
android:largeHeap="true"
But I need to decrease memory usage. Please help!
One solution is to try to display the image according the resolution of the screen. For that, as soon as the app starts, store the width and height pixels in your SharedPreferences and then use it to load a scaled down version of image. The other solution that I would advise is, if possible try using Fresco. It uses ashmem cache which can store large amounts of data. I have personally faced this issue with Picasso and couldn't find an elegant solution but after switching to Fresco, all those OOM errors are gone.
The first bitmap is not GC'ed when you decode the second one, because you make many copy of bitmap you need free memory used by bitmap ASAP with recycle.
https://developer.android.com/training/displaying-bitmaps/manage-memory.html
Related
I'm developing music player and I use picture from audio files to display it in UI of my app, if I change music very fast (previous, next button) then I can get java.lang.RuntimeException: Canvas: trying to use a recycled bitmap, but popular players from Play market doesn't have this problem if I change music very fast. How can I avoid this error as well as in other music apps?
Similar question with the same error didn't help me
MediaService class
MediaMetadataRetriever mMetaRetriever = new MediaMetadataRetriever();
mMetaRetriever.setDataSource(songPath);
byte[] art = mMetaRetriever.getEmbeddedPicture();
Bitmap iconUrl = null;
try {
if (art != null) {
iconUrl = BitmapFactory.decodeByteArray(art, 0, art.length);
}
} catch (Exception e) {
}
UI class
try {
// with or without it error happens anyway
/*if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
mBitmap = null;
mLogo.setImageBitmap(null);
}*/
mBitmap = metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
mLogo.setImageBitmap(mBitmap);
} catch (Exception e) {
}
I just decided to use AsyncTask and cancel execution (mImageViewAsync.cancel(true);) for current instance when a new instance is created and executed, so in this case onPostExecute is triggered less then doInBackground, so now it be difficult for app to crash (but it still can in some cases, but it's not important so)
private ImageViewAsync mImageViewAsync;
private void updateMetaData(MediaMetadataCompat metadata) {
//...
mLogo.setImageBitmap(null);
if (mImageViewAsync != null) {
mImageViewAsync.cancel(true);
}
mImageViewAsync = new ImageViewAsync();
mImageViewAsync.execute(metadata);
}
private class ImageViewAsync extends AsyncTask<MediaMetadataCompat,Void,Bitmap> {
#Override
protected Bitmap doInBackground(MediaMetadataCompat... meta) {
return meta[0].getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
}
#Override
protected void onPostExecute(Bitmap bitmap) {
mLogo.setImageBitmap(bitmap);
}
}
I' m using Picasso to help the cache of images.
The question is, how can I access the downloaded image to make a share intent?
any ideas? thanks!
I hope you can understand my question :-)
Sorry for my delay, I found a solution, but, not a good one...
First, I really searched for a while and looked at the code of Picasso. It seems like you should provide your own downloader and other stuff. But then, why should I use the lib...
And then, I suppose it's Picasso's design / architecture to just cache the file in the internal storage. Maybe because the external storage is not always available (like the user may plug in his SD card to his computer), or maybe because the external storage is not as fast as the internal... That's my guess. In a word, other apps cannot access the internal storage of the current app, so the share cannot be done.
Thus, I made a really ordinary solution. I just wait for Picasso to give the Bitmap, and compress it to a file in the external file, then do the share. It seems like a bad solution, but it really solves the problem, yes...
You should be aware of whether the external cache directory is available or not. If not, you cannot do the share. And you need to put the compress task in a background thread, so, waiting the external file cached... Does it seem like a bad solution? I think so...
Below is my project code, you can have a try...
private boolean mSaved; // a flag, whether the image is saved in external storage
private MenuItem mShare;
private Intent mIntent;
private ShareActionProvider mShareActionProvider;
private File mImage; // the external image file would be saved...
private Target target = new Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
new Thread(new Runnable() {
#Override
public void run() {
FileOutputStream os = null;
try {
String dir = CatnutUtils.mkdir(getActivity(), Constants.FANTASY_DIR); // check the exteral dir avaiable or not...
String[] paths = Uri.parse(mUrl).getPath().split("/");
mImage = new File(dir + File.separator + paths[2] + Constants.JPG); // resoleve the file name
} catch (Exception e) { // the external storage not available...
Log.e(TAG, "create dir error!", e);
return;
}
try {
if (mImage.length() > 10) { // > 0 means the file exists
// the file exists, done.
mIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(mImage));
mSaved = true;
return;
}
os = new FileOutputStream(mImage);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
mIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(mImage));
mSaved = true;
} catch (FileNotFoundException e) {
Log.e(TAG, "io error!", e);
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
Log.e(TAG, "io closing error!", e);
}
}
}
}
}).start();
mFantasy.setImageBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
mFantasy.setImageDrawable(errorDrawable);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
if (placeHolderDrawable != null) {
mFantasy.setImageDrawable(placeHolderDrawable);
}
}
};
#Override
public void onPrepareOptionsMenu(Menu menu) {
mShare.setEnabled(mSaved);
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fantasy, menu);
mShare = menu.findItem(R.id.action_share);
mShareActionProvider = (ShareActionProvider) mShare.getActionProvider();
mShare.setActionProvider(mShareActionProvider);
mShareActionProvider.setShareIntent(mIntent);
}
Finally, call Picasso.with(getActivity()).load(mUrl).into(target);
When the file is saved, the user can click the share menu do the share.
public static File getImageFile(Context context, String url)
{
final String CACHE_PATH = context.getCacheDir().getAbsolutePath() + "/picasso-cache/";
File[] files=new File(CACHE_PATH).listFiles();
for (File file:files)
{
String fname= file.getName();
if (fname.contains(".") && fname.substring(fname.lastIndexOf(".")).equals(".0"))
{
try
{
BufferedReader br=new BufferedReader(new FileReader(file));
if (br.readLine().equals(url))
{
File imgfile= new File(CACHE_PATH + fname.replace(".0", ".1"));
if (imgfile.exists())
{
return imgfile;
}
}
}
catch (FileNotFoundException|IOException e)
{
}
}
}
return null;
}
I've just found out this guide with a very good solution.
https://guides.codepath.com/android/Sharing-Content-with-Intents
The code will be like this:
// Can be triggered by a view event such as a button press
public void onShareItem(View v) {
// Get access to bitmap image from view
ImageView ivImage = (ImageView) findViewById(R.id.ivResult);
// Get access to the URI for the bitmap
Uri bmpUri = getLocalBitmapUri(ivImage);
if (bmpUri != null) {
// Construct a ShareIntent with link to image
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
shareIntent.setType("image/*");
// Launch sharing dialog for image
startActivity(Intent.createChooser(shareIntent, "Share Image"));
} else {
// ...sharing failed, handle error
}
}
// Returns the URI path to the Bitmap displayed in specified ImageView
public Uri getLocalBitmapUri(ImageView imageView) {
// Extract Bitmap from ImageView drawable
Drawable drawable = imageView.getDrawable();
Bitmap bmp = null;
if (drawable instanceof BitmapDrawable){
bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
} else {
return null;
}
// Store image to default external storage directory
Uri bmpUri = null;
try {
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS), "share_image_" + System.currentTimeMillis() + ".png");
file.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 90, out);
out.close();
bmpUri = Uri.fromFile(file);
} catch (IOException e) {
e.printStackTrace();
}
return bmpUri;
}
It basically consists in retrieving the bitmap from the imageview and saving it to a local temp file and then using it for sharing. I've tested it and it seems to work fine.
I am trying to display the images in galleryViews which are in ListView. I am downloading images from server to SDCard and then displaying it, first I am checking images in cache if image is not there then I am loading it from sdcard.
when the user starts the app for the first time I am downloading the images from server and saving them to sdcard in the mean time iam showing the activity with text only
I want if the image is not there in sdcard the after downloading the image it should display the image as soon as the image is download here is what I am doing.
public class AsyncImageLoader {
private boolean isImageView;
private final LinkedHashMap<String, Bitmap> cache =
new LinkedHashMap<String, Bitmap>(60, (float) 1.0, true);
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LoadImageFromSdCard loadImage = (LoadImageFromSdCard) msg.obj;
if (isImageView) {
loadImage.imageView.setImage(loadImage.bmp);
} else {
Thread thread = new Thread(new LoadImageFromSdCard(loadImage.uri, loadImage.imageView));
try {
Log.i("AsyncImageLoader", "SECOND THREAD STARTED");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.start();
}
}
};
public Bitmap loadImage(String uri,
ImageTextComboControl imageView) {
if (cache.containsKey(uri)) {
return cache.get(uri);
} else {
handler.post(new LoadImageFromSdCard(uri, imageView));
}
return null;
}
private class LoadImageFromSdCard implements Runnable {
String uri;
ImageTextComboControl imageView;
ImageView image;
Bitmap bmp = null;
public LoadImageFromSdCard(String uri, ImageTextComboControl imageView) {
this.uri = uri;
this.imageView = imageView;
}
public void run() {
FileInputStream fis;
try {
fis = new FileInputStream(new File(uri));
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
bmp = BitmapFactory.decodeStream(fis);
if (imageView != null) {
isImageView = true;
cache.put(uri, bmp);
Message message = new Message();
message.obj = this;
handler.sendMessage(message);
}
}
}
}
Thanx
Is much better use Async Task class http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html (using AsyncTask section) and, important, take care about large bitmap
http://developer.android.com/training/displaying-bitmaps/index.html
I solved my problem using onContentChanged() method in adapter. I am saving images to sdCard and getting the sdCard path saving it into sqlite database so when ever the data is changing in sqlite database onContentChanged method is called because I am using Cursor Adapter.
My app is a Wifi chat app with which you can communicate between two Android units with text messages and snap camera pictures and send them. The pictures are stored to the SD-card.
I used to have an OutOfMemoryError thrown after a couple of sent images, but I solved that problem by sending the
options.inPurgeable = true;
and
options.inInputShareable = true;
to the BitmapFactory.decodeByteArray method. This makes the pixels "deallocatable" so new images can use the memory. Thus, the error no longer remains.
But, the internal memory is still full of images and the "Low on space: Phone storage space is getting low" warning appears. The app no longer crashes but there's no more memory on the phone after the app finishes. I have to manually clear the app's data in Settings > Applications > Manage Applications.
I tried recycling the bitmaps and even tried to explicitly empty the app's cache, but it doesn't seem to do what i expect.
This function receives the picture via a TCP socket, writes it to the SD-card and starts my custom Activity PictureView:
public void receivePicture(String fileName) {
try {
int fileSize = inStream.readInt();
Log.d("","fileSize:"+fileSize);
byte[] tempArray = new byte[200];
byte[] pictureByteArray = new byte[fileSize];
path = Prefs.getPath(this) + "/" + fileName;
File pictureFile = new File(path);
try {
if( !pictureFile.exists() ) {
pictureFile.getParentFile().mkdirs();
pictureFile.createNewFile();
}
} catch (IOException e) { Log.d("", "Recievepic - Kunde inte skapa fil.", e); }
int lastRead = 0, totalRead = 0;
while(lastRead != -1) {
if(totalRead >= fileSize - 200) {
lastRead = inStream.read(tempArray, 0, fileSize - totalRead);
System.arraycopy(tempArray, 0, pictureByteArray, totalRead, lastRead);
totalRead += lastRead;
break;
}
lastRead = inStream.read(tempArray);
System.arraycopy(tempArray, 0, pictureByteArray, totalRead, lastRead);
totalRead += lastRead;
}
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(pictureFile));
bos.write(pictureByteArray, 0, totalRead);
bos.flush();
bos.close();
bos = null;
tempArray = null;
pictureByteArray = null;
setSentence("<"+fileName+">", READER);
Log.d("","path:"+path);
try {
startActivity(new Intent(this, PictureView.class).putExtra("path", path));
} catch(Exception e) { e.printStackTrace(); }
}
catch(IOException e) { Log.d("","IOException:"+e); }
catch(Exception e) { Log.d("","Exception:"+e); }
}
Here's PictureView. It creates a byte[ ] from the file on the SD-card, decodes the array to a Bitmap, compresses the Bitmap and writes it back to the SD-card. Lastly, in the Progress.onDismiss, the picture is set as the image of a full screen imageView:
public class PictureView extends Activity {
private String fileName;
private ProgressDialog progress;
public ImageView view;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Log.d("","onCreate() PictureView");
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
view = new ImageView(this);
setContentView(view);
progress = ProgressDialog.show(this, "", "Laddar bild...");
progress.setOnDismissListener(new OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
File file_ = getFileStreamPath(fileName);
Log.d("","SETIMAGE");
Uri uri = Uri.parse(file_.toString());
view.setImageURI(uri);
}
});
new Thread() { public void run() {
String path = getIntent().getStringExtra("path");
Log.d("","path:"+path);
File pictureFile = new File(path);
if(!pictureFile.exists())
finish();
fileName = path.substring(path.lastIndexOf('/') + 1);
Log.d("","fileName:"+fileName);
byte[] pictureArray = new byte[(int)pictureFile.length()];
try {
DataInputStream dis = new DataInputStream( new BufferedInputStream(
new FileInputStream(pictureFile)) );
for(int i=0; i < pictureArray.length; i++)
pictureArray[i] = dis.readByte();
} catch(Exception e) { Log.d("",""+e); e.printStackTrace(); }
/**
* Passing these options to decodeByteArray makes the pixels deallocatable
* if the memory runs out.
*/
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inInputShareable = true;
Bitmap pictureBM =
BitmapFactory.decodeByteArray(pictureArray, 0, pictureArray.length, options);
OutputStream out = null;
try {
out = openFileOutput(fileName, MODE_PRIVATE);
/**
* COMPRESS !!!!!
**/
pictureBM.compress(CompressFormat.PNG, 100, out);
pictureBM = null;
progress.dismiss(); }
catch (IOException e) { Log.e("test", "Failed to write bitmap", e); }
finally {
if (out != null)
try { out.close(); out = null; }
catch (IOException e) { }
} }
}.start();
}
#Override
protected void onStop() {
super.onStop();
Log.d("","ONSTOP()");
Drawable oldDrawable = view.getDrawable();
if( oldDrawable != null) {
((BitmapDrawable)oldDrawable).getBitmap().recycle();
oldDrawable = null;
Log.d("","recycle");
}
Editor editor =
this.getSharedPreferences("clear_cache", Context.MODE_PRIVATE).edit();
editor.clear();
editor.commit();
}
}
When the user presses the back key, the picture isn't supposed to be available anymore from within the app. Just stored on the SD-card.
In onStop() I recycle the old Bitmap and even try to empty the app's data. Still the "Low on space" warning appears. How can I be sure the images won't allocate the memory anymore when they're not needed?
EDIT: It appears the problem is the compress method. If everything after compress is commented, the problem remains. If I delete compress, the problem disappears. Compress seems to allocate memory that's never released, and it's 2-3 MB per image.
Ok, I solved it. The problem was, I was passing an OutputStream to compress, which is a stream to a private file in the app's internal memory. That's what I set as the image later. This file is never allocated.
I didn't get that I had two files: one on the SD-card and one in the internal memory, both with the same name.
Now, I'm just setting the SD-card file as the ImageView's image. I never read the file into the internal memory as a byte[], thus never decoding the array to a bitmap, thus never compressing the bitmap into the internal memory.
This is the new PictureView:
public class PictureView extends Activity {
public ImageView view;
private String path;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Log.d("","onCreate() PictureView");
path = getIntent().getStringExtra("path");
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
view = new ImageView(this);
setContentView(view);
Uri uri = Uri.parse( new File(path).toString() );
view.setImageURI(uri);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
Log.d("","Back key pressed");
Drawable oldDrawable = view.getDrawable();
if( oldDrawable != null) {
((BitmapDrawable)oldDrawable).getBitmap().recycle();
oldDrawable = null;
Log.d("","recycle");
}
view = null;
}
return super.onKeyDown(keyCode, event);
}
}
Is it bad practice to put an external file as the image of an ImageView? Should I load it into internal memory first?
If you specifically want the image to be nullified from memory for sure when a user presses back you could override the back button and make your image clean up calls there. I do that in some of my apps and it seems to work. maybe something like this:
#Override
protected void onBackPressed() {
super.onBackPressed();
view.drawable = null;
jumpBackToPreviousActivity();
}
Im pretty sure there are some view methods that clear other caches and things like that. You can recycle the bitmap but that doesnt guarantee that it will be dumped right then but only at some point when the gc gets to it.....but Im sure you probably know that already :)
EDIT: You could also do the same thing in the onPause method. That one is guaranteed to get called. The other two may never get called according to the android docs.
http://developer.android.com/reference/android/app/Activity.html
I'm writing a livewallpaper that will pull images from flickr and make them the background, and change every so often. Right now i've stubbed/ignored/circumvented the flickr part, and am ignoring the timed changes as well. Still i'm having the same null pointer error over and over and i can't figure out what's causing it.
here is the few methods related to the error
#Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
setImage(getPic());//***NULLPOINTERERROR
} else {
mHandler.removeCallbacks(getFlickrPic);
}
}
public void setImage(Bitmap bm)
{
final SurfaceHolder holder = getSurfaceHolder();
Canvas c= null;
try{
c=holder.lockCanvas();
if (c != null) {
c.setBitmap(bm);
}
} finally { if (c != null) holder.unlockCanvasAndPost(c); }
}
public Bitmap getPic()
{
Bitmap bm = null;
bm = ((BitmapDrawable) LoadImageFromWebOperations("http://farm6.static.flickr.com/5102/5655314644_b7038a5438_z.jpg")).getBitmap();
while (bm==null)
{bm = ((BitmapDrawable) getResources().getDrawable(R.drawable.no)).getBitmap();}
return bm;
}
private Drawable LoadImageFromWebOperations(String url)
{
try
{
InputStream is = (InputStream) new URL(url).openStream();
Drawable d = Drawable.createFromStream(is, "src name");
return d;
}catch (Exception e) {
System.out.println("Exc="+e);
return null;
}
}
I keep getting a NullPointerException for method getPic() in the OnVisibilityChanged(visible) method, noted with the *comment.
If any more information is necessary please ask. like i said i don't know why it's giving me this error so there's a possibility it's something from code i haven't included.
thanks!
Wow thats that for a construct?
while (bm==null) {
bm = ((BitmapDrawable) getResources().getDrawable(R.drawable.no)).getBitmap();
}
That looks evil...
Anyway, your issue is the following line:
bm = ((BitmapDrawable) LoadImageFromWebOperations("http://farm6.static.flickr.com/5102/5655314644_b7038a5438_z.jpg")).getBitmap()
LoadImageFromWebOperations can return null. In that case you still call getBitmap() on a null reference...
LoadImageFromWebOperations returns null if an error occures. in getPic you try to call getBitmap() on the return value of LoadImageFromWebOperations - if the value returned is null
you are trying to call getBitmap() on a null-object, which will cause your NPE