Android การใช้งาน Retrofit HTTP client สำหรับ Android และ Java

Android การใช้งาน Retrofit HTTP client สำหรับ Android และ Java

Retrofit เป็น Library อีกตัวที่น่าสนใจสำหรับในการใช้เป็น HTTP Client ไว้ให้ app android ของเราสามารถดึงและใส่ข้อมูลผ่าน api ที่เป็น web service หรือ restful api ได้ โดยเจ้า Retrofit นั้นรองรับทั้ง xml และ json โดยในบทความนี้จะเป็นตัวอย่างการดึงข้อมูล json แบบ GET ซึ่งจะใช้ service ของ https://jsonplaceholder.typicode.com โดยจะมี 2 url คือ

  1. https://jsonplaceholder.typicode.com/posts จะทำการคืนค่าเป็น Array Json ที่เก็บข้อมูล Post ทั้งหมดที่มี
  2. https://jsonplaceholder.typicode.com/posts/id โดยจะคืนค่าเป็น Json Object ที่เก็บข้อมูลรายละเอียดของ Post ที่ส่ง id เข้าไปนั้นเอง

ขั้นตอนแรกผมจะเริ่มโดยการสร้าง project android ขึ้นมาใหม่ชื่อว่า RetrofitSimple มี minSdkVersion ที่เวอร์ชั่น 17 และมี Activity เริ่มต้นคือ MainActivity ตามที่ android studio กำหนดค่าเริ่มต้นมาใหม่

เมื่อทำการสร้าง project เสร็จแล้วต่อมาก็จะมาเพิ่ม library เข้าไปใน project ซึ่งจะมี retrofit และ gson เพื่อเอาไว้แปลงค่า json ที่คืนค่าจาก request ให้เป็น Object เพื่อนำไปใช้งานต่อไป โดยการเพิ่ม library นั้นก็ให้ไป config ในไฟล์  build.gradle ในส่วนของ app ก็จะได้ดังรูป

retrofit เบื้องต้น

compile 'com.google.code.gson:gson:2.8.0'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'

ANDROID การใช้งาน RETROFIT HTTP CLIENT สำหรับ ANDROID และ JAVA

ต่อมาก็จะทำการสร้าง class เพื่อจัดการกับ json ที่ได้ค่ากลับมา จากรูปเมื่อลองเรียก url ดูก็จะคืนค่าข้อมูล post ซึ่งแต่ละอันก็จะมี ค่า attribute เหมื่อนกันคือ

userId, id, title และ body เพราะฉะนั้นเราจึงสร้าง class ที่มี attribute 4 อันตามค่า post ที่ได้กลับมาโดย ผมจะตั้งชื่อ class ว่า Post โดยมีโค้ดดังนี้

package com.thaicodingexample.retrofitsimple;
public class Post {
public int userId;
public int id;
public String title;
public String body;
}

เมื่อเราเตรียม class เสร็จแล้วต่อมาก็จะทำการเขียนไฟล์ interface ที่เป็นรายละเอียดในการติดต่อไปยัง server โดยผมตั้งชื่อไฟล์ว่า MyService มีโค้ดดังนี้

package com.thaicodingexample.retrofitsimple;

import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;

public interface MyService {
@GET("posts")
Call<List<Post>> getListPost();

@GET("posts/{id}")
Call<Post>getPost(@Path("id") int id);
}

ในการสร้าง interface เพื่อเป็นกำหนดรายละเอียดของ url นั้นไม่ต้องใส่ค่า domain name หรือว่า base url เข้าไปนะครับ เพราะเดียวจะใช้วิธีอ่านค่า base url จากค่าในไฟล์ string.xml

สังเกตจากโค้ดจะเห็นว่าเราจะมีการกำหนดประเภทของ request ว่าเป็นแบบ GET ส่วน post ไว้คราวหน้าละกันนะครับ และอีกอย่างคือ @Path จะเป็นการบอกว่า parameter ที่ส่งมาใช้งานนั้นจะไป map กับ url อันไหนอย่างเช่นในโค้ดตัวอย่าง @Path(“id”) int id ก็คือ parameter id จะไป map กับ {id} ใน url ของ service นั้นเอง ส่วนการ return ค่าของการ request แต่ละ url นั้นเราจะต้องเป็นผู้กำหนดว่าจะให้เจ้า gson แปลงเป็น โดยสามารถกำหนดชนิดที่ต้องการได้ภาย Call <type ที่ต้องการ >

