這篇文章將為大家詳細(xì)講解有關(guān)Android MVP中如何實(shí)現(xiàn)BaseFragment通用式封裝,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
目前創(chuàng)新互聯(lián)建站已為超過(guò)千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)頁(yè)空間、網(wǎng)站托管運(yùn)營(yíng)、企業(yè)網(wǎng)站設(shè)計(jì)、武義網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶(hù)導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶(hù)和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。
代碼示例:
新建 BaseFragment 基類(lèi):
package com.test.mvp.mvpdemo.mvp.v6.basemvp; import android.os.Bundle; import android.support.annotation.IdRes; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.test.mvp.mvpdemo.mvp.v6.inject.InjectPresenter; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public abstract class BaseFragment extends Fragment implements IBaseView { private List<BasePresenter> mInjectPresenters; private View mLayoutView; protected abstract @LayoutRes int setLayout(); protected abstract void initViews(@Nullable Bundle savedInstanceState); protected abstract void initData(); @SuppressWarnings("ConstantConditions") protected <T extends View> T $(@IdRes int viewId) { return this.getView().findViewById(viewId); } @SuppressWarnings({"unchecked", "TryWithIdenticalCatches"}) @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(setLayout(), container, false); mInjectPresenters = new ArrayList<>(); //獲得已經(jīng)申明的變量,包括私有的 Field[] fields = this.getClass().getDeclaredFields(); for (Field field : fields) { //獲取變量上面的注解類(lèi)型 InjectPresenter injectPresenter = field.getAnnotation(InjectPresenter.class); if (injectPresenter != null) { try { Class<? extends BasePresenter> type = (Class<? extends BasePresenter>) field.getType(); BasePresenter mInjectPresenter = type.newInstance(); //綁定 mInjectPresenter.attach(this); field.setAccessible(true); field.set(this, mInjectPresenter); mInjectPresenters.add(mInjectPresenter); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (java.lang.InstantiationException e) { e.printStackTrace(); } catch (ClassCastException e) { e.printStackTrace(); throw new RuntimeException("SubClass must extends Class:BasePresenter"); } } } return view; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); initViews(savedInstanceState); initData(); } @Override public void onDestroy() { super.onDestroy(); for (BasePresenter presenter : mInjectPresenters) { presenter.detach(); } mInjectPresenters.clear(); mInjectPresenters = null; } }
由于上篇文章中,我們使用了依賴(lài)注入,所以這里的 BaseFragment 類(lèi)的泛型參數(shù)就給我們?nèi)サ袅?。還有 BaseActivity 在這一版本中,我也去除了這個(gè)泛型參數(shù),如圖:
去除之后:
這里的 BaseActivity 就顯得干凈簡(jiǎn)潔了一點(diǎn),不然每次都需要傳入一個(gè)參數(shù),我覺(jué)得想想都累。好了,我們的 BaseFragment 與 BaseActivity 幾乎都一樣吧,這里也就不做多的解釋了,可以去看前面的幾篇文章中有對(duì)代碼的講解。
寫(xiě)完了一個(gè) BaseFragment 基類(lèi)后,然后就是迫不及待的去測(cè)試一些,到底能不能工作。這里,我新建了一個(gè) SecondActivity 類(lèi),目的就是為了在新的 Activity 中存放一個(gè) Fragment 用于測(cè)試。SecondActivity 沒(méi)有什么難度的代碼,就是在里面存放這一個(gè) SecondFragment,對(duì)了這里的 SecondActivity 并不是繼承我們的 BaseActivity 類(lèi),這就是一個(gè)普通的 Activity ,要特別注意。代碼很簡(jiǎn)單,如下:
新建 SecondActivity 類(lèi):
public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); /** * 開(kāi)啟一個(gè) fragment */ getSupportFragmentManager().beginTransaction().replace(R.id.second_container, new SecondFragment()).commit(); } }
SecondActivity 的布局:是一個(gè) FrameLayout 用于存放 SecondFragment。
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.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=".mvc.MainActivity"> <FrameLayout android:id="@+id/second_container" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.constraint.ConstraintLayout>
接下來(lái),才是我們的 BaseFragment 類(lèi)的正真使用。我們新建一個(gè) SecondFragment 實(shí)現(xiàn)類(lèi),繼承與 BaseFragment 類(lèi),這里的 SecondFragment 就是 MVP 的 View 層了,與我們的 Activity 一樣,同屬于 View 層。這里,我偷懶,把 MainActivity 類(lèi)的基本代碼都考過(guò)來(lái)了。這里就不要太在意什么業(yè)務(wù)邏輯了,我們只要能測(cè)試 MVP 中的 BaseFragment 能夠工作就好了。來(lái)看代碼:
View 層:新建 SecondFragment 實(shí)現(xiàn)類(lèi):
package com.test.mvp.mvpdemo.mvp.v6.view; import android.os.Bundle; import android.support.annotation.Nullable; import android.widget.TextView; import android.widget.Toast; import com.test.mvp.mvpdemo.R; import com.test.mvp.mvpdemo.mvp.v6.SecondContract; import com.test.mvp.mvpdemo.mvp.v6.basemvp.BaseFragment; import com.test.mvp.mvpdemo.mvp.v6.inject.InjectPresenter; import com.test.mvp.mvpdemo.mvp.v6.presenter.SecondPresenter; public class SecondFragment extends BaseFragment implements SecondContract.ISecondView { private TextView tvFragment; @InjectPresenter private SecondPresenter mPresenter; @Override protected int setLayout() { return R.layout.fragment_second; } @Override protected void initViews(@Nullable Bundle savedInstanceState) { tvFragment = $(R.id.tv_fragment); } @Override protected void initData() { mPresenter.handlerData(); } @Override public void showDialog() { // Toast.makeText(getContext(), "this is Fragment", Toast.LENGTH_SHORT).show(); } @SuppressWarnings("ConstantConditions") @Override public void succes(String content) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getContext(), "" + content, Toast.LENGTH_SHORT).show(); tvFragment.setText(content); } }); } }
與之對(duì)應(yīng)的就是 SecondPresenter 了,我們的 Presenter 層代碼如下,代碼與前面幾篇文章一樣,這里不做介紹了,代碼如下所示:
###Presenter 層:新建 SecondPresenter 實(shí)現(xiàn)類(lèi): package com.test.mvp.mvpdemo.mvp.v6.presenter; import com.test.mvp.mvpdemo.mvp.v6.SecondContract; import com.test.mvp.mvpdemo.mvp.v6.basemvp.BasePresenter; import com.test.mvp.mvpdemo.mvp.v6.model.SecondModel; import java.io.IOException; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Response; public class SecondPresenter extends BasePresenter<SecondContract.ISecondView, SecondModel> implements SecondContract.ISecondPresenter { @Override public void handlerData() { getView().showDialog(); getModel().requestBaidu(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { String content = response.body().string(); getView().succes(content); } }); } }
接下來(lái)剩余的就是我們的 Model 層了,我們與之對(duì)應(yīng)的是 SecondModel 類(lèi),還是請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù),因?yàn)槲覀冎罢?qǐng)求的是百度首頁(yè)的網(wǎng)頁(yè)文本,為了形成區(qū)別,我這里將 URL 改成了我的 博客 地址,哈哈。代碼如下:
Model 層:新建 SecondModel 實(shí)現(xiàn)類(lèi):
package com.test.mvp.mvpdemo.mvp.v6.model; import com.test.mvp.mvpdemo.mvp.v6.SecondContract; import com.test.mvp.mvpdemo.mvp.v6.basemvp.BaseModel; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; public class SecondModel extends BaseModel implements SecondContract.ISecondModel { @Override public void requestBaidu(Callback callback) { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://blog.csdn.net/smile_running") .build(); client.newCall(request).enqueue(callback); } }
最后,還有一個(gè)它們的契約類(lèi),其中都是接口類(lèi)型。代碼如下:
新建 SecondContract 接口類(lèi):
package com.test.mvp.mvpdemo.mvp.v6; import com.test.mvp.mvpdemo.mvp.v6.basemvp.IBasePresenter; import com.test.mvp.mvpdemo.mvp.v6.basemvp.IBaseView; import okhttp3.Callback; public interface SecondContract { interface ISecondModel { void requestBaidu(Callback callback); } interface ISecondView extends IBaseView { void showDialog(); void succes(String content); } interface ISecondPresenter extends IBasePresenter { void handlerData(); } }
分包情況就是文章篇頭的那張包圖,好了,把代碼寫(xiě)完了,就跑起來(lái)試試吧。
這里的運(yùn)行情況是,從 MainActivity 中點(diǎn)擊 textview 跳轉(zhuǎn)到 SecondActivity,由于在 SecondActivity 顯示的是我們的 SecondFragment ,所以會(huì)從網(wǎng)絡(luò)上獲取我的博客的地址文本,返回將數(shù)據(jù)設(shè)置到 SecondFragment 的 textview 上,運(yùn)行效果就是這樣,如下圖:
好吧,效果雖然簡(jiǎn)單了點(diǎn),但我們的 BaseFragment 算是封裝完成了,經(jīng)過(guò)測(cè)試,也是能夠派上用場(chǎng)的了。經(jīng)過(guò)我們的不懈努力,又把 BaseMVP 基礎(chǔ)框架的搭建工作推進(jìn)了一小步,在 BaseFragment 的封裝過(guò)程中,我寫(xiě)的代碼確實(shí)出現(xiàn)了一些小失誤,這個(gè)是我們,原因是,我沒(méi)有去拷貝代碼!哈哈哈哈,好氣啊,花了我好大把時(shí)間去改這個(gè)錯(cuò)誤。
記錄錯(cuò)誤原因:在子線(xiàn)程中更新 UI 操作。
錯(cuò)誤代碼如下:在 SecondFragment 中更新 UI
@Override public void succes(String content) { Toast.makeText(getContext(), "" + content, Toast.LENGTH_SHORT).show(); tvFragment.setText(content); }
這個(gè)不是很簡(jiǎn)單嘛,這都不會(huì)改!
這可不一樣,它報(bào)的錯(cuò)誤信息可并不是子線(xiàn)程修改主線(xiàn)程異常,而是這么一堆錯(cuò)誤日志:
07-09 23:51:21.887 9769-9788/com.test.mvp.mvpdemo E/EGL_adreno: tid 9788: eglSurfaceAttrib(1319): error 0x3009 (EGL_BAD_MATCH) 07-09 23:51:21.915 9769-9788/com.test.mvp.mvpdemo E/EGL_adreno: tid 9788: eglSurfaceAttrib(1319): error 0x3009 (EGL_BAD_MATCH) 07-09 23:51:23.362 9769-9788/com.test.mvp.mvpdemo E/EGL_adreno: tid 9788: eglSurfaceAttrib(1319): error 0x3009 (EGL_BAD_MATCH) 07-09 23:51:27.742 9769-9788/com.test.mvp.mvpdemo E/EGL_adreno: tid 9788: eglSurfaceAttrib(1319): error 0x3009 (EGL_BAD_MATCH) 07-09 23:51:28.069 9769-9798/com.test.mvp.mvpdemo E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher Process: com.test.mvp.mvpdemo, PID: 9769 java.lang.reflect.UndeclaredThrowableException at $Proxy2.succes(Unknown Source) at com.test.mvp.mvpdemo.mvp.v6.presenter.SecondPresenter$1.onResponse(SecondPresenter.java:25) at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:818) Caused by: java.lang.reflect.InvocationTargetException at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.test.mvp.mvpdemo.mvp.v6.basemvp.BasePresenter$1.invoke(BasePresenter.java:31) at java.lang.reflect.Proxy.invoke(Proxy.java:397) at $Proxy2.succes(Unknown Source) at com.test.mvp.mvpdemo.mvp.v6.presenter.SecondPresenter$1.onResponse(SecondPresenter.java:25) at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:818) Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler.<init>(Handler.java:200) at android.os.Handler.<init>(Handler.java:114) at android.widget.Toast$TN.<init>(Toast.java:359) at android.widget.Toast.<init>(Toast.java:100) at android.widget.Toast.makeText(Toast.java:273) at com.test.mvp.mvpdemo.mvp.v6.view.SecondFragment.succes(SecondFragment.java:44) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.test.mvp.mvpdemo.mvp.v6.basemvp.BasePresenter$1.invoke(BasePresenter.java:31) at java.lang.reflect.Proxy.invoke(Proxy.java:397) at $Proxy2.succes(Unknown Source) at com.test.mvp.mvpdemo.mvp.v6.presenter.SecondPresenter$1.onResponse(SecondPresenter.java:25) at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:818) 07-09 23:51:28.126 9769-9788/com.test.mvp.mvpdemo E/EGL_adreno: tid 9788: eglSurfaceAttrib(1319): error 0x3009 (EGL_BAD_MATCH)
首先,我看了標(biāo)記中的第一個(gè)和第二個(gè)錯(cuò)誤原因,原來(lái)是反射那塊有問(wèn)題,根據(jù)它代碼中提示的位置,說(shuō)我的 Presenter 中的 getView() 方法出錯(cuò)了,如:
點(diǎn)擊去看了下,是動(dòng)態(tài)代理的代碼,這里搞什么鬼,我又沒(méi)修改這里的代碼,怎么就錯(cuò)了呢?
一臉懵逼的我,回頭看了看,在這里嘗試了斷點(diǎn)調(diào)試,沒(méi)有什么結(jié)果。后來(lái)意外發(fā)現(xiàn),我的把上面圖中的 getView().succes(content) 注釋掉了就不報(bào)錯(cuò)了。這才找到了原因,原來(lái)是這里的數(shù)據(jù)是通過(guò)網(wǎng)絡(luò)請(qǐng)求傳過(guò)來(lái)的,我們的 okhttp 需要轉(zhuǎn)到 ui 線(xiàn)程中去更新,這個(gè)我是知道的。
所以要記得,切到主線(xiàn)程去更新 UI 操作。雖然發(fā)生了一點(diǎn)小失誤,剛開(kāi)始以為是動(dòng)態(tài)代理的問(wèn)題,所以查了好多關(guān)于動(dòng)態(tài)代理的知識(shí),借此還能學(xué)到一點(diǎn)額外的知識(shí),美滋滋,哈哈。
關(guān)于“Android MVP中如何實(shí)現(xiàn)BaseFragment通用式封裝”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
當(dāng)前標(biāo)題:AndroidMVP中如何實(shí)現(xiàn)BaseFragment通用式封裝
新聞來(lái)源:http://jinyejixie.com/article30/jojiso.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司、搜索引擎優(yōu)化、網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站建設(shè)、品牌網(wǎng)站建設(shè)、靜態(tài)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)