動作例:
- コードは GitHub に置きました。
- 以下の手順で動作確認可能です。
- AndroidStudio 起動
- File > New > Project from Version Control > GitHub を選択。 Git Repository URL を https://github.com/Hachimori/AndroidSamples.git に指定してリポジトリをクローンする
- File > Open として、RetrofitSample のプロジェクトを開く
概要
- Retrofit: Android の HTTP クライアントライブラリ
- Gson: JSON を Java のオブジェクトに変換するライブラリ
- Retrofit と Gson を組み合わせて、 GitHub API にアクセス & 取得したデータを表示する方法をメモ
導入
手順
- build.gradle (Module: app) に追加
dependencies {
// ... 中略 ...
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
// ... 中略 ...
}
ProGuard
- proguard-rules.pro に追加
# Retrofit
-dontnote retrofit2.Platform
-dontnote retrofit2.Platform$IOS$MainThreadExecutor
-dontwarn retrofit2.Platform$Java8
-keepattributes Signature
-keepattributes Exceptions
GitHub の API
- 今回は、以下の三つを試しに利用する
- ユーザの情報を取得する
- ユーザのリポジトリを列挙する
- リポジトリのコミットを列挙する
実装例
Java 側の実装
API のエンドポイントを定義
- 以下のような Java Interface を実装
public interface GitHubService {
/**
* ユーザの情報を取得する
* https://developer.github.com/v3/users/#get-a-single-user
*/
@GET("users/{username}")
Call<User> getUser(@Path("username") String user);
/**
* ユーザのリポジトリ一覧を取得する
* https://developer.github.com/v3/repos/#list-user-repositories
*/
@GET("users/{user}/repos")
Call<List<Repos>> listRepos(@Path("user") String user);
/**
*
* リポジトリのコミットを列挙する
* https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository
*/
@GET("repos/{owner}/{repos}/commits")
Call<List<Commits>> listCommit(@Path("owner") String owner, @Path("repos") String repos);
}
-
ポイント
- アノテーション内の文字列に API エンドポイントを記述
- "{ }" で囲った文字列は、メソッドの引数として渡した文字列に置き換えられる
-
参考
API から取得したデータを Java オブジェクトとして格納するクラスを定義
- 以下のようなクラスを定義
- ユーザの情報を格納するクラス
public class User {
private String name;
private String company;
private String email;
private String bio;
private String created_at;
private String updated_at;
public String getName() {
return name;
}
public String getCompany() {
return company;
}
public String getEmail() {
return email;
}
public String getBio() {
return bio;
}
public String getCreatedAt() {
return created_at;
}
public String getUpdatedAt() {
return updated_at;
}
}
- ユーザのリポジトリ情報を格納するクラス
public class Repos {
private String id;
private String name;
private String full_name;
private String url;
private String description;
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getFullName() {
return full_name;
}
public String getUrl() {
return url;
}
public String getDescription() {
return description;
}
}
- リポジトリのコミット情報を格納するクラス
public class Commits {
private String url;
private String sha;
private Commit commit;
public String getUrl() {
return url;
}
public String getSha() {
return sha;
}
public String getMessage() {
return commit.getMessage();
}
public String getCommiterName() {
return commit.getCommiterName();
}
public String getCommiterEmail() {
return commit.getCommiterEmail();
}
public String getCommitDate() {
return commit.getCommitDate();
}
private class Commit {
private String message;
private Committer committer;
public String getMessage() {
return message;
}
public String getCommiterName() {
return committer.getName();
}
public String getCommiterEmail() {
return committer.getEmail();
}
public String getCommitDate() {
return committer.getDate();
}
private class Committer {
private String name;
private String email;
private String date;
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getDate() {
return date;
}
}
}
}
- ポイント
- JSON のパラメータ名と Java クラスの変数名を一緒にすると、JSON の値が該当するクラスの変数に代入される
- @SerializedName("パラメータ名") のアノテーションをつけると、異なる変数名にした上で値を取得できる
- JSON の中で JSON がネストしている場合でも、クラスの中でクラスをネストさせてその中で JSON のパラメータと同じ変数名を定義すると、パージングされる
- JSON配列を取得したい場合、クラス内の変数の型を List にする
GitHub API にアクセスし、データを取得する処理の実装
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<User> userCall = service.getUser("ユーザ名");
userCall.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
// ユーザ情報の取得 (https://api.github.com/users/"ユーザ名" にアクセスした時のデータが取得される)
User user = response.body();
// ...任意の処理...
}
@Override
public void onFailure(Call<User> call, Throwable t) {
// ...失敗した時の処理...
}
});
- 参考
ExpandableListView を利用し、親グループ:リポジトリ一覧, 子グループ:各リポジトリのコミット一覧 の折り畳めるリストを表示する
Java 側の実装例
- フィールド宣言
/**
* リポジトリ/コミットのリスト
*/
@BindView(R.id.repos_commit_list) ExpandableListView mReposCommitList;
/**
* リポジトリ/コミットのリストの Adapter
*/
private ReposCommitAdapter mReposCommitAdapter;
private Retrofit mRetrofit;
private GitHubService mService;
- リポジトリ一覧, 各リポジトリのコミット一覧を取得し、 ExpandListView にセットする処理の実装
- ※冒頭に載せた gif によるデモの実装の簡略版です
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.retrofit_sample, container, false);
ButterKnife.bind(this, view);
mRetrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
mService = mRetrofit.create(GitHubService.class);
// リポジトリ一覧, 各リポジトリのコミット一覧
final List<Repos> reposList = new ArrayList<>();
final List<List<Commits>> commitsList = new ArrayList<>();
// Adapter の生成
mReposCommitAdapter = new ReposCommitAdapter(getContext(), reposList, commitsList);
mReposCommitList.setAdapter(mReposCommitAdapter);
// リポジトリ一覧/各リポジトリのコミット一覧の取得し、データを ExpandListView にセットする
new AsyncTask<String, Void, Void>() {
@Override
protected Void doInBackground(String... params) {
// ユーザ名
String userName = params[0];
try {
// GitHub API にアクセスして、ユーザの GitHub リポジトリ一覧の取得
Call<List<Repos>> reposCall = mService.listRepos(userName);
reposList.addAll(reposCall.execute().body());
// GitHub API にアクセスして、各リポジトリのコミット一覧を取得
for (Repos repos : reposList) {
String reposName = repos.getName();
Call<List<Commits>> commitsCall = mService.listCommit(userName, reposName);
commitsList.add(commitsCall.execute().body());
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
// データ更新の通知
mReposCommitAdapter.notifyDataSetChanged();
}
}.execute("Hachimori");
return view;
}
- ExpandableListView の Adapter の実装
public class ReposCommitAdapter extends BaseExpandableListAdapter {
private Context mContext;
private List<Repos> mReposList;
private List<List<Commits>> mCommitList;
public ReposCommitAdapter(Context context, List<Repos> reposList, List<List<Commits>> commitList) {
mContext = context;
mReposList = reposList;
mCommitList = commitList;
}
@Override
public int getGroupCount() {
return mReposList.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return mCommitList.get(groupPosition).size();
}
@Override
public Repos getGroup(int groupPosition) {
return mReposList.get(groupPosition);
}
@Override
public Commits getChild(int groupPosition, int childPosition) {
return mCommitList.get(groupPosition).get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return true;
}
/**
* 親アイテムの取得
*/
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
View v = LayoutInflater
.from(mContext)
.inflate(R.layout.repository_list_cell, null);
// 親アイテムを取得
Repos repos = mReposList.get(groupPosition);
// リポジトリ名をセット
((TextView) v.findViewById(R.id.repository_name)).setText(repos.getName());
// リポジトリの説明をセット
((TextView) v.findViewById(R.id.repository_description)).setText(repos.getDescription());
// URLをセット
((TextView) v.findViewById(R.id.repository_url)).setText(repos.getUrl());
return v;
}
/**
* 子アイテムの取得
*/
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
View v = LayoutInflater
.from(mContext)
.inflate(R.layout.commit_list_cell, null);
// 子アイテムを取得
Commits commit = mCommitList.get(groupPosition).get(childPosition);
// Commit ID をセット
((TextView) v.findViewById(R.id.commit_id)).setText(commit.getSha());
// 名前をセット
((TextView) v.findViewById(R.id.committer_name)).setText(commit.getCommiterName());
// Email をセット
((TextView) v.findViewById(R.id.committer_email)).setText("(" + commit.getCommiterEmail() + ")");
// 日付をセット
((TextView) v.findViewById(R.id.commit_date)).setText(commit.getCommitDate());
// 説明をセット
((TextView) v.findViewById(R.id.commit_description)).setText(commit.getMessage());
return v;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
レイアウトファイルの実装
- ExpandableListView のレイアウトファイル (retrofit_sample.xml の一部)
<ExpandableListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/repos_commit_list"/>
- リポジトリ (親グループ要素) のレイアウトファイル (repository_list_cell.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<!-- リポジトリ名 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:id="@+id/repository_name"
android:textColor="@android:color/black"
android:textSize="24sp"/>
<!-- リポジトリの説明 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
android:id="@+id/repository_description"
android:textSize="18sp"/>
<!-- リポジトリの URL -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/repository_url"
android:textSize="12sp"/>
</LinearLayout>
- リポジトリのコミット (子グループ要素) のレイアウトファイル (commit_list_cell.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="30dp">
<!-- Commit ID -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/commit_id"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- 名前 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/committer_name"/>
<!-- Email -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/committer_email"/>
</LinearLayout>
<!-- 日付 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/commit_date"/>
<!-- 説明 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginLeft="20dp"
android:id="@+id/commit_description"/>
</LinearLayout>
- ExpandableListView の実装について、以下の記事が大変参考になりました