I am new to android, could any one please tell me how to write effective code with respect to memory management.
public class XXX extends Activity {
EditText et;
ImageView iv;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
et = (EditText) findViewById(R.id.editText);
iv = (ImageView) findViewById(R.id.iv);
}
public void release(){
et=null;
Drawable drawable = ((ImageView) iv).getDrawable();
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
bitmapDrawable.getBitmap().recycle();
}
iv=null;
System.gc();
}
public void onPic(View v){
// code to capture image
}
public void onSave(View v){
DBHelper dbHelper = new DBHelper(this);
SqliteDatabase db = dbHelper.getReadableDatabase();
String name = et.getText().toString();
ContentValues cv cv= new ContentValues();
// saving , calling release(); and going back to main screen
}
Is there any thing i could do to avoid out of memory, hanging phone?
Bitmap do not easily releases the memory due to which mostly the out of memory exception is caused. You can resize the bitmap and load it.
This might help you which shows efficient bitmap loading:
Bitmap loading
You can go through complete understanding document from here
Bitmap handling
Related
I'm trying to extract Album art from MP3 file But I not able to give proper location of song, which I can give it to SetDataSource method. I tried everything but still it gives me error IllegalArgumentException.
It all happens when this line metaRetriver.setDataSource(MainActivity.this, Uri.parse("/sdcard/song.mp3")); gets executed
My Code.
public class MainActivity extends AppCompatActivity {
MediaMetadataRetriever metaRetriver;
byte[] art;
ImageView album_art;
Bitmap songImage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getInit();
metaRetriver = new MediaMetadataRetriever();
metaRetriver.setDataSource(MainActivity.this, Uri.parse("/sdcard/song.mp3"));
art = metaRetriver.getEmbeddedPicture();
if (art != null) {
songImage = BitmapFactory
.decodeByteArray(art, 0, art.length);
album_art.setImageBitmap(songImage);
} else {
String error = "art is null";
Log.i("lolol", error);
}
}
public void getInit() {
album_art = (ImageView) findViewById(R.id.album_art);
}
}
Can anyone solve this problem.
From the Android Documentation:
Throws IllegalArgumentException if the Uri is invalid
Double check that the song.mp3 file is in the right place, and the app has permission to see it.
I managed to use myimageview.setImageUri when the scheme was content://
But it doesnt work if my Uri has a file:// scheme. What is wrong and how can I make it work?
I have given both read and write permission.
Edit:
To make sure I haven't messed up anything, I created a whole new project.
Mainactivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
ArrayList<Uri> myimageUri = null;
myimageUri = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
Uri theuri = myimageUri.get(0);
ImageView myimageView = findViewById(R.id.imageView);
myimageView.setImageURI(theuri);
}
Attaching a debugger I found out that theuri is
file:///storage/emulated/0/WhatsApp/Media/WhatsApp%20Images/IMG-20171101-WA0005.jpg
It still doesn't work, but content :/// URIs work.
Try following code:
InputStream in = myimageView.g etContext().getContentResolver(). openInputStream(theuri);
Bitmap bitmap = BitmapFactory.decodeStream(in);
in.close();
myimageView.setImageBitmap(bitmap);
You need to decode image file to a bitmap and give it to ImageView.
Bitmap mBitmap = Media.getBitmap(this.getContentResolver(), yourUri);
imageView.setImageBitmap(bitmap);
I have just started Android Studio and am also a little new to java, so please excuse the inefficient code.
Anyway, when I click on the button on the main activity on my app to take the user to the activity that I want to display the images, my app stops working and it throws an OutOfMemoryError.
Here is MainActivty.java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button presidentQuizButton = (Button) findViewById(R.id.presidentQuizButton);
Button reviewPresidentsButton = (Button) findViewById(R.id.reviewPresidentsButton);
presidentQuizButton.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
//when button is clicked, do something
}
});
reviewPresidentsButton.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
//when button is clicked, do something
startActivity(new Intent(MainActivity.this, presidentReviewActivity.class));
}
});
}
Here is presidentReviewActivity.java:
ImageView imageView;
public int presidentNumber = 0;
private Drawable drawable;
private Drawable [] drawables = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_president_review);
Button menuButton = (Button) findViewById(R.id.menuButton);
Button nextButton = (Button) findViewById(R.id.nextButton);
Button backButton = (Button) findViewById(R.id.backButton);
drawables = new Drawable[]{
getResources().getDrawable(R.drawable.president1),
getResources().getDrawable(R.drawable.president2),
getResources().getDrawable(R.drawable.president3),
getResources().getDrawable(R.drawable.president4),
getResources().getDrawable(R.drawable.president5),
getResources().getDrawable(R.drawable.president6),
getResources().getDrawable(R.drawable.president7),
getResources().getDrawable(R.drawable.president8),
getResources().getDrawable(R.drawable.president9),
getResources().getDrawable(R.drawable.president10),
getResources().getDrawable(R.drawable.president11),
getResources().getDrawable(R.drawable.president12),
getResources().getDrawable(R.drawable.president13),
getResources().getDrawable(R.drawable.president14),
getResources().getDrawable(R.drawable.president15),
getResources().getDrawable(R.drawable.president16),
getResources().getDrawable(R.drawable.president17),
getResources().getDrawable(R.drawable.president18),
getResources().getDrawable(R.drawable.president19),
getResources().getDrawable(R.drawable.president20),
getResources().getDrawable(R.drawable.president21),
getResources().getDrawable(R.drawable.president22),
getResources().getDrawable(R.drawable.president23),
getResources().getDrawable(R.drawable.president24),
getResources().getDrawable(R.drawable.president25),
getResources().getDrawable(R.drawable.president26),
getResources().getDrawable(R.drawable.president27),
getResources().getDrawable(R.drawable.president28),
getResources().getDrawable(R.drawable.president29),
getResources().getDrawable(R.drawable.president30),
getResources().getDrawable(R.drawable.president31),
getResources().getDrawable(R.drawable.president32),
getResources().getDrawable(R.drawable.president33),
getResources().getDrawable(R.drawable.president34),
getResources().getDrawable(R.drawable.president35),
getResources().getDrawable(R.drawable.president36),
getResources().getDrawable(R.drawable.president37),
getResources().getDrawable(R.drawable.president38),
getResources().getDrawable(R.drawable.president39),
getResources().getDrawable(R.drawable.president40),
getResources().getDrawable(R.drawable.president41),
getResources().getDrawable(R.drawable.president42),
getResources().getDrawable(R.drawable.president43),
getResources().getDrawable(R.drawable.president44),
};
menuButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
finish();
}
});
nextButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
presidentNumber++;
drawable = drawables[presidentNumber];
imageView.setImageDrawable(drawable);
}
});
And here is most of the error log:
http://imgur.com/xzFfPrq
Instead of store all the drawable content in an array. You can actually just store the resource id in a array.
replace private Drawable [] drawables = null; with private int[] drawableids
replace drawables = new Drawable[]{...} to drawableids = new int[]{...}
replace
drawable = drawables[presidentNumber];
imageView.setImageDrawable(drawable);
to
drawable = getResource().getDrawable(drawableids[presidentNumber]);
imageView.setImageDrawable(drawable);
Try to change as above steps and try again.
The nullpointerexception may be caused by you did not init the drawableids array or you are still using the drawables array instead of drawableids.
you are trying to load large size images directly into memory which causes out of memory exceptions,this issue is discussed in details and a nice solution is given in developers site
I'am sorry for the dumb question but I'am still a beginner.
I'am using universal image loader : https://github.com/nostra13/Android-Universal-Image-Loader
(Android, Eclipse)
and I don't know how to make it load my image buttons(nearly 30 imagebuttons per layout), I don't want to change anything in the image I just want it to display it as it is in the xml file.
So if you could tell me what code should I type that would be great, Thanks
for example if I have this ImageButton set up in my java file:
ImageButton staff = (ImageButton) findViewById(R.id.staff);
and my java file is called MainActivity and this image is saved in my drawable folder what should I do.
a part of my code:
public class MainActivity extends Activity implements OnClickListener {
SoundPool sp;
int soundId;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
sp = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
soundId = sp.load(kh2hd.this, R.raw.btnclick, 1);
Picasso.with(this).load(R.drawable.staff).into(staff);
ImageButton staff = (ImageButton) findViewById(R.id.staffbtn);
staff.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()){
case R.id.staffbtn:
sp.play(soundId, 1, 1, 0, 0, 1);
startActivity(new Intent("android.intent.action.INFO"));
break;
Download Picasso library from here
Add it to your build path. Import this library to your code file.
For loading from web url:
String url = "http://example.com"
Picasso.with(getApplicationContext()).load(url).into(imageView);
For loading from drawable:
Picasso.with(getApplicationContext()).load(R.drawable.imageFile).into(imageView);
Here, imageView is the name of variable you are using to represent ImageView. Eg.
ImageView imageView = (ImageView) findViewById(R.id.ImageView1);
If you are using ImageButton, change imageView variable to imageButton variable
ImageButton imageButton = (ImageButton)findViewById(R.id.imageButton);
Note that I am passing variable name.
#user3896367 i think below code is use full for you. if you upload image from drawable of web url using this code.
download universal image loader jar.
put jar in lib folder and add jar.
create application class.
public static void initImageLoader(Context context) {
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
context)
.threadPriority(Thread.NORM_PRIORITY - 2)
.denyCacheImageMultipleSizesInMemory()
.discCacheFileNameGenerator(
new Md5FileNameGenerator())
.tasksProcessingOrder(QueueProcessingType.LIFO)
.build();
ImageLoader.getInstance().init(config);
}
4.user imageloader like below syntext.
imageLoader = ImageLoader.getInstance();
options = new DisplayImageOptions.Builder()
.showImageOnLoading(
R.drawable.wallpaper_placeholder)
.showImageForEmptyUri(
R.drawable.wallpaper_placeholder)
.showImageOnFail(R.drawable.wallpaper_placeholder)
.displayer(new FadeInBitmapDisplayer(500))
.cacheInMemory(true).cacheOnDisk(true)
.considerExifParams(true)
.bitmapConfig(Bitmap.Config.RGB_565).build();.
imageLoader.displayImage(
URL or ResourceID,
imageButton, options);
try this.
I want to store images in my database. Also I want to check that if the image and title is already in the database. If so, it will not add them to the database. This is my class.
Attractions
public class Attractions extends ListActivity {
DataBaseHandler db = new DataBaseHandler(this);
ArrayList<Contact> imageArry = new ArrayList<Contact>();
List<Contact> contacts;
ContactImageAdapter adapter;
int ctr, loaded;
int [] landmarkImages={R.drawable.oblation,R.drawable.eastwood,R.drawable.ecopark,R.drawable.circle};
String []landmarkDetails = { "Oblation", "Eastwood", "Ecopark", "QC Circle"};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_attractions);
ctr = db.checkContact(landmarkDetails[loaded]);
// get image from drawable
/**
* CRUD Operations
* */
// Inserting Contacts
Log.d("Insert: ", "Inserting ..");
for(loaded=0; loaded <landmarkDetails.length;loaded++){
Bitmap image = BitmapFactory.decodeResource(getResources(),
landmarkImages[loaded]);
// convert bitmap to byte
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte imageInByte[] = stream.toByteArray();
Log.d("Going to load images", "Image "+ loaded);
Log.d("Goind to load objects", "loading");
if(ctr == 0){
Log.d("Nothing Loaded", "Loading Now");
db.addContact(new Contact(landmarkDetails[loaded], imageInByte));}
Log.d(landmarkDetails[loaded], "Loaded!");
image.recycle();
}
loadFromDb();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.attractions, menu);
return true;
}
public void loadFromDb(){
// Reading all contacts from database
contacts = db.getAllContacts();
for (Contact cn : contacts) {
String log = "ID:" + cn.getID() + " Name: " + cn.getName()
+ " ,Image: " + cn.getImage();
// Writing Contacts to log
Log.d("Result: ", log);
//add contacts data in arrayList
imageArry.add(cn);
}
adapter = new ContactImageAdapter(this, R.layout.screen_list,
imageArry);
ListView dataList = (ListView) findViewById(android.R.id.list);
dataList.setAdapter(adapter);
}
public void onPause(){
super.onPause();
}
public void onResume(){
super.onResume();
}
}
It works fine on the emulator, but I tried testing on my S4 and then after 3 tries of going to this class, it forced stop. I tried it with usb debugging and the logcat showed java.lang.outofmemoryerror . The logcat pointed the error in my contactimageadapter.
ContactImageAdapter
public class ContactImageAdapter extends ArrayAdapter<Contact>{
Context context;
int layoutResourceId;
// BcardImage data[] = null;
ArrayList<Contact> data=new ArrayList<Contact>();
public ContactImageAdapter(Context context, int layoutResourceId, ArrayList<Contact> data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ImageHolder holder = null;
if(row == null)
{
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ImageHolder();
holder.txtTitle = (TextView)row.findViewById(R.id.txtTitle);
holder.imgIcon = (ImageView)row.findViewById(R.id.imgIcon);
row.setTag(holder);
}
else
{
holder = (ImageHolder)row.getTag();
}
Contact picture = data.get(position);
holder.txtTitle.setText(picture._name);
//convert byte to bitmap take from contact class
byte[] outImage=picture._image;
ByteArrayInputStream imageStream = new ByteArrayInputStream(outImage);
Bitmap theImage = BitmapFactory.decodeStream(imageStream);
holder.imgIcon.setImageBitmap(theImage);
return row;
}
static class ImageHolder
{
ImageView imgIcon;
TextView txtTitle;
}
}
And pointed to this line Bitmap theImage = BitmapFactory.decodeStream(imageStream);
I have little (almost none) knowledge on managing images and storing them. I also enable android:largeHeap but still force closes on multiple tries. I hope someone can help me solving this issue, or at least show me a different way of storing text and images to sqlite db. Many thanks!
You have multiple places where whole image (assuming it is big) keeps in memory:
Contact object has it. All loaded images are in imageArry which is instance level variable.
public class Attractions extends ListActivity {
DataBaseHandler db = new DataBaseHandler(this);
ArrayList<Contact> imageArry = new ArrayList<Contact>();
in ContactImageAdapter.getView method you create another copy of image as BMP in holder object and pass it out of method.
So, at some point you do not have enough memory to keep all of them. Also I sure that decodeStream needs some more memory to perform.
After all it is not predictable when each new holder created in getView will be cleaned by GC.
Usually for such situation when object created as new in some method, then passed back to the calling method, that object will be collected only by Full GC.
So, as "Software Sainath" said, do not store images in database…
and do not keep them in memory either.
P.S. Then provide to the view a link to the external image file. That also will save time to load a view. Image will be in cache and if user at least once got it, it will not pass through the network again.
I guess images there are not frequently change them self. another image of Contact will be another file…
I wrote an answer to the somewhat similar problem some while ago, here is the link that you can check. The problem is in the approach of saving the images into the database, you should not be doing this. Instead, write the images as files on the phone memory and use it further.
Don't store Image to Sqlite Database eventually, you will ran into out of memory error after three or five image saved to database. It's not the best practice, maximum memory allocated for field in a row in sqlite is less than 3mb, be aware of this.
Instead of saving Images to database, Keep the images inside your app folder, save the path to the Database.
Your are loading your image as it is to your Image adapter. Let's say your image is 1280x720 resolution and 2mb in size, it will take the same space in your memory Heap.
You can either scaledown your image and load it as bitmap to your Adapter ImageView like this.
Before loading your image as Bitmap get it height and width.
//Code read the image and give you image height and width.it won't load your bitmap.
BitmapFactory.Options option = new BitmapFactory.Options();
option.inJustDecodeBounds = true;
BitmapFactory.decodeFile(your_image_url,option);
int image_original_width = option.outHeight;
int image_original_height = option.outWidth;
Now to scale down your Image you have to know the ImageView width and height. This is because we are going to scale down the image matching the imageview with pixel perfection.
int image_view_width = image_view.getWidht();
int image_view_height = image_view.getHeight();
int new_width;
int new_height;
float scaled_width;
if(image_original_width>image_view_width)
{ //if the image_view width is lesser than original_image_width ,you have to scaled down the image.
scale_value =(float)image_original_width/(float)image_view_width;
new_width = image_original_width/scaled_value;
new_height = image_orignal_height/scale_value
}
else
{
// use the image_view width and height as sacling value widht and height;
new_width = image_view_width;
new_height = image_view_height;
}
Now Scale Down your bitmap and load it like this.
// this will load a bitmap with 1/4 the size of the original one.
// this to lower your bitmap memory occupation in heap.
BitmapFactory.Options option = new BitmapFactory.Options();
option.inSampleSize = 4;
Bitmap current_bitmap = BitmapFactory.decodeFile(image_url,option);
Bitmap scaled_bitmap = Bitmap.createScaledBitmap(current_bitmap,new_width,new_height,true);
holder.imgIcon.setImageBitmap(scaled_bitmap);
//release memory occupied by current_bitmap in heap, as we are no longer using it.
current_bitmap.recycle();
If you want to understand a little more about Bitmap and memory view this link.
If you don't want to handle rescaling bitmap by yourself. you can use Glide or Picasso library which does the same.
I have written an article about using Picasso to load image in listview, which will help you to start, if you are looking to use picasso.
http://codex2android.blogspot.in/2015/11/picasso-android-example.html
Please make sure to use the quick garbage collection eligible reference type while loading the images from the network
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import android.graphics.Bitmap;
public class MemoryCache {
private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());
public Bitmap get(String id){
if(!cache.containsKey(id))
return null;
SoftReference<Bitmap> ref=cache.get(id);
return ref.get();
}
public void put(String id, Bitmap bitmap){
cache.put(id, new SoftReference<Bitmap>(bitmap));
}
public void clear() {
cache.clear();
}
}
Don't store Image to Sqlite Database. It's not the best practice.
Instead of saving Images to database, Keep the images in a storage, but if you want to keep them private then keep them inside your app folder and save the path to the Database.
Use one of the well known libraries like http://square.github.io/picasso/ or https://github.com/bumptech/glide, they offer great help with memory issues and also some cool transition effects.
I recommend using Glide because it works very well on device with low memory restrictions