2

I tried creating a simple launcher which displays the list of apps in a GridView and the GridView doesn't have a smooth scrolling. The call to resolveInfo.activityInfo.loadIcon(pm) seems to be the one causing the lag.

Activity

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final Intent packIntent = new Intent(Intent.ACTION_MAIN,null);
    packIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    final PackageManager pm = getPackageManager();
    List<ResolveInfo> packList= pm.queryIntentActivities(packIntent,  0 );
    GridView appGrid = (GridView) findViewById(R.id.appGrid);
    GridAdapter gridAdapter = new GridAdapter(this, pm, packList);
    appGrid.setAdapter(gridAdapter);    
}

}

GridAdapter

public class GridAdapter extends BaseAdapter{

private PackageManager pm;
private List<ResolveInfo> packList;
private Context mContext;



public GridAdapter(Context mContext,PackageManager pm, List<ResolveInfo> packList) {
    super();
    this.mContext=mContext;
    this.pm = pm;
    this.packList = packList;
}

@Override
public int getCount() {
    return packList.size();
}

@Override
public Object getItem(int position) {
    return packList.get(position);
}

@Override
public long getItemId(int arg0) {
    return 0;
}
static class ViewHolder{
    TextView text;
    ImageView image;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final ViewHolder viewHolder;
    LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    if (convertView==null){
        convertView = li.inflate(R.layout.drawer_item, null);
        viewHolder = new ViewHolder();
        viewHolder.text= (TextView)convertView.findViewById(R.id.icon_text);
        viewHolder.image= (ImageView)convertView.findViewById(R.id.icon_image);
        convertView.setTag(viewHolder);         
    }
    else{
        viewHolder = (ViewHolder) convertView.getTag();
    }
    viewHolder.text.setText(packList.get(position).loadLabel(pm));
    viewHolder.image.setImageDrawable(packList.get(position).activityInfo.loadIcon(pm));
    return convertView;

}

}

activity_main.xml

<GridView
    android:id="@+id/appGrid"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="left"
    android:columnWidth="80dp"
    android:horizontalSpacing="10dp"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:verticalSpacing="10dp" />
</RelativeLayout>

and drawer_item.xml

<ImageView    
    android:id="@+id/icon_image"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:padding="3dp"/>
<TextView 
    android:id="@+id/icon_text"
    android:layout_width="wrap_content"
    android:layout_height="40dp"
    android:maxLines="2"
    android:gravity="center_horizontal"
    />
</LinearLayout>

I tried creating a custom bean which has

class AppDetails{
Drawable icon;
String label;
}

and tried populating a list of these

for(int i = 0; i<packList.size();i++){
  AppDetails temp = new AppDetails();
  temp.icon=packList.get(i).activityInfo.loadIcon(pm);
  temp.label=packList.get(i).loadLabel(pm).toString();  
  appDetails.add(temp);
}

and tried building an adapter for the same. If I do this, the GridView scroll becomes smoother, but it slows down the Activity. Tried using AyncTasks to load the icon, but icons take time to get loaded via the AsyncTask..

Is there a better way of loading the App icons or caching it?

coderplus
  • 5,793
  • 5
  • 34
  • 54

7 Answers7

0

load and set the images from an inner AsyncTask in your Adapter so that the images are loaded on another thread, this would take away a lot of lag (It did for me anyway) and use a static class ViewHolder for your grids.

Pontus Backlund
  • 1,017
  • 1
  • 10
  • 17
  • I had already used AsyncTask to load up the icons in onCreate, but this still doesn't give a smooth experience as the launcher icons take some time to show up.Also tried using AsyncTask in the Adapter, that also has performance issues. – coderplus Mar 06 '14 at 06:03
0

As Pontus says, loading images in an AsyncTask would make your load times better, but if you want a "perfect" smooth, you need to use Cache, because if not, you are redrawing icons constantly while scrolling.

Sergio Borné
  • 97
  • 1
  • 4
0

you just have to use asynch task. follow below steps.

  1. Create Asynchtask and simply initialize it in onCreate.
  2. Set adapter in onPostExecute method whatever result you are get from doInBackground method.

Result: you will not get ANR(Application Notworking Response) by this and acitivity also get faster as per your requirement. this worked for me i hope this will help you two.

please fill free to comment here if you got any doubt.

Ankur
  • 24
  • 2
0

Add this class to your adapter

private class IconLoaderTask extends AsyncTask<Void, Void, Drawable>
{

private mImageView;
private mPosition;

public IconLoaderTask(ImageView imageview, int position)
{
    imageview.setTag(position);
}

public Drawable doInBackground()
{
    return packList.get(position).activityInfo.loadIcon(pm)
}

public void onPostExcecute(Drawable drawable)
{
      if(imageView.getTag() == mPosition){

imageView.setImageDrawable(drawable);
      }
}

}

Then in your getView method do something like this

@Override
public View getView(int position, View convertView, ViewGroup parent) {


//Your code here

new IconLoaderTask(viewHolder.image, position).execute();
return convertView;
}
Artem Zelinskiy
  • 2,201
  • 16
  • 19
0

At present you are trying to load & create Bitmap in UI-Thread, and because of that UIThread is getting blocked for this jobs. So, assign this job to a non UI-Thread. Also try to recycle & reuse Bitmaps as much as possible. Because improper recycling of Bitmap will frequently call System.gc() & it is called on UI-Tread also, causing freezing affect in ScrollView(check logcat you will came know the frequency of System.gc() call).

CR Sardar
  • 921
  • 2
  • 17
  • 32
  • tried doing that using an AsyncTask, but still it's not smooth like the other launchers – coderplus Mar 06 '14 at 20:16
  • How may different kinds of Views (i.e. rows of ListView) are you using? If all rows are not same there is very chance, in `Adapter`'s `getView`, `convertView` will be `null` very frequent. Causing frequent `View` inflate, hence performance. if it is the case then over write `getViewTypeCount` & `getItemViewType` properly(you can find lot of example in internet). – CR Sardar Mar 07 '14 at 05:58
0

I think everyone's correct that activityInfo.loadIcon() should happen off the UI thread using an AsyncTask. From there you might consider using UrlImageViewHelper:

UrlImageViewHelper

It has caching built-in and should smooth your scrolling. Your use case is a little outside what UrlImageViewHelper is typically used for (image downloaded via URL). But It can be used in other scenarios (eg, I've used it for smoothing out images loading from SD).

I've used UrlImageViewHelper many times. It works really well and is usually really easy to implement.

newbyca
  • 1,523
  • 2
  • 13
  • 25
0

You can solve your problem by caching the icons of all apps using Picasso library. It is explained in this link How to load image (icons) of apps faster in gridView?

Jaura
  • 487
  • 5
  • 12