-
Notifications
You must be signed in to change notification settings - Fork 27
Tutorial
Chris Arriola edited this page Jul 26, 2016
·
6 revisions
In this exercise, we're going to interact with the GitHub API using Retrofit and RxJava. Specifically, we will be displaying a list of starred repositories for a given user.
- Start by cloning this repository:
git clone https://github.com/arriolac/GithubRxJava.git
- Include Retrofit and RxJava in
app/build.gradle
.
dependencies {
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'io.reactivex:rxandroid:1.2.0'
compile 'io.reactivex:rxjava:1.1.8'
// ...
}
- Create the
GitHubRepo
data object class. This class encapsulates a repository in GitHub (the network response contains more data but we're only interested in a subset of that).
public class GitHubRepo {
public final int id;
public final String name;
public final String htmlUrl;
public final String description;
public final String language;
public final int stargazersCount;
public GitHubRepo(int id, String name, String htmlUrl, String description, String language, int stargazersCount) {
this.id = id;
this.name = name;
this.htmlUrl = htmlUrl;
this.description = description;
this.language = language;
this.stargazersCount = stargazersCount;
}
}
- Create the
GitHubService
interface. We will pass this interface into Retrofit and retrofit will create an implementation ofGitHubService
.
public interface GitHubService {
@GET("users/{user}/starred") Observable<List<GitHubRepo>> getStarredRepositories(@Path("user") String userName);
}
- Create the
GitHubClient
class. This will be the object we will interact with to make network calls from the UI-level. - When constructing an implementation of
GitHubService
through Retrofit, we need to pass in anRxJavaCallAdapterFactory
as the call adapter so that network calls can returnObservable
objects (passing a call adapter is needed for any network call that returns a result other than aCall
). - We also need to pass in a
GsonConverterFactory
so that we can useGson
as a way to marshall JSON objects to Java objects.
public class GitHubClient {
private static final String GITHUB_BASE_URL = "https://api.github.com/";
private static GitHubClient instance;
private GitHubService gitHubService;
private GitHubClient() {
final Gson gson =
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
final Retrofit retrofit = new Retrofit.Builder().baseUrl(GITHUB_BASE_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
gitHubService = retrofit.create(GitHubService.class);
}
public static GitHubClient getInstance() {
if (instance == null) {
instance = new GitHubClient();
}
return instance;
}
public Observable<List<GitHubRepo>> getStarredRepos(@NonNull String userName) {
return gitHubService.getStarredRepositories(userName);
}
}
-
Next, create a simple UI that displays the retrieved repos given an inputted GitHub username.
-
activity_home.xml
- this is the layout for ourActivity
.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/list_view_repos"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/edit_text_username"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/username"/>
<Button
android:id="@+id/button_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/search"/>
</LinearLayout>
</LinearLayout>
-
item_github_repo.xml
- this is theListView
item layout for aGitHub
repo object.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp">
<TextView
android:id="@+id/text_repo_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textStyle="bold"
tools:text="Cropper"/>
<TextView
android:id="@+id/text_repo_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="2"
android:ellipsize="end"
android:textSize="16sp"
android:layout_below="@+id/text_repo_name"
tools:text="Android widget for cropping and rotating an image."/>
<TextView
android:id="@+id/text_language"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_repo_description"
android:layout_alignParentLeft="true"
android:textColor="?attr/colorPrimary"
android:textSize="14sp"
android:textStyle="bold"
tools:text="Language: Java"/>
<TextView
android:id="@+id/text_stars"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_repo_description"
android:layout_alignParentRight="true"
android:textColor="?attr/colorAccent"
android:textSize="14sp"
android:textStyle="bold"
tools:text="Stars: 1953"/>
</RelativeLayout>
- Create a
ListAdapter
that we will use to bindGitHubRepo
data into aListView
.
public class GitHubRepoAdapter extends BaseAdapter {
private List<GitHubRepo> gitHubRepos = new ArrayList<>();
@Override public int getCount() {
return gitHubRepos.size();
}
@Override public GitHubRepo getItem(int position) {
if (position < 0 || position >= gitHubRepos.size()) {
return null;
} else {
return gitHubRepos.get(position);
}
}
@Override public long getItemId(int position) {
return position;
}
@Override public View getView(int position, View convertView, ViewGroup parent) {
final View view = (convertView != null ? convertView : createView(parent));
final GitHubRepoViewHolder viewHolder = (GitHubRepoViewHolder) view.getTag();
viewHolder.setGitHubRepo(getItem(position));
return view;
}
public void setGitHubRepos(@Nullable List<GitHubRepo> repos) {
if (repos == null) {
return;
}
gitHubRepos.clear();
gitHubRepos.addAll(repos);
notifyDataSetChanged();
}
private View createView(ViewGroup parent) {
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final View view = inflater.inflate(R.layout.item_github_repo, parent, false);
final GitHubRepoViewHolder viewHolder = new GitHubRepoViewHolder(view);
view.setTag(viewHolder);
return view;
}
private static class GitHubRepoViewHolder {
private TextView textRepoName;
private TextView textRepoDescription;
private TextView textLanguage;
private TextView textStars;
public GitHubRepoViewHolder(View view) {
textRepoName = (TextView) view.findViewById(R.id.text_repo_name);
textRepoDescription = (TextView) view.findViewById(R.id.text_repo_description);
textLanguage = (TextView) view.findViewById(R.id.text_language);
textStars = (TextView) view.findViewById(R.id.text_stars);
}
public void setGitHubRepo(GitHubRepo gitHubRepo) {
textRepoName.setText(gitHubRepo.name);
textRepoDescription.setText(gitHubRepo.description);
textLanguage.setText("Language: " + gitHubRepo.language);
textStars.setText("Stars: " + gitHubRepo.stargazersCount);
}
}
}
- Glue everything together in
MainActivity
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private GitHubRepoAdapter adapter = new GitHubRepoAdapter();
private Subscription subscription;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ListView listView = (ListView) findViewById(R.id.list_view_repos);
listView.setAdapter(adapter);
final EditText editTextUsername = (EditText) findViewById(R.id.edit_text_username);
final Button buttonSearch = (Button) findViewById(R.id.button_search);
buttonSearch.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
final String username = editTextUsername.getText().toString();
if (!TextUtils.isEmpty(username)) {
getStarredRepos(username);
}
}
});
}
@Override protected void onDestroy() {
if (subscription != null && !subscription.isUnsubscribed()) {
subscription.unsubscribe();
}
super.onDestroy();
}
private void getStarredRepos(String username) {
subscription = GitHubClient.getInstance()
.getStarredRepos(username)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<GitHubRepo>>() {
@Override public void onCompleted() {
Log.d(TAG, "In onCompleted()");
}
@Override public void onError(Throwable e) {
e.printStackTrace();
Log.d(TAG, "In onError()");
}
@Override public void onNext(List<GitHubRepo> gitHubRepos) {
Log.d(TAG, "In onNext()");
adapter.setGitHubRepos(gitHubRepos);
}
});
}
}
- How can you filter the starred repos by language?
- How can you cache an Observable so that it returns the previous result rather than making a new network call each time?