Simple Persistence with Shared Preferences

The last piece of functionality you need to add is to actually use the query entered in the SearchView when the search request is submitted.

In your app, there will only be one active query at a time. That query should be persisted (remembered by the app) between restarts of the app (even after the user turns off the device). You will achieve this by writing the query string to shared preferences. Any time the user submits a query, you will first write the query to shared preferences, overwriting whatever query was there before. When a search is executed against Flickr, you will pull the query string from shared preferences and use it as the value for the text parameter.

Shared preferences are files on your filesystem that you read and edit using the SharedPreferences class. An instance of SharedPreferences acts like a key-value store, much like Bundle, except that it is backed by persistent storage. The keys are strings, and the values are atomic data types. If you look at them you will see that the files are simple XML, but SharedPreferences makes it easy to ignore that implementation detail. Shared preferences files are stored in your application’s sandbox, so you should not store sensitive information (like passwords) there.

To get a specific instance of SharedPreferences, you can use the Context.getSharedPreferences(String, int) method. However, in practice, you will often not care too much about the specific instance, just that it is shared across the entire app. In that case, it is better to use the PreferenceManager.getDefaultSharedPreferences(Context) method, which returns an instance with a default name and private permissions (so that the preferences are only available from within your application).

Add a new class named QueryPreferences, which will serve as a convenient interface for reading and writing the query to and from shared preferences.

Listing 27.11  Adding class to manage stored query (QueryPreferences.java)

public class QueryPreferences {
    private static final String PREF_SEARCH_QUERY = "searchQuery";

    public static String getStoredQuery(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context)
                .getString(PREF_SEARCH_QUERY, null);
    }

    public static void setStoredQuery(Context context, String query) {
        PreferenceManager.getDefaultSharedPreferences(context)
                .edit()
                .putString(PREF_SEARCH_QUERY, query)
                .apply();
    }
}

PREF_SEARCH_QUERY is used as the key for the query preference. You will use this key any time you read or write the query value.

The getStoredQuery(Context) method returns the query value stored in shared preferences. It does so by first acquiring the default SharedPreferences for the given context. (Because QueryPreferences does not have a Context of its own, the calling component will have to pass its context as input.)

Getting a value you previously stored is as simple as calling SharedPreferences.getString(…), getInt(…), or whichever method is appropriate for your data type. The second input to SharedPreferences.getString(PREF_SEARCH_QUERY, null) specifies the default return value that should be used if there is no entry for the PREF_SEARCH_QUERY key.

The setStoredQuery(Context) method writes the input query to the default shared preferences for the given context. In your code above, you call SharedPreferences.edit() to get an instance of SharedPreferences.Editor. This is the class you use to stash values in your SharedPreferences. It allows you to group sets of changes together in transactions, much like you do with FragmentTransaction. If you have a lot of changes, this will allow you to group them together into a single storage write operation.

Once you are done making all of your changes, you call apply() on your editor to make them visible to other users of that SharedPreferences file. The apply() method makes the change in memory immediately and then does the actual file writing on a background thread.

QueryPreferences is your entire persistence engine for PhotoGallery. Now that you have a way to easily store and access the user’s most recent query, update PhotoGalleryFragment to read and write the query as necessary.

First, update the stored query whenever the user submits a new query.

Listing 27.12  Storing submitted query in shared preferences (PhotoGalleryFragment.java)

public class PhotoGalleryFragment extends Fragment {
    ...
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
        ...
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
                Log.d(TAG, "QueryTextSubmit: " + s);
                QueryPreferences.setStoredQuery(getActivity(), s);
                updateItems();
                return true;
            }

            @Override
            public boolean onQueryTextChange(String s) {
                Log.d(TAG, "QueryTextChange: " + s);
                return false;
            }
        });
    }
    ...
}

Next, clear the stored query (set it to null) whenever the user selects the Clear Search item from the overflow menu.

Listing 27.13  Clearing stored query (PhotoGalleryFragment.java)

public class PhotoGalleryFragment extends Fragment {
    ...
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
        ...
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_item_clear:
                QueryPreferences.setStoredQuery(getActivity(), null);
                updateItems();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
    ...
}

Note that you call updateItems() after you update the stored query, just as you did in Listing 27.12. This ensures that the images displayed in the RecyclerView reflect the most recent search query.

Last, but not least, update FetchItemsTask to use the stored query rather than a hardcoded string. Add a custom constructor to FetchItemsTask that accepts a query string as input and stashes it in a member variable. In updateItems(), pull the stored query from shared preferences and use it to create a new instance of FetchItemsTask. All of these changes are shown in Listing 27.14.

Listing 27.14  Using stored query in FetchItemsTask (PhotoGalleryFragment.java)

public class PhotoGalleryFragment extends Fragment {
    ...
    private void updateItems() {
        String query = QueryPreferences.getStoredQuery(getActivity());
        new FetchItemsTask(query).execute();
    }
    ...
    private class FetchItemsTask extends AsyncTask<Void,Void,List<GalleryItem>> {
        private String mQuery;

        public FetchItemsTask(String query) {
            mQuery = query;
        }

        @Override
        protected List<GalleryItem> doInBackground(Void... params) {
            String query = "robot"; // Just for testing

            if (querymQuery == null) {
                return new FlickrFetchr().fetchRecentPhotos();
            } else {
                return new FlickrFetchr().searchPhotos(querymQuery);
            }
        }

        @Override
        protected void onPostExecute(List<GalleryItem> items) {
            mItems = items;
            setupAdapter();
        }
    }
}

Search should now work like a charm. Run PhotoGallery, try searching for something, and see what you get.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.130.31