मैं चयनित सूची आइटम के जवाब में एक विस्तृत दृश्य को पॉप्युलेट करने के लिए पृष्ठभूमि में डेटा लोड करने के लिए AsyncTaskLoader
का उपयोग करने का प्रयास कर रहा हूं। मैं इसे ज्यादातर काम कर रहा हूं लेकिन मुझे अभी भी एक मुद्दा है। यदि मैं सूची में दूसरा आइटम चुनता हूं और फिर पहले चयनित आइटम के लोड को पूरा करने से पहले डिवाइस डिवाइस को घुमाता है, तो onLoadFinished()
कॉल नई गतिविधि की बजाय गतिविधि को रोकने के लिए रिपोर्ट कर रहा है। यह केवल एक ही आइटम चुनते समय और घूर्णन करते समय ठीक काम करता है।AsyncTaskLoader ऑनलोड एक लंबित कार्य और कॉन्फ़िगरेशन के साथ परिभाषित
यहां कोड है जिसका मैं उपयोग कर रहा हूं। गतिविधि:
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;
}
}
}
सूची टुकड़ा:
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));
}
}
परिणाम टुकड़ा:
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
s का उपयोग करके काम कर रहा है, लेकिन मैं अधिक जानने के लिए कोशिश कर रहा हूँ लगभग Loader
एस क्योंकि वे कॉन्फ़िगरेशन को स्वचालित रूप से बदलते हैं।
संपादित: मुझे लगता है मैं LoaderManager के लिए स्रोत को देखकर मुद्दा नीचे ट्रैक किए गए हो सकता है। कॉन्फ़िगरेशन परिवर्तन के बाद initLoader
को कॉल किया जाता है, LoaderInfo
ऑब्जेक्ट में mCallbacks
फ़ील्ड 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
क्षेत्र में नई गतिविधि के साथ अद्यतन कभी नहीं किया गया है। मैं इस के बजाय की तरह कोड नज़र देखने की अपेक्षा करेंगे:
// This line was already there
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
// This line is not currently there
info.mPendingLoader.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
यह इस है कि लंबित लोडर कॉल onLoadFinished
वर्ष गतिविधि उदाहरण पर की वजह से प्रतीत होता है। अगर मैं इस विधि में टूटता हूं और कॉल करता हूं जो मुझे लगता है कि डीबगर का उपयोग कर गायब है, तो सबकुछ काम करता है जैसा मैं उम्मीद करता हूं।
नया सवाल यह है: क्या मुझे एक बग मिला है, या यह अपेक्षित व्यवहार है?
['CursorLoader.java'] पर एक नज़र डालें (http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/ सामग्री/CursorLoader.java /? v = स्रोत) स्रोत कोड। 'ऑनस्टार्ट लोडिंग', 'ऑनस्टॉपलोडिंग', 'ऑन कैंसल', 'ऑनसेट', और 'डिलिवरी रिसेट' को स्रोत कोड के समान ही लागू करने का प्रयास करें ... 'लोडर प्रबंधक' मानता है कि इन सभी विधियों को सही तरीके से कार्यान्वित किया गया है। यही कारण है कि आपका कार्यान्वयन केवल आंशिक रूप से कॉन्फ़िगरेशन परिवर्तनों में काम कर रहा है। –
तो यह पता चला है कि 'ऑनलोड लोड() '* * वास्तव में कहा जा रहा है - यह सिर्फ पुराने गतिविधि की बजाय पुरानी गतिविधि (कॉन्फ़िगरेशन परिवर्तन से पहले एक) को रिपोर्ट कर रहा है। प्रश्न संपादित किया गया है और कोड अद्यतन किया गया है। –
ईमानदारी से, मुझे लगता है कि यहां समस्या इस उदाहरण की तुच्छता है ... वास्तविक जीवन की स्थिति में 'लोडर' में वास्तविक डेटा स्रोत नहीं होगा (यानी' निजी अंतिम int 'फ़ील्ड आपका वास्तविक डेटा स्रोत है, क्या ऐसा नहीं है?)। 'लोडर को अपने डेटा स्रोत की निगरानी भी करनी चाहिए और परिवर्तन किए जाने पर वापस रिपोर्ट करना चाहिए। अधिकांश मामलों में 'ऑनलोड लोडेड' कॉन्फ़िगरेशन परिवर्तन के बाद आपकी नई गतिविधि पर नहीं कहा जाएगा क्योंकि लोडर अपने पुराने डेटा को बरकरार रखने के लिए पर्याप्त स्मार्ट है ... यह केवल तब लोड होगा जब यह देखता है कि उसके बैकिंग डेटा में परिवर्तन हुए हैं बनाया गया। –