gpt4 book ai didi

android - 使用什么设计理念来异步更新UI

转载 作者:塔克拉玛干 更新时间:2023-11-02 21:15:14 25 4
gpt4 key购买 nike

我正在开发一款在时间线上显示工作时间表的应用。

这是目前应用程序设计的粗略布局:

enter image description here

数据存储在 SQLite 数据库中。当 Timeline(一个单例对象)从数据库助手类请求数据时,它会得到一个包含 Event 的 ArrayList(例如,一个 Event 可以是从 2016 年 5 月 1 日 03:00 开始到 2016 年 5 月 3 日 16:00 结束的职责)。 Timeline 然后将这些 Event 转换为 TimelineItem,一个表示(部分)Event 的类特定的一天。

Event 的加载和 EventTimelineItem 的转换都在 AsyncTasks 中完成。到目前为止一切顺利。

现在是我正在苦苦挣扎的部分:在获取新的数据库后更新 UI。

我的第一种方法是将更新的 TimelineItems 的 ArrayList 传递给 RecyclerView 适配器,并让适配器知道数据已通过 notifyDatasetChanged() 更改。这种方法的问题是1) 正在做很多不必要的工作(因为我们正在重新计算所有事件/时间线项目,而不仅仅是那些改变的)和2) RecyclerView 上的滚动位置在每次 DB 获取后重置

在我的第二种方法中,我实现了一些方法来检查自上次显示以来哪些事件/TimelineItems 发生了变化,其想法是使用 notifyItemChanged() 仅更改那些 TimelineItems。完成的工作更少,根本无需担心滚动位置。棘手的一点是检查哪些项目已更改确实需要一些时间,因此也需要异步完成:

我尝试在 doInBackground() 中进行代码操作,并通过在 onProgressUpdate() 中发布奥托总线事件来更新 UI。

private class InsertEventsTask extends AsyncTask<Void, Integer, Void> {

@Override
protected Void doInBackground(Void... params) {
ArrayList<Event> events = mCachedEvents;

// if mChangedEvents is not null and not empty
if (events != null && !events.isEmpty()) {
// get the list of pairs for the events
ArrayList<TimelineItemForDateTimePair> listOfPairs = convertEventsToPairs(events);
// insert the TimelineItems from the pairs into the Timeline
for (int i = 0; i < listOfPairs.size(); i++) {
// get the last position for the DateTime associated with the pair
int position = findLastPositionForDate(listOfPairs.get(i).dateTime);
// if position is -1, the events started on a day before the timeline starts
// so keep skipping pairs until position > -1
if (position > -1) {
// if the item is a PlaceholderItem
if (mTimelineItems.get(position).isPlaceholderItem) {
// remove the PlaceholderItem
mTimelineItems.remove(position);
// and add the TimelineItem from the pair at the position the PlaceholderItem was at
mTimelineItems.add(position, listOfPairs.get(i).timelineItem);
// update the UI on the UI thread
publishProgress(position, TYPE_CHANGED);
} else { // if the item is not a PlaceholderItem, there's already an normal TimelineItem in place
// place the new item at the next position on the Timeline
mTimelineItems.add(position + 1, listOfPairs.get(i).timelineItem);
publishProgress(position, TYPE_ADDED);
}
}
}
}
return null;
}

/**
* onProgressUpdate handles the UI changes on the UI thread for us. Type int available:
* - TYPE_CHANGED
* - TYPE_ADDED
* - TYPE_DELETED
*
* @param values value[0] is the position as <code>int</code>,
* value[1] is the type of manipulation as <code>int</code>
*/
@Override
protected void onProgressUpdate(Integer... values) {
int position = values[0];
int type = values[1];

// update the UI for each changed/added/deleted TimelineItem
if (type == TYPE_CHANGED) {
BusProvider.getInstance().post(new TimelineItemChangedNotification(position));
} else if (type == TYPE_ADDED) {
BusProvider.getInstance().post((new TimelineItemAddedNotification(position)));
} else if (type == TYPE_DELETED) {
// TODO: make delete work bro!
}
}
}

问题是,不知何故,在发布此进度时滚动会完全弄乱 UI。

我的主要问题是:当我更新适配器的数据集 (TimelineItems) 中的特定项目时,notifyItemChanged() 确实更改了项目但没有将项目放在正确的位置。

这是我的适配器:

