最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android MVP BaseFragment 通用式封裝的實(shí)現(xiàn)

 更新時間:2019年08月14日 14:45:59   作者:Android架構(gòu)師丨小熊  
這篇文章主要介紹了Android MVP BaseFragment 通用式封裝的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

這篇已經(jīng)是我們的 BaseMVP 基礎(chǔ)框架系列文章的第六篇了,BaseMVP 已經(jīng)被我們封裝了快差不多了,從上篇的文章(Android MVP 架構(gòu)(五)MVP 多個 Presenter 依賴注入)中,我們解決了多的 Presenter 的問題,這是利用依賴注入及反射的方式,動態(tài)的去實(shí)例化不同 Presenter 層實(shí)現(xiàn)類,并且與同一個 View 做了綁定和解綁的操作,這就是一個 View 對多 Presenter 的需要手動 new 的解決方案。

BaseMVP 基礎(chǔ)框架的搭建,到這里的話,還缺少一點(diǎn)東西。我們之前只封裝過了一個基類的 BaseActivity 類,這個類是提供給 Activity 來繼承的,但是,我們的實(shí)際項(xiàng)目中,難免會有 Fragment 的出現(xiàn),于是乎,今天我們又帶大家來封裝一個 BaseFragment 基類吧,盡量的把基礎(chǔ)框架給完善。

要封裝 BaseFragment 基類,參考 BaseActivity 的封裝并不難,因?yàn)?Activity 和 Fragment 的生命周期很相似,而且 Fragment 是寄托在 Activity 上的,所以說白了都差不多,不過代碼肯定有一點(diǎn)偏差。對比之前的版本,這一次我在包中添加了一個 BaseFragment 基類,以及添加了幾個測試它的類。

下面我們來看看 BaseFragment 基類吧,直接上代碼:

 

新建 BaseFragment 基類:

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) {
   //獲取變量上面的注解類型
   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;
 }
}

由于上篇文章中,我們使用了依賴注入,所以這里的 BaseFragment 類的泛型參數(shù)就給我們?nèi)サ袅恕_€有 BaseActivity 在這一版本中,我也去除了這個泛型參數(shù),如圖:

去除之后:

這里的 BaseActivity 就顯得干凈簡潔了一點(diǎn),不然每次都需要傳入一個參數(shù),我覺得想想都累。好了,我們的 BaseFragment 與 BaseActivity 幾乎都一樣吧,這里也就不做多的解釋了,可以去看前面的幾篇文章中有對代碼的講解。

寫完了一個 BaseFragment 基類后,然后就是迫不及待的去測試一些,到底能不能工作。這里,我新建了一個 SecondActivity 類,目的就是為了在新的 Activity 中存放一個 Fragment 用于測試。SecondActivity 沒有什么難度的代碼,就是在里面存放這一個 SecondFragment,對了這里的 SecondActivity 并不是繼承我們的 BaseActivity 類,這就是一個普通的 Activity ,要特別注意。代碼很簡單,如下:

新建 SecondActivity 類:

public class SecondActivity extends AppCompatActivity {
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_second);
  /**
   * 開啟一個 fragment
   */
  getSupportFragmentManager().beginTransaction().replace(R.id.second_container, new SecondFragment()).commit();
 }
}

SecondActivity 的布局:是一個 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>

接下來,才是我們的 BaseFragment 類的正真使用。我們新建一個 SecondFragment 實(shí)現(xiàn)類,繼承與 BaseFragment 類,這里的 SecondFragment 就是 MVP 的 View 層了,與我們的 Activity 一樣,同屬于 View 層。這里,我偷懶,把 MainActivity 類的基本代碼都考過來了。這里就不要太在意什么業(yè)務(wù)邏輯了,我們只要能測試 MVP 中的 BaseFragment 能夠工作就好了。來看代碼:

View 層:新建 SecondFragment 實(shí)現(xiàn)類:

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);
   }
  });
 }
 
}

