Github API를 활용해서 특정 키워드로 Github의 저장소들을 검색해 불러오는 안드로이드 앱을 구현할 예정이다.
build.gradle (Module: app)
파일 dependencies 블록에 의존성 프로젝트를 추가하였다.
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.5'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava3:rxjava:3.0.6'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
implementation 'com.jakewharton:butterknife:10.2.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
Retrofit은 네트워크 통신 기능을 포함하고 있어 AndroidManifest.xml
파일에 네트워크 통신 퍼미션을 추가해준다.
<application
...
</application>
...
<uses-permission android:name="android.permission.INTERNET"/>
또한 API28 이후에서 Http에 접근하려면 cleartext HTTP를 활성화 시켜야 하기에 한 줄을 추가해준다.
<application
...
...
android:usesCleartextTraffic="true">
public class GithubResponse implements Serializable {
@SerializedName("id")
private int id;
@SerializedName("full_name")
private String fullName;
@SerializedName("description")
private String description;
@SerializedName("owner")
private Owner owner;
...
// getter/setter 생략
}
@SerializeName
은 Annotation value에 해당하는 데이터를 변수에 바인딩한다.
여기까지의 자세한 내용은 이전 블로그에서 확인
public interface GithubApiService {
@GET("/users/{username}/repos")
Single<List<GithubResponse>> getGithubData(@Path("username") String username);
}
여기서 RxJava를 이용한다.
기존에 Call만 가능했던 코드를, RxJava를 이용하여 Observable, Flowable, Single, Maybe, Completable
을 이용할 수 있게 된다.
값을 받아오는 GET
의 경우 보통 Single
을 이용하고, POST,DELETE,PUT의 경우 Completable을 이용합니다!
반드시는 아님!! 보통의 경우..
🖐🏻
Single
은 Observable의 변형된 형태 🖐🏻
Observable과 비슷하지만, 여러 개의 데이터를 발행할 수 있는 Observable과 달리 Single은 한 개의 데이터(or 에러)만을 발행한다.
Single은 onSuccess, onError 2가지의 알림을 보낸다.
onSuccess
: 데이터 하나를 발행함과 동시에 종료onError
: 에러가 발생했음을 알림public static Retrofit GithubBuild() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(Constant.GITHUB_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(createOkHttpClient)
.build();
}
return retrofit;
}
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
RxJava를 사용하기 위해 다음과 같이 addCallAdapterFactory에 RxJava2CallAdapterFactory.create()를 해주어 초기화를 시켜주었다.
RxJava Github 참고
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
위의 코드는 Json 형태로 전달 받는 Response를 사용자가 정의해놓은 인자들에 매칭시키기 위한 Converter이다.
public static OkHttpClient createOkHttpClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient
.Builder()
.addInterceptor(interceptor)
.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.build();
return client;
}
HttpLoggingInterceptor
을 생한 후 로그의 Level을 지정하고, 이를 OkHttpClient.Builder
에 추가한다.
아래와 같은 로그를 확인할 수 있다.
mGithubApiService.getGithubData(username)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<GithubResponse>>() {
// method to implement
}
subscribeOn
에서 백그라운드 쓰레드를 이용해 작업도록 만들어주며, 변경은 메인 쓰레드에서 할 수 있도록 한다.
통신 성공과 에러시에 작업을 분기해주고, compositeDisposable에 추가하여 마지막에 모두 구독을 취소해줄 수 있도록 합니다!
@Override
public void onSubscribe(Disposable d) { }
@Override
public void onSuccess(List<GithubResponse> githubResponses) {
// githubResponses 사이즈 만틈 이름, 설명, 이미지 url을 받아오기
for (int i = 0; i < githubResponses.size(); i++) {
String name = githubResponses.get(i).getFullName();
String description = githubResponses.get(i).getDescription();
String url = githubResponses.get(i).getOwner().getAvatarUrl();
// description == null 이라면 '비어있음' 텍스트 삽입
if (description == null) {
description = getString(R.string.description_is_null);
}
repoList.add(new Repo(name, description, url));
}
if (repoList != null && !repoList.isEmpty()) {
mGithubRepoAdapter = new GithubRepoAdapter(context, repoList, null);
recyclerView.setAdapter(mGithubRepoAdapter);
mGithubRepoAdapter.notifyDataSetChanged();
} else {
// 로그
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
if (Objects.equals(e.getCause(), IOError.class) || e.getCause() == null) {
// 로그
}
}
예상대로 Repository가 잘 넘어왔다!
잘 실행이 되지 않는다면 에러 코드를 통해 디버깅을 해보거나, Interceptor를 통해 에러 코드를 분기해줄 수도 있음.