/**
* A custom RecyclerView Adapter to display a Timeline in a TimelineFragment.
*/
public class TimelineAdapter extends RecyclerView.Adapter<TimelineAdapter.TimelineItemViewHolder> {

/*************
* VARIABLES *
*************/

private ArrayList<TimelineItem> mTimelineItems;

/****************
* CONSTRUCTORS *
****************/

/**
* Constructor with <code>ArrayList<TimelineItem></code> as data set argument.
*
* @param timelineItems ArrayList with TimelineItems to display
*/
public TimelineAdapter(ArrayList<TimelineItem> timelineItems) {
this.mTimelineItems = timelineItems;
}

// Create new views (invoked by the layout manager)
@Override
public TimelineItemViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_timeline, parent, false);
// set the view's size, margins, paddings and layout parameters
// ...

return new TimelineItemViewHolder(v);
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(TimelineItemViewHolder holder, int position) {
// - get element from your data set at this position
// - replace the contents of the view with that element

// if the item is a ShowPreviousMonthsItem, set the showPreviousMonthsText accordingly
if (mTimelineItems.get(position).isShowPreviousMonthsItem) {
holder.showPreviousMonthsText.setText(mTimelineItems.get(position).showPreviousMonthsText);
} else { // otherwise set the showPreviousMonthsText blank
holder.showPreviousMonthsText.setText("");
}

// day of month & day of week of the TimelineItem
if (mTimelineItems.get(position).isFirstItemOfDay) {
holder.dayOfWeek.setText(mTimelineItems.get(position).dayOfWeek);
holder.dayOfMonth.setText(mTimelineItems.get(position).dayOfMonth);
} else {
holder.dayOfWeek.setText("");
holder.dayOfMonth.setText("");
}

// Event name for the TimelineItem
holder.name.setText(mTimelineItems.get(position).name);

// place and goingTo of the TimelineItem

// if combinedPlace == ""
if(mTimelineItems.get(position).combinedPlace.equals("")) {
if (mTimelineItems.get(position).isFirstDayOfEvent) {
holder.place.setText(mTimelineItems.get(position).place);
} else {
holder.place.setText("");
}
if (mTimelineItems.get(position).isLastDayOfEvent) {
holder.goingTo.setText(mTimelineItems.get(position).goingTo);
} else {
holder.goingTo.setText("");
}
holder.combinedPlace.setText("");
} else {
holder.place.setText("");
holder.goingTo.setText("");
holder.combinedPlace.setText(mTimelineItems.get(position).combinedPlace);
}

if(mTimelineItems.get(position).startDateTime != null) {
holder.startTime.setText(mTimelineItems.get(position).startDateTime.toString("HH:mm"));
} else {
holder.startTime.setText("");
}

if(mTimelineItems.get(position).endDateTime != null) {
holder.endTime.setText(mTimelineItems.get(position).endDateTime.toString("HH:mm"));
} else {
holder.endTime.setText("");
}


if (!mTimelineItems.get(position).isShowPreviousMonthsItem) {
if (mTimelineItems.get(position).date.getDayOfWeek() == DateTimeConstants.SUNDAY) {
holder.dayOfWeek.setTextColor(Color.RED);
holder.dayOfMonth.setTextColor(Color.RED);
} else {
holder.dayOfWeek.setTypeface(null, Typeface.NORMAL);
holder.dayOfMonth.setTypeface(null, Typeface.NORMAL);
holder.dayOfWeek.setTextColor(Color.GRAY);
holder.dayOfMonth.setTextColor(Color.GRAY);
}
} else {
((RelativeLayout) holder.dayOfWeek.getParent()).setBackgroundColor(Color.WHITE);
}

holder.bindTimelineItem(mTimelineItems.get(position));
}

// Return the size of the data set (invoked by the layout manager)
@Override
public int getItemCount() {
return mTimelineItems.size();
}

// replace the data set
public void setTimelineItems(ArrayList<TimelineItem> timelineItems) {
this.mTimelineItems = timelineItems;
}

// replace an item in the data set
public void swapTimelineItemAtPosition(TimelineItem item, int position) {
mTimelineItems.remove(position);
mTimelineItems.add(position, item);
notifyItemChanged(position);
}