與之對應(yīng)的就是 SecondPresenter 了,我們的 Presenter 層代碼如下,代碼與前面幾篇文章一樣,這里不做介紹了,代碼如下所示:

###Presenter 層:新建 SecondPresenter 實(shí)現(xiàn)類:

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);
   }
  });
 }
}

 接下來剩余的就是我們的 Model 層了,我們與之對應(yīng)的是 SecondModel 類,還是請求網(wǎng)絡(luò)數(shù)據(jù),因?yàn)槲覀冎罢埱蟮氖前俣仁醉摰木W(wǎng)頁文本,為了形成區(qū)別,我這里將 URL 改成了我的 博客 地址,哈哈。代碼如下:

Model 層:新建 SecondModel 實(shí)現(xiàn)類:

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);
 }
}

最后,還有一個它們的契約類,其中都是接口類型。代碼如下:

新建 SecondContract 接口類:

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();
 }
}

分包情況就是文章篇頭的那張包圖,好了,把代碼寫完了,就跑起來試試吧。

這里的運(yùn)行情況是,從 MainActivity 中點(diǎn)擊 textview 跳轉(zhuǎn)到 SecondActivity,由于在 SecondActivity 顯示的是我們的 SecondFragment ,所以會從網(wǎng)絡(luò)上獲取我的博客的地址文本,返回將數(shù)據(jù)設(shè)置到 SecondFragment 的 textview 上,運(yùn)行效果就是這樣,如下圖:

好吧,效果雖然簡單了點(diǎn),但我們的 BaseFragment 算是封裝完成了,經(jīng)過測試,也是能夠派上用場的了。經(jīng)過我們的不懈努力,又把 BaseMVP 基礎(chǔ)框架的搭建工作推進(jìn)了一小步,在 BaseFragment 的封裝過程中,我寫的代碼確實(shí)出現(xiàn)了一些小失誤,這個是我們,原因是,我沒有去拷貝代碼!哈哈哈哈,好氣啊,花了我好大把時間去改這個錯誤。

記錄錯誤原因:在子線程中更新 UI 操作。

錯誤代碼如下:在 SecondFragment 中更新 UI

 @Override
 public void succes(String content) {
  Toast.makeText(getContext(), "" + content, Toast.LENGTH_SHORT).show();
  tvFragment.setText(content);
 }

這個不是很簡單嘛,這都不會改!

這可不一樣,它報(bào)的錯誤信息可并不是子線程修改主線程異常,而是這么一堆錯誤日志:

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)記中的第一個和第二個錯誤原因,原來是反射那塊有問題,根據(jù)它代碼中提示的位置,說我的 Presenter 中的 getView() 方法出錯了,如:

點(diǎn)擊去看了下,是動態(tài)代理的代碼,這里搞什么鬼,我又沒修改這里的代碼,怎么就錯了呢?

一臉懵逼的我,回頭看了看,在這里嘗試了斷點(diǎn)調(diào)試,沒有什么結(jié)果。后來意外發(fā)現(xiàn),我的把上面圖中的 getView().succes(content) 注釋掉了就不報(bào)錯了。這才找到了原因,原來是這里的數(shù)據(jù)是通過網(wǎng)絡(luò)請求傳過來的,我們的 okhttp 需要轉(zhuǎn)到 ui 線程中去更新,這個我是知道的。

所以要記得,切到主線程去更新 UI 操作。雖然發(fā)生了一點(diǎn)小失誤,剛開始以為是動態(tài)代理的問題,所以查了好多關(guān)于動態(tài)代理的知識,借此還能學(xué)到一點(diǎn)額外的知識,美滋滋,哈哈。

