安卓项目实战之:最实用的Retrofit2+RxJava2+MVP框架搭建,避免各种内存泄漏

版权声明:转载必须注明本文转自郭子轩的博客 https://blog.csdn.net/gpf1320253667/article/details/84363059

工程目录结构

在这里插入图片描述
目前网上的mvp框架大多存在以下问题:
1,Presenter持有View的引用,容易导致出现内存泄漏

MvpPresenter mvpPresenter = new MvpPresenter(this);  // 不推荐这样写,持有activty引用,容易出现内存泄露

2,如果每一个Activity都对应一个Model和Presenter的话,试想一下,如果有几百个Activity的话那么是不是也要创建很多个Model和Presenter,关键是很多地方都是重复的可以通用的。所以,创建基类就显得很有必要了,具体查看本例写法,优不优雅看过之后您评论。

添加依赖

// Retrofit
compile 'com.squareup.retrofit2:retrofit:2.4.0'
// Retrofit和jxjava关联
compile 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
// Retrofit使用Gson转换
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
// RxJava
compile 'io.reactivex.rxjava2:rxjava:2.1.12'
// RxAndroid
compile 'io.reactivex.rxjava2:rxandroid:2.0.2'

1,RetrofitHelper
主要用于Retrofit的初始化:

// 声明Retrofit对象
    private Retrofit mRetrofit = null;
    OkHttpClient client = new OkHttpClient();

    // 由于该对象会被频繁调用,采用单例模式,下面是一种线程安全模式的单例写法,构造方法已被private修饰
    private volatile static RetrofitHelper instance = null;
    public static RetrofitHelper getInstance(){
        if (instance == null){
            synchronized (RetrofitHelper.class) {
                if(instance == null){
                    instance = new RetrofitHelper();
                }
            }
        }
        return instance;
    }

    private RetrofitHelper(){
        initRetrofit();
    }

    /**
     * 初始化 retrofit
     */
    private void initRetrofit() {
        mRetrofit = new Retrofit.Builder()
                .baseUrl(UrlConstant.BASE_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }

    public RetrofitService getServer(){
        return mRetrofit.create(RetrofitService.class);
    }

其中getServer方法就是为了获取RetrofitService接口类的实例化。然后定义了一个静态方法getInstance用于获取自身RetrofitHelper的实例化,并且只会实例化一次。

2,RetrofitService

public interface RetrofitService {
    @GET("book/search")
    Observable<Book> getSearchBooks(@Query("q") String name,
                                    @Query("tag") String tag, @Query("start") int start,
                                    @Query("count") int count);
}

3,DataManager

/**
 * 该类用来管理RetrofitService中对应的各种API接口
 * 当做Retrofit和presenter中的桥梁,Activity就不用直接和retrofit打交道了
 */

public class DataManager {

    private RetrofitService mRetrofitService;

    private DataManager(){
        this.mRetrofitService = RetrofitHelper.getInstance().getServer();
    }

    //由于该对象会被频繁调用,采用单例模式,下面是一种线程安全模式的单例写法,构造方法已被private修饰
    private volatile static DataManager instance;
    public static DataManager getInstance() {
        if (instance == null) {
            synchronized (DataManager.class) {
                if (instance == null) {
                    instance = new DataManager();
                }
            }
        }
        return instance;
    }


    // 将retrofit的业务方法映射到DataManager中,以后统一用该类来调用业务方法
    // 以后在RetrofitService中增加业务方法的时候,相应的这里也要添加一个方法,建议方法名字相一致
    public Observable<Book> getSearchBooks(String name, String tag, int start, int count){
        return mRetrofitService.getSearchBooks(name,tag,start,count);
    }
}

这个类其实就是为了让你更方便的调用RetrofitService 中定义的方法,可以看到,在它的构造方法中,我们得到了RetrofitService 的实例化,然后定义了一个和RetrofitService 中同名的方法,里面其实就是调用RetrofitService 中的这个方法。这样,把RetrofitService 中定义的方法都封装到DataManager 中,以后无论在哪个要调用方法时直接在DataManager 中调用就可以了,而不是重复建立RetrofitService 的实例化,再调用其中的方法。

4,其中bean下放我们请求的实体类,这里就是Book

package com.gpf.mvp.service.bean;

import java.util.List;

/**
 * Created by Administrator on 2018/11/23.
 */

public class Book {

    /**
     * count : 1
     * start : 0
     * total : 20
     * books : [{"rating":{"max":10,"numRaters":3342,"average":"7.9","min":0},"subtitle":"西北有雏凤","author":["烽火戏诸侯"],"pubdate":"2013-9-1","tags":[{"count":520,"name":"烽火戏诸侯","title":"烽火戏诸侯"},{"count":477,"name":"武侠","title":"武侠"},{"count":436,"name":".好书,很值得一看。","title":".好书,很值得一看。"},{"count":381,"name":"雪中悍刀行,你值得拥有","title":"雪中悍刀行,你值得拥有"},{"count":365,"name":"达到了我对武侠江湖的那个感觉~人物特点脱出。文笔好。。希望烽火","title":"达到了我对武侠江湖的那个感觉~人物特点脱出。文笔好。。希望烽火"},{"count":301,"name":"小说","title":"小说"},{"count":276,"name":"新武侠","title":"新武侠"},{"count":208,"name":"玄幻","title":"玄幻"}],"origin_title":"小二  上酒","image":"https://img3.doubanio.com/view/subject/m/public/s27142913.jpg","binding":"平装","translator":[],"catalog":"第一章 六千里归乡上酒,白狐脸佩刀入城\n老仆笑得很淳朴很灿烂,一张老脸像只有出了远门到了荒郊才能瞅见的大片芦苇丛,可能谈不上旖旎或者壮阔,却有着自己的情怀。\n第二章 武媚娘遥望城头,湖心里老魁带刀\n枯槁如鬼的男人开口,如一股子金石声,\u201c从一品。阁内修行十年,可此下众生,此上无人。\u201d\n第三章 莲花峰骑牛问道,武帝城竖剑留语\n洪洗象等世子殿下走远了,然后姿势不雅地蹲着,双手托着腮帮,怔怔出神,喃喃自语:\u201c红豆生南国,春来发枝冬凋敝,相思不如不相思。\u201d\n第四章 赴城外杀人赏雪,上武当姜泥送书\n天底下什么东西最重?情义?忠孝?放屁,是书最重。\n第五章 拔悍刀血战双卫,大庚角留帖离山\n徐凤年记起三年游历中在洛水河畔,远远看到的一个窈窕背影,怔怔出神道:\u201c相思刀最是能杀人。\n第六章 老掌教黄庭作嫁,小和尚秀色参禅\n你便是我的禅,秀色可\u201c参\u201d。\n第七章 北凉歌再奠英灵,阴阳间喝酒换刀\n白狐儿脸说了句几乎让徐凤年吐血的话,\u201c你要是女人就好了,我便娶了你。\u201d\n第八章 忆王妃老卒瞑目,出凉州世子挎刀\n徐凤年丢掉树叶,膝上叠放着绣冬、春雷双刀,望着墓碑柔声道:\u201c娘,你的仇,徐骁不报,凤年还记着呢。\u201d\n第九章 红符甲大雨拦道,老剑神初显身手\n魏爷爷,你说一品有四重,金刚之上是指玄。原来一弹玄机即指玄。\n第十章 晋兰亭鱼跃龙门,青城山怒斩马贼\n\u201c温华,没钱买不起好剑又何妨,希望你小子能一直提着把破木剑去名动天下。到时候按照兄弟约定,你请我吃牛肉,我给你叫好。\u201d\n第十一章 山河图随行细绘,青羊宫闯阵玉宵\n北凉铁骑甲天下,不是靠文人士子用嘴喊出来的,而是马踏六国加上半座江湖一个一个铁蹄踩踏出来的!\n第十二章 滚刀术先手五十,北凉王悍然入宫\n他便一直擦下去,哽咽着温柔道:\u201c姑姑好看,姑姑不哭。\u201d","pages":"320","images":{"small":"https://img3.doubanio.com/view/subject/s/public/s27142913.jpg","large":"https://img3.doubanio.com/view/subject/l/public/s27142913.jpg","medium":"https://img3.doubanio.com/view/subject/m/public/s27142913.jpg"},"alt":"https://book.douban.com/subject/25714176/","id":"25714176","publisher":"江苏文艺出版社","isbn10":"7539962879","isbn13":"9787539962870","title":"雪中悍刀行 1","url":"https://api.douban.com/v2/book/25714176","alt_title":"小二  上酒","author_intro":"2005年底烽火开始在起点中文网发文,后一直以挖\u201c坑\u201d不填而广受读者抱怨,因此被称为\u201c大内太监总管\u201d。 代表作《陈二狗的妖孽人生》《极品公子》《天神下凡》等\n其作品订阅极高、口碑极好,但是他毅然太监,绝不\u201c出宫\u201d。更新速度犹如龟爬,马甲众多!其马甲有:婆娑世界教主、八部浮屠。\n2010年4月,烽火转战纵横中文网。如今,他毅然\u201c出宫\u201d,更是执着一把悍刀,带着第一本完美收官之作征这出版之道!试问,八百白袍,今安在?","summary":"道门真人飞天入地,千里取人首级;佛家菩萨低眉怒目,抬手可撼昆仑;谁又言书生无意气,一怒敢叫天子露戚容。踏江踏湖踏歌,我有一剑仙人跪;提刀提剑提酒,三十万铁骑征天。\n老仆剑九背匣,吕祖修道骑牛,天道悠悠难觅,情之一字可杀人。\n漠北黄沙如刀舞, 世子白马出北凉,身后跟着一百佩刀甲士。\n鲜衣怒马,美婢娇娘,还有一个抠脚的断臂老人。\n犹记得跟缺门牙老黄的那段对话。\n\u201c老黄,你是高手吗?\u201d\n\u201c是哩。\u201d\n\u201c有多高?\u201d\n\u201c啧啧,只比公子差一点点。\u201d\n回想当初自己不屑地吐出黄瓜花儿,还骂了句去你娘的。却不承想,那老黄乃是传说中高手中的高手。这世道果然难测。\n世子如今带双刀,剑神武夫,江湖庙堂,可否一并斩之?","series":{"id":"20883","title":"雪中悍刀行"},"price":"25.00元"}]
     */

    private int count;
    private int start;
    private int total;
    private List<BooksBean> books;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }

    public List<BooksBean> getBooks() {
        return books;
    }

    public void setBooks(List<BooksBean> books) {
        this.books = books;
    }

    public static class BooksBean {

        /**
         * rating : {"max":10,"numRaters":3342,"average":"7.9","min":0}
         * subtitle : 西北有雏凤
         * author : ["烽火戏诸侯"]
         * pubdate : 2013-9-1
         * tags : [{"count":520,"name":"烽火戏诸侯","title":"烽火戏诸侯"},{"count":477,"name":"武侠","title":"武侠"},{"count":436,"name":".好书,很值得一看。","title":".好书,很值得一看。"},{"count":381,"name":"雪中悍刀行,你值得拥有","title":"雪中悍刀行,你值得拥有"},{"count":365,"name":"达到了我对武侠江湖的那个感觉~人物特点脱出。文笔好。。希望烽火","title":"达到了我对武侠江湖的那个感觉~人物特点脱出。文笔好。。希望烽火"},{"count":301,"name":"小说","title":"小说"},{"count":276,"name":"新武侠","title":"新武侠"},{"count":208,"name":"玄幻","title":"玄幻"}]
         * origin_title : 小二  上酒
         * image : https://img3.doubanio.com/view/subject/m/public/s27142913.jpg
         * binding : 平装
         * translator : []
         * catalog : 第一章 六千里归乡上酒,白狐脸佩刀入城
         老仆笑得很淳朴很灿烂,一张老脸像只有出了远门到了荒郊才能瞅见的大片芦苇丛,可能谈不上旖旎或者壮阔,却有着自己的情怀。
         第二章 武媚娘遥望城头,湖心里老魁带刀
         枯槁如鬼的男人开口,如一股子金石声,“从一品。阁内修行十年,可此下众生,此上无人。”
         第三章 莲花峰骑牛问道,武帝城竖剑留语
         洪洗象等世子殿下走远了,然后姿势不雅地蹲着,双手托着腮帮,怔怔出神,喃喃自语:“红豆生南国,春来发枝冬凋敝,相思不如不相思。”
         第四章 赴城外杀人赏雪,上武当姜泥送书
         天底下什么东西最重?情义?忠孝?放屁,是书最重。
         第五章 拔悍刀血战双卫,大庚角留帖离山
         徐凤年记起三年游历中在洛水河畔,远远看到的一个窈窕背影,怔怔出神道:“相思刀最是能杀人。
         第六章 老掌教黄庭作嫁,小和尚秀色参禅
         你便是我的禅,秀色可“参”。
         第七章 北凉歌再奠英灵,阴阳间喝酒换刀
         白狐儿脸说了句几乎让徐凤年吐血的话,“你要是女人就好了,我便娶了你。”
         第八章 忆王妃老卒瞑目,出凉州世子挎刀
         徐凤年丢掉树叶,膝上叠放着绣冬、春雷双刀,望着墓碑柔声道:“娘,你的仇,徐骁不报,凤年还记着呢。”
         第九章 红符甲大雨拦道,老剑神初显身手
         魏爷爷,你说一品有四重,金刚之上是指玄。原来一弹玄机即指玄。
         第十章 晋兰亭鱼跃龙门,青城山怒斩马贼
         “温华,没钱买不起好剑又何妨,希望你小子能一直提着把破木剑去名动天下。到时候按照兄弟约定,你请我吃牛肉,我给你叫好。”
         第十一章 山河图随行细绘,青羊宫闯阵玉宵
         北凉铁骑甲天下,不是靠文人士子用嘴喊出来的,而是马踏六国加上半座江湖一个一个铁蹄踩踏出来的!
         第十二章 滚刀术先手五十,北凉王悍然入宫
         他便一直擦下去,哽咽着温柔道:“姑姑好看,姑姑不哭。”
         * pages : 320
         * images : {"small":"https://img3.doubanio.com/view/subject/s/public/s27142913.jpg","large":"https://img3.doubanio.com/view/subject/l/public/s27142913.jpg","medium":"https://img3.doubanio.com/view/subject/m/public/s27142913.jpg"}
         * alt : https://book.douban.com/subject/25714176/
         * id : 25714176
         * publisher : 江苏文艺出版社
         * isbn10 : 7539962879
         * isbn13 : 9787539962870
         * title : 雪中悍刀行 1
         * url : https://api.douban.com/v2/book/25714176
         * alt_title : 小二  上酒
         * author_intro : 2005年底烽火开始在起点中文网发文,后一直以挖“坑”不填而广受读者抱怨,因此被称为“大内太监总管”。 代表作《陈二狗的妖孽人生》《极品公子》《天神下凡》等
         其作品订阅极高、口碑极好,但是他毅然太监,绝不“出宫”。更新速度犹如龟爬,马甲众多!其马甲有:婆娑世界教主、八部浮屠。
         2010年4月,烽火转战纵横中文网。如今,他毅然“出宫”,更是执着一把悍刀,带着第一本完美收官之作征这出版之道!试问,八百白袍,今安在?
         * summary : 道门真人飞天入地,千里取人首级;佛家菩萨低眉怒目,抬手可撼昆仑;谁又言书生无意气,一怒敢叫天子露戚容。踏江踏湖踏歌,我有一剑仙人跪;提刀提剑提酒,三十万铁骑征天。
         老仆剑九背匣,吕祖修道骑牛,天道悠悠难觅,情之一字可杀人。
         漠北黄沙如刀舞, 世子白马出北凉,身后跟着一百佩刀甲士。
         鲜衣怒马,美婢娇娘,还有一个抠脚的断臂老人。
         犹记得跟缺门牙老黄的那段对话。
         “老黄,你是高手吗?”
         “是哩。”
         “有多高?”
         “啧啧,只比公子差一点点。”
         回想当初自己不屑地吐出黄瓜花儿,还骂了句去你娘的。却不承想,那老黄乃是传说中高手中的高手。这世道果然难测。
         世子如今带双刀,剑神武夫,江湖庙堂,可否一并斩之?
         * series : {"id":"20883","title":"雪中悍刀行"}
         * price : 25.00元
         */

        private RatingBean rating;
        private String subtitle;
        private String pubdate;
        private String origin_title;
        private String image;
        private String binding;
        private String catalog;
        private String pages;
        private ImagesBean images;
        private String alt;
        private String id;
        private String publisher;
        private String isbn10;
        private String isbn13;
        private String title;
        private String url;
        private String alt_title;
        private String author_intro;
        private String summary;
        private SeriesBean series;
        private String price;
        private List<String> author;
        private List<TagsBean> tags;
        private List<?> translator;

        public RatingBean getRating() {
            return rating;
        }

        public void setRating(RatingBean rating) {
            this.rating = rating;
        }

        public String getSubtitle() {
            return subtitle;
        }

        public void setSubtitle(String subtitle) {
            this.subtitle = subtitle;
        }

        public String getPubdate() {
            return pubdate;
        }

        public void setPubdate(String pubdate) {
            this.pubdate = pubdate;
        }

        public String getOrigin_title() {
            return origin_title;
        }

        public void setOrigin_title(String origin_title) {
            this.origin_title = origin_title;
        }

        public String getImage() {
            return image;
        }

        public void setImage(String image) {
            this.image = image;
        }

        public String getBinding() {
            return binding;
        }

        public void setBinding(String binding) {
            this.binding = binding;
        }

        public String getCatalog() {
            return catalog;
        }

        public void setCatalog(String catalog) {
            this.catalog = catalog;
        }

        public String getPages() {
            return pages;
        }

        public void setPages(String pages) {
            this.pages = pages;
        }

        public ImagesBean getImages() {
            return images;
        }

        public void setImages(ImagesBean images) {
            this.images = images;
        }

        public String getAlt() {
            return alt;
        }

        public void setAlt(String alt) {
            this.alt = alt;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getPublisher() {
            return publisher;
        }

        public void setPublisher(String publisher) {
            this.publisher = publisher;
        }

        public String getIsbn10() {
            return isbn10;
        }

        public void setIsbn10(String isbn10) {
            this.isbn10 = isbn10;
        }

        public String getIsbn13() {
            return isbn13;
        }

        public void setIsbn13(String isbn13) {
            this.isbn13 = isbn13;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getAlt_title() {
            return alt_title;
        }

        public void setAlt_title(String alt_title) {
            this.alt_title = alt_title;
        }

        public String getAuthor_intro() {
            return author_intro;
        }

        public void setAuthor_intro(String author_intro) {
            this.author_intro = author_intro;
        }

        public String getSummary() {
            return summary;
        }

        public void setSummary(String summary) {
            this.summary = summary;
        }

        public SeriesBean getSeries() {
            return series;
        }

        public void setSeries(SeriesBean series) {
            this.series = series;
        }

        public String getPrice() {
            return price;
        }

        public void setPrice(String price) {
            this.price = price;
        }

        public List<String> getAuthor() {
            return author;
        }

        public void setAuthor(List<String> author) {
            this.author = author;
        }

        public List<TagsBean> getTags() {
            return tags;
        }

        public void setTags(List<TagsBean> tags) {
            this.tags = tags;
        }

        public List<?> getTranslator() {
            return translator;
        }

        public void setTranslator(List<?> translator) {
            this.translator = translator;
        }

        @Override
        public String toString() {
            return "BooksBean{" +
                    "rating=" + rating +
                    ", subtitle='" + subtitle + '\'' +
                    ", pubdate='" + pubdate + '\'' +
                    ", origin_title='" + origin_title + '\'' +
                    ", image='" + image + '\'' +
                    ", binding='" + binding + '\'' +
                    ", catalog='" + catalog + '\'' +
                    ", pages='" + pages + '\'' +
                    ", images=" + images +
                    ", alt='" + alt + '\'' +
                    ", id='" + id + '\'' +
                    ", publisher='" + publisher + '\'' +
                    ", isbn10='" + isbn10 + '\'' +
                    ", isbn13='" + isbn13 + '\'' +
                    ", title='" + title + '\'' +
                    ", url='" + url + '\'' +
                    ", alt_title='" + alt_title + '\'' +
                    ", author_intro='" + author_intro + '\'' +
                    ", summary='" + summary + '\'' +
                    ", series=" + series +
                    ", price='" + price + '\'' +
                    ", author=" + author +
                    ", tags=" + tags +
                    ", translator=" + translator +
                    '}';
        }

        public static class RatingBean {
            /**
             * max : 10
             * numRaters : 3342
             * average : 7.9
             * min : 0
             */

            private int max;
            private int numRaters;
            private String average;
            private int min;

            public int getMax() {
                return max;
            }

            public void setMax(int max) {
                this.max = max;
            }

            public int getNumRaters() {
                return numRaters;
            }

            public void setNumRaters(int numRaters) {
                this.numRaters = numRaters;
            }

            public String getAverage() {
                return average;
            }

            public void setAverage(String average) {
                this.average = average;
            }

            public int getMin() {
                return min;
            }

            public void setMin(int min) {
                this.min = min;
            }
        }

        public static class ImagesBean {
            /**
             * small : https://img3.doubanio.com/view/subject/s/public/s27142913.jpg
             * large : https://img3.doubanio.com/view/subject/l/public/s27142913.jpg
             * medium : https://img3.doubanio.com/view/subject/m/public/s27142913.jpg
             */

            private String small;
            private String large;
            private String medium;

            public String getSmall() {
                return small;
            }

            public void setSmall(String small) {
                this.small = small;
            }

            public String getLarge() {
                return large;
            }

            public void setLarge(String large) {
                this.large = large;
            }

            public String getMedium() {
                return medium;
            }

            public void setMedium(String medium) {
                this.medium = medium;
            }
        }

        public static class SeriesBean {
            /**
             * id : 20883
             * title : 雪中悍刀行
             */

            private String id;
            private String title;

            public String getId() {
                return id;
            }

            public void setId(String id) {
                this.id = id;
            }

            public String getTitle() {
                return title;
            }

            public void setTitle(String title) {
                this.title = title;
            }
        }

        public static class TagsBean {
            /**
             * count : 520
             * name : 烽火戏诸侯
             * title : 烽火戏诸侯
             */

            private int count;
            private String name;
            private String title;

            public int getCount() {
                return count;
            }

            public void setCount(int count) {
                this.count = count;
            }

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public String getTitle() {
                return title;
            }

            public void setTitle(String title) {
                this.title = title;
            }
        }
    }

    @Override
    public String toString() {
        return "Book{" +
                "count=" + count +
                ", start=" + start +
                ", total=" + total +
                ", books=" + books +
                '}';
    }
}

5,创建抽象类BasePresenter,代码如下:

public abstract class BasePresenter<V extends BaseView> {

    private V baseView;

    public void attachView(V baseView) {
        this.baseView = baseView;
    }

    /**
     * 解绑View
     */
    public void detachView(){
        baseView = null;
    }

    /**
     *  获取View
     * @return
     */
    public V getMvpView(){
        return baseView;
    }

}

6,创建接口BaseView,代码如下:

/**
 * 定义通用的接口方法
 */

public interface BaseView {

    // 显示进度框
    void showProgressDialog();
    // 关闭进度框
    void hideProgressDialog();
    // 出错信息的回调
    void onError(String result);

}

7,创建BaseActivity,代码如下:

public abstract class BaseActivity extends AppCompatActivity {


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());

        initPresenter();
        //初始化控件,一般在BaseActivity中通过ButterKnife来绑定,所以该方法内部一般我们初始化界面相关的操作
        initViews();
        //获取数据
        getDataFromServer();
    }


    // 设置布局
    protected abstract int getLayoutId();

    // 初始化界面
    protected abstract void initViews();
    // 获取数据
    protected void getDataFromServer(){}
    // 实例化presenter
    protected void initPresenter(){}

}

