Why can't I load a bitmap from an AsyncTask? - java

I am trying to load bitmaps using an AsyncTask, in order to load them outside UI Thread as it was too slow.
Unfortunatly, I am having issues when starting the app.
The AsyncTask is a class declared inside a "BitmapBank" class, used to handle the different bitmaps of the app. I did it that way to avoid the fact that you can't return something from an Asynctask.
Here is my code :
public Bitmap loadBitmap(Drawable drawable){
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
Canvas canvas = new Canvas(bitmap);
drawable.draw(canvas);
return bitmap;
}
private Bitmap scaleImage(Bitmap bitmap, int ratio){
float widthHeightRatio = (float)(bitmap.getHeight())/(float)(bitmap.getWidth());
int scaleHeight = (int)(widthHeightRatio * AppConstants.SCREEN_WIDTH/ratio);
return Bitmap.createScaledBitmap(bitmap, AppConstants.SCREEN_WIDTH/ratio, scaleHeight, false);
}
private class BitmapTask extends AsyncTask<Drawable, Void, Void> {
public BitmapTask(){
super();
}
#Override
protected Void doInBackground(Drawable... drawables) {
rocket = prepareBitmap(drawables[0]);
astronaut = prepareBitmap(drawables[1]);
bloc = prepareBitmap(drawables[2]);
background = prepareBitmap(drawables[3]);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
rocket = scaleImage(rocket, 10);
astronaut = scaleImage(astronaut, 20);
bloc = scaleImage(bloc, AppConstants.NB_BLOCS);
background = scaleImage(background, 1);
}
}
And here is the logcat :
Caused by: java.lang.OutOfMemoryError: Failed to allocate a 121701132 byte allocation with 16777216 free bytes and 86MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:975)
at android.graphics.Bitmap.createBitmap(Bitmap.java:946)
at android.graphics.Bitmap.createBitmap(Bitmap.java:913)
at com.example.myapplication.BitmapBank.prepareBitmap(BitmapBank.java:93)
at com.example.myapplication.BitmapBank$BitmapTask.doInBackground(BitmapBank.java:117)
at com.example.myapplication.BitmapBank$BitmapTask.doInBackground(BitmapBank.java:106)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
at java.lang.Thread.run(Thread.java:818)
Thanks if you can have a look !

I recommend you use the "Glide" library that automatically handles your problem. Glide is an open source Android library for loading animated images, videos and GIFs. With Glide you can upload and display media from many different sources, such as remote servers or the local file system.
Glide.with(context)
.load(new File(fileUri.getPath())) // Uri of the picture
.into(profileAvatar);

Related

Adding image in ARCore database at runtime error

