26

我习惯于使用适配器在 android 中构建列表。如果我需要一些长时间获取的数据,我会使用 asynctask 或简单的 runnable 来更新适配器所依赖的数据结构,并在适配器上调用 notifyDataChanged。

虽然它并不简单,但我最终发现这是一个简单的模型,它可以很好地分离逻辑表示(在 asynctask 中,更新数据结构)和视图(主要是充当视图工厂的适配器)。

尽管如此,我最近阅读了有关 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
4

2 回答 2

15

如果有人正在寻找我之前示例的加载器版本:这里是:

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 );
    }
}

在此示例中添加支持(支持库)会更加困难,因为支持库中没有等效的 ListAcitivity。我必须创建一个 ListFragment 或创建一个 FragmentActivity 并给它一个包含列表的布局。

于 2012-05-03T07:04:26.197 回答
11

加载程序要解决的一个问题是,如果您的活动在异步任务仍在进行中时重新启动(例如由于设备轮换或配置更改),会发生什么情况?在您的情况下,您重新启动的活动将启动任务的第二个实例并丢弃第一个实例的结果。当第一个完成时,您最终可能会崩溃,因为您的异步任务有一个引用是现在已完成的活动。

是的,使用加载器通常会产生更多/更复杂的代码,特别是如果您不能使用提供的加载器之一。

于 2012-05-02T23:19:34.003 回答