動作例:
- コードは 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 の実装について、以下の記事が大変参考になりました