A cliche

  • What is theRetrofit
  • RetrofitThis is not a new technology, and everyone reading this blog is already familiar with it, so I will not be verbose here, but a brief introduction:
  • Retrofit is designed for Java and AndroidRESTThe client. It works by building onRESTWeb services to retrieve and upload JSON (or other structured data) relatively easily. In use, you can configure the converter for data serialization. For JSON, it is usually usedGson, but you can add custom converters to handle itXMLOr other agreements. Retrofit is used for HTTP requestsOkHttpLibrary.

A type-safe HTTP client for Android and Java

  • Ok, the introduction is over, I think everyone’s big knives are hungry, so let’s start straight away

In this paper, the process

Dependency injection

  • So Easy
  • In the App Modulebuild.gradleAdd the following dependencies to:
// OkHttp3
api 'com. Squareup. Okhttp3: okhttp: 3.10.0'
api 'com. Squareup. Okio: okio: 1.8.0 comes with'
// Retrofit
api 'com. Squareup. Retrofit2: retrofit: 2.7.0'
// Gson server data interaction
api 'com. Google. Code. Gson: gson: 2.8.6'
Copy the code

Dependency injection is very simple, Retrofit has always been a combination of OkHttp and Gson (I don’t care what JSON parser I’m using here)

  • Don’t worry. I told you that beforeRetrofitIs combined withOkHttpDo network request, so carefully remind to open the network permissions:
<uses-permission android:name="android.permission.INTERNET" />
Copy the code

Comprehensive rashly

  • About onlineRetrofitThe tutorial is full of things, but it always gives a feeling of fog and fog
  • So the great thing about this article is that I’m going to use my own actual project code to show you what Retrofit is all about

Before Retrofit started

  • Here I will use the login module of my open source project FIWKeepApp as an example
  • inRetrofitBefore the emergence of the primitive society, we generally conducted network requests like this:
    public void login2(a) {
        OkHttpClient okHttpClient = new OkHttpClient();
        //Form passes parameters in the Form format
        FormBody formBody = new FormBody
            .Builder()
            // Set the parameter name and value
            .add("username",mAccountEdit.getText().toString())
            .add("password",mPasswordEdit.getText().toString())
            .build();
        Request request = new Request
            .Builder()
            // The parameters of the Post request are passed
            .post(formBody)
            .url("http://hyh.hljdx.net:8080/SitUpWebServer/login")
            .build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                Log.d("my_Test", e.getMessage());
            }

            @Override
            public void onResponse(okhttp3.Call call, Response response) throws IOException {
                String result = response.body().toString();
                UserBean userBean = JSON.parseObject(result, UserBean.class);
                Log.d("my_Test",userBean.getUser_head_img()); response.body().close(); }}); }Copy the code
  • Is there a feeling of being in a fog?
  • The first thing you need to do is wrap the form information you want to send asPostThe request ofBodyObject, so some students will ask what isPOSTAnd what isBody? This is a question for youGoogleHere I suggest you learn some back-end or network knowledge, very simple and very necessary
  • And then you need to encapsulate another oneRequestObject, which is the body of our request, sets up where the information should be submitted
  • The last callokHttpClientThe corresponding method sends a combination of the previously implemented things and receives them in a callback
  • So, step by step, encapsulation againFormBodyIs encapsulatedRequestI still need it after a long timeokHttpClientSend, a set down dizziness, so how to solve it?
  • thenRetrofitThe savior appeared

