Retrofit
? Retrofit
早已不是什么新技术了,想必看到这篇博客的大家都早已熟知,这里就不啰嗦了,简单介绍下: REST
客户机。它通过基于 REST
的 web 服务检索和上传 JSON (或其他结构化数据)变得相对容易。在使用中,您可以配置用于数据序列化的转换器。对于 JSON ,通常使用 Gson
,但是可以添加自定义转换器来处理 XML
或其他协议。Retrofit 对 HTTP 请求使用 OkHttp
库。 A type-safe HTTP client for Android and Java
build.gradle
// OkHttp3 api 'com.squareup.okhttp3:okhttp:3.10.0' api 'com.squareup.okio:okio:1.8.0' // Retrofit api 'com.squareup.retrofit2:retrofit:2.7.0' // Gson 服务器数据交互 api 'com.google.code.gson:gson:2.8.6'
依赖注入很简单, Retrofit 一直是结合 OkHttp
和 Gson(无所谓什么 JSON 解析器都行,这里就用 Gson
了)
我这里专门找了最新的版本库,so~ 大家直接用即可
Retrofit
是结合 OkHttp
做网络请求用的,所以悉心提醒记得开下网络权限: <uses-permission android:name="android.permission.INTERNET" />
Retrofit
Retrofit
出现之前,原始社会的我们一般是这样进行网络请求的: public void login2() { OkHttpClient okHttpClient = new OkHttpClient(); //Form表单格式的参数传递 FormBody formBody = new FormBody .Builder() //设置参数名称和参数值 .add("username",mAccountEdit.getText().toString()) .add("password",mPasswordEdit.getText().toString()) .build(); Request request = new Request .Builder() //Post请求的参数传递 .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(); } }); }
Post
请求的 Body
对象,那么有的同学会问什么是 POST
,什么是 Body
?这个问题建议大家 Google
下,这里我建议大家学一些后端或者计网的知识,很简单也很有必要 Request
对象,也就是我们的请求体,在这里设置信息要提交到哪去 okHttpClient
的相应方法,将前面实现的东西组合发送,并在回调里接收 FormBody
又是封装 Request
,搞了半天还要用 okHttpClient
发送,一套下来头晕眼花,那么如何解决呢? Retrofit
救世主就出现了 Retrofit Retrofit
// baseUrl() 设置路由地址 Retrofit retrofit = new Retrofit .Builder() .baseUrl(ApiUtils.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); // 设置参数 Call<UserBean> call = retrofit.create(UserMgrService.class) .login( mAccountEdit.getText().toString(), mPasswordEdit.getText().toString()); // 回调 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) { // 失败时做处理 } });
okHttp Retrofit
OkHttp
我们的返回值是一个 Response
对象,我们还需要在其中提取相应 JSON
对象,进行类型转换,而在 Retrofit
中,由于使用了数据解析器,所以这一大块代码都省略了 /** * @author fishinwater-1999 * @version 2019-12-21 */ public interface UserMgrService { /** * GET 用 Query */ @GET("login") Call<UserBean> login(@Query("username") String username, @Query("password") String password); }
@GET()
注解就可以猜到,这将会是一个 Get
请求 UserBean
的 Call<>
对象 String username
和 String password
@Query("...")
注解 @Query("...")
里的参数我们发现,这与 okHttp
创建 FormBody
时, add
的参数不谋而合 看到这里想必大家都明白了,如果大家还不明白什么是 Get 请求,以及 @Query("...") 里的 username 和 password 是怎么的话,我这里简单说下
比如说我们现在随便打开一个网页,就拿百度图片里搜索 Github 页面为例:
HashMap get(“key”)
方法取值一样拿出来 GET
方法之外 还有一种 POST
方法,相比于使用 GET
,使用 POST
有很多其他的优点,这里就不多说了 GET
的思路一样,如果用 POST
那么我们的代码将会是这样的: public interface UserMgrService { /** * POST 用 Field */ @POST("login") @FormUrlEncoded Call<UserBean> login(@Field("username") String username, @Field("password") String password); }
@POST("...")
下再加上一个 @FormUrlEncoded
注解 // baseUrl() 设置路由地址 Retrofit retrofit = new Retrofit .Builder() .baseUrl(ApiUtils.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build();
baseUrl
、设置数据解析器 baseUrl
?就拿我之前用 OkHttp
设置的那个 url 为例 http://hyh.hljdx.net:8080/SitUpWebServer/login
url = baseurl + @GET("...")
注解里传入的字符串 @GET("login")
那这里 baseurl
就是: http://hyh.hljdx.net:8080/SitUpWebServer/
是不是一下子就明白了,但是其他博客不照顾新人,从没说清楚 // Gson 服务器数据交互 api 'com.google.code.gson:gson:2.8.6'
JSON
的形式交互的,比如 Bing
每日壁纸接口
具体这个对象怎么获得,大家可以联系后端,或者百度搜下 JsonFormat 插件使用或者 JSON 对象生成器,门路很多这里都告诉你们啦
UserMgrService service = retrofit.create(UserMgrService.class);
retrofit
对象的 create()
方法传入接口的 class
文件即可 call
对象的 enqueue()
方法 Call
对象怎么获得呢?其实很简单: Call<UserBean> call = service.login( mAccountEdit.getText().toString(), mPasswordEdit.getText().toString());
Call
对象
Response<UserBean> response = call.execute(); Log.d("123123", "msg--" + response.body().getUser_head_img());
call
的 execute()
会返回一个值 ANR
) // 回调 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) { // 失败时做处理 } });
call
的 enqueue
方法,传入一个 Callback
接口即可 onResponse
方法,方法 里的 response
就是处理好的结果 Retrofit
的使用 Retrofit
MVP
+ ButterKnife
,大家很容易在网上找到资料,这就不赘述了 ILoginModel
/** * @author fishinwater-1999 * @version 2019-11-12 */ public interface IBaseLog<L> { /** * 登录 Api * @param userAccount * @param mPassword * @param loginCallback */ void login(String userAccount, String mPassword, L loginCallback); }
/** * @author fishinwater-1999 * @version 2019-12-23 */ public interface IBaseRetCallback<T> { void onSucceed(Response<T> response); void onFailed(Throwable t); }
LoginModel
实现 ILoginModel
接口 login
方法,请求成功后回调 IBaseRetCallback
监听 /** * @author fishinwater-1999 * @version 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() 设置路由地址 Retrofit retrofit = new Retrofit .Builder() .baseUrl(ApiUtils.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); // 设置参数 UserMgrService service = retrofit.create(UserMgrService.class); retrofit2.Call<UserBean> call = service.login( userAccount, userPassword); // 回调 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) { // 失败时做处理 retCallback.onFailed(t); } }); } }
Presenter Presenter
/** * @author fishinwater-1999 * @version 2019-11-12 */ public interface IBasePresenter<V> { /** * 绑定 * @param mLogView */ void attachView(V mLogView); /** * 解绑 */ void detachView(); /** * 登录 * @param userName * @param userPassword * @param resultListener */ void login(String userName, String userPassword, V resultListener); }
BasePresenter
实现 IBasePresenter
接口 /** * @author fishinwater-1999 * @version 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() { this.view = null; } @Override public V getLoginVew() { return this.view; } }
LogPresenter LogPresenter
/** * @author fishinwater-1999 * @version 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); } }); } }
View
层负责实例化 Model
层,并与 Presenter
层绑定 BaseFragment<V , P extends IBasePresenter<V>>
基类 /** * @author fishinwater-1999 * @version 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); // 自动绑定 if (mBaseResister == null) { mBaseResister = createProsenter(); } } /** * 在这里确定要生成的 Presenter 对象类型 * @return */ public abstract P createProsenter(); /** * 获得 Presenter 对象 * @return */ public P getPresenter() { if (mBaseResister == null) { createProsenter(); } return mBaseResister; } /** * 碎片销毁时解绑 */ @Override public void onStop() { super.onStop(); mBaseResister = null; } }
View
/** * @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 = new LogViewModel(); } } public void login(View v) { getPresenter().login( getUserName(), getUserPwd(), this); } @Override public LogPresenter createPresenter() { if (mLogViewModel == null) { mLogViewModel = new LogViewModel(); } return new LogPresenter(mLogViewModel); } @Override public String getUserName() { return binding.userAccount.getText().toString(); } @Override public String getUserPwd() { return binding.userPassword.getText().toString(); } @Override public void showLoginSuccess(String response) { Toast.makeText(getActivity(), "登录成功", 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(), "用户名错误", Toast.LENGTH_LONG).show(); }else if (errCode == ErrCode.WRONG_USER_PWD){ Toast.makeText(getActivity(), "密码错误", Toast.LENGTH_LONG).show(); }else if (errCode == ErrCode.WRONG_NET_WORK) { Toast.makeText(getActivity(), "未知,请检查网络", Toast.LENGTH_LONG).show(); } } }
这里我将上述过程写在我的 Demo
里,地址在 GitHub
大家可以直接查看改仓库源码,记得给我点个 star
哦~:
Demo
地址: FIWKeepApp - LoginFragment
Android
的各种知识点、 Framework
层源码,三方库等进行解析,欢迎大家关注_yuanhao 博客园 及时接收更多优质博文!