Список контактов с Фото контактов создает проблему производительности - PullRequest
3 голосов
/ 18 февраля 2012

Я извлекаю контактные данные с помощью курсора и пытаюсь загрузить их в ListView

Я разработал свой код на основе QuickContact образца APIDemos (уровень API 8)

и вот мой код, который я немного изменил, но я столкнулся с проблемой производительности, если я удалю код фотографии контакта, тогда производительность будет хорошей, в противном случае она будет слишком медленной, как перетаскивание списка

Код адаптера

    private class ContactListAdapter extends ResourceCursorAdapter{

         Cursor cur;
        public ContactListAdapter(Context context, int layout, Cursor c) {
            super(context, layout, c);

            cur = c;
            // TODO Auto-generated constructor stub
        }

        @Override
        public void bindView(View view, Context arg1, Cursor arg2) {
            // TODO Auto-generated method stub
            final ContactListItemCache cache = (ContactListItemCache) view.getTag();
            TextView nameView = cache.nameView;
            QuickContactBadge photoView = cache.photoView;            

            cur.copyStringToBuffer(cur.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME), cache.nameBuffer);
            int size = cache.nameBuffer.sizeCopied;
            cache.nameView.setText(cache.nameBuffer.data, 0, size);          

            long contactId = cur.getLong(cur.getColumnIndex(ContactsContract.Contacts._ID));
            Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactId));


           InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(getContentResolver(), contactUri);            
            cache.photoView.setImageBitmap(BitmapFactory.decodeStream(input));


        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {

            View view = super.newView(context, cursor, parent);
            ContactListItemCache cache = new ContactListItemCache();
            cache.nameView = (TextView) view.findViewById(R.id.name);
            cache.photoView = (QuickContactBadge) view.findViewById(R.id.badge);
            view.setTag(cache);

            return view;

        }
       }
       final static class ContactListItemCache {
            public TextView nameView;
            public QuickContactBadge photoView;
            public CharArrayBuffer nameBuffer = new CharArrayBuffer(128);
        }
}

Получить код контакта

private Cursor getContacts()
   {
        // Run query
        Uri uri = ContactsContract.Contacts.CONTENT_URI;
        String[] projection = new String[] {
                ContactsContract.Contacts._ID,
                ContactsContract.Contacts.DISPLAY_NAME,
                ContactsContract.Contacts.LOOKUP_KEY
        };
        String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" +
                (mShowInvisible ? "0" : "1") + "'";
        String[] selectionArgs = null;
        String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE             LOCALIZED ASC";

        return managedQuery(uri, projection, selection, selectionArgs, sortOrder);
}

Примечание: я прошел эту тему, но мы оба используем разные подходы Загрузитьфотография контакта в представлении со списком

РЕДАКТИРОВАТЬ

Я записал следующий код, но позвонил

imageDownloader.download(contactUri, 
(ImageView) cache.photoView , 
getContentResolver(), 
contactId);

Загрузить код изображения

Bitmap downloadBitmap(Uri url) {
        final int IO_BUFFER_SIZE = 4 * 1024;

        InputStream inputStream = null;
        try {
            inputStream = ContactsContract.Contacts.openContactPhotoInputStream(resolver, url);            

            if(inputStream ==null){

                return  BitmapFactory.decodeStream(inputStream);
            }else
            {

                return  BitmapFactory.decodeStream(new FlushedInputStream(inputStream));

            }
            // return BitmapFactory.decodeStream(inputStream);
            // Bug on slow connections, fixed in future release.

        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }

    }

Но проблема в том, что я использую этот код и быстро прокручиваюсь, через несколько секунд загружается виджет QuickBadgeContact, до того как в этом заполнителе QuickBadgeContact ничего не появляется.

см. Изображение

enter image description here

иногда изображения появляются довольно долго, смотрите здесь

enter image description here

, поэтому егоне работает так, как работает Contact ListView в Android, если мы прокручиваем намного быстрее, то также QuickBadgeContact будет там по умолчанию и выступать в качестве заполнителя, изображение внутри QuickBadgeContact загружается позже, но в моем коде весь значок появляется через несколько секунд

Мне просто нужно продолжать загружать изображения в контакт, у которого есть фотография, и оставить нетронутым другой, так как он загружает ЧЕРНОЕ растровое изображение, где для пользователя не найдено изображение, но я не знаю, как мне это сделать?

Ответы [ 3 ]

3 голосов
/ 24 февраля 2012

Я думаю, что ленивая загрузка - это ответ для вас. Это реализовано с использованием ScrollListener для вашего ListView.

В Деятельности это выглядит примерно так:

public class ListViewActivity extends Activity {

    public boolean mBusy = false;
    // even more initializing

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // The code to build the activity
        ListView listView = findViewById(R.id.ListView);
        listView.setOnScrollListener(listViewOnScrollListener);
        listView.setAdapter(new ContactListAdapter(this, R.layout.whatsoever, null)
        // it is important to give the Adapter 'this' as context because we need the mBusy boolean
        // we should populate the adapter later in onCreate...
        // even more code
    }