Retrofit implementation

  • Again, the login module in my project, which I changed toRetrofitIn the form of
  • Also complete the above function if usedRetrofitImplementation only requires:
    // baseUrl() sets the routing address
    Retrofit retrofit = new Retrofit
        .Builder()
        .baseUrl(ApiUtils.BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();
        
    // Set parameters
    Call<UserBean> call = retrofit.create(UserMgrService.class)
        .login( mAccountEdit.getText().toString(),
            mPasswordEdit.getText().toString());
            
    / / callback
    call.enqueue(new Callback<UserBean>() {
        @Override
        public void onResponse(Call<UserBean> call, Response<UserBean> response) {
            Log.d("123123"."msg--" + response.body().getUser_head_img());
        }

        @Override
        public void onFailure(Call<UserBean> call, Throwable t) {
            // Handle failure}});Copy the code
  • The sum pure is realized as aboveokHttpCode like functionality
  • And you might say, well, that’s not that easy, right? But a closer look reveals the first stepRetrofitThe instantiation process is almost constant as long as the server does not change the code, so we can completely encapsulate it

  • And did you notice that if you just useOkHttpOur return value is oneResponseObject, which we also need to extract fromJSONObject to perform type conversion while inRetrofitBecause of the data parser, this chunk of code is omitted
  • There are a lot of advantages, here is not nagging, we directly began to learn the way to use it!

The implementation process

  • So now I’m going to explain each of these steps

Create the interface

  • First we will create the UserMgrService interface
/ * * *@author fishinwater-1999
 * @versionThe 2019-12-21 * /
public interface UserMgrService {

    /** * GET uses Query */
    @GET("login")
    Call<UserBean> login(@Query("username") String username, @Query("password") String password);

}
Copy the code
  • from@GET()You can guess from the notes that this is going to be aGetrequest
  • We’re looking at the method body, and the return value will be a wrapperUserBeanCall<>object
  • There are two parameters, respectivelyString usernameString password
  • Unlike the usual method, these two parameters are carried separately@Query("..." )annotations
  • through@Query("..." )We find that this is related tookHttpcreateFormBodyWhen,addThe parameters coincide

If you don’t already know what a Get request is, and @query (“…” For example, if we open a web page at random, take baidu image search Github page as an example:

  • The backend write server will pass these parameters likeA HashMap get (" key ")I’m going to take the same values

POST

  • I think you’ll get the idea
  • In addition toGETThere’s another wayPOSTMethod, compared to usingGET, the use ofPOSTThere are many other advantages that I won’t go into here
  • He use andGETSame idea if you usePOSTOur code would look like this:
public interface UserMgrService {

    /** * POST uses Field */
    @POST("login")
    @FormUrlEncoded
    Call<UserBean> login(@Field("username") String username, @Field("password") String password);

}
Copy the code
  • You just change the name of the note and put it in@POST("..." )Let’s add another one@FormUrlEncodedannotations
  • Without further ado, let’s go straight to the next step

Generate Retrofit objects

  • Let’s take a look at how to create and set it:
// baseUrl() sets the routing address
Retrofit retrofit = new Retrofit
    .Builder()
    .baseUrl(ApiUtils.BASE_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .build();
Copy the code
  • So this is basically two steps, setupbaseUrlSet up the data parser
  • Same old what is itbaseUrl? Just take what I used beforeOkHttpTake the url you set as an example
http://hyh.hljdx.net:8080/SitUpWebServer/login
Copy the code
  • You can think of it this way: the top oneurl = baseurl + @GET("..." )The string passed in the annotation
  • If we set it to be@GET("login")That herebaseurlIs this:http://hyh.hljdx.net:8080/SitUpWebServer/It was obvious all at once, but other bloggers don’t take care of newcomers and never make it clear
  • Then there is the data parser. You may recall that we imported a tripartite library at the beginning:
// Gson server data interaction
api 'com. Google. Code. Gson: gson: 2.8.6'
Copy the code
  • Our data, and the server’s data, is based onJSONInteractive in the form of, for exampleBingDaily Wallpaper Interface

  • When you set up the data parser, you can automatically encapsulate the returned information into corresponding objects, you see

How to obtain this object, you can contact the backend, or Baidu search JsonFormat plug-in or JSON object generator, many ways here tell you

Generating interface objects

  • As usual, let’s look at the code
UserMgrService service = retrofit.create(UserMgrService.class);
Copy the code
  • Too simple, call beforeretrofitThe object’screate()Method passed into the interfaceclassfile

Get the Call object

  • We know that from the code we started with
  • We send a request to the server that needs to be calledcallThe object’senqueue()methods
  • thenCallHow do you get objects? It’s really simple:
Call<UserBean> call = service.login( mAccountEdit.getText().toString(), mPasswordEdit.getText().toString());
Copy the code
  • In plain English, it calls the corresponding method of the interface directly, and it returns one directlyCallobject

Send the request

  • There are two types of requests: synchronous and asynchronous

  • Because requests are time consuming, if we send a synchronous request, the application will block before the request is returned
  • To put it bluntly, will be stuck, even stuck to death… So this request is rarely used
  • Although not, but responsible I still give you the code:
Response<UserBean> response = call.execute();
Log.d("123123"."msg--" + response.body().getUser_head_img());
Copy the code
  • I don’t want to talk about it. It’s calledcallexecute()Will return a value
  • This value is the result of the request, you can use it directly (but in this only return, such as slow connection, the phone can be stuck or evenANR
  • Here I introduce asynchronous requests:
/ / callback
call.enqueue(new Callback<UserBean>() {
    @Override
    public void onResponse(Call<UserBean> call, Response<UserBean> response) {
        Log.d("123123"."msg--" + response.body().getUser_head_img());
    }

    @Override
    public void onFailure(Call<UserBean> call, Throwable t) {
        // Handle failure}});Copy the code
  • This is the asynchronous method, called directlycallenqueueMethod, passing in aCallbackThe interface can be
  • The system automatically releases the resource without blocking until the result of the request is returned
  • It’s automatically calledonResponseThe method, the methodresponseThat’s the result of the processing
  • After this code runs the result Demo Example is not particularly simple!

Login function combat

  • I think you’ve all learned it by nowRetrofitThe use of
  • So let’s take the login function as an example and see how it can be referenced in a projectRetrofit
  • The actual combat part of the pre-condition isMVP + ButterKnifeIt’s easy to find information on the Internet

Set up the Model layer

  • Create the interfaceILoginModel
  • The interface exposes the Username password and a listening callback interface (the interface is passed in via generics)
/ * * *@author fishinwater-1999
 * @versionThe 2019-11-12 * /
public interface IBaseLog<L> {

    /** * login Api *@param userAccount
     * @param mPassword
     * @param loginCallback
     */
    void login(String userAccount, String mPassword, L loginCallback);

}
Copy the code
  • Implement the callback interface
  • Observer mode, which dynamically notifies layer P when a request message is returned
/ * * *@author fishinwater-1999
 * @versionThe 2019-12-23 * /
public interface IBaseRetCallback<T> {

    void onSucceed(Response<T> response);

    void onFailed(Throwable t);

}
Copy the code
  • createLoginModelimplementationILoginModelinterface
  • implementationloginMethod to call back after a successful requestIBaseRetCallbackListening to the
/ * * *@author fishinwater-1999
 * @versionThe 2019-11-12 * /
public class LogViewModel implements IBaseLog<IBaseRetCallback<UserBean>> {

    private final String TAG = "LogViewModel";

    @Override
    public void login(String userAccount, String userPassword, final IBaseRetCallback<UserBean> retCallback) {
        // baseUrl() sets the routing address
        Retrofit retrofit = new Retrofit
                .Builder()
                .baseUrl(ApiUtils.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        // Set parameters
        UserMgrService service = retrofit.create(UserMgrService.class);
        retrofit2.Call<UserBean> call = service.login( userAccount, userPassword);
        / / callback
        call.enqueue(new Callback<UserBean>() {
            @Override
            public void onResponse(retrofit2.Call<UserBean> call, Response<UserBean> response) {
                retCallback.onSucceed(response);
            }

            @Override
            public void onFailure(retrofit2.Call<UserBean> call, Throwable t) {
                // Handle failureretCallback.onFailed(t); }}); }}Copy the code

To build the Presenter layer

  • First of all achievePresenterLayer of the base class
  • Again, buildPresenterThe layer base class first implements the interface
/ * * *@author fishinwater-1999
 * @versionThe 2019-11-12 * /
public interface IBasePresenter<V> {

    /** * bind *@param mLogView
     */
    void attachView(V mLogView);

    /** * unbind */
    void detachView(a);

    /** * login *@param userName
     * @param userPassword
     * @param resultListener
     */
    void login(String userName, String userPassword, V resultListener);

}
Copy the code
  • Writing abstract classesBasePresenterimplementationIBasePresenterinterface
/ * * *@author fishinwater-1999
 * @versionThe 2019-11-12 * /
public abstract class BasePresenter<V> implements IBasePresenter<V> {

    private V view;

    @Override
    public void attachView(V mLogView) {
        this.view = mLogView;
    }

    @Override
    public void detachView(a) {
        this.view = null;
    }

    @Override
    public V getLoginVew(a) {
        return this.view; }}Copy the code
  • And then we get to our specificLogPresenterThe realization of the class
  • LogPresenterClasses need to hold View layer and Model layer interfaces
/ * * *@author fishinwater-1999
 * @versionThe 2019-11-12 * /
public class LogPresenter extends BasePresenter<ILoginView> {

    private IBaseLog logViewModel;

    public LogPresenter(IBaseLog logViewModel) {
        this.logViewModel = logViewModel;
    }

    @Override
    public void login(String userName, String userPassword, final ILoginView iLoginView) {
        logViewModel.login(userName, userPassword, new IBaseRetCallback<UserBean>() {
            @Override
            public void onSucceed(Response<UserBean> response) {
                UserBean userBean = response.body();
                if(userBean ! =null) { String user_id = userBean.getUser_id(); iLoginView.showLoginSuccess(user_id); }}@Override
            public void onFailed(Throwable t) { iLoginView.showLoginFailed(ILoginView.ErrCode.WRONG_NET_WORK); }}); }}Copy the code
  • In the code above, the constructor LogPresenter holds the Model layer
  • Also exposing login(… . , Listener) interface that can be called by the caller

The View layer

  • ViewThe layer is responsible for instantiationModelLayer, andPresenterLayer binding
  • Same old, createBaseFragment<V , P extends IBasePresenter<V>>The base class
/ * * *@author fishinwater-1999
 * @versionThe 2019-11-12 * /
public abstract class BaseFragment<V , P extends IBasePresenter<V>> extends Fragment {

    /** * Presenter */
    private P mBaseResister;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Automatic binding
        if (mBaseResister == null) { mBaseResister = createProsenter(); }}/** * Here determine the type of Presenter to be generated *@return* /
    public abstract P createProsenter(a);

    /** * Get Presenter *@return* /
    public P getPresenter(a) {
        if (mBaseResister == null) {
            createProsenter();
        }
        return mBaseResister;
    }

    /** ** Unbind fragments when they are destroyed */
    @Override
    public void onStop(a) {
        super.onStop();
        mBaseResister = null; }}Copy the code
  • implementationViewlogic
  • The View layer is only responsible for user interface responses
/ * * *@author fishinwater-1999
 */
public class LoginFragment extends BaseFragment<ILoginView.LogPresenter> implements ILoginView {

    private static final String TAG = "LoginFragment";

    private LogViewModel mLogViewModel;

    private LoginFragmentBinding binding;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, R.layout.login_fragment, container, false);
        View view = binding.getRoot();
        binding.setLogCallback(getLogActivity());
        binding.setFragment(this);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (mLogViewModel == null) {
            mLogViewModel = newLogViewModel(); }}public void login(View v) {
        getPresenter().login(
                getUserName(),
                getUserPwd(),
                this);
    }

    @Override
    public LogPresenter createPresenter(a) {
        if (mLogViewModel == null) {
            mLogViewModel = new LogViewModel();
        }
        return new LogPresenter(mLogViewModel);
    }

    @Override
    public String getUserName(a) {
        return binding.userAccount.getText().toString();
    }

    @Override
    public String getUserPwd(a) {
        return binding.userPassword.getText().toString();
    }

    @Override
    public void showLoginSuccess(String response) {
        Toast.makeText(getActivity(), "Login successful", Toast.LENGTH_LONG).show();
        SharedPreferencesUtil.putString(getActivity(), SharedPreferencesUtil.PRE_NAME_SITUP, SharedPreferencesUtil.USER_ID, response);
        ARouter.getInstance().build(RouteUtils.MainActivity).navigation();
        getActivity().finish();
    }

    @Override
    public void showLoginFailed(ErrCode errCode) {
        if (errCode == ErrCode.WRONG_USER_NAME) {
            Toast.makeText(getActivity(), "User name error", Toast.LENGTH_LONG).show();
        }else if (errCode == ErrCode.WRONG_USER_PWD){
            Toast.makeText(getActivity(), "Password error", Toast.LENGTH_LONG).show();
        }else if (errCode == ErrCode.WRONG_NET_WORK) {
            Toast.makeText(getActivity(), "Unknown, please check network", Toast.LENGTH_LONG).show(); }}}Copy the code
  • Here I use the form of DataBinding to bind the data
  • Of course, you can also use a good tripartite library like ButterKnife
  • So why did I choose DataBinding? Your own son, you know? / bad smile

run

  • So much for sequencing
  • For details, you can go directly to the library, the address is at the end of the article

More modulesFIWKeepApp

  • Here I will write the above process in my Demo, address at GitHub you can directly view the repository source, remember to give me a star oh ~ :

  • Demo address: fiwKeepapp-LoginFragment

conclusion

  • I think the readers here are rightRetrofitThese are not the only benefits of Retrofit, and there are many more that need to be understood, but this article doesn’t have the space to cover them all
  • As for the storehouse FIWKeepApp in front of me, I will transform it step by step into the form of Retrofit + OkHttp. Welcome everyone to pay attention to my storehouse and learn about it, and also welcome everyone to give a star
  • I’ll do it again laterAndroidA variety of knowledge points,FrameworkLayer source code, tripartite library for analysis, welcome your attentionThe nuggets of _yuanhaoReceive more quality blog posts in time!