Tomus Tomus - 3 months ago 29
Android Question

AsyncTaskLoader not initialized after screen rotation

I am using recyclerview which is getting its data from adapter which gets data trough AsyncTaskLoader. Everything runs fine until I rotate the screen from portrait to landscape. At that point I get nullpointer exception when using .forceLoad(); on my asyncTaskLoader
I have absolutelly no idea why is the fileLoader null when the same onCreate method is called regardless of orientation and it works on portrait and doesnt on landscape.

Here is the code:

package sk.tomus.filescoper;

import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import java.io.File;
import java.util.ArrayList;
mport java.util.List;

public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<File>> {

private android.support.v4.content.AsyncTaskLoader<List<File>> fileLoader;
private DirectoryManager directoryManager;
private RecyclerView recyclerView;
private FileRecyclerAdapter recyclerAdapter;
private RecyclerView.LayoutManager layoutManager;
private boolean isLandscape;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
directoryManager = new DirectoryManager(prefs.getString("PREFERENCE_EDIT_DEF_FOLDER", "/"));

recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

isLandscape = getResources().getBoolean(R.bool.isLandscape);
if (isLandscape) {
layoutManager = new GridLayoutManager(this, 4);
} else {
layoutManager = new LinearLayoutManager(this);
}
recyclerView.setLayoutManager(layoutManager);
recyclerAdapter = new FileRecyclerAdapter(new ArrayList<File>());
getSupportLoaderManager().initLoader(0, null, this);
//below is line 51 where the code crashes
fileLoader.forceLoad();
recyclerView.setAdapter(recyclerAdapter);

}

@Override
protected void onResume() {
super.onResume();
recyclerAdapter.setOnItemClickListener(new FileRecyclerAdapter.MyClickListener() {
@Override
public void onItemClick(int position, View v) {
Log.i("LOG", " Clicked on Item " + position);
}
});
}

private void onFileClicked(File file) {
if (file.isDirectory()) {
Log.i("opening directory", file.getAbsolutePath());
if (!file.canRead()) {
Toast.makeText(getApplicationContext(), "inaccessible", Toast.LENGTH_SHORT).show();
return;
}
directoryManager.setPreviousDir(directoryManager.getCurrentDir());
directoryManager.setCurrentDir(file);

if (fileLoader.isStarted()) {
fileLoader.onContentChanged();
}
} else {
openFile(Uri.fromFile(file));
}
}

private void openFile(Uri fileUri) {
String mimeType = directoryManager.getMimeType(fileUri);

if (mimeType != null) {
try {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setDataAndType(fileUri, mimeType);
startActivity(i);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "file type recognized, but no apps to open it", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(this, "unknown file type", Toast.LENGTH_LONG).show();
}
}

@Override
public Loader<List<File>> onCreateLoader(int i, Bundle bundle) {
fileLoader = new android.support.v4.content.AsyncTaskLoader<List<File>>(this) {
@Override
public List<File> loadInBackground() {
Log.i("loader loading:", directoryManager.getCurrentDir().toString());
return directoryManager.getAllFiles(directoryManager.getCurrentDir());
}
};
return fileLoader;
}

@Override
public void onLoadFinished(Loader<List<File>> loader, List<File> data) {
recyclerAdapter.setFiles(data);
}

@Override
public void onLoaderReset(Loader<List<File>> loader) {
}
}


here is the stacktrace:

08-29 00:50:21.176 16411-16411/sk.tomus.filescoper E/AndroidRuntime: FATAL EXCEPTION: main
Process: sk.tomus.filescoper, PID: 16411
java.lang.RuntimeException: Unable to start activity ComponentInfo{sk.tomus.filescoper/sk.tomus.filescoper.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.content.AsyncTaskLoader.forceLoad()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2379)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2442)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4053)
at android.app.ActivityThread.access$900(ActivityThread.java:156)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1357)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:211)
at android.app.ActivityThread.main(ActivityThread.java:5389)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1020)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:815)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.content.AsyncTaskLoader.forceLoad()' on a null object reference
at sk.tomus.filescoper.MainActivity.onCreate(MainActivity.java:51)
at android.app.Activity.performCreate(Activity.java:5990)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2332)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2442) 
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4053) 
at android.app.ActivityThread.access$900(ActivityThread.java:156) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1357) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:211) 
at android.app.ActivityThread.main(ActivityThread.java:5389) 
at java.lang.reflect.Method.invoke(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:372) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1020) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:815) 


thanks in advance for any help

Answer

You shouldn't be calling forceLoad() in your onCreate() method at all - it won't be created until onCreateLoader() is called (which is after onCreate() finishes).

As per the Making Loading Data Lifecycle Aware blog post, you should instead be calling forceLoad() in your AsyncTaskLoader's onStartLoading method. This ensures that your Loader is created and ready to start loading before you call forceLoad().

fileLoader = new android.support.v4.content.AsyncTaskLoader<List<File>>(this) {
    @Override
    public void onStartLoading() {
      forceLoad();
    }

    @Override
    public List<File> loadInBackground() {
        Log.i("loader loading:", directoryManager.getCurrentDir().toString());
        return directoryManager.getAllFiles(directoryManager.getCurrentDir());
    }
};