Reputation: 167
First I apologize for my bad English.
I have an app for Android that there is an activity for product catalog. The data from the product list and pictures are downloaded in a separate routine. The only responsibility of the Activity is just load data products and their respective photo.
To demonstrate the data was created one listview and a Custom Adapter class inheriting from BaseAdapter follows the code;
Some considerations.
The Source images have a maximum size of 500 pixels height or width.
The maximum file size on average is 45 kb.
The number of records is between 1200-2000
layout/catalogo.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<GridView
android:id="@+id/gv_catalogo_gridprods"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:horizontalSpacing="5dp"
android:numColumns="3"
android:verticalSpacing="5dp"
tools:listitem="@layout/catalogo_item" >
</GridView>
</LinearLayout>
layout/catalogo.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/fundo_imagem_catalogo"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:baselineAligned="false" >
<LinearLayout
android:layout_width="50dp"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_codigo"
android:textSize="12sp" />
<TextView
android:id="@+id/lb_catalogoitem_codigo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_000"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical" >
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_descricao"
android:textSize="12sp" />
<TextView
android:id="@+id/lb_catalogoitem_descricao"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_descricao"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/iv_catalogoitem_imagem"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:baselineAligned="false"
android:contentDescription="Imagem Produto"
android:src="@drawable/icone_android" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:baselineAligned="false" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical" >
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_codbarras"
android:textSize="12sp" />
<TextView
android:id="@+id/lb_catalogoitem_codbarras"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_789000000"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_embalagem"
android:textSize="12sp" />
<TextView
android:id="@+id/lb_catalogoitem_emb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_cx012"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
Adater to GridView
public class AdapterImagensCatalogo extends BaseAdapter {
Context contexto;
List<ItemCatalogo> catalogo;
BitmapFactory.Options optBitMap;
public AdapterImagensCatalogo(Context contexto, List<ItemCatalogo> catalogo) {
this.contexto = contexto;
this.catalogo = catalogo;
this.optBitMap = new BitmapFactory.Options();
this.optBitMap.inSampleSize = 10;
}
@Override
public int getCount() {
return catalogo.size();
}
@Override
public Object getItem(int position) {
return catalogo.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(contexto).inflate(R.layout.catalogo_item, null);
TextView tv = (TextView) convertView.findViewById(R.id.lb_catalogoitem_codigo);
tv.setText(catalogo.get(position).getProduto());
tv = (TextView) convertView.findViewById(R.id.lb_catalogoitem_descricao);
tv.setText(catalogo.get(position).getDescricao());
tv = (TextView) convertView.findViewById(R.id.lb_catalogoitem_codbarras);
tv.setText(catalogo.get(position).getCodigoBarras());
tv = (TextView) convertView.findViewById(R.id.lb_catalogoitem_emb);
tv.setText(catalogo.get(position).getEmbalagem());
ImageView imgV = (ImageView) convertView.findViewById(R.id.iv_catalogoitem_imagem);
imgV.setImageBitmap(getMiniaturaImagem(catalogo.get(position).getPathArquivo()));
return convertView;
}
private Bitmap getMiniaturaImagem(String path){
return BitmapFactory.decodeFile(path, optBitMap);
}
}
The problem happens when the User is browsing the list of images. especially when he makes navigation so quickly.
Here's the LogCat Error.
11-10 21:17:06.781: E/MessageQueue-JNI(2526): java.lang.OutOfMemoryError
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:651)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:407)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at com.cleber.myapp.adapter.AdapterImagensCatalogo.getMiniaturaImagem(AdapterImagensCatalogo.java:69)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at com.cleber.myapp.adapter.AdapterImagensCatalogo.getView(AdapterImagensCatalogo.java:62)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.widget.AbsListView.obtainView(AbsListView.java:2461)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.widget.GridView.makeAndAddView(GridView.java:1331)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.widget.GridView.makeRow(GridView.java:331)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.widget.GridView.fillDown(GridView.java:283)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.widget.GridView.fillGap(GridView.java:243)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5549)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3429)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.widget.AbsListView.onTouchEvent(AbsListView.java:3921)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.View.dispatchTouchEvent(View.java:7392)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2229)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1964)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2235)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1978)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2235)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1978)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2235)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1978)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2235)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1978)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2177)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1482)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.app.Activity.dispatchTouchEvent(Activity.java:2483)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2125)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.View.dispatchPointerEvent(View.java:7577)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3376)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3308)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4421)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4399)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4505)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:178)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.os.MessageQueue.nativePollOnce(Native Method)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.os.MessageQueue.next(MessageQueue.java:125)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.os.Looper.loop(Looper.java:124)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at android.app.ActivityThread.main(ActivityThread.java:4949)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at java.lang.reflect.Method.invokeNative(Native Method)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at java.lang.reflect.Method.invoke(Method.java:511)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1043)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:810)
11-10 21:17:06.781: E/MessageQueue-JNI(2526): at dalvik.system.NativeStart.main(Native Method)
11-10 21:17:06.781: D/AndroidRuntime(2526): Shutting down VM
11-10 21:17:06.781: W/dalvikvm(2526): threadid=1: thread exiting with uncaught exception (group=0x41a892a0)
11-10 21:17:06.781: E/AndroidRuntime(2526): FATAL EXCEPTION: main
11-10 21:17:06.781: E/AndroidRuntime(2526): java.lang.OutOfMemoryError
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:651)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:407)
11-10 21:17:06.781: E/AndroidRuntime(2526): at com.cleber.myapp.adapter.AdapterImagensCatalogo.getMiniaturaImagem(AdapterImagensCatalogo.java:69)
11-10 21:17:06.781: E/AndroidRuntime(2526): at com.cleber.myapp.adapter.AdapterImagensCatalogo.getView(AdapterImagensCatalogo.java:62)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.widget.AbsListView.obtainView(AbsListView.java:2461)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.widget.GridView.makeAndAddView(GridView.java:1331)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.widget.GridView.makeRow(GridView.java:331)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.widget.GridView.fillDown(GridView.java:283)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.widget.GridView.fillGap(GridView.java:243)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5549)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3429)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.widget.AbsListView.onTouchEvent(AbsListView.java:3921)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.View.dispatchTouchEvent(View.java:7392)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2229)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1964)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2235)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1978)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2235)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1978)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2235)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1978)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2235)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1978)
11-10 21:17:06.781: E/AndroidRuntime(2526): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2177)
11-10 21:17:06.781: E/AndroidRuntime(2526): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1482)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.app.Activity.dispatchTouchEvent(Activity.java:2483)
11-10 21:17:06.781: E/AndroidRuntime(2526): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2125)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.View.dispatchPointerEvent(View.java:7577)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3376)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3308)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4421)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4399)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4505)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:178)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.os.MessageQueue.nativePollOnce(Native Method)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.os.MessageQueue.next(MessageQueue.java:125)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.os.Looper.loop(Looper.java:124)
11-10 21:17:06.781: E/AndroidRuntime(2526): at android.app.ActivityThread.main(ActivityThread.java:4949)
11-10 21:17:06.781: E/AndroidRuntime(2526): at java.lang.reflect.Method.invokeNative(Native Method)
11-10 21:17:06.781: E/AndroidRuntime(2526): at java.lang.reflect.Method.invoke(Method.java:511)
11-10 21:17:06.781: E/AndroidRuntime(2526): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1043)
11-10 21:17:06.781: E/AndroidRuntime(2526): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:810)
11-10 21:17:06.781: E/AndroidRuntime(2526): at dalvik.system.NativeStart.main(Native Method)
I've tried to solve the problem with the following discuçoes here from stackoverflow, however unsuccessfully.
Lazy Loading (Image + Text) for Linear Layouts
Lazy load of images in ListView
Custom Endless ListView Android
https://github.com/thest1/LazyList
https://github.com/nostra13/Android-Universal-Image-Loader
Scaling and loading bitmap causes OOM (OutOfMemoryError) (Android)
Upvotes: 1
Views: 679
Reputation: 2728
1)Use ActivityManager.getMemoryClass() and ActivityManager.getLargeMemoryClass() to verify the approximated values assigned to your app.
2) You could declare large heap size in the manifest android:largeHeap-"true"
3) Presentation from Google I/O contain some good answers http://static.googleusercontent.com/external_content/untrusted_dlcp/www.google.com/en//events/io/2011/static/presofiles/memory_management_for_android_apps.pdf
4) In case the image is to be displayed in a size smaller to its original form (like a thumbnail etc, scale it before you load it.
This tutorial by Google Developers should help: https://www.youtube.com/watch?v=12cB7gnL6po&list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0&index=61
From Description: Sub-sampling can speed up load times and reduce the need for large bitmaps in memory if your target bitmap size is much smaller, although it's good to understand that you can't get specific Bitmap sizes, but rather power-of-two reductions in sizes.
Upvotes: 1
Reputation: 16394
First, you're not re-using the existing convertView
in your adapter. You should re-use this view rather than inflating a new one every time:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(contexto).inflate(R.layout.catalogo_item, parent, false);
}
...
}
(Note: You know here what the items parent is going to be (the ViewGroup parent
argument) so you should tell the inflater this by using inflate(id, parent, false);
rather than inflate(id, null);
- you should almost never be calling inflate(id, null)
)
This should solve your problem, since you're no longer creating hundreds of views, rather you're recycling existing ones. If it doesn't, you can explicitly recycle the bitmaps when you're replacing them, like so:
ImageView imgV = (ImageView) convertView.findViewById(R.id.iv_catalogoitem_imagem);
// Obtain a reference to the old image
Drawable oldImage = imgV.getDrawable();
// Set the new image
imgV.setImageBitmap(getMiniaturaImagem(catalogo.get(position).getPathArquivo()));
// Recycle the old image
if (oldImage != null) {
((BitmapDrawable)oldImage).getBitmap().recycle();
}
Upvotes: 1