Java – Prevents Android fragments from being recreated when the device spins

Prevents Android fragments from being recreated when the device spins… here is a solution to the problem.

Prevents Android fragments from being recreated when the device spins

Question:

When I rotate my device, the information contained in the fragment in my app reloads. Since this application extracts information from the internet, it means that some time of data is missing when the data is recollected.

I tried :

I’ve read many articles and S/O issues that provide some general guidance and tried their solutions, from android:configChanges="orientation|screenSize" in the list to adding setRetainInstance(true); In my MainActivity and FragmentActivity. None of this prevents my fragment from reloading. I used android in another activity in the app: configChanges (detailed activity, activated when the user selects a news story) and it works fine there, preventing the activity from reloading.

Structure of my application:

It is a simple application that extracts data from the “Hacker News” API and displays it to the user. There is a NavigationDrawer for users to choose from different news types. Each project in NavigationDrawer is a different fragment loaded into the MainActivity.class. The fragment discussed here is the HomeFragment .class, which loads the main news stories.

Why am I asking here:

I

can’t seem to find anything that fits into the current structure of my app, so I’m hoping for some help trying to find a solution to this seemingly trivial (but confusing) problem.

Code:

The main activity .java

package com.material.tdapps.hackernews.activity;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import com.material.tdapps.hackernews.R;

public class MainActivity extends AppCompatActivity implements FragmentDrawer.FragmentDrawerListener {

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

Toolbar appToolbar = (Toolbar) findViewById(R.id.toolbar);

setSupportActionBar(appToolbar);

noinspection ConstantConditions
        getSupportActionBar().setDisplayShowHomeEnabled(true);

FragmentDrawer drawerFragment = (FragmentDrawer) getSupportFragmentManager().findFragmentById(R.id.fragment_navigation_drawer);
        drawerFragment.setUp(R.id.fragment_navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), appToolbar);
        drawerFragment.setDrawerListener(this);

displayView(0);

}

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

return id == R.id.action_settings || super.onOptionsItemSelected(item);

}

@Override
    public void onDrawerItemSelected(View view, int position) {
        displayView(position);
    }

private void displayView(int position){
        Fragment fragment = null;
        String title = getString(R.string.app_name);
        switch (position) {
            case 0:
                fragment = new HomeFragment();
                title = getString(R.string.title_news);
                break;
            case 1:
                fragment = new ShowFragment();
                title = getString(R.string.title_show);
                break;
            case 2:
                fragment = new AskFragment();
                title = getString(R.string.title_ask);
                break;
            case 3:
                fragment = new JobsFragment();
                title = getString(R.string.title_job);
                break;
            default:
                break;

}

if (fragment != null){
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            fragmentTransaction.replace(R.id.container_body, fragment);
            fragmentTransaction.commit();

getSupportActionBar().setTitle(title);

}
    }

}

Home fragment .java

package com.material.tdapps.hackernews.activity;

import android.app.Activity;

import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.material.tdapps.hackernews.JSON.JSONNewsParser;
import com.material.tdapps.hackernews.R;
import com.material.tdapps.hackernews.model.StoryFeed;
import com.material.tdapps.hackernews.model.StoryItem;

import java.net.MalformedURLException;

public class HomeFragment extends Fragment {

StoryFeed storyFeed;
    Context context;
    ListView listView;
    NewsListAdapter newsListAdapter;

public HomeFragment() {

}

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

}

@Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_home, container, false);
        setRetainInstance(true);
        context = this.getActivity();
        storyFeed = new StoryFeed();
        newsListAdapter = new NewsListAdapter(this);
        listView = (ListView) rootView.findViewById(R.id.newsListView);
        if (savedInstanceState != null && (savedInstanceState.getSerializable("previousFeed") != null)) {
            Log.e("NOTIFY >> ", "GOING TO BUNDLE");
            storyFeed = (StoryFeed)savedInstanceState.getSerializable("previousFeed");
            listView.setAdapter(newsListAdapter);
            newsListAdapter.notifyDataSetChanged();
        } else {
            Log.e("NOTIFY >> ", "GOING TO ASYNCTASK");
            new AsyncLoadNewsFeed().execute();
            listView.setAdapter(newsListAdapter);
        }

setRetainInstance(true);

return rootView;

}

@Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
    }

@Override
    public void onDetach() {
        super.onDetach();
    }