I want to add image at runtime in ARCore database. So, in my MainActivity.java, I have a button Registered Image, which on Click , added the image in ARCore database.
MainActivity.java :
public class MainActivity extends AppCompatActivity {
private CustomArFragment arFragment;
private TextView textView;
private AugmentedImageDatabase aid;
private Frame frame;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
arFragment = (CustomArFragment) getSupportFragmentManager().findFragmentById(R.id.arFragment);
textView = findViewById(R.id.textView);
arFragment.getArSceneView().getScene().addOnUpdateListener(this::onUpdate);
findViewById(R.id.registeredBtn).setOnClickListener(v -> {
if(ActivityCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
return;
}
registeredImage();
});
}
private static byte[] NV21toJPEG(byte[] nv21, int width, int height) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);
return out.toByteArray();
}
private static byte[] YUV_420_888toNV21(Image image) {
byte[] nv21;
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
nv21 = new byte[ySize + uSize + vSize];
//U and V are swapped
yBuffer.get(nv21, 0, ySize);
vBuffer.get(nv21, ySize, vSize);
uBuffer.get(nv21, ySize + vSize, uSize);
return nv21;
}
// This function triggered when Registered image button clicked
private void registeredImage() {
File file = new File(getExternalFilesDir(null) + "/db.imgdb");
Frame CurrFrame = frame;
Image currentImage;
int idx = -1;
try {
currentImage = CurrFrame.acquireCameraImage();
byte[] data = null;
data = NV21toJPEG(YUV_420_888toNV21(currentImage),
currentImage.getWidth(), currentImage.getHeight());
FileOutputStream outputStream = new FileOutputStream(file);
Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
idx = aid.addImage("earth",bitmap);
aid.serialize(outputStream);
outputStream.close();
Toast.makeText(this, "image Registered", Toast.LENGTH_SHORT).show();
} catch (NotYetAvailableException | IOException e) {
e.printStackTrace();
}
}
private void onUpdate(FrameTime frameTime) {
frame = arFragment.getArSceneView().getArFrame();
Collection<AugmentedImage> images = frame.getUpdatedTrackables(AugmentedImage.class);
for(AugmentedImage image : images){
if(image.getTrackingMethod() == AugmentedImage.TrackingMethod.FULL_TRACKING){
if(image.getName().equals("test")){
textView.setText("Test is visible");
}
else if(image.getName().equals("earth")){
textView.setText("earth is visible");
}
}
}
}
}
When I clicked on button, the app crashes and image didnot add in database. Following are the errors when idx = aid.addImage("earth",bitmap); line called in registeredImage() function.
2020-10-05 14:11:39.738 31013-31013/com.example.artag E/native: error_policy_util.cc:261
################ ARCore Native Error ##################
BUILD_CHANGELIST:331869482
BUILD_BASELINE_CHANGELIST:331085015
################### Stack Trace Begin ################
ARCoreError: third_party/arcore/ar/planar_targets/augmented_image_database_utils.cc:58 https://cs.corp.google.com/piper///depot/google3/third_party/arcore/ar/planar_targets/augmented_image_database_utils.cc?g=0&l=58
ARCoreError: third_party/arcore/ar/core/c_api/augmented_image_database_c_api.cc:133 https://cs.corp.google.com/piper///depot/google3/third_party/arcore/ar/core/c_api/augmented_image_database_c_api.cc?g=0&l=133
################### Stack Trace End #################
2020-10-05 14:11:39.739 31013-31013/com.example.artag D/AndroidRuntime: Shutting down VM
2020-10-05 14:11:39.742 31013-31013/com.example.artag E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.artag, PID: 31013
com.google.ar.core.exceptions.ImageInsufficientQualityException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at com.google.ar.core.Session.throwExceptionFromArStatus(Session.java:101)
at com.google.ar.core.AugmentedImageDatabase.nativeAddImage(Native Method)
at com.google.ar.core.AugmentedImageDatabase.addImage(AugmentedImageDatabase.java:5)
at com.example.artag.MainActivity.registeredImage(MainActivity.java:130)
at com.example.artag.MainActivity.lambda$onCreate$0$MainActivity(MainActivity.java:72)
at com.example.artag.-$$Lambda$MainActivity$5zEkixen6UibjSKLs5AkDUICWdM.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7333)
at android.widget.TextView.performClick(TextView.java:14160)
at android.view.View.performClickInternal(View.java:7299)
at android.view.View.access$3200(View.java:846)
at android.view.View$PerformClick.run(View.java:27773)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:6990)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)
Please help to find out the problem,
Thanks :)
The image that you attempted to add to the database does not have enough features.
Check https://developers.google.com/ar/reference/java/com/google/ar/core/exceptions/ImageInsufficientQualityException#ImageInsufficientQualityException()
for the complete specification of the errors.
If you want users to upload images, I recommend using a try-catch-block and letting the user know about the error instead of having the app crash.

OpenCV-Android java.lang.IllegalArgumentException: bmp == null error

I got this error.
java.lang.IllegalArgumentException: bmp == null
I referred this link
OpenCV - Android : java.lang.IllegalArgumentException: bmp == null
So I made code like this.
inputBitmap.createBitmap(matInput.cols(), matInput.rows(),
Bitmap.Config.ARGB_8888);
And this is my code related to Bitmap
private ImageView imageView_matInput;
private ImageView imageView_matResult;
private Mat matInput;
private Mat image_matches;
Bitmap myBitmap = null;
Bitmap inputBitmap = null;
Bitmap resultBitmap = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
matInput = new Mat();
image_matches = new Mat();
imageView_matInput = (ImageView)findViewById(R.id.imageView_matInput);
imageView_matResult = (ImageView)findViewById(R.id.imageView_matResult);
File imageFile = new File(Global.imageFileName);
if(imageFile.exists()) {
myBitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath());
Log.d(TAG, imageFile.getAbsolutePath());
}
processingImage();
}
public void processingImage() {
Utils.bitmapToMat(myBitmap, matInput);
Imgproc.cvtColor(matInput, matInput, Imgproc.COLOR_RGB2GRAY);
surfWithFlann4(matInput.getNativeObjAddr(), image_matches.getNativeObjAddr());
Imgproc.resize(image_matches, image_matches, matInput.size());
inputBitmap.createBitmap(matInput.cols(), matInput.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(matInput, inputBitmap);
resultBitmap.createBitmap(image_matches.cols(), image_matches.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(image_matches, resultBitmap);
imageView_matInput.setImageBitmap(inputBitmap);
imageView_matResult.setImageBitmap(resultBitmap);
}
Global is a Class that has a path of a image taken by camera.
matInput is not null and surfWithFlann4 is a native function that compares a picture taken by camera to images from asset directory.
The java.lang.IllegalArgumentException: bmp == null error occured in here
Utils.matToBitmap(matInput, inputBitmap);

using bitmap, camera capture but occur nullpointerException on android

I try camera preview capture on android.
use Bitmap.
but occur NullpointerException
java.lang.NullPointerException
at kr.co.mytest.test.video.CameraTextureView.getImage(CameraTextureView.java:167)
at kr.co.mytest.test.video.CameraTextureView.onPreviewFrame(CameraTextureView.java:161)
at android.hardware.Camera$EventHandler.handleMessage(Camera.java:936)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
CameraTextureView.class
public class CameraTextureView extends TextureView implements TextureView.SurfaceTextureListener, Camera.PreviewCallback {
private Camera mCamera;
private CaptureViewer captureView;
.
.
.
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
Log.d(TAG,"CHECK");
YuvImage image = new YuvImage(data, 20, 640 ,480, null);
Rect rect = new Rect(0, 0, 640, 480);
ByteArrayOutputStream bao = new ByteArrayOutputStream();
image.compressToJpeg(rect, 100, bao);
byte[] jpeg = bao.toByteArrary();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap convertedImage = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length, options);
Log.d(TAG, "convertedImage" + convertedImage); //android.graphics.Bitmap#21ac0b78
getImage(Bitmap.createScaleBitmap(convertedImage, 480, 360, true)); //nullPointerException
}
public void getImage(Bitmap bitmap) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
captureView.displayImage(bitmap);
}
CaptureView.class
public class CaptureView extends View {
private Bitmap mBitmap = null;
public void displayImage(Bitmap image) {
mBitmap = Bitmap.createBitmap(image);
postInvalidate();
}
}
displayImage is only showing capture image.
why occur nullpointerException?
onPreviewFrameis Called as preview frames are displayed.
how to process occur nullPointerException?
thanks.!

