Retrofit in Android networking

 useful video :

https://www.youtube.com/watch?v=4JGvDUlfk7Y&list=PLrnPJCHvNZuCbuD3xpfKzQWOj3AXybSaM

Call Adapters : https://futurestud.io/tutorials/retrofit-2-introduction-to-call-adapters



RETROFIT provides a abstraction layer over all the low level networking code, We just need to provide an Interface that contains method declarations for different network operations that we want to do & just give annotations to them , then Retrofit will Generate all things by itself.


Put & Patch : used to update Data using Rest api.

Network operations are sometimes slow and may take few seconds.


DO NOT FORGET TO GET INTERNET USAGE PERMISSION IN MANIFEST

<uses-permission android:name="android.permission.INTERNET"/>


-------------------------------------------------------------------------------------------------


Dependencies : 

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.google.code.gson:gson:2.8.6'


Also add the below lines in the gradle files , just to be safe.

-----------------------------------------------------------------------------------------------

If you get  java.lang.NoSuchMethodError: No static method metafactory 

Add the following lines in your Gradle.build(App) file


android {
compileSdkVersion 30
buildToolsVersion "30.0.2"

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

---------------------------------------------------------------------------------------------------


If you have  a different name of variable in your modal class & Json data , and you want to match them up , you can use @SerializeName anotation from Gson & provide the name of the variable in the Json data to match with your variable in Modal class.


Eg - Below is a Modal class variable with different name in Json data ( newbody) , so to tell Gson that "newbody" in Json data is to be set at the variable "body" in modal data , put @SerializeName() above that variable.


@SerializedName("newbody")
private String body;


--------------------------------------------------------------------------------------------------------------




Simple GET requests to get JSON array of objects & display their data on a textview.


Client Interface.

It acts as an API method we'll use to fetch the data from web api.

Inside the @GET() give the remaining part of the api url other than the base url.

Inside the Call< > pass the return type of the method 

package com.example.myapplication;

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


public interface JsonPlaceHolder {

@GET("posts")
Call<List<Post>> getPosts();
}


Modal class


package com.example.myapplication;

import com.google.gson.annotations.SerializedName;

public class Post {

int userId;
int id;
String title;

@SerializedName("body")
String text;

public int getUserId() {
return userId;
}

public int getId() {
return id;
}

public String getTitle() {
return title;
}

public String getText() {
return text;
}
}


MainActivity.class

OnResponse() is called when our request was sucessful , but doesnt mean we got what we were expecting hence we need to check for error codes. 

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textView = findViewById(R.id.mytextview);


getCode();

}


public void getCode(){



Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://jsonplaceholder.typicode.com/")
.build();


//We cannot write "new jsonplaceholder()" since its an interface we need to implement it.
// And that is what retrofit will do , so we dont need to write extra code.
JsonPlaceHolder jsonPlaceHolder = retrofit.create(JsonPlaceHolder.class);


Call<List<Post>> call = jsonPlaceHolder.getPosts();


// execte() - runs on main thread.
// enqueue() - runs on background thread.
call.enqueue(new Callback<List<Post>>() {
@Override
public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {

//This method is called if the response code that retrofit code was between 200-299
// But this dont mean response we get are sucessful, we need to check for 404.

//check if response is empty i.e 404 - Not found error
if (!response.isSuccessful()){
textView.setText("Code : " + response.code());
return;
}

// body() returns the contents of the response
List<Post> posts = response.body();

for (Post i : posts){

String content = " ";
content += "Id : " + i.getId() + "\n";
content += "UserId : " + i.getUserId() + "\n";
content += "Title : " + i.getTitle()+ "\n";
content += "Body : " + i.getText() + "\n\n";

textView.append(content);

}

}

@Override
public void onFailure(Call<List<Post>> call, Throwable t) {

//This method is called if there is a failure in response.
textView.setText(t.getMessage());
}
});
}

}



EXAMPLE 2 :


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity">

<ImageView
android:id="@+id/meme_ImageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="36dp"
android:layout_marginBottom="53dp"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="58dp"
android:onClick="getNextMeme"
android:text="Click"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/meme_ImageView" />

<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/meme_ImageView"
app:layout_constraintEnd_toEndOf="@+id/meme_ImageView"
app:layout_constraintStart_toStartOf="@+id/meme_ImageView"
app:layout_constraintTop_toTopOf="@+id/meme_ImageView" />


</androidx.constraintlayout.widget.ConstraintLayout>


package com.deepesh.exampleapp4;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;

import java.lang.annotation.Target;
import java.util.List;

import javax.sql.DataSource;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;

import static android.content.ContentValues.TAG;


public class MainActivity extends AppCompatActivity {

final static String BASE_URL = "https://meme-api.herokuapp.com";
final static String URL_ENDPOINT = "/gimme";
ImageView memeImageView;
ProgressBar progressBarCircle;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

progressBarCircle = findViewById(R.id.progressBar);
memeImageView = findViewById(R.id.meme_ImageView);
getNextMeme(null);
}

// Button OnClick()
public void getNextMeme(View view) {

progressBarCircle.setVisibility(View.VISIBLE);

Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build();

MemeAPI memeAPI = retrofit.create(MemeAPI.class);
Call<Meme> call = memeAPI.getMemeUrl();

call.enqueue(new Callback<Meme>() {
@Override
public void onResponse(Call<Meme> call, Response<Meme> response) {

if (!response.isSuccessful()) {
Log.d(TAG, "onResponse: CODE " + response.code());
return;
}

Meme meme = response.body();
String url = meme.getUrl();

// Loads image into imageview given image url
Glide.with(MainActivity.this).load(url).into(memeImageView);

progressBarCircle.setVisibility(View.INVISIBLE);

}

@Override
public void onFailure(Call<Meme> call, Throwable t) {
Log.d(TAG, "onFailure: RESPONSE FAILED !");
Log.d(TAG, "onFailure: EXCEPTION MESSAGE : " + t.getMessage());
}
});

}