    private AbsListView.OnScrollListener listViewOnScrollListener = new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView absListView, int scrollState) {
            if (absListView instanceof ListView) {
                ListView listView = (ListView)absListView;
                switch (scrollState) {
                case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
                    mBusy = false;
                    // Find out which elements of the ListView are visible to the user
                    int first = listView.getFirstVisiblePosition();
                    int childMargin = 0;
                    if (first < listView.getHeaderViewsCount()) {
                        first = listView.getHeaderViewsCount();
                        childMargin = listView.getHeaderViewsCount();
                    }
                    int last = listView.getLastVisiblePosition();
                    if (last > (listView.getCount() - listView.getFooterViewsCount() - 1)) {
                        last = listView.getCount() - listView.getFooterViewsCount() - 1;
                    }

                    for (int i = first; i <= last; i++) {
                        // This is for only populating the visible items of the list
                        // Action to perform the loading of your contactPhoto
                        // or displaying it
                        // maybe you call a method from the adapter to populate the views
                        adapter.loadImageBadges(listView.getChildAt(i - first + childMargin)),i);  
                    }
                    break;
                case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
                    mBusy = true;
                    break;
                case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
                    mBusy = true;
                    break;
                }
            }
        }

        @Override
        public void onScroll(AbsListView absListView, int i, int i1, int i2) {
            // Do something when the list is scrolling
        }
      };

    // The rest of the activity code

}

Адаптер получит дополнительный код:

private class ContactListAdapter extends ResourceCursorAdapter {

    public ContactListAdapter(Context context, int layout, Cursor c) {
        super(context, layout, c);
        cur = c;
    }

    @Override
    public void bindView(View view, Context arg1, Cursor arg2) {
        // TODO Auto-generated method stub
        final ContactListItemCache cache = (ContactListItemCache) view.getTag();
        TextView nameView = cache.nameView;           
        cur.copyStringToBuffer(cur.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME), cache.nameBuffer);
        int size = cache.nameBuffer.sizeCopied;
        cache.nameView.setText(cache.nameBuffer.data, 0, size);          

        if (!context.mBusy) {
            // Do the expensive things only if the list is not scrolling...
            loadImageBadges(convertView, arg2.getPosition())
        } else {
            // initialize with dummy data
        }
    }

    public void ImageBadges(View view, int position) {
        final ContactListItemCache cache = (ContactListItemCache) view.getTag();
        QuickContactBadge photoView = cache.photoView;            
        long contactId = cur.getLong(cur.getColumnIndex(ContactsContract.Contacts._ID));
        Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactId));
        InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(getContentResolver(), contactUri);            
        cache.photoView.setImageBitmap(BitmapFactory.decodeStream(input));
    }

    // The rest of the adapter code

}

Приняв этот код, вы получаете ленивую загрузку, которая заполняет только видимые в настоящее время представления, поэтому производительность должна возрасти, как вы и сказали.

Еще одна вещь, которая также имеет смысл, - это способ кэширования фотографий, полученных при его загрузке курсором.

Изменить:

Код, упомянутый выше, заимствован и принят из примеров Android на http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/List13.html -> Медленный адаптер

Редактировать 2:

Я согласен с Рувимом Скрэттоном за помещение трудолюбивой части адаптера (такой как обработка изображений) в другой поток.

3 голосов
/ 23 февраля 2012

Существует простое правило, позволяющее приложениям реагировать: не выполняйте ввод-вывод в потоке пользовательского интерфейса.Ваша реализация bindView () нарушает правило, потому что она читает растровые изображения из базы данных.Основной поток заблокирован, пока медленные операции ввода-вывода завершены.

Вы должны создавать AsyncTasks для загрузки изображений.Лучшая практика AFAIK - иметь полностью повторно используемый класс ImageCache в вашем приложении ... что-то с таким методом:

public static void getImage(Uri imageUri, ImageView imageView);

Т.е. вы даете ему URI и ссылку на ImageView, и он позаботится озагрузка изображения в фоновом потоке и обновление ImageView, когда он будет готов.

Это немного больше, чем это ... невыполненные запросы должны быть отменены, и вы должны держать ImageViews в WeakReferences, чтобы избежать утечкиАктивность, к которой он относится (об этом есть в блоге разработчика Android).

0 голосов
/ 07 августа 2012

У меня была такая же проблема с вами, и я нашел это решение.Вы можете взглянуть на ContactsPhotoLoader по умолчанию Android Contacts.Положите mPhotoLoader.loadPhoto(contactAvatar, photoId); в getView с contactAvatar, равным ImageView, и photoID, равным Contacts.PHOTO_ID (в зависимости от контакта)

...