gpt4 book ai didi

android - AsyncTaskLoader onLoadFinished 有一个挂起的任务和配置更改

转载 作者:IT老高 更新时间:2023-10-28 22:18:53 31 4
gpt4 key购买 nike

我正在尝试使用 AsyncTaskLoader 在后台加载数据以填充详细 View 以响应选择的列表项。我已经让它大部分工作了,但我仍然有一个问题。如果我在列表中选择第二个项目,然后在第一个选定项目的加载完成之前旋转设备,那么 onLoadFinished() 调用将向 Activity 报告被停止而不是新的 Activity 。当只选择一个项目然后旋转时,这很好用。

这是我正在使用的代码。 Activity :

public final class DemoActivity extends Activity
implements NumberListFragment.RowTappedListener,
LoaderManager.LoaderCallbacks<String> {

private static final AtomicInteger activityCounter = new AtomicInteger(0);

private int myActivityId;

private ResultFragment resultFragment;

private Integer selectedNumber;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

myActivityId = activityCounter.incrementAndGet();
Log.d("DemoActivity", "onCreate for " + myActivityId);

setContentView(R.layout.demo);

resultFragment = (ResultFragment) getFragmentManager().findFragmentById(R.id.result_fragment);

getLoaderManager().initLoader(0, null, this);

}

@Override
protected void onDestroy() {
super.onDestroy();
Log.d("DemoActivity", "onDestroy for " + myActivityId);
}

@Override
public void onRowTapped(Integer number) {
selectedNumber = number;
resultFragment.setResultText("Fetching details for item " + number + "...");
getLoaderManager().restartLoader(0, null, this);
}

@Override
public Loader<String> onCreateLoader(int id, Bundle args) {
return new ResultLoader(this, selectedNumber);
}

@Override
public void onLoadFinished(Loader<String> loader, String data) {
Log.d("DemoActivity", "onLoadFinished reporting to activity " + myActivityId);
resultFragment.setResultText(data);
}

@Override
public void onLoaderReset(Loader<String> loader) {

}

static final class ResultLoader extends AsyncTaskLoader<String> {

private static final Random random = new Random();

private final Integer number;

private String result;

ResultLoader(Context context, Integer number) {
super(context);
this.number = number;
}

@Override
public String loadInBackground() {
// Simulate expensive Web call
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}

return "Item " + number + " - Price: $" + random.nextInt(500) + ".00, Number in stock: " + random.nextInt(10000);
}

@Override
public void deliverResult(String data) {
if (isReset()) {
// An async query came in while the loader is stopped
return;
}

result = data;

if (isStarted()) {
super.deliverResult(data);
}
}

@Override
protected void onStartLoading() {
if (result != null) {
deliverResult(result);
}

// Only do a load if we have a source to load from
if (number != null) {
forceLoad();
}
}

@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}

@Override
protected void onReset() {
super.onReset();

// Ensure the loader is stopped
onStopLoading();

result = null;
}

}

}

列表 fragment :

public final class NumberListFragment extends ListFragment {

interface RowTappedListener {

void onRowTapped(Integer number);

}

private RowTappedListener rowTappedListener;

@Override
public void onAttach(Activity activity) {
super.onAttach(activity);

rowTappedListener = (RowTappedListener) activity;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(getActivity(),
R.layout.simple_list_item_1,
Arrays.asList(1, 2, 3, 4, 5, 6));
setListAdapter(adapter);

}

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
ArrayAdapter<Integer> adapter = (ArrayAdapter<Integer>) getListAdapter();
rowTappedListener.onRowTapped(adapter.getItem(position));
}

}

结果 fragment :

public final class ResultFragment extends Fragment {

private TextView resultLabel;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.result_fragment, container, false);

resultLabel = (TextView) root.findViewById(R.id.result_label);
if (savedInstanceState != null) {
resultLabel.setText(savedInstanceState.getString("labelText", ""));
}

return root;
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);

outState.putString("labelText", resultLabel.getText().toString());
}

void setResultText(String resultText) {
resultLabel.setText(resultText);
}

}