8,创建BaseMvpActivity,代码如下:

public abstract class BaseMvpActivity<V extends BaseView, P extends BasePresenter<V>> extends BaseActivity {

    private P presenter;

    @Override
    protected void initPresenter() {
        //实例化Presenter
        presenter = createPresenter();
        //绑定
        if (presenter != null){
            presenter.attachView((V) this);
        }
    }

    // 初始化Presenter
    protected abstract P createPresenter();

    protected P getPresenter(){
        return presenter;
    }


    @Override
    protected void onDestroy() {
        //解绑
        if (presenter != null){
            presenter.detachView();
        }
        super.onDestroy();
    }

}

9,UrlConstant类代码如下:

public class UrlConstant {

    public static final String BASE_URL = "https://api.douban.com/v2/";

}

经过以上的步骤针对该baseurl的mvp框架就已经搭建完毕了,如果使用其他网络框架如OkGo等,前三步可以直接跳过不写,因为前三步主要是针对Retrofit网络请求过程的封装,接下来我们来实现请求接口数据,获取服务器端返回的数据。

1,根据具体的业务需求创建BaseView的接口,此处为BookView:

/**
 * 根据具体的业务需求,定义特有的方法,该接口为所有的view公用
 * 不同的activity定义不同的View,这些View有个共同点,那就是都继承自BaseView
 */

