22
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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>
    
  • ExpandableListView の実装について、以下の記事が大変参考になりました

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?