Retrofit-HTTP-Android

Retrofit 은 안드로이드와 자바에서 HTTP client 에서 REST API통신을 하기 위한 라이브러리이다. 공식 홈페이지. 이번에는 간단한 node server와 android client 의 HTTP 통신을 하는 간단한 예제를 소개하려고 한다. 소스코드는 link를 첨부한다. server repository에는 node server가 들어있고, client repository에는 android client가 들어있다.

이 글에서는 Server 보다는 Retrofit 에 집중 하므로 서버에 관한 내용은 간단하게만 소개한다.

Server

Server는 nodejs로 작성 할 필요는 없다. 단지 HTTP 통신을 확인하기 위한 소스이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var express = require('express');
var http = require('http');
var path = require('path');
var app = express();
var bodyParser = require('body-parser');
// parse JSON inputs
app.use(bodyParser.json());
// parse URL encoded inputs
app.use(bodyParser.urlencoded());
app.set('port', process.env.PORT || 3000);
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
app.post('/ping', function(req, res){
res.status(200).send("pong");
});
app.post('/user/one', function(req, res){
if (req.body.name == "clucle") {
res.status(200).send({name: 'clucle', age: 23});
} else if(req.body.name == "junsu") {
res.status(200).send({name: 'junsu', age: 25});
} else {
res.status(404).send("ERROR");
}
});
app.post('/user/list', function(req, res){
res.status(200).send({users: [
{name: 'clucle', age: 23},
{name: 'junsu', age: 25}
]});
});
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listeneing on port ' + app.get('port'));
})

이 소스에서 중요한 부분은 app.post 라고 쓰여진 부분 중에서 /user/ 으로 접근하는 부분이다.

client 에서 user/one 으로 접근 할 때는 req.body.name 에 이름을 받아서 검색한다. body 는 json 형식으로 {“name”: “something”} 보내주면 파싱한다.

client 에서 user/list 으로 접근 할 때는 파라미터 없이 users 리스트를 전달해준다. body 는 json 형식으로 {“name”: “something”} 보내주면 파싱한다.

res.send(code) 에서 code 는 일반적으로 200번이 성공이고 400대가 넘어가면 관리자가 적용해둔 에러로 처리하고 client에서 200번에 왔을 때 통신을 처리하고, 에러코드가 넘어오면 에러처리를 해주면 된다.

Client

1. gradle setting

  • app의 build.gradle에 다음 소스를 추가한다. 우리가 주목해야 하는 부분은 아래와 같다. 이글의 작성 시점 2017-05-19의 최신 버전 2.3.0 버전을 사용하였다.

    compile ‘com.squareup.retrofit2:retrofit:2.3.0’
    compile ‘com.squareup.retrofit2:converter-gson:2.3.0’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.android_dev.clucle.retrofittutor"
minSdkVersion 18
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
testCompile 'junit:junit:4.12'
}

2. example

우선 통신을 하는 API를 정해서 미리 정의한다.

POST 명령으로 user/one을 실행하면 name을 인자로 줘서 User class에 맞는 json을 받는다. 주는 형식은 {“name”: name} 으로 전달된다

POST 명령으로 user/list를 실행하면 인자 없이 Users class에 맞는 json을 받는다.

@FormUrlEncoded는 json 인자를 서버에 보내줘야할 때 써준다.

1
2
3
4
5
6
7
8
9
10
11
public interface RetrofitAPI {
@POST("user/one")
@FormUrlEncoded
Call<User> getUser(
@Field("name") String name
);
@POST("user/list")
Call<Users> getUserList();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return ("name : " + name + " age : " + age);
}
}
1
2
3
public class Users {
public List<User> users;
}

