Insider's Guide To Udacity Android Developer Nanodegree Part 2
Insider's Guide To Udacity Android Developer Nanodegree Part 2
Written by Nikos Vaggalis   
Monday, 24 April 2017
Article Index
Insider's Guide To Udacity Android Developer Nanodegree Part 2
Lifecycle issues
Preferences
Loaders
Uploading the Project and Feedback

Including a Loader

Since everything database-related goes through the Content Provider, we also need a Loader and specifically a CursorLoader to wrap all database access in, in order to  retrieve our Favourite movies list upon clicking on the corresponding menu option of the MainActivity. The CursorLoader then returns a Cursor object which we iterate over to retrieve the encapsulated movie data:


private LoaderManager.LoaderCallbacks
dataResultLoaderListener = new LoaderManager.LoaderCallbacks()
{ @Override public Loader onCreateLoader(int id, Bundle args) { return new AsyncTaskLoader(
getApplicationContext()) { Cursor mData = null; @Override public Cursor loadInBackground() { Cursor data; mMovieDbDataArrayList = new ArrayList(); try { data = getContentResolver().
query(Contract.TableEntry.CONTENT_URI, null, null, null, Contract.TableEntry._ID); int movieDbIdIndex =
data.getColumnIndex(
Contract.TableEntry.
COLUMN_MOVIEDBID); while (data.moveToNext()) { int id = data.getInt(movieDbIdIndex);

Uri builtUri = Uri.parse(BASE_URL).
buildUpon().
appendPath(String.valueOf(id)) .appendQueryParameter(
QUERY_PARAM, API_KEY) .build();
String serviceResponse =
NetworkUtils.buildAndCallUrl(builtUri);
MovieDbData test = new MovieDbData();
ArrayList mMovieDbDataArrayListOut =
(ArrayList) JSONUtils.getJson(
test, serviceResponse); mMovieDbDataArrayList.add(
mMovieDbDataArrayListOut.get(0)); } return data; }
catch (Exception e) { ex1 = e; e.printStackTrace(); return null; } }

The last requirement to fulfil is making a movie's list of trailers and reviews accessible through its Details screen (ChildActivity).For this we need to make a call to the MovieDB API to retrieve the Trailers
http://api.themoviedb.org/3/movie/127380/videos?api_key=1777b338c5a5ffd1f89a472ca17ecac4


{ id: 127380, results: [ { id: "579511269251412c2b000dc0", iso_639_1: "en", iso_3166_1: "US", key: "eVSAZv4oqW8", name: "Official Sneak Peek", site: "YouTube", size: 720, type: "Teaser" }, { id: "57975a57925141399400405a", iso_639_1: "en", iso_3166_1: "US", key: "NQu-153MnGQ", name: "Official Trailer 2", site: "YouTube", size: 1080, type: "Trailer" }, { id: "571cdda9c3a3684e98001850", iso_639_1: "en", iso_3166_1: "US", key: "iG0P6bjyUNI", name: "Official US Teaser Trailer", site: "YouTube", size: 1080, type: "Trailer" }, { id: "571cdd7cc3a3684e9800184b", iso_639_1: "en", iso_3166_1: "US", key: "JhvrQeY3doI", name: "Official US Trailer", site: "YouTube", size: 1080, type: "Trailer" } ] }

and another one to retrieve the Reviews

http://api.themoviedb.org/3/movie/127380/reviews?api_key=1777b338c5a5ffd1f89a472ca17ecac4

{
 id: 127380,
 page: 1,
 results: [
  {
    id: "5835dce5c3a3682fad019c4a",
    author: "Reno",
   content: "**Before Nemo, a long ago another child 
got lost in the open ocean...** Whoa! 200 million
dollar film, 1 billion box office collections worldwide.
The 27th film to do that so and the 5th animation film.
This sequel was made after 13 years, surely a long
gab, but 'Finding Nemo' was one of my favourite
animations _9/10_",
   url: "https://www.themoviedb.org/review/
5835dce5c3a3682fad019c4a"
  }
 ],
 total_pages: 1,
 total_results: 1
}
 

So we need to add two extra objects ReviewData and TrailerData to hold the reviews and the trailers instances, two extra Adapters, ReviewsAdapter and TrailersAdapter, and two RecyclerViews to host those Adapters.
 
In each object's core, be it MovieDBData, ReviewData or TrailerData, I've added a generic convertJSONtoObject method that is part of an IJSONUtil interface, which takes a JSONObject and returns an ArrayList<?>.This is done so that although a JSONobject's parsing varies depending on the class it has to be converted to (MovieDBData, ReviewData or TrailerData), I'm are able to call convertJSONtoObject uniformly and through a common interface:


public interface IJSONUtil { ArrayList getJson(IJSONUtil objectIn,
String serviceResponse)
throws JSONException { JSONObject ObjJson =
new JSONObject(serviceResponse); return objectIn.convertJSONtoObject(ObjJson); } }

This infrastructure makes using just a single polymorphic Loader for  both ReviewData and TrailerData possible, instead of having a separate tailor made Loader class for each case, as it soon became apparent that creating separate Loader classes was an inefficient way which resulted in code duplication. As such, the introduction of the generic LoaderCallbacks2 class with its inner nested LoaderCallbacks3 class:


LoaderCallbacks2(IAdapter<D, Context> mAdapterIn,
RecyclerView mRecyclerViewIn,
Context contextIn) { public class LoaderCallbacks2 { private IAdapter mAdapter; private RecyclerView mRecyclerView; private Context mContext; private Exception ex; public LoaderCallbacks2(IAdapter<D, Context> mAdapterIn,
RecyclerView mRecyclerViewIn, Context contextIn) { mAdapter = mAdapterIn; mRecyclerView = mRecyclerViewIn; mContext = contextIn; } public LoaderCallbacks3 GetLoader(D mData) { return new LoaderCallbacks3(mData); }; class LoaderCallbacks3 extends
android.app.Application implements
LoaderManager.LoaderCallbacks<ArrayList> {
IJSONUtil mData;
public LoaderCallbacks3(D mDataIn) { super(); mData = (IJSONUtil) mDataIn; } @Override public Loader<ArrayList> onCreateLoader(
int id, Bundle args) { return new AsyncTaskLoader<ArrayList>(mContext) { @Override public ArrayList loadInBackground() { ArrayList mDataArrayList = new ArrayList(); try { Uri builtUri = Uri.parse(BASE_URL).buildUpon() .appendPath(args.getString(
mContext.getString(
R.string.put_extra_movie_id))) .appendPath(args.getString(
mContext.getString(
R.string.put_extra_path))) .appendQueryParameter(
QUERY_PARAM, API_KEY) .build(); String serviceResponse =
NetworkUtils.buildAndCallUrl(builtUri); mDataArrayList = (ArrayList) JSONUtils.getJson(
mData, serviceResponse); }
catch (Exception e) { ex = e; e.printStackTrace(); return null; } return mDataArrayList; } @Override protected void onStartLoading() { super.onStartLoading(); if (args == null) { return; } forceLoad(); } };
}
@Override public void onLoadFinished(Loader<ArrayList> loader,
ArrayList mDataArrayList) {
if (mDataArrayList != null &&
mDataArrayList.size() > 0){ mAdapter.setData(mDataArrayList, mContext); mRecyclerView.setAdapter(
(RecyclerView.Adapter) mAdapter); } else if (ex != null) { Toast.makeText(mContext, mContext.getString(
R.string.exception_GeneralException),
Toast.LENGTH_LONG).show();
   }
 }
@Override
 public void onLoaderReset(Loader<ArrayList> loader) {
  }
 }
}
  


Called from ChildActivity with either a ReviewData or TrailerData object:

           
TrailerData mDummyTrailerData = new TrailerData(); IAdapter<TrailerData, Context> mTrailersAdapter =
new TrailersAdapter(this); LoaderCallbacks2 loadTrailers = new LoaderCallbacks2(
mTrailersAdapter, tRecyclerView, this);
LoaderManager.LoaderCallbacks mTrailerLoader = 
(LoaderManager.LoaderCallbacks) loadTrailers.
GetLoader(mDummyTrailerData);
getSupportLoaderManager().restartLoader(
TRAILERS_LOADER_ID, myBundle, mTrailerLoader); ReviewData reviewData = new ReviewData(); IAdapter<ReviewData, Context> reviewAdapter =
new ReviewsAdapter(); LoaderCallbacks2 loadReviews = new LoaderCallbacks2(
reviewAdapter, rRecyclerView, this);
LoaderManager.LoaderCallbacks mReviewsAdapter = 
(LoaderManager.LoaderCallbacks) loadReviews.
GetLoader(reviewData);
getSupportLoaderManager().restartLoader(
REVIEWS_LOADER_ID,
myBundle, mReviewsAdapter);


Last Updated ( Monday, 24 April 2017 )
 
 

   
Banner
RSS feed of all content
I Programmer - full contents
Copyright © 2017 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.