// the ViewHolder class containing the relevant views,
// also binds the Timeline item itself to handle onClick events
public class TimelineItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
protected TextView dayOfWeek;
protected TextView dayOfMonth;
protected TextView showPreviousMonthsText;
protected TextView name;
protected TextView place;
protected TextView combinedPlace;
protected TextView goingTo;
protected TextView startTime;
protected TextView endTime;

protected TimelineItem timelineItem;

public TimelineItemViewHolder(View view) {
super(view);
view.setOnClickListener(this);
this.dayOfWeek = (TextView) view.findViewById(R.id.day_of_week);
this.dayOfMonth = (TextView) view.findViewById(R.id.day_of_month);
this.showPreviousMonthsText = (TextView) view.findViewById(R.id.load_previous_data);
this.name = (TextView) view.findViewById(R.id.name);
this.place = (TextView) view.findViewById(R.id.place);
this.combinedPlace = (TextView) view.findViewById(R.id.combined_place);
this.goingTo = (TextView) view.findViewById(R.id.going_to);
this.startTime = (TextView) view.findViewById(R.id.start_time);
this.endTime = (TextView) view.findViewById(R.id.end_time);
}

public void bindTimelineItem(TimelineItem item) {
timelineItem = item;
}

// handles the onClick of a TimelineItem
@Override
public void onClick(View v) {
// if the TimelineItem is a ShowPreviousMonthsItem
if (timelineItem.isShowPreviousMonthsItem) {
BusProvider.getInstance().post(new ShowPreviousMonthsRequest());
}
// if the TimelineItem is a PlaceholderItem
else if (timelineItem.isPlaceholderItem) {
Toast.makeText(v.getContext(), "(no details)", Toast.LENGTH_SHORT).show();
}
// else the TimelineItem is an actual event
else {
Toast.makeText(v.getContext(), "eventId = " + timelineItem.eventId, Toast.LENGTH_SHORT).show();
}
}
}

这是在事件总线上发布更改时在 TimelineFragment 中触发的方法:

@Subscribe
public void onTimelineItemChanged(TimelineItemChangedNotification notification) {
int position = notification.position;
Log.d(TAG, "TimelineItemChanged detected for position " + position);
mAdapter.swapTimelineItemAtPosition(mTimeline.mTimelineItems.get(position), position);
mAdapter.notifyItemChanged(position);
Log.d(TAG, "Item for position " + position + " swapped");
}

需要注意的是,在我滚动离开更改的数据足够远并返回到之后的位置后,适配器的数据集似乎正确显示。不过,最初 UI 完全乱七八糟。

编辑:

我发现添加

mAdapter.notifyItemRangeChanged(position, mAdapter.getItemCount());

解决了这个问题,但是 - 不幸的是 - 将滚动位置设置为正在更改的位置 :(

这是我的 TimelineFragment:

/**
* Fragment displaying a Timeline using a RecyclerView
*/
public class TimelineFragment extends BackHandledFragment {
// DEBUG flag and TAG
private static final boolean DEBUG = false;
private static final String TAG = TimelineFragment.class.getSimpleName();

// variables
protected RecyclerView mRecyclerView;
protected TimelineAdapter mAdapter;
protected LinearLayoutManager mLinearLayoutManager;
protected Timeline mTimeline;
protected MenuItem mMenuItemScroll2Today;
protected MenuItem mMenuItemReload;
protected String mToolbarTitle;
// TODO: get the value of this boolean from the shared preferences
private boolean mUseTimelineItemDividers = true;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get a handle to the app's Timeline singleton
mTimeline = Timeline.getInstance();
setHasOptionsMenu(true);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_timeline, container, false);
rootView.setTag(TAG);

mRecyclerView = (RecyclerView) rootView.findViewById(R.id.timeline_list);
mRecyclerView.hasFixedSize();

// LinearLayoutManager constructor
mLinearLayoutManager = new LinearLayoutManager(getActivity());
// set the layout manager
setRecyclerViewLayoutManager();
// adapter constructor
mAdapter = new TimelineAdapter(mTimeline.mTimelineItems);
// set the adapter for the RecyclerView.
mRecyclerView.setAdapter(mAdapter);

// add lines between the different items if using them
if (mUseTimelineItemDividers) {
RecyclerView.ItemDecoration itemDecoration =
new TimelineItemDivider(this.getContext());
mRecyclerView.addItemDecoration(itemDecoration);
}

// add the onScrollListener
mRecyclerView.addOnScrollListener(new TimelineOnScrollListener(mLinearLayoutManager) {
// when the first visible item on the Timeline changes,
// adjust the Toolbar title accordingly
@Override
public void onFirstVisibleItemChanged(int position) {
mTimeline.mCurrentScrollPosition = position;
try {
String title = mTimeline.mTimelineItems
.get(position).date
.toString(TimelineConfig.TOOLBAR_DATE_FORMAT);
// if mToolbarTitle is null, set it to the new title and post on bus
if (mToolbarTitle == null) {
if (DEBUG)
Log.d(TAG, "mToolbarTitle is null - posting new title request on bus: " + title);
mToolbarTitle = title;
BusProvider.getInstance().post(new ChangeToolbarTitleRequest(mToolbarTitle));
} else { // if mToolbarTitle is not null
// only post on the bus if the new title is different from the previous one
if (!title.equals(mToolbarTitle)) {
if (DEBUG)
Log.d(TAG, "mToolbarTitle is NOT null, but new title detected - posting new title request on bus: " + title);
mToolbarTitle = title;
BusProvider.getInstance().post(new ChangeToolbarTitleRequest(mToolbarTitle));
}
}

} catch (NullPointerException e) {
// if the onFirstVisibleItemChanged is called on a "ShowPreviousMonthsItem",
// leave the title as it is
}
}
});

return rootView;
}