ต่อมาจะไฟล์ Class ที่สร้างขึ้นเพื่อเป็นตัว control ว่าจะใช้ service อันไหนในกรณีที่มี service อยู่หลายๆอัน โดยผมจะตั้งชื่อ Class ว่า ControlService มีโค้ดดังนี้

package com.thaicodingexample.retrofitsimple;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class ControlService {

public static MyService getPostService(String base_url){
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(base_url)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
return retrofit.create(MyService.class);
}
}

จากโค้ดจะเห็นว่าในกรณีที่มี interface service หลายๆอันเราก็แค่สร้าง method ขึ้นมาใหม่จากนั้นก็ return service ที่ต้องการออกไป แต่จุดสังเกตคือจะมีการ pass base url เข้ามาจากนั้นจะทำให้ set ค่าให้ตัวแปล retrofit ตาม code ตัวอย่างเลยครับ นอกจากนี้แล้วยังต้อง add gson เข้าไปด้วยเพื่อทำการ แปล json มาเป็น object ของ class post เพื่อใช้งานต่อไป

เมื่อทำการสร้างไฟล์ที่เกี่ยวข้องกับการ call request เสร็จต่อมาจะเป็นการสร้าง UI เพื่อนำค่าที่ได้จากการ request มาแสดงผลต่อไป

โดย MainActivity นั้นจะเป็นการแสดง Post ใน ListView เรียงตามจำนวน Post ที่มี แต่การ request ข้อมูลแล้วแปลงข้อมูลเป็น Object ของ Class Post นั้นจะต้องทำการ pass object ไปยัง Adapter เพื่อไปแสดงใน ListView ซึ่งผมทำการสร้าง class ที่ชื่อว่า PostAdapter ทำการ extends มาจาก ArrayAdapter ที่เก็บข้อมูล Object Post โดย constructor นั้นจะรับ List<Post> เข้ามาเพื่อทำการ set ข้อมูลให้กับ TextView 2 อันที่อยู่ใน Layout activity_detail

โค้ด PostAdapter

package com.thaicodingexample.retrofitsimple;

import android.content.Context;
import android.content.Intent;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;

import org.w3c.dom.Text;

import java.util.ArrayList;
import java.util.List;

public class PostAdapter extends ArrayAdapter<Post> {

private static class ViewHolder {
private TextView titleView;
private TextView idView;
}

ViewHolder viewHolder;
public PostAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<Post> objects) {
super(context, resource, objects);
}

public View getView(int position, View convertView, ViewGroup parent) {

if (convertView == null) {
convertView = LayoutInflater.from(this.getContext())
.inflate(R.layout.list_post, parent, false);

viewHolder = new ViewHolder();
viewHolder.titleView = (TextView) convertView.findViewById(R.id.titleView);
viewHolder.idView = (TextView)convertView.findViewById(R.id.idView);

convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}

Post item = getItem(position);
if (item!= null) {
// My layout has only one TextView
// do whatever you want with your string and long
viewHolder.idView.setText(String.valueOf(item.id));
viewHolder.titleView.setText(item.title);
Log.d("Adapter", item.title);
final int id = item.id;
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Context context = getContext();
Intent intent = new Intent(context,DetailActivity.class);
intent.putExtra("id",id);
context.startActivity(intent);
}
});

}

return convertView;
}
}

จากโค้ดจะเห็นว่าจะมีการ setText ใน method getView โดยกำหนดค่าจาก object ที่อยู่ใน List นั้นเอง ต่อมาจะเป็นโค้ดของ MainActivity ที่จะมีการเรียกใช้ service ผ่าน retrofit สังเกต method callService จะมีการประกาศตัวแปร mySevice ซึ่งกำหนดค่าให้เท่ากับ service ที่ได้จาก method ใน ControlService จะเห็นว่าจะมีการ pass base_url เข้าไปซึ่งเป็นการนำค่ามาจาก string.xml โดยใช้ method getString() นั้นเอง

โค้ด MainActivity.java

package com.thaicodingexample.retrofitsimple;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import java.util.List;

public class MainActivity extends AppCompatActivity {

final String TAG = "MainActivity";
ListView listView;
PostAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.list);
callService();
}

