Android-загрузчики, путь?

Я привык строить списки в android с помощью адаптеров. Если мне нужны какие-то длинные данные, я использую asynctask или простой runnable для обновления структуры данных, на которую полагается адаптер, и вызывает notifyDataChanged на адаптере.

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

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

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

Хорошо, это форум разработчиков, вот пример. Пожалуйста, сделайте это лучше с погрузчиками:

package com.sof.test.loader; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.ArrayAdapter; import android.widget.TextView; /** The activity. */ public class LoaderTestActivity extends ListActivity { private DataSourceOrDomainModel dataSourceOrDomainModel = new DataSourceOrDomainModel(); private List<Person> listPerson; private PersonListAdapter personListAdapter; private TextView emptyView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); listPerson = new ArrayList<Person>(); personListAdapter = new PersonListAdapter( listPerson ); setListAdapter( personListAdapter ); setUpEmptyView(); new PersonLoaderThread().execute(); } public void setUpEmptyView() { emptyView = new TextView( this ); emptyView.setLayoutParams( new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT ) ); emptyView.setVisibility(View.GONE); ((ViewGroup)getListView().getParent()).addView(emptyView); getListView().setEmptyView(emptyView); } /** Simulate a long task to get data. */ private class PersonLoaderThread extends AsyncTask<Void, Integer, List<Person>> { @Override protected List<Person> doInBackground(Void... params) { return dataSourceOrDomainModel.getListPerson( new ProgressHandler()); } @Override protected void onProgressUpdate(Integer... values) { emptyView.setText( "Loading data :" + String.valueOf( values[ 0 ] ) +" %" ); } @Override protected void onPostExecute(List<Person> result) { listPerson.clear(); listPerson.addAll( result ); personListAdapter.notifyDataSetChanged(); } private class ProgressHandler implements ProgressListener { @Override public void personLoaded(int count, int total) { publishProgress( 100*count / total ); } } } /** List item view factory : the adapter. */ private class PersonListAdapter extends ArrayAdapter<Person> { public PersonListAdapter( List<Person> listPerson ) { super(LoaderTestActivity.this, 0, listPerson ); } @Override public View getView(int position, View convertView, ViewGroup parent) { if( convertView == null ) { convertView = new PersonView( getContext() ); } PersonView personView = (PersonView) convertView; personView.setPerson( (Person) getItem(position) ); return personView; } } } 

Небольшой интерфейс обратного вызова для прогресса

 package com.sof.test.loader; /** Callback handler during data load progress. */ public interface ProgressListener { public void personLoaded(int count, int total ); } 