정의해둔 HTTP API 통신을 하기 위해서 Retrofit 의 Builder로 서버와 통신한다. 이를 편하게 하기 위해서 RetrofitHelper를 제작한다. 아래 url과 port는 내 로컬 pc의 ip와 node서버를 열어둔 포트이다. getInstance() Method를 통해 어디서나 통신을 할 수 있도록 만들고 retrofit.create에서 위에서 만든 API를 넣어주면 서버와 통신한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class RetrofitHelper {
// server address, port
private final static String url = "http://192.168.0.22";
private final static int port = 3000;
private static Retrofit retrofit;
public static RetrofitAPI getInstance() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(url + ":" + port)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit.create(RetrofitAPI.class);
}
public static boolean returnNetworkState(Context context) {
ConnectivityManager connectivityManager =
((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
return connectivityManager.getActiveNetworkInfo() != null &&
connectivityManager.getActiveNetworkInfo().isConnected();
}
}

3. start retrofit

만들어둔 RetrofitHelper와 API로 통신하는 법이다. RetrofitHelper.getInstance().에서 Interface에 정의해둔 함수명을 적는다.

RetrofitHelper.getInstance().Method(parameter).enque(new Callback<인터페이스에서 정한 받을 타입>(){ 여기에서 엔터 치고 명령어들을 override한다. })

그러면 onResponse와 onFailure에 나오는데 onResponse는 서버에서 어떤 값이 넘어온 상태이고 onFailure는 서버에서 값이 넘어오지 않은 상태이거나, 너무 많은 요청이 들어간 등 서버에서 에러가 난 상태이다.

onResponse에서 code가 에러코드가 아니지 체크하고 response.code() == 200

response.body()는 내부에 json파일이 들어있는지 확인한다 response.body() != null

만약 Model Class 여기서는 User 에 getter를 만들어 두었으면 json형식의 파일을 맞춰서 받으면 자동으로 파싱되서 들어간다. 잘 모르겠으면 소스를 위에 링크한 소스를 참고하면 편할 것이다.

updateUserList에서는 updateUser와 다르게 json이 배열 형식으로 오는데 받는 json 형식이 현재 {users: [{“name”:”name1”,”age”:”age1”},{“name”:”name2”,”age”:”age2”}]}
배열에 담겨서 온다. 이럴 때는 response.body().users로 해주면 json의 users에 담긴 배열을 받아와서 List에 넣을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class RetrofitPresenter {
private View view;
private User user;
private List<User> userList;
// Constructor
public RetrofitPresenter(View view) {
this.view = view;
}
// Search One User and Show View
public void updateUser(String name) {
Log.e("[Client]", "Too many request?");
RetrofitHelper.getInstance().getUser(name).enqueue(new Callback<User>() {
@Override
public void onResponse(@NonNull Call<User> call, @NonNull Response<User> response) {
if (response.code() == 200 && response.body() != null) {
user = response.body();
view.updateTextView(user.toString());
} else {
onFailure(call, new Throwable("may be response something but error"));
}
}
@Override
public void onFailure(@NonNull Call<User> call, @NonNull Throwable t) {
Log.e("[Server]", t.getLocalizedMessage());
view.updateTextView("Error! at Search One");
}
});
}
// Search User List and Show View
public void updateUserList() {
RetrofitHelper.getInstance().getUserList().enqueue(new Callback<Users>() {
@Override
public void onResponse(@NonNull Call<Users> call, @NonNull Response<Users> response) {
if (response.code() == 200 && response.body().users != null) {
userList = response.body().users;
// 임시로 2번 째 사람의 이름만 출력해보자
view.updateTextView(userList.get(1).getName());
} else {
onFailure(call, new Throwable("may be response something but error"));
}
}
@Override
public void onFailure(@NonNull Call<Users> call, @NonNull Throwable t) {
Log.e("[Server]", t.getLocalizedMessage());
view.updateTextView("Error! at Search List");
}
});
}
public interface View {
void updateTextView(String text);
}
}

Retrofit REST API Android 후기

  • 이번에 해커톤 대회에서 새로 배워온 내용이라 써보면서 편하다고 느꼇고, 글만 봐서는 어떤 내용인지 잘 모를 것 같고 한번 써보면 쉽게 사용 할 수 있는 좋은 라이브러리 인 것 같다.