/**
* Set RecyclerView's LayoutManager to the one given.
*/
public void setRecyclerViewLayoutManager() {
int scrollPosition;

// If a layout manager has already been set, get current scroll position.
if (mRecyclerView.getLayoutManager() != null) {
scrollPosition = ((LinearLayoutManager) mRecyclerView.getLayoutManager())
.findFirstCompletelyVisibleItemPosition();
} else {
scrollPosition = mTimeline.mFirstPositionForToday;
}

mRecyclerView.setLayoutManager(mLinearLayoutManager);
mLinearLayoutManager.scrollToPositionWithOffset(scrollPosition, 0);
}

// set additional menu items for the Timeline fragment
@Override
public void onPrepareOptionsMenu(Menu menu) {
// scroll to today
mMenuItemScroll2Today = menu.findItem(R.id.action_scroll2today);
mMenuItemScroll2Today.setVisible(true);
mMenuItemScroll2Today.setIcon(Timeline.getIconForDateTime(new DateTime()));
mMenuItemScroll2Today.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
// stop scrolling
mRecyclerView.stopScroll();
// get today's position
int todaysPosition = mTimeline.mFirstPositionForToday;
// scroll to today's position
mLinearLayoutManager.scrollToPositionWithOffset(todaysPosition, 0);
return false;
}
});

// reload data from Hacklberry
mMenuItemReload = menu.findItem(R.id.action_reload_from_hacklberry);
mMenuItemReload.setVisible(true);
mMenuItemReload.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
// stop scrolling
mRecyclerView.stopScroll();
//
mTimeline.reloadDBForCurrentMonth();
mTimeline.loadEventsFromUninfinityDBAsync(mTimeline.mTimelineStart, mTimeline.mTimelineEnd);
return false;
}
});

super.onPrepareOptionsMenu(menu);
}

@Override
public void onResume() {
super.onResume();
// if the Timeline has been invalidated, let AllInOneActivity know it needs to replace
// this Fragment with a new one
if (mTimeline.isInvalidated()) {
Log.d(TAG, "posting TimelineInvalidatedNotification on the bus ...");
BusProvider.getInstance().post(
new TimelineInvalidatedNotification());
}
// fetch today's menu icon
if (mMenuItemScroll2Today != null) {
if (DEBUG) Log.d(TAG, "fetching scroll2today menu icon");
mMenuItemScroll2Today.setIcon(Timeline.getIconForDateTime(new DateTime()));
}
}

// from BackHandledFragment
@Override
public String getTagText() {
return TAG;
}

// from BackHandledFragment
@Override
public boolean onBackPressed() {
return false;
}

@Subscribe
public void onHacklberryReloaded(HacklberryLoadedNotification notification) {
resetReloading();
}