public interface BookView extends BaseView {
    // 当前页面比较简单仅仅是获取接口数据进行展示,
    // 业务比较复杂的时候,可能一个页面需要不同的接口得到不同的数据类型
    void onSuccess(Book mBook);
    //获取到广告位数据
    // void loadBannerSuccess(BannerBean bean);
}

2,根据具体的业务需求创建BasePresenter的实现类,此处为BookPresenter:

public class BookPresenter extends BasePresenter<BookView> {

    private DataManager dataManager;
    private Book mBook;

    public BookPresenter() {
        dataManager = DataManager.getInstance();
    }

    public void getSearchBooks(String name,String tag,int start,int count){
        if(getMvpView() != null){
            // 显示进度提示
            getMvpView().showProgressDialog();
            // 进行网络请求
            dataManager.getSearchBooks(name,tag,start,count)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<Book>() {

                        @Override
                        public void onSubscribe(@NonNull Disposable d) {

                        }

                        @Override
                        public void onNext(@NonNull Book book) {
                            mBook = book;
                        }

                        // 此处注意:onComplete和onError只会调用其中一个,不可能同时被触发
                        @Override
                        public void onError(@NonNull Throwable e) {
                            // 在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出
                            e.printStackTrace();
                            getMvpView().onError("请求失败!!");
                            getMvpView().hideProgressDialog();
                        }

                        @Override
                        public void onComplete() {
                            // onComplete方法和onError方法是互斥的,
                            // RxJava 规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志。
                            if (mBook != null){
                                getMvpView().onSuccess(mBook);
                            }
                            // 隐藏进度
                            getMvpView().hideProgressDialog();
                        }
                    });
        }
    }
}