private class AsyncLoadNewsFeed extends AsyncTask<Void, Void, Void> {

@Override
        protected Void doInBackground(Void... params) {
            JSONNewsParser newsParser = new JSONNewsParser();
            storyFeed = newsParser.parseJSON("https://hacker-news.firebaseio.com/v0/topstories.json",0,30);
            return null;
        }

@Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            if (storyFeed == null || storyFeed.getStoryCount() == 0){
                StoryItem nullStoryItem = new StoryItem();
                nullStoryItem.setTitle("ERROR: Null error!");
                nullStoryItem.setNumberComments(0);
                nullStoryItem.setScore(0);
                nullStoryItem.setBodyText("NULL");
                nullStoryItem.setTime(0);
                nullStoryItem.setAuthor("NULL");
                nullStoryItem.setUrl("http://www.google.com");
                storyFeed.addStory(nullStoryItem);
            }
            newsListAdapter.notifyDataSetChanged();
        }
    }

class NewsListAdapter extends BaseAdapter {

private LayoutInflater layoutInflater;

public NewsListAdapter(HomeFragment homeFragment){
            layoutInflater = (LayoutInflater) homeFragment.getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

@Override
        public int getCount() {
            return storyFeed.getStoryCount();
        }

@Override
        public Object getItem(int position) {
            return position;
        }

@Override
        public long getItemId(int position) {
            return position;
        }

class listViewHolder {

TextView titleTxtV;
            TextView timeTxtV;
            TextView scoreTxtV;
            TextView authorTxtV;
            TextView domTxtV;
            Button commentB;
            RelativeLayout detailsLayout;

listViewHolder(View v) {
                titleTxtV = (TextView) v.findViewById(R.id.titleText);
                timeTxtV = (TextView) v.findViewById(R.id.timeText);
                scoreTxtV = (TextView) v.findViewById(R.id.pointsText);
                authorTxtV = (TextView) v.findViewById(R.id.authorText);
                domTxtV = (TextView) v.findViewById(R.id.domainText);
                commentB = (Button) v.findViewById(R.id.commentButton);
                detailsLayout = (RelativeLayout) v.findViewById(R.id.newsDetailRelLayout);
            }

}

@Override
        public View getView(final int position, View convertView, ViewGroup parent) {

View listItem = convertView;
            listViewHolder holder;

if (listItem == null) {
                listItem = layoutInflater.inflate(R.layout.news_item_layout, parent, false );
                holder = new listViewHolder(listItem);
                listItem.setTag(holder);
            } else {
                holder = (listViewHolder) listItem.getTag();
            }

holder.titleTxtV.setText(storyFeed.getStory(position).getTitle());
            holder.timeTxtV.setText("Posted " + storyFeed.getStory(position).getTime());
            holder.authorTxtV.setText(" By " + storyFeed.getStory(position).getAuthor());
            holder.scoreTxtV.setText(storyFeed.getStory(position).getScore() + " Points ");
            holder.commentB.setText(Integer.toString(storyFeed.getStory(position).getNumberComments()));
            try {
                holder.domTxtV.setText("(" + storyFeed.getStory(position).getURLDomain() + ")");
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }

holder.commentB.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent showComments = new Intent(getActivity(), CommentActivity.class);
                    getActivity().startActivity(showComments);
                }
            });

holder.detailsLayout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent showDetails = new Intent(getActivity(), DetailWebActivity.class);
                    Bundle detailsBundle = new Bundle();
                    detailsBundle.putSerializable("newsFeed", storyFeed);
                    showDetails.putExtra("news",storyFeed);
                    showDetails.putExtras(detailsBundle);
                    showDetails.putExtra("position", position);
                    getActivity().startActivity(showDetails);
                }
            });

return listItem;

}
    }

}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.material.tdapps.hackernews" >

<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/HackerNewsTheme" >
        <activity
            android:name=".activity. MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".activity. CommentActivity"
            android:label="@string/title_activity_comment"
            android:parentActivityName=".activity. MainActivity"
            android:configChanges="orientation|screenSize">

<meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.material.tdapps.hackernews.activity.MainActivity" />
        </activity>
        <activity
            android:name=".activity. DetailWebActivity"
            android:label="@string/title_activity_detail_web"
            android:parentActivityName=".activity. MainActivity"
            android:configChanges="orientation|screenSize">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.material.tdapps.hackernews.activity.MainActivity" />
        </activity>
    </application>

</manifest>

Solution

Calling setRetainInstance(true) will prevent the fragment from being recreated. However, now the activity always recreates the DrawerFragment and forces the HomeFragment to be recreated, and the result is in the behavior you see. Check in your Activity.onCreate() for savedInstanceState to be null. If so, create your fragment. If not, don’t because the system will recover them automatically.

Related Problems and Solutions