유지보수 중인 소스의 API 연동 NetworkManager 클래스 관련된 소스들이 오래전에 사용하던 소스코드여서 리팩토링 해보았습니다.
RxJava 및 retrofit2 를 이용해서 간단하게 적용했습니다. RxJava 기능이 너무 광범위해서 간단한 기능만 사용해 봅니다.
프로젝트에 포함한 라이브러리
def retrofit_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
implementation "com.squareup.retrofit2:converter-scalars:$retrofit_version"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"
implementation "io.reactivex.rxjava2:rxandroid:2.0.1"
인터페이스 구현
프로젝트에서 API 연동하는 부분이 한군대라 간단하게 구현해 놨습니다.
Observable<JsonObject> 로 받는 방법도 있고 Call<ResponseBody> 형태로도 구현해봤습니다.
Observable< T >
Call< T >
T 에 모델 TestModel 를 바로 넣어서 받아도 됩니다.
public class TestModel {
// {"title":"title","body":"body","userId":"1","id":101}
public String title;
public String body;
public String userId;
public int id;
}
public interface APIInterface {
////////////////////////////////////////////////
// rxjava2 Observable 통해서 받을 경우
@FormUrlEncoded
@Headers({
"accept: application/json",
"content-type: application/x-www-form-urlencoded; charset=utf-8"
})
@POST
Observable<JsonObject> getPostGsonObject(@Url String suburl, @FieldMap Map<String, String> param);
////////////////////////////////////////////////
// rxjava2 Observable 방식으로 Model Class 받을 경우
@FormUrlEncoded
@POST
Observable<TestModel> getPostObservable(@Url String suburl, @FieldMap Map<String, String> param);
////////////////////////////////////////////////
// retrofit2 Call<ResponseBody> 방식으로 받을 경우
@FormUrlEncoded
@Headers({
"accept: application/json",
"content-type: application/x-www-form-urlencoded; charset=utf-8"
})
@POST
Call<ResponseBody> getPostResponseBody(@Url String suburl, @FieldMap Map<String, String> param);
////////////////////////////////////////////////
// Call 방식으로 Model Class 받을 경우
@FormUrlEncoded
@POST
Call<TestModel> getPostCall(@Url String suburl, @FieldMap Map<String, String> param);
}
[API 연동 클래스]
구문에 .client(TrustOkHttpClientUtil.getUnsafeOkHttpClient()) 해당 하는 부분은 개발서버 연동할때 SSL 인증서가 없거나 만료되서 SSL 에러 발생으로 SSL 인증서 예외처리하는 클래스 입니다.
public class API {
static Retrofit retrofit = null;
public static APIInterface getInstance() {
return getRetrofitInstance().create(APIInterface.class);
}
private static synchronized Retrofit getRetrofitInstance() {
if (retrofit == null) {
Gson gson = new GsonBuilder().setLenient().create();
retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl("https://jsonplaceholder.typicode.com") // 테스트 API 서버 (구동됩니다.)
.client(TrustOkHttpClientUtil.getUnsafeOkHttpClient()) // 개발서버 SSL 인증서 없는 경우 예외처리 적용
.build();
}
return retrofit;
}
}
[TrustOkHttpClientUtil.java]
public class TrustOkHttpClientUtil {
public static OkHttpClient getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { //
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { //throws CertificateException
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// 그냥 true 리턴해도 되지만 취약점 점검시 취약점으로 걸림.
if (hostname.equalsIgnoreCase("m.운영도메인.co.kr") ||
hostname.equalsIgnoreCase("m.개발도메인.co.kr")) {
return true;
} else {
return false;
}
}
});
return builder.build();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
};
}
[사용 방법]
총 4가지 방식으로 API 받아서 처리하는 방법을 소개하겠습니다. 간단한 예제니 한번씩 테스트 해보세요.
1. Call<ResponseBody>
2. Call<TestModel>
3. Observable<JsonObject>
4. Observable<TestModel>
1. Call<ResponseBody> 사용 방법
Map params = new HashMap();
params.put("userId" , "1");
params.put("title" , "title");
params.put("body" , "body");
// "/posts" 서브 URL 입니다.
Call<ResponseBody> result = API.getInstance().getPostResponseBody("/posts", params);
result.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String result = response.body().string();
JSONObject jsonObject = new JSONObject(result);
DLog.d(new Exception(), jsonObject.toString());
} catch (JSONException | IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
DLog.e(new Exception(), call.toString());
}
});
/* 결과
{
"title": "title",
"body": "body",
"userId": "1",
"id": 101
}
*/
2. Call Model Class 사용 방법
TestModel 클래스를 다이렉트로 받아서 저리하는 방식
Map params = new HashMap();
params.put("userId", "1");
params.put("title", "title");
params.put("body", "body");
API.getInstance()
.getPostCall("/posts", params)
.enqueue(new Callback<TestModel>() {
@Override
public void onResponse(@NonNull Call<TestModel> call, @NonNull Response<TestModel> response) {
if (response.isSuccessful()) {
TestModel testModel = response.body();
DLog.d(new Exception(), testModel.title + " / " +
testModel.body + " / " +
testModel.userId + " / " +
testModel.id);
} else {
Toast.makeText(getApplicationContext(), "서버에 문제가 발생했습니다.", Toast.LENGTH_SHORT).show();
DLog.d(new Exception(), "Retrofit2 - Failure " + response.code() + " : " + response.message());
}
}
//통신 실패
@Override
public void onFailure(@NonNull Call<TestModel> call, @NonNull Throwable t) {
Toast.makeText(getApplicationContext(), "서버에 문제가 발생했습니다.", Toast.LENGTH_SHORT).show();
t.printStackTrace();
}
});
/* 결과
title / body / 1 / 101
*/
3. Observable<JsonObject> 사용 방법
// Retrofit 테스트 코드
Map params = new HashMap();
params.put("userId", "1");
params.put("title", "title");
params.put("body", "body");
Disposable disposable = API.getInstance()
.getPostGsonObject("/posts", params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
// JSONObject 객체 저장 해서 사용
JSONObject jsonObject = new JSONObject(result.toString());
DLog.d(new Exception(), jsonObject.toString());
// Gson JsonObject 객체 저장
// JsonObject resultObject = (JsonObject) result;
// DLog.d(new Exception(), resultObject.toString());
// String title = resultObject.get("title").getAsString();
// String body = resultObject.get("body").getAsString();
// String userId = resultObject.get("userId").getAsString();
// int id = resultObject.get("id").getAsInt();
// DLog.d(new Exception(), title + " / " + body + " / " + userId + " / " + id);
},
error -> {
DLog.e(new Exception(), "onError() : err : " + error.toString());
},
() -> {
DLog.d(new Exception(), "onComplete()");
}
);
/* 결과
{"title":"title","body":"body","userId":"1","id":101}
*/
4. Observable<TestModel> Class Model 사용 방법
Map params = new HashMap();
params.put("userId", "1");
params.put("title", "title");
params.put("body", "body");
Disposable disposable = API.getInstance()
.getPostObservable("/posts", params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
TestModel testModel = (TestModel) result;
if (testModel != null) {
DLog.d(new Exception(), testModel.title + " / " + testModel.body + " / " + testModel.userId + " / " + testModel.id);
}
},
error -> {
DLog.e(new Exception(), "onError() : err : " + error.toString());
},
() -> {
DLog.d(new Exception(), "onComplete()");
}
);
/* 결과
title / body / 1 / 101
*/
'Android(Java)' 카테고리의 다른 글
[Java] AsyncTask Deplecated Observable.fromCallable() 로 대체하기 (0) | 2024.12.02 |
---|---|
[Java] 하이브리드앱 웹페이지 PopupView에서 스크롤 시 Y값 0으로만 나올경우 (0) | 2024.10.24 |
[Java] FileProvider 설정 / 파일 저장 (2) | 2024.10.18 |
[Java] uses-feature 란? (0) | 2024.10.18 |
[Java] android.permission.QUERY_ALL_PACKAGES (0) | 2024.10.18 |