//=============================================================

// Modal Class

public static class Meme {

private String postLink;
private String subreddit;
private String title;
private String url;
private Boolean nsfw;
private Boolean spoiler;
private String author;
private Integer ups;
private List<String> preview;

public String getUrl() {
return url;
}
}

//=============================================================

public interface MemeAPI {

@GET(URL_ENDPOINT)
Call<Meme> getMemeUrl();
}


}





--------------------------------------------------------------------------------------------------------------

useful video : 

https://www.youtube.com/watch?v=TyJEDhauUeQ&list=PLrnPJCHvNZuCbuD3xpfKzQWOj3AXybSaM&index=2


URL manipulation : 


1] @Path - used to make dynamic manipulation to url. eg - put parameters in url


  // use @Path("name") to tell retrofit to put the parameter at 
// brackets with value "name".

@GET("posts/{id}/comments")
Call<List<Comment>> getComments(@Path("id") int PostId);


2] @Query - used to make query url 


 // Query url : https://jsonplaceholder.typicode.com/comments?postId=1

// pass @query() the actual query variable, as in our case postId.
// retrofit will automatically put " ? " in front of the variable.

@GET("comments")
Call<List<Comment>> getComments(@Query("postId") int PostId);


3] @Url - used to pass an entire url if the entire url is complex.

using this you can pass an entire url .


 @GET
Call<List<Comment>> getComments(@Url String url);
Call<List<Comment>> call = jsonPlaceHolder.getComments("comments?postId=3");



-----------------------------------------------------------------------------------------------------------------

useful video : https://youtu.be/GP5OyYDu_mU


Simple POST requests to get JSON array of objects & display their data on a textview.


POST request is the opposite of GET request.


package com.example.myapplication;

import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;



public interface JsonPlaceHolder {

// use the @Body annotation to pass the body/data that needs to be send with request.
// Retrofit automatically converts the data into required form i.e JSON. // Use a converter if any other form needed
@POST("posts")
Call<Post> createPost(@Body Post post);

}


--------------------------------------------------------------------------------------------


You can also use @FormUrlEncoded , to pass complex data into post request.

Using this , you dont need to create a object , rather just pass the data as parameters.

// Forms a encoded url & send the post request
// @Field must contain names of actual fields the api needs.
@FormUrlEncoded
@POST("posts")
Call<MyModal> post_userData(
@Field("userId") int userId ,
@Field("title") String title ,
@Field("body") String body);


// MyModal myModal1 = new MyModal(3,"Hello user 3","This is the body");
Call<MyModal> modal = jsonplaceholderAPI.post_userData(3,
"This is the title bitch","This is a mf body !");


-------------------------------------------------------------------------------------------


The site where we send the Post request here , sends the fake data back to the Us on sucessful post response.




package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

TextView textView;
JsonPlaceHolder jsonPlaceHolder;
Retrofit retrofit;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textView = findViewById(R.id.mytextview);

retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://jsonplaceholder.typicode.com/")
.build();


//We cannot write "new jsonplaceholder()" since its an interface we need to implement it.
// And that is what retrofit will do , so we dont need to write extra code.
jsonPlaceHolder = retrofit.create(JsonPlaceHolder.class);


// getPosts();
// getComments();
createPost();

}

private void createPost() {

Post post = new Post(23,"New text","New title");// REAL APP WE USE EDITTEXT

Call<Post> call = jsonPlaceHolder.createPost(post);
//The site where we send the Post request here , sends
// the fake data back to the Us on sucessful post response.
call.enqueue(new Callback<Post>() {
@Override
public void onResponse(Call<Post> call, Response<Post> response) {


if (!response.isSuccessful()) {
textView.setText("Code : " + response.code());
return;
}


Post postResponse = response.body();


String content = " ";
content += "Response Code : " + response.code() + "\n";
content += "Id : " + postResponse.getId() + "\n";
content += "UserId : " + postResponse.getUserId() + "\n";
content += "Title : " + postResponse.getTitle() + "\n";
content += "Body : " + postResponse.getText() + "\n\n";

textView.setText(content);


}

}



-------------------------------------------------------------------------------------------------------


Setup a logging intercepter to get all the data in the LOGCAT , during the execution.


Step 1 ]

Get the Dependency for LoggingIntercepter from Square's github & add to the android studio.

https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor


Step 2 ] Follow the code below 

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textView = findViewById(R.id.mytextview);

//------------------ Logging intercepter code starts here ---------------------------------

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build();

retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://jsonplaceholder.typicode.com/")
//add client for logger
.client(okHttpClient)
.build();

//------------------ Logging intercepter code end here ---------------------------------


//We cannot write "new jsonplaceholder()" since its an interface we need to implement it.
// And that is what retrofit will do , so we dont need to write extra code.
jsonPlaceHolder = retrofit.create(JsonPlaceHolder.class);


// getPosts();
// getComments();
createPost();

}



-------------------------------------------------------------------------------------------------------


Add Headers in Retrofit 


Response headers provide information about the status of the request, and return ETag information. The response also includes a status code.


Headers are like Meta-Data


You can send Static Headers or Dynamic Headers , whose values can be taken from the user.




-------------------------------------------------------------------------------------------------------









Comments

Popular posts from this blog

React Js + React-Redux (part-2)

React Js + CSS Styling + React Router (part-1)

ViteJS (Module Bundlers, Build Tools)