How to clear the memory to prevent java.lang.OutOfMemoryError

I have an application with a lot of pages and each of them have their own image which is loaded from web with an asynctask. The problem is when you open and close a lot of pages, after some time it gives the outofmemory error. How to clear the image from memory when leaving the activity to prevent this?
The images are loaded by this Asynctask:
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
by calling:
new DownloadImageTask((ImageView) findViewById(R.id.ivCard)).execute("http://myurl.com/example.png");
This activity is opened and closed with different values and different images. After opening some instances of this activity, it gives the following outofmemory error:
07-24 16:37:57.870 27717-28014/com.yigitserin.hsboard E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:299)
at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
Caused by: java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:650)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:722)
at com.yigitserin.hsboard.HeroActivity$DownloadImageTask.doInBackground(HeroActivity.java:137)
at com.yigitserin.hsboard.HeroActivity$DownloadImageTask.doInBackground(HeroActivity.java:125)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at java.lang.Thread.run(Thread.java:856)
How to remove the image from memory after the user quits one instance of this activity?
You need to call the recycle method for the bitmaps, eg. in your activies onDestroy/onStop methods.
See Recycle ImageView's Bitmap
In my opinion you have to use third party lib like :-
Universal Loader
Picasso
URL helper
Above lib maintain bitmap memory automatically.
I think You should close the stream in finally block and then give a try.i hope this can rule out one possibility then we can think of other optimization techniques.
You likely don't need to "clear" the memory but rather downsample the Bitmap you are loading to avoid the out of memory exception. There are some good tutorials on how to process and load large bitmap files. For example:
public static Bitmap decodeSampledBitmapFromResource(Resources res, 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(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Here rather than loading the entire bitmap you can scale down the image prior to loading into memory and avoid this issue entirely.
If you're still running into the problem, I would sooner be concerned with why you have an application that is keeping references to a lot of images it is no longer using. When pages are destroyed the bitmaps should be garbage collected like anything else if your app is set up correctly.

Bitmap is to large to be uploaded into a texture

I have a app that uploads a picture made by the camera and upload it to a server.
But when i try to get it from the server into my imageview it says that the bitmap is to large ( in pixels )
How can i fix this?
new DownloadImageTask(imgProfile)
.execute("http://api.nl/local/index.php?action=get_user_profile_picture&username="
+ SaveSharedPreferences.user.getUsername()
+ "&password="
+ SaveSharedPreferences.user.getPassword()
+ "&user_number="
+ SaveSharedPreferences.user.getUserNumber());
This is my code that i use to process the image to the imageview:
class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
try{
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch(OutOfMemoryError e){
}
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
What you are seeing is the effect of hardware acceleration. The system tries to load an extremely huge bitmap into an image view. If you have hardware acceleration turned on (e.g. via the Manifest) android will try to put the bitmap into an openGL texture, and your image is too big for this. It does not matter if you use JPEG or bitmaps for this, as the final openGL texture will have raw data and thus be a bitmap either way (each pixel is represented as it's own float values).
The only chance you have is to either download the image a lot smaller or resize it on the device. I'd go with the smaller download because it will also save the users's bandwith (and therefore costs)
BitmapFactory.Options options = null;
options = new BitmapFactory.Options();
options.outHeight = IMAGE_WIDTH_HEIGHT;//give some value
options.outWidth = IMAGE_WIDTH_HEIGHT;//which is suitable for your app image size
BitmapFactory.decodeStream(is, outPadding, options); //try this

Categories