Reputation: 1117
i'm trying to save my ListView data in onSaveInstanceState. For some reason when I call a class variable inside onSaveInstanceState its not populated with the correct data.
public class ArtistTopTen extends Fragment {
private ListView artistTopTen;
private String artistId;
private Tracks listTracks;
private exposed.coding.android_nano.Adapters.ArtistTopTen topTenListAdapter;
private SpotifyApi spotifyApi;
private SpotifyService spotifyService;
private MusicPlayerDialogFragment musicPlayerDialogFragment;
public ArtistTopTen() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
final View view = inflater.inflate(R.layout.fragment_artist_top_ten, container, false);
if(getActivity().findViewById(R.id.topten_container) == null) {
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
// Start the service
getActivity().startService(new Intent(getActivity(), Music.class));
if(savedInstanceState == null) {
try {
if (getActivity().getIntent().hasExtra("id")) {
artistId = getActivity().getIntent().getStringExtra("id");
} else {
artistId = getArguments().getString("artistid");
}
} catch (Exception e) {
Log.e("ERROR", e.toString());
}
}else {
Log.e("NOTICE", "SavedInstanceState has data");
}
if(artistId != null) {
artistTopTen = (ListView) view.findViewById(R.id.artist_top_ten);
topTenListAdapter = new exposed.coding.android_nano.Adapters.ArtistTopTen(getActivity(), listTracks);
spotifyApi = new SpotifyApi();
spotifyService = spotifyApi.getService();
SharedPreferences sharedPreferences = getActivity().getPreferences(Context.MODE_PRIVATE);
HashMap country = new HashMap<String, Object>();
if (sharedPreferences.getString("country", "us").length() != 0) {
country.put("country", sharedPreferences.getString("country", "us").toString());
} else {
country.put("country", "us");
}
spotifyService.getArtistTopTrack(artistId, country, new Callback<Tracks>() {
@Override
public void success(Tracks tracks, Response response) {
listTracks = tracks;
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
notifyAdapter();
}
});
}
@Override
public void failure(RetrofitError error) {
Toast.makeText(view.getContext(), "Somthing went wrong", Toast.LENGTH_LONG).show();
}
});
artistTopTen.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(":MediaPlayerUpdateList");
intent.putExtra("tracks", new ParcelablesTracks(listTracks, position));
getActivity().sendBroadcast(intent);
if(getActivity().findViewById(R.id.topten_container) != null) {
musicPlayerDialogFragment = new MusicPlayerDialogFragment();
musicPlayerDialogFragment.setCancelable(false);
musicPlayerDialogFragment.show(getFragmentManager(), "fragment_music_player");
}else {
intent = new Intent(getActivity(), MusicPlayerActivity.class);
intent.putExtra("returning", false);
startActivity(intent);
}
}
});
}
if(savedInstanceState != null) {
}
return view;
}
private void notifyAdapter() {
Log.e("First Track Name - SHOWING", listTracks.tracks.get(0).name);
topTenListAdapter.setListTracks(listTracks);
if(artistTopTen.getAdapter() == null) {
artistTopTen.setAdapter(topTenListAdapter);
}else {
topTenListAdapter.notifyDataSetChanged();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
Log.e("First Track Name - NOT SHOWING", listTracks.tracks.get(0).name);
//outState.putParcelable("list", new ParcelablesTracks(listTracks, 0));
super.onSaveInstanceState(outState);
}
}
If you look in the function notifiyAdapter() you can see that i'm printing out the name of the song:
Log.e("First Track Name - SHOWING", listTracks.tracks.get(0).name);
However when onSaveInstanceState is called it errors out whenever I try to access tracks:
Log.e("First Track Name - NOT SHOWING", listTracks.tracks.get(0).name);
The error:
07-16 10:12:07.759 29091-29091/exposed.coding.android_nano E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: exposed.coding.android_nano, PID: 29091
java.lang.NullPointerException: Attempt to read from field 'java.util.List kaaes.spotify.webapi.android.models.Tracks.tracks' on a null object reference
at exposed.coding.android_nano.Fragments.ArtistTopTen.onSaveInstanceState(ArtistTopTen.java:162)
at android.app.Fragment.performSaveInstanceState(Fragment.java:2198)
at android.app.FragmentManagerImpl.saveFragmentBasicState(FragmentManager.java:1605)
at android.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1662)
at android.app.Activity.onSaveInstanceState(Activity.java:1367)
at android.app.Activity.performSaveInstanceState(Activity.java:1298)
at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1288)
at android.app.ActivityThread.callCallActivityOnSaveInstanceState(ActivityThread.java:3961)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3924)
at android.app.ActivityThread.access$900(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1309)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5257)
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:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
I'm pretty new to Android development but i'm using the same process to save data in another fragment and its working just fine. I'm not sure whats going on here.
Repo: https://github.com/Bioto/Android-Nano-Degree-Project1.2
EDIT:
Setting listTracks:
E/Setting listTracks﹕ ArtistTopTen{98c918d #2 id=0x7f0c0051}
onSaveInstanceState:
E/onSaveInstanceState()﹕ ArtistTopTen{3b7de284 #1 id=0x7f0c0051}
Upvotes: 0
Views: 98
Reputation: 134664
EDIT: So what seems to be happening is that the Fragment instance that's receiving the response is not the same one that is saving its state. This is probably due to how you're handling the callback for the Spotify request.
I think the general idea of what's happening is:
So what you're actually seeing is a memory leak (where your old Fragment is held until the request completes). I would suggest looking into the Loader
framework to handle network requests across configuration changes. Alternatively, put that work into an IntentService
, persist the tracks once they're loaded, and broadcast the event. If the Fragment is still around and receives the broadcast, it can load the new results. Otherwise, when it next comes to onStart() or onResume() it can look for any existing results (that may have been loaded while the fragment was not listening).
Attempt to read from field 'java.util.List kaaes.spotify.webapi.android.models.Tracks.tracks' on a null object reference
listTracks
is null. So calling listTracks.tracks
will cause a NullPointerException
. There seem to be cases where listTracks
is never assigned (it is only assigned in your success()
method for getArtistTopTracks()
, so if that fails for any reason, it will never be assigned. In this case, it's probably sufficient to wrap that line in a null-check; i.e.:
if (listTracks != null) {
// do stuff with listTracks
}
Upvotes: 2