這里的 BaseFragment 還是有代碼重復(fù)的問題,比如我們的依賴注入那塊代碼,就放到下篇文章中去解決這個問題吧,這篇文章已經(jīng)完成我們該完成的任務(wù)了,明天的事情放到后天干吧,哈哈哈哈。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android自定義相機(jī)聚焦和顯示框

    Android自定義相機(jī)聚焦和顯示框

    這篇文章主要為大家詳細(xì)介紹了Android自定義相機(jī)聚焦和顯示框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • Android廣播機(jī)制原理與開發(fā)

    Android廣播機(jī)制原理與開發(fā)

    Android廣播機(jī)制就是在Android中,有一些操作完成以后,會發(fā)送廣播,比如說發(fā)出一條短信,或打出一個電話,如果某個程序接收了這個廣播,就會做相應(yīng)的處理
    2023-02-02
  • Android studio 自動換行和取消自動換行操作

    Android studio 自動換行和取消自動換行操作

    這篇文章主要介紹了Android studio 自動換行和取消自動換行操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • Android使用AudioRecord判斷是否有音頻輸入

    Android使用AudioRecord判斷是否有音頻輸入

    這篇文章主要介紹了Android使用AudioRecord判斷是否有音頻輸入的實(shí)例代碼,非常不錯,具有參考借鑒價值,需要的的朋友參考下吧
    2017-01-01
  • kotlin協(xié)程之coroutineScope函數(shù)使用詳解

    kotlin協(xié)程之coroutineScope函數(shù)使用詳解

    這篇文章主要為大家介紹了kotlin協(xié)程之coroutineScope函數(shù)使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Android 組件Gallery和GridView示例講解

    Android 組件Gallery和GridView示例講解

    本文主要講解Android 組件Gallery和GridView,這里詳細(xì)介紹組件Gallery和GridView的知識要點(diǎn),并附示例代碼和實(shí)現(xiàn)效果圖,有興趣的小伙伴可以參考下
    2016-08-08
  • Android將圖片上傳到php服務(wù)器的實(shí)例代碼

    Android將圖片上傳到php服務(wù)器的實(shí)例代碼

    這篇文章主要介紹了Android將圖片上傳到php服務(wù)器的實(shí)例代碼,需要的朋友可以參考下
    2017-07-07
  • Android 自繪控件

    Android 自繪控件

    Android中自定義控件分為三種:1.自繪控件 2.組合控件 3.繼承控件。本篇介紹下自繪控件。下面跟著小編一起來看下吧
    2017-02-02
  • Android dumpsys簡介

    Android dumpsys簡介

    本文詳細(xì)介紹了Android系統(tǒng)中的dumpsys工具,包括其原理、使用方法、環(huán)境要求和相關(guān)概念,dumpsys是一個強(qiáng)大的系統(tǒng)診斷工具,可以幫助開發(fā)者了解系統(tǒng)服務(wù)狀態(tài),分析問題,并設(shè)計(jì)新功能,感興趣的朋友一起看看吧
    2025-03-03
  • Android基于TextView不獲取焦點(diǎn)實(shí)現(xiàn)跑馬燈效果

    Android基于TextView不獲取焦點(diǎn)實(shí)現(xiàn)跑馬燈效果

    這篇文章主要介紹了Android基于TextView不獲取焦點(diǎn)實(shí)現(xiàn)跑馬燈效果,結(jié)合實(shí)例形式分析了Android基于TextView實(shí)現(xiàn)跑馬燈的功能與布局相關(guān)技巧,需要的朋友可以參考下
    2017-02-02

最新評論

尼玛县| 贵定县| 溧阳市| 乌海市| 冕宁县| 汉川市| 鄂伦春自治旗| 张掖市| 吴忠市| 河津市| 阿克苏市| 陕西省| 新巴尔虎左旗| 富顺县| 无棣县| 柳林县| 泸州市| 玉溪市| 洪洞县| 龙川县| 葵青区| 柘荣县| 平塘县| 姜堰市| 庆阳市| 讷河市| 平顶山市| 漳浦县| 天气| 淮阳县| 香港 | 农安县| 新和县| 孝感市| 郸城县| 外汇| 上林县| 通许县| 陵川县| 犍为县| 新昌县|