private void callService(){
MyService myService = ControlService.getPostService(getString(R.string.base_url));
Call<List<Post>> call;
call = myService.getListPost();
call.enqueue(new Callback<List<Post>>(){

@Override
public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {
if(response.isSuccessful()){

List<Post> posts = response.body();
adapter = new PostAdapter(getApplicationContext(),R.layout.list_post,posts);
listView.setAdapter(adapter);
}else{
Log.d(TAG,response.errorBody().toString());
}
}

@Override
public void onFailure(Call<List<Post>> call, Throwable t) {
Log.d(TAG,t.getMessage());
}
});
}
}

จากนั้นก็จะประกาศตัวแปรชนิด Call<List<Post>> ขึ้นมาเพื่อจะเป็นการบอกว่าถ้า request service แล้วจะทำการแปลงข้อมูล json เป็น List<Post> นั้นเอง หลังจากนั้นก็ทำการ request service โดย call.enqueue ตามโค้ดตัวอย่างเลยครับ โดยจะแบ่งเป็น onResponse คือมีการ request แล้วมี response กลับมา ส่วน onFailure จะเป็นการ request service แล้วไม่มี response กลับมา ที่นี้มาดูโค้ดที่อยู่ใน onResponse จะเห็นว่ามีการ check ตัวแปร response.isSuccessful() เพื่อให้แน่ใจว่า response code ที่ server ตอบกลับมานั้น ok บรรทัดต่อมาจะมีการประกาศตัวแปร List<Post> posts = response.body(); ซึ่งจะเห็นว่ามันได้ทำการแปลงข้อมูล json เป็น List<Post> เรียบร้อยแล้วและสะดวกอีกด้วย เสร็จแล้วก็ pass ไปยัง adapter เพื่อ set ค่าให้ ListView เพื่อแสดงผลก็เป็นอันจบ


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.thaicodingexample.retrofitsimple.MainActivity">

<ListView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
</ListView>

</android.support.constraint.ConstraintLayout>

ผลลัพธ์เวลารันโปรแกรม

ANDROID การใช้งาน RETROFIT HTTP CLIENT สำหรับ ANDROID และ JAVA

ANDROID การใช้งาน RETROFIT HTTP CLIENT สำหรับ ANDROID และ JAVA

ส่วนด้านล่างจะเป็นโค้ดของ DetailActivity ซึ่งจะเป็น Activity ที่จะแสดงรายละเอียดเมื่อกด ListView แต่ละอันใน MainActiviy ซึ่งการทำงานก็จะคล้ายๆกับ MainActivity จะต่างกันของแค่ request service แล้วแปลง response เป็น แค่ object post อันเดียวจากนั้นก็จะทำการกำหนดค่า TextView เลย เพราะใน Activity นี้ไม่มี ListView เหมือน MainActivity นั้นเอง นอกจากนี้ยังมีการส่่งค่า id ไปให้ service ด้วยเพื่อที่จะทำ id ไป map กับ path ใน url ที่กำหนดไว้ใน interface ที่สร้างไว้ก่อนหน้านี้ครับ

package com.thaicodingexample.retrofitsimple;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class DetailActivity extends AppCompatActivity {

final String TAG = "DetailActivity";
TextView txtTitle;
TextView txtBody;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
Intent intent = this.getIntent();
int id = intent.getIntExtra("id",0);
txtTitle = (TextView)findViewById(R.id.txt_title);
txtBody = (TextView)findViewById(R.id.txt_body);
callService(id);
}

private void callService(int id){
MyService service = ControlService.getPostService(getString(R.string.base_url));
Call<Post> call = service.getPost(id);
call.enqueue(new Callback<Post>() {
@Override
public void onResponse(Call<Post> call, Response<Post> response) {
if(response.isSuccessful()){
Post post = response.body();
txtTitle.setText(post.title);
txtBody.setText(post.body);
}
}

@Override
public void onFailure(Call<Post> call, Throwable t) {
Log.d(TAG,t.getMessage());
}
});
}
}

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.thaicodingexample.retrofitsimple.DetailActivity">

<TextView
android:id="@+id/txt_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:padding="10dp"/>
<TextView
android:id="@+id/txt_body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/txt_title"
android:paddingLeft="10dp"/>
</RelativeLayout>

ยังไงก็ลองศึกษาจากโค้ดตัวอย่างดูได้นะครับ สำหรับผม retrofit ถือว่าเป็น Library ที่ใช้เป็น HTTP Client ที่ดีและสะดวกเลยก็ว่าได้ครับ ผมก็ขอจบบทความนี้ไว้เท่านี้นะครับ

Download SourceCode

Add a Comment

Your email address will not be published. Required fields are marked *