3,接下来就是BookActivity的实现了,注意此处继承的是BaseMvpActivity,布局文件也很简单一个textview和一个button,点击button将获取到的数据显示在textView上即可:

public class BookActivity extends BaseMvpActivity<BookView,BookPresenter> implements BookView {

    private ProgressDialog progressDialog;
    private Button button;
    private TextView text;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_book;
    }

    // 获取presenter实例
    @Override
    protected BookPresenter createPresenter() {
        return new BookPresenter();
    }

    // 初始化界面
    @Override
    protected void initViews() {
        button = (Button) findViewById(R.id.button);
        text = (TextView) findViewById(R.id.text);

        progressDialog = new ProgressDialog(this);
        progressDialog.setCancelable(false);
        progressDialog.setMessage("正在拼命加载中...");
    }

    // 按钮的点击事件:请求接口,获取网络数据
    public void getData(View view){
        // 调用presenter层的方法执行网络请求
        getPresenter().getSearchBooks("天涯明月刀", null, 0, 1);
    }

    @Override
    public void onSuccess(Book mBook) {
        // 将获取到的数据的信息显示在TextView组件上
        text.setText(mBook.toString());
    }

    // 或者通过设置progress组件的VISIBLE和GONE来控制进度提示的显示和隐藏,此处是使用对话框提示
    @Override
    public void showProgressDialog() {
        if(!progressDialog.isShowing()){
            progressDialog.show();
        }
    }

    @Override
    public void hideProgressDialog() {
        if(progressDialog.isShowing()){
            progressDialog.dismiss();
        }
    }

    @Override
    public void onError(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

猜你喜欢

转载自blog.csdn.net/gpf1320253667/article/details/84363059