AsyncTaskLoader onLoadFinished with a pending task and config change

后端 未结 4 1165
醉酒成梦
醉酒成梦 2021-02-02 10:11

I\'m trying to use an AsyncTaskLoader to load data in the background to populate a detail view in response to a list item being chosen. I\'ve gotten it mostly work

相关标签:
4条回答
  • 2021-02-02 10:47

    A possible solution is to start the AsyncTask in a custom singleton object and access the onFinished() result from the singleton within your Activity. Every time you rotate your screen, go onPause() or onResume(), the latest result will be used/accessed. If you still don't have a result in your singleton object, you know it is still busy or that you can relaunch the task.

    Another approach is to work with a service bus like Otto, or to work with a Service.

    0 讨论(0)
  • 2021-02-02 10:55

    Ok I'm trying to understand this excuse me if I misunderstood anything, but you are losing references to something when the device rotates.

    Taking a stab...

    would adding

    android:configChanges="orientation|keyboardHidden|screenSize"
    

    in your manifest for that activity fix your error? or prevent onLoadFinished() from saying the activity stopped?

    0 讨论(0)
  • 2021-02-02 10:56

    In most cases you should just ignore such reports if Activity is already destroyed.

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

    Also you should insert checking isDestroyed() in any inner classes. Runnable - is the most used case.

    For example:

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

    But in such cases the best way is to avoid passing strong reference on Activity to another thread (AsynkTask, Loader, Executor, etc).

    The most reliable solution is here:

    // 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();
                }
            });
        }
    }
    
    0 讨论(0)
  • 2021-02-02 11:04

    Maybe not best solution but ... This code restart loader every time, which is bad but only work around that works - if you want to used loader.

    Loader l = getLoaderManager().getLoader(MY_LOADER);
    if (l != null) {
        getLoaderManager().restartLoader(MY_LOADER, null, this);
    } else {
        getLoaderManager().initLoader(MY_LOADER, null, this);
    }
    

    BTW. I am using Cursorloader ...

    0 讨论(0)
提交回复
热议问题