我已经能够使用普通的 AsyncTask 来完成这项工作,但我正在尝试了解有关 Loader 的更多信息,因为它们会自动处理配置更改。


编辑:我想我可能已经通过查看 LoaderManager 的来源找到了问题所在。 .在配置更改后调用 initLoader 时,LoaderInfo 对象的 mCallbacks 字段会随着新的 Activity 而更新,作为 LoaderCallbacks 的实现,正如我所料。

public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
if (mCreatingLoader) {
throw new IllegalStateException("Called while creating a loader");
}

LoaderInfo info = mLoaders.get(id);

if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);

if (info == null) {
// Loader doesn't already exist; create.
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
if (DEBUG) Log.v(TAG, " Created new loader " + info);
} else {
if (DEBUG) Log.v(TAG, " Re-using existing loader " + info);
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}

if (info.mHaveData && mStarted) {
// If the loader has already generated its data, report it now.
info.callOnLoadFinished(info.mLoader, info.mData);
}

return (Loader<D>)info.mLoader;
}

但是,当有一个挂起的加载器时,主 LoaderInfo 对象也有一个 mPendingLoader 字段,其中也引用了一个 LoaderCallbacks ,并且此对象永远不会使用 mCallbacks 字段中的新 Activity 进行更新。我希望看到代码看起来像这样:

// This line was already there
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
// This line is not currently there
info.mPendingLoader.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;

似乎正因为如此,挂起的加载程序在旧的 Activity 实例上调用 onLoadFinished。如果我在此方法中设置断点并使用调试器进行我觉得缺少的调用,那么一切都会按我的预期进行。

新问题是:我是否发现了错误,或者这是预期的行为?

最佳答案

在大多数情况下,如果 Activity 已经销毁,您应该忽略此类报告。

public void onLoadFinished(Loader<String> loader, String data) {
Log.d("DemoActivity", "onLoadFinished reporting to activity " + myActivityId);
if (isDestroyed()) {
Log.i("DemoActivity", "Activity already destroyed, report ignored: " + data);
return;
}
resultFragment.setResultText(data);
}

您还应该在任何内部类中插入检查 isDestroyed()。 Runnable - 是最常用的情况。

例如:

// UI thread
final Handler handler = new Handler();
Executor someExecutorService = ... ;
someExecutorService.execute(new Runnable() {
public void run() {
// some heavy operations
...
// notification to UI thread
handler.post(new Runnable() {
// this runnable can link to 'dead' activity or any outer instance
if (isDestroyed()) {
return;
}

// we are alive
onSomeHeavyOperationFinished();
});
}
});

但在这种情况下最好的方法是避免将 Activity 的强引用传递给另一个线程(AsynkTask、Loader、Executor 等)。

最可靠的解决方案在这里:

// BackgroundExecutor.java
public class BackgroundExecutor {
private static final Executor instance = Executors.newSingleThreadExecutor();

public static void execute(Runnable command) {
instance.execute(command);
}
}

// MyActivity.java
public class MyActivity extends Activity {
// Some callback method from any button you want
public void onSomeButtonClicked() {
// Show toast or progress bar if needed

// Start your heavy operation
BackgroundExecutor.execute(new SomeHeavyOperation(this));
}

public void onSomeHeavyOperationFinished() {
if (isDestroyed()) {
return;
}

// Hide progress bar, update UI
}
}

// SomeHeavyOperation.java
public class SomeHeavyOperation implements Runnable {
private final WeakReference<MyActivity> ref;

public SomeHeavyOperation(MyActivity owner) {
// Unlike inner class we do not store strong reference to Activity here
this.ref = new WeakReference<MyActivity>(owner);
}

public void run() {
// Perform your heavy operation
// ...
// Done!

// It's time to notify Activity
final MyActivity owner = ref.get();
// Already died reference
if (owner == null) return;

// Perform notification in UI thread
owner.runOnUiThread(new Runnable() {
public void run() {
owner.onSomeHeavyOperationFinished();
}
});
}
}

关于android - AsyncTaskLoader onLoadFinished 有一个挂起的任务和配置更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11515934/

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