// handles ShowPreviousMonthsRequests posted on the bus by the TimelineAdapter's ShowPreviousMonthsItem onClick()
@Subscribe
public void onShowPreviousMonthsRequest(ShowPreviousMonthsRequest request) {
// create an empty OnItemTouchListener to prevent the user from manipulating
// the RecyclerView while it loads more data (would mess up the scroll position)
EmptyOnItemTouchListener listener = new EmptyOnItemTouchListener();
// add it to the RecyclerView
mRecyclerView.addOnItemTouchListener(listener);
// load the previous months (= add the required TimelineItems)
int newScrollToPosition = mTimeline.showPreviousMonths();
// pass the new data set to the TimelineAdapter
mAdapter.setTimelineItems(mTimeline.mTimelineItems);
// notify the adapter the data set has changed
mAdapter.notifyDataSetChanged();
// scroll to the last scroll (updated) position
mLinearLayoutManager.scrollToPositionWithOffset(newScrollToPosition, 0);
}

@Subscribe
public void onTimelineItemChanged(TimelineItemChangeNotification notification) {
int position = notification.position;
Log.d(TAG, "TimelineItemChanged detected for position " + position);
mAdapter.swapTimelineItemAtPosition(mTimeline.mTimelineItems.get(position), position);
//mAdapter.notifyItemRangeChanged(position, position);
Log.d(TAG, "Item for position " + position + " swapped");
}

我在应用首次加载后截取了该应用的屏幕截图。我将快速解释初始化时发生的情况:

  1. 时间线是通过使用 PlaceholderItems(只有日期的 TimelineItem)填充所有日期而构建的。
  2. 事件从数据库中加载并转换为 TimelineItems
  3. 每当新的 TimelineItem 发生变化并准备就绪时,Timeline 就会通过 otto 总线调用 TimelineFragment,以使用新的 TimelineItem 为该特定位置更新适配器的数据集。

这是初始加载后发生的情况的屏幕截图:

时间轴已加载,但某些项目插入了错误的位置。

enter image description here

当滚动离开并返回到之前显示不正确的日期范围时,一切都很好:

enter image description here

enter image description here

最佳答案

关于您的第二种方法。您的代码可能无法正常工作,因为您在 mTimelineItemsmCachedEvents 上有Data Race。我看不到您的所有代码,但您似乎在 doInBackground() 中同时使用 mTimelineItems 与 UI 线程,但没有任何同步。

我建议您混合使用第一种和第二种方法:

  1. 复制原始数据 (mTimelineItems) 并将其发送到 AsyncTask
  2. doInBackground() 中异步更改副本并记录所有更改。
  3. 将更改的数据和日志返回到 UI 线程。
  4. 使用日志将新数据应用到 RecyclerView。

让我用代码来说明这种方法。

数据管理:

public class AsyncDataUpdater
{
/**
* Example data entity. We will use it
* in our RecyclerView.
*/
public static class TimelineItem
{
public final String name;
public final float value;

public TimelineItem(String name, float value)
{
this.name = name;
this.value = value;
}
}

/**
* That's how we will apply our data changes
* on the RecyclerView.
*/
public static class Diff
{
// 0 - ADD; 1 - CHANGE; 2 - REMOVE;
final int command;
final int position;

Diff(int command, int position)
{
this.command = command;
this.position = position;
}
}

/**
* And that's how we will notify the RecyclerView
* about changes.
*/
public interface DataChangeListener
{
void onDataChanged(ArrayList<Diff> diffs);
}


private static class TaskResult
{
final ArrayList<Diff> diffs;
final ArrayList<TimelineItem> items;

TaskResult(ArrayList<TimelineItem> items, ArrayList<Diff> diffs)
{
this.diffs = diffs;
this.items = items;
}
}

private class InsertEventsTask extends AsyncTask<Void, Void, TaskResult>
{
//NOTE: this is copy of the original data.
private ArrayList<TimelineItem> _old_items;

InsertEventsTask(ArrayList<TimelineItem> items)
{
_old_items = items;
}

@Override
protected TaskResult doInBackground(Void... params)
{
ArrayList<Diff> diffs = new ArrayList<>();

try
{
//TODO: long operation(Database, network, ...).
Thread.sleep(1000);
}
catch(InterruptedException e)
{
e.printStackTrace();
}

//Some crazy manipulation with data...
//NOTE: we change the copy of the original data!
Random rand = new Random();
for(int i = 0; i < 10; i ++)
{
float rnd = rand.nextFloat() * 100.0f;
for(int j = 0; j < _old_items.size(); j++)
{
if(_old_items.get(j).value > rnd)
{
TimelineItem item = new TimelineItem("Item " + rnd, rnd);
//Change data.
_old_items.add(j, item);
//Log the changes.
diffs.add(new Diff(0, j));
break;
}
}
}

for(int i = 0; i < 5; i ++)
{
int rnd_index = rand.nextInt(_old_items.size());
//Change data.
_old_items.remove(rnd_index);
//Log the changes.
diffs.add(new Diff(2, rnd_index));
}
//...

return new TaskResult(_old_items, diffs);
}

@Override
protected void onPostExecute(TaskResult result)
{
super.onPostExecute(result);

//Apply the new data in the UI thread.
_items = result.items;
if(_listener != null)
_listener.onDataChanged(result.diffs);
}
}

private DataChangeListener _listener;
private InsertEventsTask _task = null;

/** Managed data. */
private ArrayList<TimelineItem> _items = new ArrayList<>();

public AsyncDataUpdater()
{
// Some test data.
for(float i = 10.0f; i <= 100.0f; i += 10.0f)
_items.add(new TimelineItem("Item " + i, i));
}

public void setDataChangeListener(DataChangeListener listener)
{
_listener = listener;
}

public void updateDataAsync()
{
if(_task != null)
_task.cancel(true);

// NOTE: we should to make the new copy of the _items array.
_task = new InsertEventsTask(new ArrayList<>(_items));
_task.execute();
}

public int getItemsCount()
{
return _items.size();
}

public TimelineItem getItem(int index)
{
return _items.get(index);
}
}

