Qua bài viết này hi vọng bạn có thể hiểu và sử dụng được thư viện Retrofit. Trước giờ để kết nối và lấy dữ liệu từ 1 WebService trên Android có nhiều cách khác nhau. Có thể bạn đã từng dùng các lớp trong gói Http Apache hoặc đã từng dùng 1 số thư viện để kết nối với internet và nhận các dữ liệu từ server như Volley của Google, KSOAP …
Hôm nay tôi xin giới thiệu một thư viện vô cùng lợi ích cho việc kết nối internet và nhận dữ liệu từ server một cách dễ dàng và viết code theo mô hình chuẩn RESTFul Webservices đó là:
Retrofit (Latest version: Ver2.2.0)
Retrofit là gì?
Retrofit là một type-safe HTTP client cho Android và Java và được tạo ra bởi Square. Nó làm cho việc nhận và tải lên JSON (hoặc dữ liệu khác) một cách khá dễ dàng tới một WebService dựa trên mô hình REST. Retrofit giúp dễ dàng kết nối đến một dịch vụ REST ở trên web bằng cách dịch API thành các Interface của Java. Trong hướng dẫn này, tôi sẽ chỉ cho bạn cách sử dụng một trong các thư viện HTTP phổ biến nhất và thường được khuyến khích sử dụng cho Android.
Thư viện mạnh mẽ này giúp bạn dễ dàng lấy dữ liệu JSON hoặc XML, sau đó phân tích cú pháp thành Plain Old Java Objects (POJOs). Các yêu cầu GET, POST, PUT, PATCH, và DELETE tất cả đều có thể được thực thi.
Giống như hầu hết các phần mềm nguồn mở khác, Retrofit được xây dựng trên nền của một số thư viện mạnh mẽ và công cụ khác. Đằng sau nó, Retrofit sử dụng OkHttp (từ cùng một nhà phát triển) để xử lý các yêu cầu mạng. Ngoài ra, Retrofit không tích hợp sẵn một bộ chuyển đổi JSON để phân tích từ JSON thành các đối tượng Java. Thay vào đó, nó hỗ trợ cho các thư viện chuyển đổi JSON sau đây để xử lý điều đó.
Các gói trang bị thêm cho phép sử dụng các bộ chuyển đổi sau đây:
- Gson : com.squareup.retrofit2:converter-gson
- Jackson : com.squareup.retrofit2:converter-jackson
- Moshi : com.squareup.retrofit2:converter-moshi
- Protobuf : com.squareup.retrofit2:converter-protobuf
- Wire : com.squareup.retrofit2:converter-wire
- Simple XML : com.squareup.retrofit2:converter-simplexml
- Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
Tạo một dự án Android Studio
Mở Android Studio lên và tạo ra một dự án mới với một Empty Activity đặt tên là MainActivity.
Thêm thư viện Retrofit vào Project
Sau khi tạo một dự án mới, thêm các thư viện cần thiết của Retrofit sau đây trong build.gradle (Module) của bạn. Bao gồm thư viện Retrofit, Glide để xử lý hình ảnh và Gson của Google để chuyển đổi JSON thành POJO (Plain Old Java Objects) cũng như Gson tích hợp của Retrofit.
dependencies { .... // Retrofit compile 'com.squareup.retrofit2:retrofit:2.2.0' // JSON Parsing compile 'com.google.code.gson:gson:2.8.0' compile 'com.squareup.retrofit2:converter-gson:2.2.0' // Glide compile 'com.github.bumptech.glide:glide:3.7.0' .... }
Bạn nhớ Sync Project with Gradle File sau khi thêm các thư viện vào.
Thêm quyền truy cập Internet vào AndroidManifest.xml
Để có thể Parse Json với thư viện Retrofit, chúng ta cần phải bao thêm quyền INTERNET trong tập tin manifest: AndroidManifest.xml.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.dev4u.ntc.retrofitdemo"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Tạo các lớp
Trong việc nhận về JSON từ link này http://dev.ntcde.com/news/api.php?latest_news=10 . Do đó chúng ta tạo ra lớp dữ liệu sau đây:
Các bạn ném link trên vào đây http://pro.jsonlint.com cho dễ nhìn.
Tạo class Post
Để class Post trong packge model cho dễ quản lý.
package com.dev4u.ntc.retrofitdemo.model; /** * IDE: Android Studio * Created by Nguyen Trong Cong - NTCDE.COM * Name packge: com.dev4u.ntc.retrofitdemo * Name project: RetrofitDemo * Date: 3/12/2017 * Time: 17:49 */ public class Post { int id_post; int id_ca; String name_ca; String title; String description; String content; String datetime; String image; public int getId_post() { return id_post; } public int getId_ca() { return id_ca; } public String getName_ca() { return name_ca; } public String getTitle() { return title; } public String getDescription() { return description; } public String getContent() { return content; } public String getDatetime() { return datetime; } public String getImage() { return image; } @Override public String toString() { return (title); } }
Tạo class RetrofitClient
Để sử dụng các yêu cầu mạng đến một RESTful API bằng Retrofit, chúng ta cần tạo ra một đối tượng bằng cách sử dụng lớp Retrofit Builder và cấu hình nó với một URL cơ sở.
package com.dev4u.ntc.retrofitdemo; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; /** * IDE: Android Studio * Created by Nguyen Trong Cong - NTCDE.COM * Name packge: com.dev4u.ntc.retrofitdemo * Name project: RetrofitDemo * Date: 3/12/2017 * Time: 17:49 */ public class RetrofitClient { private static Retrofit retrofit = null; public static Retrofit getClient(String baseUrl) { if (retrofit == null) { retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build(); } return retrofit; } }
Tạo API Interface
Tạo API Interface đặt tên là APIService. Interface này dùng để chứa các phương thức mà chúng ta sẽ sử dụng để gọi các yêu cầu truy vấn HTTP chẳng hạn như GET, POST, PUT, và DELETE. Hãy bắt đầu với yêu cầu GET.
package com.dev4u.ntc.retrofitdemo; import com.dev4u.ntc.retrofitdemo.model.Post; import java.util.List; import retrofit2.Call; import retrofit2.http.GET; /** * IDE: Android Studio * Created by Nguyen Trong Cong - NTCDE.COM * Name packge: com.dev4u.ntc.retrofitdemo * Name project: RetrofitDemo * Date: 3/12/2017 * Time: 17:49 */ public interface APIService { /* * Retrofit get annotation with our URL * And our method that will return us details of post. */ // GET latest news from server // Server return json array @GET("/news/api.php?latest_news=10") Call<List<Post>> getPostsDetails(); // GET news by id post from server // Server return json object @GET("/news/api.php?id_post=2") Call<Post> getPostDetails(); }
Tạo class ApiUtils
Class ApiUtils dùng để khởi tạo Retrofit trong MainActivity và gọi các phương thức trong Interface APIService.
package com.dev4u.ntc.retrofitdemo; /** * IDE: Android Studio * Created by Nguyen Trong Cong - NTCDE.COM * Name packge: com.dev4u.ntc.retrofitdemo * Name project: RetrofitDemo * Date: 3/12/2017 * Time: 17:49 */ public class ApiUtils { public static final String BASE_URL = "http://dev.ntcde.com"; private ApiUtils() { } public static APIService getAPIService() { return RetrofitClient.getClient(BASE_URL).create(APIService.class); } }
Tạo Layout để hiển thị dữ liệu
Tập tin activity_main.xml là layout cho MainActivity của chúng ta. Layout này sẽ có hai trường Button, một cái để Get Json Array và một Button để Get Json Object và một ListView để hiện thị dữ liệu.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.dev4u.ntc.retrofitdemo.view.MainActivity"> <Button android:id="@+id/jsonArray" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Json Array" /> <Button android:id="@+id/jsonObject" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Json Object" /> <ListView android:id="@+id/lvPost" android:layout_width="match_parent" android:layout_height="match_parent"></ListView> </LinearLayout>
Tạo class Custom ListView cho Item ListView
Tạo Adapter custom cho listview class PostAdapter extends BaseAdapter
package com.dev4u.ntc.retrofitdemo.view; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.dev4u.ntc.retrofitdemo.model.Post; import com.dev4u.ntc.retrofitdemo.R; import java.util.List; /** * IDE: Android Studio * Created by Nguyen Trong Cong - NTCDE.COM * Name packge: com.dev4u.ntc.retrofitdemo * Name project: RetrofitDemo * Date: 3/12/2017 * Time: 17:49 */ public class PostAdapter extends BaseAdapter { List<Post> postList; Context context; public PostAdapter(Context context, List<Post> postList) { this.postList = postList; this.context = context; } @Override public int getCount() { return postList.size(); } @Override public Object getItem(int i) { return i; } @Override public long getItemId(int i) { return i; } @Override public View getView(int i, View view, ViewGroup viewGroup) { view = LayoutInflater.from(context).inflate(R.layout.item_list_post, null, false); ViewHolder holder = new ViewHolder(view); holder.tvTitle.setText(postList.get(i).getTitle()); holder.tvCategory.setText(postList.get(i).getName_ca()); Glide.with(context).load(postList.get(i).getImage()) .error(R.mipmap.ic_launcher) .crossFade(3000) .diskCacheStrategy(DiskCacheStrategy.SOURCE) .into(holder.imgThumbnail); return view; } public static class ViewHolder { public View rootView; public ImageView imgThumbnail; public TextView tvTitle; public TextView tvCategory; public ViewHolder(View rootView) { this.rootView = rootView; this.imgThumbnail = (ImageView) rootView.findViewById(R.id.imgThumbnail); this.tvTitle = (TextView) rootView.findViewById(R.id.tvTitle); this.tvCategory = (TextView) rootView.findViewById(R.id.tvCategory); } } }
Layout cho Item ListView
Layout custom cho item listview đặt tên là item_list_post.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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="wrap_content" android:orientation="horizontal" android:padding="4dp"> <ImageView android:id="@+id/imgThumbnail" android:layout_width="100dp" android:layout_height="80dp" android:scaleType="centerCrop" app:srcCompat="@mipmap/ic_launcher" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="2dp"> <TextView android:id="@+id/tvTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:ellipsize="end" android:maxLines="3" android:text="Tiêu đề" android:textColor="#302f2f" android:textSize="13dp" /> <TextView android:id="@+id/tvCategory" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_margin="4dp" android:text="Category" android:textSize="11dp" tools:ignore="RtlCompat" /> </RelativeLayout> </LinearLayout>
Tạo Activity hiển thị chi tiết bài viết
Tạo PostDetailActivity để hiển thị chi tiết bài viết khi click vào Item trên listview, layout activity_post_detail.xml bao gồm một Webview để hiện thị bào viết.
package com.dev4u.ntc.retrofitdemo.view; import android.graphics.Color; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.webkit.WebView; import com.dev4u.ntc.retrofitdemo.R; public class PostDetailActivity extends AppCompatActivity { private WebView webView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_post_detail); initView(); } private void initView() { webView = (WebView) findViewById(R.id.webView); showDetailPost(getIntent().getStringExtra("title"), getIntent().getStringExtra("content"), getIntent().getStringExtra("nameCa"), getIntent().getStringExtra("des")); Log.d("abc",getIntent().getStringExtra("title")); Log.d("abc",getIntent().getStringExtra("nameCa")); Log.d("abc",getIntent().getStringExtra("des")); } private void showDetailPost(String title, String content, String nameCa, String des) { webView.setBackgroundColor(Color.WHITE); webView.setFocusableInTouchMode(false); webView.setFocusable(false); webView.getSettings().setDefaultTextEncodingName("UTF-8"); webView.getSettings().setLoadWithOverviewMode(true); webView.getSettings().setUseWideViewPort(true); String mimeType = "text/html; charset=UTF-8"; String encoding = "utf-8"; String htmlText = content; String text = "<html><head>" + "<meta name="viewport" content="width=device-width, Result-scalable=no" />" + "<style type="text/css">img {margin: 0.5% 0px !important;border: 0;font-size: 0;line-height: 0;max-width: 100%;}</style></head>" + "<body><h2>" + title + "</h2></br>" + "<span style="color:#0aa485;">" + nameCa + "</span> </br> " + "<p><h3>" + des + "</h3></p>" + htmlText + "</body></html>"; webView.loadDataWithBaseURL(null,text, mimeType, encoding, null); } }
Gửi yêu cầu GET với Retrofit 2
Trong phương thức onCreate() của MainActivity, chúng ta khởi tạo View layout và đối tượng của interface APIService .
private Button mJsonArray, mJsonObject; private ListView mLvPost; private PostAdapter postAdapter; private APIService mAPIService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mJsonArray = (Button) findViewById(R.id.jsonArray); mJsonObject = (Button) findViewById(R.id.jsonObject); mLvPost = (ListView) findViewById(R.id.lvPost); mAPIService = ApiUtils.getAPIService(); mJsonArray.setOnClickListener(this); mJsonObject.setOnClickListener(this); }
Ở dòng private PostAdapter postAdapter; trong đoạn code trên mục đích để khởi tạo Adapter Custom Listview.
Phương thức Get Json Array với Retrofit
private void getJsonRetrofitArray() { mAPIService.getPostsDetails().enqueue(new Callback<List<Post>>() { @Override public void onResponse(Call<List<Post>> call, Response<List<Post>> response) { try { final List<Post> postList = response.body(); postAdapter = new PostAdapter(MainActivity.this, postList); mLvPost.setAdapter(postAdapter); mLvPost.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(MainActivity.this, PostDetailActivity.class); intent.putExtra("title", postList.get(i).getTitle()); intent.putExtra("content", postList.get(i).getContent()); intent.putExtra("nameCa", postList.get(i).getName_ca()); intent.putExtra("des", postList.get(i).getDescription()); startActivity(intent); } }); } catch (Exception e) { Log.d("onResponse", "Error"); e.printStackTrace(); } } @Override public void onFailure(Call<List<Post>> call, Throwable t) { Log.d("onFailure", t.toString()); } }); }
Ở trên có đoạn code mLvPost.setOnItemClickListener(…){…} mục đích khi click vào một Item trên ListView nó sẽ Intent dữ liệu qua PostDetailsActivity và hiển thị chi tiết bài viết đó.
Phương thức Get Json Object với Retrofit
private void getJsonRetrofitObject() { mAPIService.getPostDetails().enqueue(new Callback<Post>() { @Override public void onResponse(Call<Post> call, Response<Post> response) { try { final List<Post> postList = new ArrayList<Post>(); postList.add(response.body()); postAdapter = new PostAdapter(MainActivity.this, postList); mLvPost.setAdapter(postAdapter); mLvPost.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(MainActivity.this, PostDetailActivity.class); intent.putExtra("title", postList.get(i).getTitle()); intent.putExtra("content", postList.get(i).getContent()); intent.putExtra("nameCa", postList.get(i).getName_ca()); intent.putExtra("des", postList.get(i).getDescription()); startActivity(intent); } }); } catch (Exception e) { Log.d("onResponse", "Error"); e.printStackTrace(); } } @Override public void onFailure(Call<Post> call, Throwable t) { Log.d("onFailure", t.toString()); } }); }
Toàn bộ Class MainActivity
package com.dev4u.ntc.retrofitdemo.view; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.Button; import android.widget.ListView; import com.dev4u.ntc.retrofitdemo.APIService; import com.dev4u.ntc.retrofitdemo.ApiUtils; import com.dev4u.ntc.retrofitdemo.R; import com.dev4u.ntc.retrofitdemo.model.Post; import java.util.ArrayList; import java.util.List; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button mJsonArray, mJsonObject; private ListView mLvPost; private PostAdapter postAdapter; private APIService mAPIService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mJsonArray = (Button) findViewById(R.id.jsonArray); mJsonObject = (Button) findViewById(R.id.jsonObject); mLvPost = (ListView) findViewById(R.id.lvPost); mAPIService = ApiUtils.getAPIService(); mJsonArray.setOnClickListener(this); mJsonObject.setOnClickListener(this); } private void getJsonRetrofitArray() { mAPIService.getPostsDetails().enqueue(new Callback<List<Post>>() { @Override public void onResponse(Call<List<Post>> call, Response<List<Post>> response) { try { final List<Post> postList = response.body(); postAdapter = new PostAdapter(MainActivity.this, postList); mLvPost.setAdapter(postAdapter); mLvPost.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(MainActivity.this, PostDetailActivity.class); intent.putExtra("title", postList.get(i).getTitle()); intent.putExtra("content", postList.get(i).getContent()); intent.putExtra("nameCa", postList.get(i).getName_ca()); intent.putExtra("des", postList.get(i).getDescription()); startActivity(intent); } }); } catch (Exception e) { Log.d("onResponse", "Error"); e.printStackTrace(); } } @Override public void onFailure(Call<List<Post>> call, Throwable t) { Log.d("onFailure", t.toString()); } }); } private void getJsonRetrofitObject() { mAPIService.getPostDetails().enqueue(new Callback<Post>() { @Override public void onResponse(Call<Post> call, Response<Post> response) { try { final List<Post> postList = new ArrayList<Post>(); postList.add(response.body()); postAdapter = new PostAdapter(MainActivity.this, postList); mLvPost.setAdapter(postAdapter); mLvPost.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(MainActivity.this, PostDetailActivity.class); intent.putExtra("title", postList.get(i).getTitle()); intent.putExtra("content", postList.get(i).getContent()); intent.putExtra("nameCa", postList.get(i).getName_ca()); intent.putExtra("des", postList.get(i).getDescription()); startActivity(intent); } }); } catch (Exception e) { Log.d("onResponse", "Error"); e.printStackTrace(); } } @Override public void onFailure(Call<Post> call, Throwable t) { Log.d("onFailure", t.toString()); } }); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.jsonArray: getJsonRetrofitArray(); break; case R.id.jsonObject: getJsonRetrofitObject(); break; } } }
Tổng kết
Trong hướng dẫn này, bạn đã tìm hiểu về Retrofit: lý do tại sao bạn nên sử dụng nó và làm thế nào để tích hợp nó trong dự án của bạn để thực hiện các yêu cầu POST, PUT, DELETE và hủy bỏ các yêu cầu. Trong bài tiếp theo của tôi về việc sử dụng Retrofit, tôi sẽ chỉ cho bạn cách làm thế nào để tải lên tập tin.
Sử dụng Retrofit thực sự chuyên nghiệp khi bạn thực hiện các request một cách dễ dàng, việc theo chuẩn mô hình RESTFul cho các API Service khiến code rất clear và dễ maintain. Việc thực hiện các kết nối Http Retrofit sử dụng mặc định OkHttp một Lib cũng của Square và được triển khai trong các SDK rất nổi tiếng như:
Facebook SDK, Fabric (Twitter) ….
Trong bái viết sau mình sẽ hướng dẫn các bạn sử dụng phương thức POST, PUT, DELETE với Retrofit.
Xin cảm ơn!
Tham khảo
Project on Github: https://github.com/trongcong/ParseJsonWithRetrofit
Android Library: Tìm hiểu Retrofit 2.0: https://viblo.asia/hungtdo/posts/AQrMJVojM40E
Gởi dữ liệu bằng Retrofit 2 HTTP Client cho Android: https://code.tutsplus.com/tutorials/sending-data-with-retrofit-2-http-client-for-android–cms-27845
Retrofit 2: https://square.github.io/retrofit/
[…] Retrofit […]
[…] dẫn các bạn cách tạo web service với php và mysql cho phương thức GET và cách parse json trong android với retrofit. Ở part 2 mình sẽ hướng dẫn các bạn tạo web service cho phương thức POST bằng […]
[…] tìm hiểu về web service và PHP, cụ thể là web service trong android và hướng dẫn parse json bằng thư viện retrofit với web service đã tạo. Bạn đã từng nghe về web service chưa, web service là gì […]