I was having a problem where my bitmaps were causing my application to skip frames at a rapid rate. I have looked into android's "Displaying Bitmap's Efficiently" documentation, and have implemented some into my code.
I am pretty sure the image is loading correctly, but its not showing in my application. The height and width of the image view is 0 after I do this decoding, so this might be one of the reasons, but I can't change the size of the ImageView for some reason.
BitmapWorkerTask.java:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
private Context context;
public BitmapWorkerTask(ImageView imageView, Context context) {
this.context = context;
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
System.out.println("T" + buildBitmap(data, 100, 100));
return buildBitmap(data, 100, 100);
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask =
AsyncDrawable.getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
public Bitmap buildBitmap(int resId, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(), resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(context.getResources(), resId, options);
}
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int width = options.outWidth;
final int height = options.outHeight;
int inSampleSize = 1;
if(height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
public static boolean cancelPotentialWork(int data, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final int bitmapData = bitmapWorkerTask.data;
// If bitmapData is not yet set or it differs from the new data
if (bitmapData == 0 || bitmapData != data) {
// Cancel previous task
bitmapWorkerTask.cancel(true);
} else {
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task was cancelled
return true;
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
}
}
I am creating the image in the constructor of my View class, and then drawing the TextView in the onDraw method.
public Game(Context context) {
loadBitmap(R.drawable.logo, scoreHolderView);
}
#Override
protected void onDraw(Canvas canvas) {
if(STATE == State.Playing) {
synchronized(spawner.activeTiles) {
for(Iterator<Tile> iter = spawner.activeTiles.iterator(); iter.hasNext();) {
Tile tile = iter.next();
canvas.drawRect(tile, tile.getColor());
}
}
//canvas.drawBitmap(scoreholder, null, scoreIcon, null);
canvas.drawText(String.valueOf(SCORE), 50, 50, blackText);
scoreHolderView.draw(canvas);
}
}
Is the position and size of the ImageView just not being set for some reason? Or are there other problems with my code?
EDIT:
public void loadBitmap(int resId, ImageView imageView) {
if (AsyncDrawable.cancelPotentialWork(resId, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(imageView, getContext());
final AsyncDrawable asyncDrawable =
new AsyncDrawable(getResources(), logo, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}
Related
I'm trying to upload an image from phone's gallery to a server. I tried Glide and Picasso library. But it is not showing the selected image in ImageView. So, I'm scaling down the image as mentioned here. This method works fine with most of the phones. But I found that it is not working in OnePlus 5 or 5T (Android - Oxygen: 8.0.0). It is showing following exception. The same code works fine if I take a picture using the camera and upload it.
04-14 16:13:43.207 26947-27295/me.example.app W/System.err: java.io.IOException: Resetting to invalid mark
at java.io.BufferedInputStream.reset(BufferedInputStream.java:450)
at me.example.app .Fragment.ImageUploadFragment.decodeSampledBitmapFromUri(ImageUploadFragment.java:172)
at me.example.app .Fragment.ImageUploadFragment$GetResizedBitmapTask.doInBackground(ImageUploadFragment.java:124)
at me.example.app .Fragment.ImageUploadFragment$GetResizedBitmapTask.doInBackground(ImageUploadFragment.java:105)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
Code for reference:
MainActivity:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_FROM_GALLARY && resultCode == Activity.RESULT_OK){
imageUploadFragment.resizeImage(data.getData(), IMAGE_MAX_DIMENSION);
}
}
....
#Override
public void onBitmapResized(Bitmap resizedBitmap, int maxDimension) {
if (resizedBitmap != null){
Log.i("image", "resized");
imgSelected.setImageBitmap(resizedBitmap);
} else {
Toast.makeText(this, "Something went wrong! Please try again.", Toast.LENGTH_SHORT).show();
}
}
ImageUploadFragment:
public void resizeImage(Uri uri, int maxDimension){
GetResizedBitmapTask getResizedBitmapTask = new GetResizedBitmapTask(maxDimension);
getResizedBitmapTask.execute(uri);
}
#SuppressLint("StaticFieldLeak")
private class GetResizedBitmapTask extends AsyncTask<Uri, Void, Bitmap> {
private int mMaxDimension;
GetResizedBitmapTask(int maxDimension) {
this.mMaxDimension = maxDimension;
}
#Override
protected Bitmap doInBackground(Uri... uris) {
Uri uri = uris[0];
if (uri != null){
Bitmap bitmap;
try {
bitmap = decodeSampledBitmapFromUri(uri, mMaxDimension, mMaxDimension);
return bitmap;
} catch (IOException e) {
e.printStackTrace();
//****this exception I'm getting****
return null;
}
}
return null;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (mImageTaskCallbacks != null)
mImageTaskCallbacks.onBitmapResized(bitmap, mMaxDimension);
}
}
public Bitmap decodeSampledBitmapFromUri(Uri imageUri, int reqWidth, int reqHeight) throws IOException{
//gets bytes of data from image file
InputStream inputStream = mContext.getContentResolver().openInputStream(imageUri);
if (inputStream == null){
return null;
}
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
//marks the current position in the InputStream based on the number of bytes remaining to read in the buffer
bufferedInputStream.mark(bufferedInputStream.available());
BitmapFactory.Options options = new BitmapFactory.Options();
//decoder will return null (no bitmap)
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(bufferedInputStream, null, options);
bufferedInputStream.reset();
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
BitmapFactory.decodeStream(bufferedInputStream, null, options);
//****this line of code causing the exception****
bufferedInputStream.reset();
return BitmapFactory.decodeStream(bufferedInputStream, null, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
int inSampleSize = 1;
final int width = options.outWidth;
final int height = options.outHeight;
if (width > reqWidth || height > reqHeight){
final int halfWidth = width/2;
final int halfHeight = height/2;
while ((halfWidth/inSampleSize) > reqWidth && (halfHeight/inSampleSize) > reqHeight){
inSampleSize *= 2;
}
}
return inSampleSize;
}
Thank you in advance.
I'm loading the Album-Artworks in my Music-App. Because I couldn't load them on Main-Thread, I'm using Threads, and they get muddled up!
Sometimes the Image isn't loaded in the correct size or it is shown as a brown square. These issues appear if I scroll fast. If I scroll slow, it works!
The important methods of my MusicStore-Class:
public Bitmap getAlbumArtwork(long AlbumID, int Height, int Width) {
ParcelFileDescriptor pfd;
Bitmap bCover = null;
BitmapFactory.Options bOptions = new BitmapFactory.Options();
bOptions.inJustDecodeBounds = true;
try {
Uri ArtworkUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(ArtworkUri, AlbumID);
pfd = mContext.getContentResolver().openFileDescriptor(uri, "r");
if (pfd != null) {
BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, bOptions);
bOptions.inSampleSize = calculateInSampleSize(bOptions, Width, Height);
bOptions.inJustDecodeBounds = false;
bCover = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, bOptions);
pfd.close();
}
}
catch (IOException ioe) {
BitmapFactory.decodeResource(mContext.getResources(), R.drawable.standardartwork, bOptions);
bOptions.inSampleSize = calculateInSampleSize(bOptions, Width, Height);
bOptions.inJustDecodeBounds = false;
bCover = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.standardartwork, bOptions);
}
return bCover;
}
public void setAlbumArtwork(final long AlbumID, final ImageView ArtworkView) {
Thread thArtwork = new Thread(new Runnable() {
#Override
public void run() {
final Bitmap bArtwork = getAlbumArtwork(AlbumID, ArtworkView.getHeight() / 2, ArtworkView.getWidth() / 2);
handler.postDelayed(new Runnable() {
#Override
public void run() {
ArtworkView.setImageBitmap(bArtwork);
threadList.remove(Thread.currentThread());
}
}, 50);
}
});
threadList.add(thArtwork);
thArtwork.start();
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int size = 1;
if (height > reqHeight && width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / size) > reqHeight && (halfWidth / size) > reqWidth) {
size *= 2;
}
}
return size;
}
And my RecyclerViewAdapter:
public class SongRecyclerViewAdapter extends RecyclerView.Adapter<SongRecyclerViewAdapter.Holder> {
private Context mContext;
private Song[] sSongs;
private MusicStore musicStore;
public SongRecyclerViewAdapter(Context context, Song[] songs) {
mContext = context;
sSongs = songs;
musicStore = new MusicStore(mContext);
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_songview, parent, false);
Holder holder = new Holder(view);
return holder;
}
#Override
public void onBindViewHolder(Holder holder, int position) {
musicStore.setAlbumArtwork(sSongs[position].getAlbumID(), holder.imvSong);
holder.txvSongTitle.setText(sSongs[position].getTitle());
holder.txvSongInfo.setText(sSongs[position].getArtists());
}
#Override
public void onViewDetachedFromWindow(Holder holder) {
}
#Override
public int getItemCount() {
return sSongs != null ? sSongs.length : 0;
}
public class Holder extends RecyclerView.ViewHolder {
LinearLayout linearLayout;
ImageView imvSong;
TextView txvSongTitle;
TextView txvSongInfo;
public Holder(View layout) {
super(layout);
linearLayout = (LinearLayout) layout;
imvSong = (ImageView) layout.findViewById(R.id.imvSong);
txvSongTitle = (TextView) layout.findViewById(R.id.adap_txvSongtitle);
txvSongInfo = (TextView) layout.findViewById(R.id.adap_txvSongInfo);
}
}
}
I'm absolutely open for any other idead to load the Bitmaps correctly!
You could try it out if you want:
https://play.google.com/store/apps/details?id=at.guger.musixs
Thanks!
http://square.github.io/picasso/
The setup and docs are quite simple. You can also passing in the Uri and Picasso will resolve it.
Example
Picasso.with(this).load(uri).into(imageView);
If you can also specify things like width and height, placeholder, and much more.
.resize(width, height).placeholder(placeholderImg)
In the following code, why does function loadImageFromResouce run 22 times when I display two images from SQLite?
public class ArrayAdapter_Product extends ArrayAdapter<Product_Object>
{
private TextView Ten_SP_TxtV;
private TextView DonGia_SP_TxtV;
private Activity context;
private int layoutResourceId;
private ByteArrayInputStream ImageStream;
private Product_Object San_Pham = null;
private Product_Holder holder;
ArrayList<Product_Object> product = new ArrayList<Product_Object>();
private static int reqWidth = 90;
private static int reqHeight = 90;
int dem = 0;
int dem_2 = 0;
int count_set = 0;
public ArrayAdapter_Product(Activity context, int layoutResourceId, ArrayList<Product_Object> product) {
super(context, layoutResourceId, product);
this.context = context;
this.layoutResourceId = layoutResourceId;
this.product = product;
}
public View getView(int position, View view, ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View row = null;
if (row == null) {
dem++;
row = inflater.inflate(R.layout.item_product, null, true);
holder = new Product_Holder();
holder.ImageV_Temp = (ImageView) row.findViewById(R.id.Anh_SP);
ViewTreeObserver observer = holder.ImageV_Temp.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
reqWidth = holder.ImageV_Temp.getWidth();
reqHeight = holder.ImageV_Temp.getHeight();
Log.i("BITMAP", "ReqW:" + reqWidth + ";ReqH:" + reqHeight);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
holder.ImageV_Temp.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
else
holder.ImageV_Temp.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
row.setTag(holder);
Log.d("BITMAP", "dem "+String.valueOf(dem));
}
else {
holder = (Product_Holder) view.getTag();
dem_2++;
Log.d("BITMAP", "dem_2"+String.valueOf(dem_2));
}
San_Pham = product.get(position);
byte[] Out_Image;
Out_Image = San_Pham.Get_Image();
ImageWorker imageWorker = new ImageWorker(holder.ImageV_Temp);
imageWorker.execute(Out_Image);
return row;
}
private class ImageWorker extends AsyncTask<byte[], Void, Bitmap> {
private WeakReference<ImageView> WeakReference;
public ImageWorker(ImageView imgV) {
WeakReference = new WeakReference<ImageView>(imgV);
}
protected Bitmap doInBackground(byte[]... resId) {
return loadImageFromResouce(resId[0], reqWidth, reqHeight);
}
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (WeakReference != null && bitmap != null) {
ImageView imgV = WeakReference.get();
if (imgV != null) {
imgV.setImageBitmap(bitmap);
count_set++;
Log.d("BITMAP", "BYTE BITMAP SET IMAGEVIEW:" + bitmap.getByteCount());
Log.d("BITMAP", "SO LAN SET BITMAP:" + count_set);
}
}
}
private Bitmap loadImageFromResouce(byte[] Out_Image, int reqWidth, int reqHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(Out_Image, 0, Out_Image.length, options);
Log.d("BITMAP", "BYTE BITMAP BAN DAU:" + Out_Image.length);
int insampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inSampleSize = insampleSize;
Bitmap bm = BitmapFactory.decodeByteArray(Out_Image, 0, Out_Image.length, options);
Log.d("BITMAP", "BYTE BITMAP SAU KHI XU LY:" + bm.getByteCount());
return bm;
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
int consumeMemory = options.outWidth * options.outHeight * 4;
String mineType = options.outMimeType;
Log.d("BITMAP", "MEMORY: " + consumeMemory
+ "; Ori_W: " + options.outWidth
+ "; Ori_H: " + options.outHeight);
Log.d("BITMAP", " " + mineType);
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
Log.d("BITMAP", "inSampleSize - " + inSampleSize);
}
}
return inSampleSize;
}
}
static class Product_Holder {
ImageView ImageV_Temp;
}
}
To use the viewholder pattern, you need to set the tag on the view. This code:
View row = null;
if (row == null) {
is not useful. It should be more like:
if (view == null) {
// inflate the view and construct the viewholder
...
} else {
holder = (ViewHolder)view.getTag();
}
// Now operate on the view and holder
I have a CountDownTimer which calls the ChangeWallpaper() method every 15 seconds. The Wallpaper changes as it should but when I try and open the App, it makes the App Drawer screen unresponsive for few seconds. When the App finally opens, everything that I select takes 5-10 seconds to respond. I read about AsyncTask in Android Developer which is supposed to load the Bitmaps outside the UI thread and prevent the App from hanging, but it doesn't seem to be working.
The following code is inside my Activity class:
/** changeWallpaper() **/ - called by CountDownTimer every 15 seconds
protected void changeWallpaper() throws IOException {
Integer totalimages = finallist.size();
if (lastloopcount == totalimages) { // if end of the list of images is reached, it resets and goes back to top.
loopcount = 0;
lastloopcount = 0;
}
for (String imagepath : finallist) { // "finallist" is global variable with all the image's paths in an array list. The Loop count is to select the next image in the array list every 15 seconds.
loopcount++;
if (loopcount > lastloopcount) {
lastloopcount = loopcount;
loopcount = 0;
WallpaperManager wm = WallpaperManager.getInstance(this);
wm.setBitmap(decodeImageFromPath(imagepath));
break;
}
}
}
/** AsyncTask Wallpaper Load **/
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
public BitmapWorkerTask(ImageView imageView) {
new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground(Integer... params) {
return null;
}
}
/** decodeImageFromPath() **/
public Bitmap decodeImageFromPath(String imagepath) {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels << 2;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagepath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, width, height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(imagepath, options);
}
/** WallpaperManager (Method) **/
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// ... Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int stretch_width = Math.round((float)width / (float)reqWidth);
int stretch_height = Math.round((float)height / (float)reqHeight);
if (stretch_width <= stretch_height) return stretch_height;
else return stretch_width;
}
Have I used the AsyncTask function correctly?
Is there an easier way to write this?
Thanks in advance.
EDIT:
/** Spinner **/
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String chosenTime = parent.getItemAtPosition(position).toString();
int chosenTimeNew = 0;
if (chosenTime.contains("sec")) {
chosenTime = chosenTime.replace(" sec","");
chosenTimeNew = Integer.parseInt(chosenTime) * 500;
} else if (chosenTime.contains("min") ) {
chosenTime = chosenTime.replace(" min","");
chosenTimeNew = Integer.parseInt(chosenTime) * 30000;
} else if (chosenTime.contains("hour")) {
chosenTime = chosenTime.replace(" hour","");
chosenTimeNew = (Integer.parseInt(chosenTime) * 30000) * 60;
} else if (chosenTime.contains("day")) {
chosenTime = chosenTime.replace(" day","");
chosenTimeNew = ((Integer.parseInt(chosenTime) * 30000) * 60) * 24;
}
rSpeed = chosenTimeNew;
}
EDIT 2:
Called by CountDownTimer():
new BitmapWorkerTask(null).execute(imagepath);
Then:
/** AsyncTask Wallpaper Load **/
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
public BitmapWorkerTask(ImageView imageView) {
new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground(String... params) {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels << 2;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(params[0], options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, width, height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(params[0], options);
return bmp;
}
protected void onPostExecute(Bitmap bmp) {
Context context = getApplicationContext();
WallpaperManager wm = WallpaperManager.getInstance(context);
try {
wm.setBitmap(bmp);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/** WallpaperManager (Method) **/
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// ... Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int stretch_width = Math.round((float)width / (float)reqWidth);
int stretch_height = Math.round((float)height / (float)reqHeight);
if (stretch_width <= stretch_height) return stretch_height;
else return stretch_width;
}
Your main code(processing the bitmap) should be called from within the doInBackground method. Else it's the same as a synchronous call here.
#Override
protected Bitmap doInBackground(String... params) {
Bitmap bmp = decodeImageFromPath(params[0]);
return bmp;
}
protected void onPostExecute(Bitmap bmp) {
wm.setBitmap(bmp)
}
new BitmapWorkerTask ().execute(imagePath);
http://developer.android.com/reference/android/os/AsyncTask.html
You can refer to the example on this link.
You don't even use your BitmapWorkerTask! There's no magic that does arbitrary parts of your code in background just when you write some AsyncTaskinto your code. You've got to use it too, you know.
Move the long lasting parts of your codes into the doInBackground() method of the AsyncTask and invoke it like so: new BitmapWorkerTask().execute();
EDIT
To pass the image-path, change the definition of your BitmapWorkerTask like to something like this ... extends AsyncTask<String, Void, Bitmap> ... (note the String instead of the Integer), and pass the image-path as parameter to the execute() method.
new BitmapWorkerTask().execute(imagePath);
Be aware that now this runs asynchronously, so the execute() call returns immediately, but the loading of the image will take some time still.
Also read the Painless Threading article.
For my application, I have a texture in high resolution. To reduce the size for small screens I do like that:
#Override
public void onLoadResources(){
Options options = new BitmapFactory.Options();
options.inScaled = false;
// calculation inSampleSize
int sm = 1;
if (cameraWidth+cameraHeight < 1280) sm = 2;// < 800x480
if (cameraWidth+cameraHeight < 800) sm = 4;// < 480x320
options.inSampleSize = sm;
mTexture = new BitmapTextureAtlas(2048/sm, 2048/sm, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
// Loading bitmap
sky_bm = BitmapFactory.decodeResource(getResources(), R.drawable.sky, options);
sky_src = new BitmapTextureAtlasSource(sky_bm);
skyRegion = TextureRegionFactory.createFromSource(mTexture, sky_src, 0, 0, false);
mEngine.getTextureManager().loadTexture(mTexture);
BitmapTextureAtlasSource code:
public class BitmapTextureAtlasSource extends BaseTextureAtlasSource implements IBitmapTextureAtlasSource {
private Bitmap mBitmap;
public BitmapTextureAtlasSource(Bitmap pBitmap) {
super(0,0);
//this.mBitmap = pBitmap;
this.mBitmap = pBitmap.copy(Bitmap.Config.ARGB_8888, false);
}
public int getWidth() {
return mBitmap.getWidth();
}
public int getHeight() {
return mBitmap.getHeight();
}
#Override
public BitmapTextureAtlasSource clone() {
return new BitmapTextureAtlasSource(Bitmap.createBitmap(mBitmap));
}
public Bitmap onLoadBitmap(Config pBitmapConfig) {
return mBitmap;
}
#Override
public IBitmapTextureAtlasSource deepCopy() {
return null;
}
}
But when rotating the screen, I get the error:
FATAL EXCEPTION: GLThread 4895
java.lang.IllegalArgumentException: bitmap is recycled
at android.opengl.GLUtils.texSubImage2D(GLUtils.java:220)
at org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas.writeTextureToHardware(BitmapTextureAtlas.java:162)
at org.anddev.andengine.opengl.texture.Texture.loadToHardware(Texture.java:116)
at org.anddev.andengine.opengl.texture.TextureManager.updateTextures(TextureManager.java:146)
at org.anddev.andengine.engine.Engine.onDrawFrame(Engine.java:507)
at org.anddev.andengine.opengl.view.RenderSurfaceView$Renderer.onDrawFrame(RenderSurfaceView.java:154)
at net.rbgrn.opengl.GLThread.guardedRun(GLThread.java:235)
at net.rbgrn.opengl.GLThread.run(GLThread.java:94)
Please tell me what I'm doing wrong. I would be grateful for any information
I think the problem could be on onLoadBitmap, you should return a copy. I suggest you to try this implementation extending EmptyBitmapTextureAtlasSource:
public class BitmapTextureSource extends EmptyBitmapTextureAtlasSource {
private Bitmap mBitmap;
public BitmapTextureSource(Bitmap bitmap) {
super(bitmap.getWidth(), bitmap.getHeight());
mBitmap = bitmap;
}
#Override
public Bitmap onLoadBitmap(Config pBitmapConfig) {
return mBitmap.copy(pBitmapConfig, true);
}
}
I suggest you set your resolution policy to RatioResolutionPolicy and let the engine do the scaling for you.
http://andengine.wikidot.com/detect-screen-resolution