Виджет элемента списка

 package com.sof.test.loader; import com.sof.test.loader.R; import android.content.Context; import android.view.LayoutInflater; import android.widget.LinearLayout; import android.widget.TextView; /** List Item View, display a person */ public class PersonView extends LinearLayout { private TextView personNameView; private TextView personFirstNameView; public PersonView(Context context) { super(context); LayoutInflater inflater= (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate( R.layout.person_view,this ); personNameView = (TextView) findViewById( R.id.person_name ); personFirstNameView = (TextView) findViewById( R.id.person_firstname ); } public void setPerson( Person person ) { personNameView.setText( person.getName() ); personFirstNameView.setText( person.getFirstName() ); } } 

Это xml: res / person_view.xml

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/person_view" android:layout_width="fill_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/person_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" /> <TextView android:id="@+id/person_firstname" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/person_name" /> </RelativeLayout> 

Источник данных или модель, предоставляющая данные (медленно)

 package com.sof.test.loader; import java.util.ArrayList; import java.util.List; /** A source of data, can be a database, a WEB service or a model. */ public class DataSourceOrDomainModel { private static final int PERSON_COUNT = 100; public List<Person> getListPerson( ProgressListener listener ) { List<Person> listPerson = new ArrayList<Person>(); for( int i=0; i < PERSON_COUNT ; i ++ ) { listPerson.add( new Person( "person", "" + i ) ); //kids, never do that at home ! pause(); if( listener != null ) { listener.personLoaded(i,PERSON_COUNT); }//if } return listPerson; }//met private void pause() { try { Thread.sleep( 100 ); } catch (InterruptedException e) { e.printStackTrace(); } } } 

POJO, представляющий человека:

 package com.sof.test.loader; /** A simple POJO to be displayed in a list, can be manipualted as a domain object. */ public class Person { private String name; private String firstName; public Person(String name, String firstName) { this.name = name; this.firstName = firstName; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } }//class 

Solutions Collecting From Web of "Android-загрузчики, путь?"

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

И да использование загрузчиков часто делает более сложным код, особенно если вы не можете использовать один из предоставленных загрузчиков.

Если кто-то ищет версию загрузчика моего предыдущего примера: вот он:

 package com.sof.test.loader; import java.util.ArrayList; import android.app.LoaderManager; import java.util.List; import android.app.ListActivity; import android.content.AsyncTaskLoader; import android.content.Context; import android.content.Loader; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.ArrayAdapter; import android.widget.TextView; /** The activity. */ public class LoaderTestActivity2 extends ListActivity implements LoaderManager.LoaderCallbacks<List<Person>> { private DataSourceOrDomainModel dataSourceOrDomainModel = new DataSourceOrDomainModel(); private List<Person> listPerson; private PersonListAdapter personListAdapter; private TextView emptyView; private Loader<List<Person>> personLoader; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); listPerson = new ArrayList<Person>(); personListAdapter = new PersonListAdapter(listPerson); setListAdapter(personListAdapter); personLoader = new PersonLoader(this, dataSourceOrDomainModel, new ProgressHandler() ); setUpEmptyView(); getLoaderManager().initLoader(0, null, this); personLoader.forceLoad(); } public void setUpEmptyView() { emptyView = new TextView(this); emptyView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); emptyView.setVisibility(View.GONE); ((ViewGroup) getListView().getParent()).addView(emptyView); getListView().setEmptyView(emptyView); } public void publishProgress(int progress) { emptyView.setText("Loading data :" + String.valueOf(progress) + " %"); } @Override public Loader<List<Person>> onCreateLoader(int arg0, Bundle arg1) { return personLoader; } @Override public void onLoadFinished(Loader<List<Person>> personLoader, List<Person> result) { listPerson.clear(); listPerson.addAll(result); personListAdapter.notifyDataSetChanged(); } @Override public void onLoaderReset(Loader<List<Person>> arg0) { listPerson.clear(); personListAdapter.notifyDataSetChanged(); } /** List item view factory : the adapter. */ private class PersonListAdapter extends ArrayAdapter<Person> { public PersonListAdapter(List<Person> listPerson) { super(LoaderTestActivity2.this, 0, listPerson); } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = new PersonView(getContext()); } PersonView personView = (PersonView) convertView; personView.setPerson((Person) getItem(position)); return personView; } } private class ProgressHandler implements ProgressListener { @Override public void personLoaded(final int count, final int total) { runOnUiThread( new Runnable() { @Override public void run() { publishProgress(100 * count / total); } }); } } } class PersonLoader extends AsyncTaskLoader<List<Person>> { private DataSourceOrDomainModel dataSourceOrDomainModel; private ProgressListener progressHandler; public PersonLoader(Context context, DataSourceOrDomainModel dataSourceOrDomainModel, ProgressListener progressHandler ) { super(context); this.dataSourceOrDomainModel = dataSourceOrDomainModel; this.progressHandler = progressHandler; } @Override public List<Person> loadInBackground() { return dataSourceOrDomainModel.getListPerson( progressHandler ); } } 

Было бы сложнее добавить поддержку (поддержку librairy) в этот пример, поскольку в librairy поддержки нет эквивалента ListAcitivity. Мне нужно было бы создать ListFragment или создать FragmentActivity и дать ему макет, включая список.