Search code examples
javaandroidandroid-listviewandroid-listfragmentandroid-support-library

App crashing after update FragmentList from loader using support library


I have a loader that uses the Support Library v4, it is being used to carry the loading of a ListView in an Activity that contains two fragments, one containing the ListView (being it an extension of ListFragment) and the other carrying a button (which can be clicked while the loader is doing the work).

The implementation is pretty similar to the one available at the Android documentation of the AsyncTaskLoader, which is also creating a ListView trough the loader, with the exception of the monitoring part, where my implementation does not need the monitoring of changes: http://developer.android.com/reference/android/content/AsyncTaskLoader.html

As the app has support to API level 8, I am using the FragmentActivity::getSupportLoaderManager method to start the loader as advised in the documentation in order to keep the support.

http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html

When using this class as opposed to new platform's built-in fragment and loader support, you must use the getSupportFragmentManager() and getSupportLoaderManager() methods respectively to access those features.

Being the loader started from the Fragment, I had to use the ListFragment::getActivity method to call the method FragmentActivity::getSupportLoaderManager, resulting in the following code being used to start the loader:

getActivity().getSupportLoaderManager().initLoader(0,  null, this).forceLoad();

The app is running fine with APIs higher than 8, but on level 8 it is crashing when the loader tries to renders the list on UI after loading (Loader::onLoadFinished method).

Debugging I found that it is crashing at the moment that the ArrayAdapter<>::addAll method is being called on the adapter, which confirms that the problem is on the rendering of the UI. At that moment, the app is thrown to the SamplingProfilerIntegration class where something related to a snapshot is trying to be done at a static part of the class:

/** Whether or not a snapshot is being persisted. */
private static final AtomicBoolean pending = new AtomicBoolean(false);

static {
    samplingProfilerMilliseconds = SystemProperties.getInt("persist.sys.profiler_ms", 0);
    samplingProfilerDepth = SystemProperties.getInt("persist.sys.profiler_depth", 4);
    if (samplingProfilerMilliseconds > 0) {
        File dir = new File(SNAPSHOT_DIR);
        dir.mkdirs();
        // the directory needs to be writable to anybody to allow file writing
        dir.setWritable(true, false);
        // the directory needs to be executable to anybody to allow file creation
        dir.setExecutable(true, false);
        if (dir.isDirectory()) {
            snapshotWriter = Executors.newSingleThreadExecutor(new ThreadFactory() {
                    public Thread newThread(Runnable r) {
                        return new Thread(r, TAG);
                    }
                });
            enabled = true;
            Log.i(TAG, "Profiling enabled. Sampling interval ms: "
                  + samplingProfilerMilliseconds);
        } else {
            snapshotWriter = null;
            enabled = true;
            Log.w(TAG, "Profiling setup failed. Could not create " + SNAPSHOT_DIR);
        }
    } else {
        snapshotWriter = null;
        enabled = false;
        Log.i(TAG, "Profiling disabled.");
    }
}

It could be related to the specific behavior of the UI rendering on prior to Honeycomb versions as stated in the documentation, but I can not think of what.

http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html

Prior to Honeycomb (3.0), an activity's state was saved before pausing. Fragments are a significant amount of new state, and dynamic enough that one often wants them to change between pausing and stopping. These classes throw an exception if you try to change the fragment state after it has been saved, to avoid accidental loss of UI state. However this is too restrictive prior to Honeycomb, where the state is saved before pausing. To address this, when running on platforms prior to Honeycomb an exception will not be thrown if you change fragments between the state save and the activity being stopped. This means that in some cases if the activity is restored from its last saved state, this may be a snapshot slightly before what the user last saw.


Solution

  • I found that the support libraries v4 and v7, which are the being imported to the project, does not support the method ArrayAdapter<>::addAll() and this was making the app crash.

    This question is related to this issue and the solution presented was suitable to address my problem:

    ListViews - how to use ArrayAdapter.addAll() function before API 11?

    So, the solution was to implement my own version of the ArrayAdapter class, so that the prior to Honeycomb versions of Android could use it.