here's the interface:
public interface BitmapCallBackInterface {
void onCallBack(Bitmap bitmap);
}
and here's the method and calling it:
public void downloadImagesFromFireStorage(String imgName, final BitmapCallBackInterface bitmapCallBackInterface) {
StorageReference storageRef = FirebaseStorage.getInstance()
.getReferenceFromUrl("gs://fake-mc-app.appspot.com").child("imgs").child(imgName + ".png");
final long ONE_MEGABYTE = 1024 * 1024;
storageRef.getBytes(ONE_MEGABYTE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
#Override
public void onSuccess(byte[] bytes) {
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
bitmapCallBackInterface.onCallBack(bitmap);
}
});
}
Edit
call it:
for (i = 0; i < nameList.size(); i++) {
String couponName = nameList.get(i);
storageHandler.downloadImagesFromFireStorage(imgName, new BitmapCallBackInterface() {
#Override
public void onCallBack(Bitmap bitmap) {
insertImg(couponName, bitmap); // store image into SQLite
}
});
}
getDataFromSQLite();
// and then display Bitmaps to the a list of ImageView
Edit
someone had taught me that the thread will continue after receive callback, so i write a for loop to download all the images from firebase-storage, store them all into SQlite database, then retrieve them out and display to ImageView. But it seems like the thread continues before the image download complete? how can i fix it? Maybe i am misunderstanding what i had learned?
what i exectly want to do is, download several images, after all downloading complete, refresh the listview
First, I hope you are using a CursorAdapter for your listview, and not pulling data from sqlite, to an arraylist, to an ArrayAdapter, etc.
Secondly, you ideally shouldn't be storing listview images as part of the database. You can store firebase links, and then in the adapter, you will request the image content (using an image loading library such as Glide, see below)
In any case, what you need to do is notify the adapter dataSetChanged for every call to onCallBack, after your image is inserted, then the list will update after the image is in sqlite.
If you want to wait for all images to be inserted before you notify the adapter, you can check the inserted image position against nameList.size()
You might also want to consider just using the OnSuccessListener<byte[]> interface for storing bytes into sqlite as a BLOB, not necessarily a Bitmap object. Under that scenario, you don't need that extra interface
However, the encouraged pattern on the Android documentation for image loading in general is to use Glide
Getting Image from Firebase Storage using Glide
Related
I am using cardlayout in my project and I am retrieving all data from Firebase. I used three classes.
In the MainActivity I am getting the image using piccaso.
MainActivity.java
Picasso.with(MainActivity.this).load(uri).fit().centerCrop().into(img);
ContactInfo obj=new ContactInfo();
obj.image=img;
ContactInfo.java
public ImageView image;
CardLayout.java
ContactInfo object=new ContactInfo();
Cardlayout.img=object.image.
My problem is I want to pass the image from MainActivity to CardLayout. By doing like this my app getting crash.
Can anyone help me please.
I think you can just load an image in one activity and then pass URL to the second one. Since Picasso caches all the image second call with the same URL will result in fetching the same image from the cache:
Picasso.with(context).load(url).into(imageView)
To check if the image was loaded from the cache just enable indicators:
Picasso.with(getContext()).setIndicatorsEnabled(true).
Red - it was fetched from network
Blue - from the disk cache
Green - from the memory cache
UPD:
If you are have a separate class that calls Picasso.with(MainActivity.this) just do something like this:
public class Util {
private Context context;
public Util(Context context) {
this.context = context;
}
public void callPicasso() {
Picasso.with(context)......
}
}
And in MainActivity:
public void onCreate() {
Util u = new Util(this.getApplicationContext());
u.callPicasso()
Or just use you picasso calls directly in the MainActivity
Can anybody point me at resources with best practice on clearing sensitive runtime images?
Consider a scenario where a sensitive image is downloaded from a server at runtime, loaded into a Bitmap object, and is then displayed in an ImageView in a Fragment.
When the user leaves that screen, or the app is exited/put in the background for a long time, then I want to clear that image data so that it isn't easy to recover.
I was wondering if there is a reliable way to zero out the bitmap data as soon as the Fragment containing the image is destroyed?
This feel tricky to me, as Bitmaps are usually returned as immutable objects, e.g. BitmapFactory.decodeByteArray says:
Decode an immutable bitmap from the specified byte array.
Presumably I would have to create a mutable Bitmap and then copy over its data?
It looks like recycle() won't help me, as that will just mark the data as available for garbage collection, it won't wipe it.
You can simply clear the Bitmap using
someBitmap.eraseColor(android.graphics.Color.TRANSPARENT);
It will fill the bitmap with TRANSPARENT color and erase everything on it.
However, if you have no any references to your bitmap (e.g. you've set null to ImageView that was containing your Bitmap like this
someImageView.setDrawable(null)
the garbage collector should collect it shortly.
Thanks to #IlyaGulya for the eraseColor suggestion. Below is the code I've written so far.
Creating the mutable Bitmap:
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inMutable = true;
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, bitmapOptions);
The code to clear the image data in my Fragment (I save the BitmapDrawable into a myBitmapDrawable field when the Fragment receives it):
#Override
public void onDestroy() {
myImageView.setImageDrawable(null);
try {
MyUtils.zeroOutBitmapData(myBitmapDrawable.getBitmap());
} catch (Exception e) {
loggingUtil.logHandledException(e);
}
super.onDestroy();
}
My utility for zeroing out a Bitmap:
public static void zeroOutBitmapData(Bitmap mutableBitmap) {
if (mutableBitmap.isMutable()) {
mutableBitmap.eraseColor(android.graphics.Color.TRANSPARENT);
} else {
logger.error("Expected bitmap to be mutable");
}
}
...and here is a unit test (well, an ApplicationTestCase since I want to test with a real Bitmap):
public void testZeroOutBitmap() throws Exception {
Resources resources = getContext().getResources();
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inMutable = true;
Bitmap mutableBitmap = BitmapFactory.decodeResource(resources, R.drawable.an_example_image);
// Assert that some pixels start out with non zero colors
assertEquals(-789517, mutableBitmap.getPixel(0, 0));
assertEquals(-723724, mutableBitmap.getPixel(10, 10));
MyUtils.zeroOutBitmapData(mutableBitmap);
// Check that pixel data has now been cleared out
assertEquals(android.graphics.Color.TRANSPARENT, mutableBitmap.getPixel(0, 0));
assertEquals(android.graphics.Color.TRANSPARENT, mutableBitmap.getPixel(10, 10));
}
I'm generating a base64 image source at server side and then retrieving it to GWT client through async calls. Unfortunatelly, images are not showing after the callback ends (everything is working fine), but it is shown after a second callback. I have tryied to catch onLoad event immediatelly after creating the Image object, but it was no good.
Thanks!
Edit 1
After some investigation, it is a matter of dimensions, I mean, the base64 is there and the image tag is created correctly, but width and height are setted both to 0.
Edit 2
This is how I'm placing the image in the website:
import net.customware.gwt.dispatch.server.Dispatch
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Image;
dispatch.execute(action, new AsyncCallback<GenerateImageResult> () {
#Override
public void onFailure(Throwable caught) {};
#Override
public void onSuccess(GetCarpetasResult result) {
String base64 = result.getBase64();
Image image = new Image(base64);
RootPanel.get().add(image);
}
});
I've been able to successfully display (in google-chome) base64 image data using this:
String base64= "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCwAAAAAAAAAAAAAAAAAAbGxt/2xsbf9sbG3/bGxt/2xsbf9sbG3/bGxt/2xsbf9sbG3/bGxt/2xsbf9sbG3/bGxt/2xsbf8AAAAAAAAAAGxsbf9sbG3/bGxt/2xsbf9sbG3/bGxt/2xsbf9sbG3/bGxt/2xsbf9sbG3/bGxt/2xsbf9sbG3/AAAAAAAAAABsbG3/bGxt/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbG3/bGxt/wAAAAAAAAAAbGxt/2xsbf8AAAAAbGxt/2xsbf9sbG3/bGxt/2xsbf9sbG3/bGxt/2xsbf8AAAAAbGxt/2xsbf8AAAAAAAAAAGxsbf9sbG3/AAAAAGxsbf9sbG3/bGxt/2xsbf9sbG3/bGxt/2xsbf9sbG3/AAAAAGxsbf9sbG3/AAAAAAAAAABsbG3/bGxt/wAAAAAAAAAAAAAAAAAAAAAAAAAAVHibFE94oDxKeKRkRHiqkUx4ohlsbG3/bGxt/wAAAAAAAAAAAAAAAAAAAABLeKMPPHixUj54sIQ+eLC5Pniw3j54sP8/eK//QXet/0J2q/9Ed6k+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPXiwKkF4rf9BeK3/QXit/0J3q+xFdqnAQ3erhTt7s1Qqg8VSFY3cWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD94rwlCeKxzRHaqUUR2qis7e7MIAAAAABiM2QwJk+hyA5fv4gKX7v8AmfIbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACZPoGQOX7oIDlu7tBZXs/wSX6+wJkeqCHXjmTiBy6QIAAAAAAAAAAAAAAAAAAAAAAAAAAA+R4QICl+4kA5fuhgWW7PMGlev/BpXr6wSY64MNjOkQJ23lTC9j5PwuZOSBAAAAAAAAAAAAAAAAAAAAAAAAAAAElu0JBpXr1QaV6/8GlevpBpbrfASY6xcAAAAAK2fkNi5k5PgsZuT4LGbkRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaV62wGlet+BZbrFQAAAAAAAAAALWTkIi1l5OUsZuT/LGbkWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALmPkDyxl5MssZuT/LGbkewAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALmPkAixm5K0sZuT/LGbkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALmTkAixm5JMsZuT/LGbkwCxm5AcAAAAAAAAAAAAAAAAAAAAAgAEAAIAB8L+f+QAAkAkAAJAJAACfAQAA4AcAAOAHAADggwAA/gEAAPABAADwIQAA+MP///+H////DwAA/g8AAA==";
Image image = new Image(base64);
RootPanel.get().add(image);
Check these things:
is your base64 data starting with data:${mime};base64,?
is your mime really describing the decoded base64 image format?
Have you tryied to add onLoadHandler to the image object? Something like this:
image.addLoadHandler(new LoadHandler () {
#Override
public void onLoad(LoadEvent event) {
image.setUrl(base64);
}
});
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
OutOfMemory Exception when handling images
I have a Bitmap creation within a AsyncTask, which goes like this:
private WeakReference<Bitmap> myBitmap;
private WeakReference<Bitmap> endResultBitmap;
private ImageView imv;
...
private class SendBitmap extends AsyncTask<Integer, Void, Bitmap> {
public SendBitmap(Bitmap bitmap) {
myBitmap = new WeakReference<Bitmap>(bitmap);
}
#Override
protected Bitmap doInBackground(Integer... params) {
Bitmap bm = null;
bm = getBitmapFromNet(params[0]);
return bm;
}
And then I want to create Bitmap on which the received Bitmap would appear twice (one next to another)
protected void onPostExecute(Bitmap result) {
endResultBitmap = new WeakReference<Bitmap>(Bitmap.createBitmap(result.getWidth() * 2, result.getHeight(), result.getConf()));
Canvas canvas = new Canvas(endResultBitmap.get());
canvas.drawBitmap(result, 0, 0, null);
canvas.drawBitmap(result, result.getWidth(), 0, null);
imv.setImageBitmap(endResultBitmap);
}
then I have my onCancelled() method:
#Override
protected void onCancelled(Bitmap result) {
if(endResultBitmap!=null) {
endResultBitmap.recycle();
endResultBitmap = null;
}
}
The thing is that if I execute this AsyncTask couple of times, the heap grows as mad.
I execute the AsyncTask when a button is pressed, but at first I do:
public void onClicked(View v) {
if(asyncTaskInstance != null)
asyncTaskInstance.cancel();
asynctaskInstance.execute(2);
}
But again, the Heap grows as mad and at some point it will crash with OutOfMemoryError.
Any idea? Do I have something wrong in my Design of the task?
Android has some memory limits for apps (16 MB if i remember correctly), and that image is too big in an uncompressed format. Theres some interesting discussion in this question.
To solve it, there are afaik only two ways:
1. Reduce the size of the image to consume less memory
2. Load the image in native code using the NDK.
To 1.: I don't know what exactly you are trying to do and can't tell if thats really a viable option. If it is, you may want download the image file from the net and open it with the BitmapFactory class. There are some static functions that take an BitmapFactory.Options object. Use inSampleSize from this Options object to reduce the image size by a certain factor at loading time (inSampleSize should be a power of two by the way).
To 2.: The memory limits that I mentioned above don't apply to native code. So you may be able to load the image and display in a native way. I don't have any experience with that, I just know that it's possible, but googling around should turn up a few results.
I have a getViewBitmap() method that creates a Bitmap with a 3rd party library (which is proprietary) and goes like this:
public Bitmap getViewBitmap(int index) {
Bitmap retBitmap = null;
int width = 400;
int height = 200;
try {
retBitmap = lib.createBitmap(width, height, index);
} catch(BMException e) {
e.printStacktrace();
}
return retBitmap;
}
This method is used for creating two page view bitmap in another method:
public Bitmap getTwoPageBitmap(int firstPageIndex, intSecondPageIndex) {
Bitmap first = getViewBitmap(firstPageIndex);
Bitmap second = getViewBitmap(secondPageIndex);
Bitmap retBitmap = Bitmap.create(800, 400, first.getConfig());
Canvas helperCanvas = new Canvas(splitViewBm);
helperCanvas.drawBitmap(leftPageBitmap, 0, 0, null);
helperCanvas.drawBitmap(rightPageBitmap, leftPageBitmap.getWidth(), 0, null);
return retBitmap;
}
And then finally in initiated method, I have this:
public View createView() {
MyView v = new MyView();
if(pagePortratit) {
v.setPageView(getViewBitmap(0));
} else {
// if page is landscape
v.setPageView(getTwoPageBitmap(0, 1));
}
return v;
}
Now - I wanna make the getViewBitmap(int) method Asynchronous. Since the "lib.createBitmap(int, int, int)" is pretty slow and it blocks the UI, I want the creation of the bitmap (getViewBitmap(int)) to be in another thread, with possibility to interrupt it's work.
What is the correct design for such design so that the method that is actually heavy goes async?
You likely want to subclass AsyncTask (read here) and put your getBitmapView code in the doInBackground() method (#Override). When it's done, have the onPostExecute() method update the View/UI. The logic for determining landscape or portrait will want to be outside the AsyncTask and you'll just want to farm out to the task (using .execute()) which ever view is needed.
That might be one approach.
I think the best thing that you can do is to use AysncTask which allows u to update the UI Directly without special handling for UI Thread update.