Android - Right design to implement AsyncTask or Thread - java

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.

Related

Best practice on deleting/zeroing out a security sensitive image?

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

Modifying ListView's smoothScrollToPosition transition

The default behavior for a ListView when calling smoothScrollToPosition on it, it to move with linear speed to the specified position.
Digging into ListView's and AbsListView's code, I can see that this behavior takes place because AbsListView uses a PositionScroller object (implementing AbsPositionScroller) that in turn uses a FlingRunnable object on which the method startScroll gets called with linear = true (which ends up having its OverScroller object use a LinearInterpolator).
I want to modify this behavior, and have it use for example the Scroller.ViscousFluidInterpolator class that the OverScroller class would use by default, but I'm not finding a way to do it.
I see that AbsListView defines a AbsPosScroller interface (that himself implements with a PositionScroller class), that I could try to implement with my own class to have it end up using the ViscousFluidInterpolator, but for some reason this interface is private to the package android.widget...
Am I missing something, or does it look like this has been written in a way that prevents it to have a behavior like that one be customized? Why would they bother writing up a AbsPosScroller interface in first place?
Any leads on how could I get the behavior I want without having to write my entire ListView class from scratch?
While I still don't know why would they write these components in a way that their behavior can't be customized easily when it would've been pretty easy to do it, I came up with an alternative implementation of smoothScrollToPosition (awesomeScrollToPosition in the code below) that does what I needed.
This solution makes use of an OverScroller object (that internally uses the ViscousInterpolator unless a different one is specified) to provide the effect I was looking for, for scrolling to elements within the visible page (the solution to achieve scrolling across pages is more convoluted, but this works for the problem I needed to solve).
I basically implemented a Runnable class private to my own ListView subclass (MyListView) that deals with the scrolling animation, re-posting itself to the UI thread for as long as the animation needs to run, using scrollingListBy in every frame (this method is only available since KitKat [19] though).
public class MyListView extends ListView {
private MyScroller mScroller;
/* MyListView constructors here */
public void awesomeScrollToPosition(int position, int duration) {
if (getChildCount() == 0) {
// Can't scroll without children (visible list items)
return;
}
if (mScroller == null) {
mScroller = new MyScroller();
}
if (mScroller.isRunning()) {
mScroller.stop();
}
int firstPos = getFirstVisiblePosition();
int lastPos = getLastVisiblePosition();
if (!(firstPos <= position && position <= lastPos)) {
// Can't scroll to an item outside of the visible range this easily
return;
}
int targetPosition = position - firstPos;
int targetTop = getChildAt(targetPosition).getTop();
mScroller.start(targetTop, duration);
}
private class MyScroller implements Runnable {
OverScroller mScroller;
boolean mRunning;
int mLastY;
MyScroller() {
mScroller = new OverScroller(getContext());
mRunning = false;
}
void start(int y, int duration) {
// start scrolling
mLastY = 0;
mScroller.startScroll(0, 0, 0, y, duration);
mRunning = true;
postOnAnimation(this);
}
boolean isRunning() {
return mRunning;
}
#Override
public void run() {
boolean more = mScroller.computeScrollOffset();
final int currentY = mScroller.getCurrY();
// actual scrolling
scrollListBy(currentY - mLastY);
if (more) {
mLastY = currentY;
// schedule next run
postOnAnimation(this);
} else {
stop();
}
}
public void stop() {
mRunning = false;
removeCallbacks(this);
}
}
}

Android: Implicit super constructer undefined for default constructer

