AsyncTaskLoader onLoadFinished with a pending task and config change

戏子无情 提交于 2019-12-02 20:24:29

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

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 ...

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.

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?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!