LoginSignup
22
21

More than 5 years have passed since last update.

Retrofit + Gson で、 API にアクセス & JSON のパージング処理を簡潔に実装する

Last updated at Posted at 2016-08-17

動作例:

retrofit_sample.gif

  • コードは GitHub に置きました。
  • 以下の手順で動作確認可能です。
    1. AndroidStudio 起動
    2. File > New > Project from Version Control > GitHub を選択。 Git Repository URL を https://github.com/Hachimori/AndroidSamples.git に指定してリポジトリをクローンする
    3. 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;
              }
          }
      }
  }

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>
22
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
21