在 UI 中使用:

public class MainActivity extends AppCompatActivity
{
private static class ViewHolder extends RecyclerView.ViewHolder
{
private final TextView name;
private final ProgressBar value;

ViewHolder(View itemView)
{
super(itemView);

name = (TextView)itemView.findViewById(R.id.tv_name);
value = (ProgressBar)itemView.findViewById(R.id.pb_value);
}

void bind(AsyncDataUpdater.TimelineItem item)
{
name.setText(item.name);
value.setProgress((int)item.value);
}
}

private static class Adapter extends RecyclerView.Adapter<ViewHolder>
implements AsyncDataUpdater.DataChangeListener
{
private final AsyncDataUpdater _data;

Adapter(AsyncDataUpdater data)
{
_data = data;
_data.setDataChangeListener(this);
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
return new ViewHolder(v);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position)
{
holder.bind(_data.getItem(position));
}

@Override
public int getItemCount()
{
return _data.getItemsCount();
}

@Override
public void onDataChanged(ArrayList<AsyncDataUpdater.Diff> diffs)
{
//Apply changes.
for(AsyncDataUpdater.Diff d : diffs)
{
if(d.command == 0)
notifyItemInserted(d.position);
else if(d.command == 1)
notifyItemChanged(d.position);
else if(d.command == 2)
notifyItemRemoved(d.position);
}
}
}

private AsyncDataUpdater _data = new AsyncDataUpdater();

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

RecyclerView rv_content = (RecyclerView)findViewById(R.id.rv_content);
rv_content.setLayoutManager(new LinearLayoutManager(this));
rv_content.setAdapter(new Adapter(_data));

Button btn_add = (Button)findViewById(R.id.btn_add);
btn_add.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
_data.updateDataAsync();
}
});
}
}

我把Example GH 上的应用程序,因此您可以根据需要进行测试。

更新 1

关于数据竞赛。

  1. this.mTimelineItems = timelineItems;TimelineAdapter() 构造函数中复制了对 ArrayList 的引用,但不是ArrayList 本身的副本。因此,您有两个引用:TimelineAdapter.mTimelineItemsTimeline.mTimelineItems,它们都引用相同的 ArrayList 对象。请看this .

  2. 当从 Worker Thread 调用 doInBackground() 和从 UI Thread 调用 onProgressUpdate() 时发生数据竞争同时。主要原因是 publishProgress() 没有同步调用onProgressUpdate()。相反,publishProgress()计划在将来UI 线程 上调用onProgressUpdate()Here很好地描述了问题。

题外话。

这个:

mTimelineItems.set(position, item);

应该比这更快:

mTimelineItems.remove(position);
mTimelineItems.add(position, item);

关于android - 使用什么设计理念来异步更新UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34840470/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com