Retrofit
?Retrofit
早已不是什麼新技術了,想必看到這篇博客的你們都早已熟知,這裏就不囉嗦了,簡單介紹下:REST
客戶機。它經過基於 REST
的 web 服務檢索和上傳 JSON (或其餘結構化數據)變得相對容易。在使用中,您能夠配置用於數據序列化的轉換器。對於 JSON ,一般使用Gson
,可是能夠添加自定義轉換器來處理 XML
或其餘協議。Retrofit 對 HTTP 請求使用 OkHttp
庫。A type-safe HTTP client for Android and Javajava
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~ 你們直接用便可android
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 頁面爲例:git
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 對象生成器,門路不少這裏都告訴大家啦github
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
類須要持有 View 層和 Model 層接口/** * @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
哦~:web
Demo
地址:FIWKeepApp - LoginFragment後端
Retrofit
的使用都已近有了必定的瞭解,但 Retrofit 的好處並不僅是這些,還有不少跟深刻的只是須要瞭解,但本文限於篇幅,沒法向你們一一介紹Android
的各類知識點、Framework
層源碼,三方庫等進行解析,歡迎你們關注 _yuanhao的掘金 及時接收更多優質博文!