Ok, first of all, I know you have seen this problem before, and I'll tell you why this is different. I have a class, DrawView (followed some Canvas tutorials) and it extends View. Ok, but I want a separate class to handle all the animations, so I can just call, for example, mainMenuAnimation() and it will draw it instead of coding it to the actual game loop. Well, if I create a class for holding the animations, Animations.java, and extend DrawView, I get an error from Eclipse:
Implicit super constructor DrawView() is undefined for default constructor. Must define an explicit constructor
The problem is, if I call the DrawView() constructor, it makes a new Animations.java, and so on. (Maybe I should define Animations a = new Animations()? Not sure if I would run into problems later on though). So, if I add an empty constructor in DrawView(), it gives me this error:
Implicit super constructor View() is undefined for default constructor. Must define an explicit constructor
I have no idea what to do, help?
Okay, the reason why I instanced Animations in the DrawView() constructor is because Animations' constructor has to be super(context) and the only way to access the context is through the DrawView() constructor.
DrawView constructor code:
Paint paint; //initialize EVERYTHING
Resources res;
Bitmap title;
Rect titleRect;
boolean inMainMenu, issetBackgroundDrawableSupported;
List<BitmapDrawable> mainMenuAnimation;
int mainMenuAnimationIndex = 0;
public DrawView(Context context) {
super(context);
res = getResources(); //required stuff
title = BitmapFactory.decodeResource(getResources(),R.drawable.title); //title stuff
titleRect = new Rect(res.getDisplayMetrics().widthPixels/2 - title.getWidth()*10 , 100, res.getDisplayMetrics().widthPixels/2 + title.getWidth()*10, 200); //left, top, right, bottom
inMainMenu = false; //main menu stuff
issetBackgroundDrawableSupported = true;
mainMenuAnimation = new ArrayList<BitmapDrawable>();
mainMenuAnimation.add(new BitmapDrawable(getResources(), BitmapFactory.decodeResource(res, R.drawable.mainmenu_background_1)));
mainMenuAnimation.add(new BitmapDrawable(getResources(), BitmapFactory.decodeResource(res, R.drawable.mainmenu_background_2)));
mainMenuAnimation.add(new BitmapDrawable(getResources(), BitmapFactory.decodeResource(res, R.drawable.mainmenu_background_3)));
Animations animations = new Animations(getApplication());
}
And the Animations.java code:
public class Animations extends DrawView {
//define animations
#SuppressLint("NewApi")
public void mainMenuScroll(Canvas canvas) {
inMainMenu = true;
//draw main menu here
if (inMainMenu = true) { //main menu loop
if (issetBackgroundDrawableSupported) { //check if background drawing is supported
try {
setBackgroundDrawable(mainMenuAnimation.get(mainMenuAnimationIndex));
} catch (Exception e){
issetBackgroundDrawableSupported = false; //say it is unsupported
setBackground(mainMenuAnimation.get(mainMenuAnimationIndex));
}
}
else {
setBackground(mainMenuAnimation.get(mainMenuAnimationIndex));
}
mainMenuAnimationIndex++;
if (mainMenuAnimationIndex == 3) { //restart main menu animation
mainMenuAnimationIndex = 0;
}
}
}
}
Ok, I realized another Eclipse notification, might be useful. It says:
Custom view com/spng453/agenericrpg/Animations is missing constructor used by tools: (Context) or (Context,AttributeSet) or (Context,AttributeSet,int)
Sounds relevant, but I'm not sure what to do about it.
All Views run within the context of a Context. (I guess that's why it's called that =P). This includes your custom View.
You're going to want to define an Animations constructor that takes a Context, so you can pass it through to the super constructors. This is the cleanest way to get rid of your errors, and will also fix the last problem you mentioned (namely, the Android system is trying to instantiate your class, but it doesn't know what to do with a View that doesn't take a Context in its constructor).
public Animations(Context context) {
super(context);
}

Update a BufferedImage in a JFrame

I have a BufferedImage displayed in a JFrame through my own class. I opted to display the BufferedImage using my own class so I can scale it. My paintComponent and update
public class MyBuffIm{
public void paintComponent(Graphics canvas) {
if (bi == null) {
} else {
//bi, maxWidth, and maxHeight were passed to constructor
canvas.drawImage(bi, 0, 0, maxWidth, maxHeight, null);
}
}
public void update(Graphics canvas) {
super.update(canvas);
if(bi != null){
//Got this from some tutorial in the net.
//Done out of desperation :|
paintComponent(bi.getGraphics());
}
}
}
I overrode update since the docs are saying something like "If this component is not a lightweight component, the AWT calls the update method in response to a call to repaint". I'm not exactly sure if my component is lightweight or not.
In any case, I have the following code in my Runnable (does not work as I expect it to):
BufferedImage p = SomeMyBuffIm.getBuffIm();
Vector<Point> randomPixels = getRandomPixels(500);
int limit = randomPixels.size()
for (i = 0; i < limit; i++) {
Point rp = randomPixels.get(i)
p.setRGB(rp.x, rp.y, Color.red.getRGB());
}
SomeMyBuffIm.repaint();
mainFrame.repaint(); //JFrame call to repaint
I'd like to think that, since I'm scaling my image, I just can't discern the difference between the new and old images. But I've tried the largest values for getRandomPixels still to no effect. My test image, by the way, is just a white sheet so red pixels should stand out in it.
Anything wrong I'm doing?
I overrode update since the docs are saying something like "If this component is not a lightweight component, the AWT calls the update method in response to a call to repaint". I'm not exactly sure if my component is lightweight or not.
No you should NOT override update(). You would do that with AWT but not with Swing.
If you update the BufferedImage then all you need to do is invoke repaint() on your instance of the MyBuffin class.
If you need more help than post your SSCCE that demonstrates the problem.

Android - AsyncTask working with Bitmap - OutOfMemoryError [duplicate]

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.

Categories