Android架构设计-简单粗暴,搞定MVP网络请求内存泄漏

一、使用Mvp为什么怎么开始内存内存泄漏了?

我们先简单的看一段Presenter层的代码

 override fun requestData(isRefresh: Boolean, type: String) {
        val call: Call<BaseResponse<Data>> = RequestManager.getInstance().apiService.getData(type)

        call.enqueue(object : Callback<BaseResponse<Data>> {
            override fun onFailure(call: Call<BaseResponse<Data>>, t: Throwable) {
                view.responseData(false, hasMore = false, cards = null, isRefresh = isRefresh)
            }

            override fun onResponse(call: Call<BaseResponse<Data>>, response: Response<BaseResponse<Data>>) {
                if (response.body() != null) {
                    if (response.body()?.state == 200) {
                       view.responseChannelData(true, hasMore = hasMore, cards = response.body()?.data, isRefresh = isRefresh)
                    }
                }
            }
        })
    }
}

大体上这段代码是完成一个单次的网络请求,然后进行回调的过程,那么网络操作是一个耗时操作,我们不知道请求什么时候可以回来。

场景模拟:比如说现在是一个弱网,网络请求回调需要5秒才行。 用户等了2秒,有点不太耐烦,直接把界面关了。

那么由于回调没有完成,view被强持有,无法释放,等到网络回调完成会发生
1.内存泄漏,没有及时释放view
2.空指针,如果下面的逻辑没有写好,数据回来view不已再。(咋还有丝凄凉)

二、解决内存泄漏的思路

第一步:从现象找本质

从上述内存泄漏的原因来看,其实就是Presenter没有跟上view的生命周期。我们其实在view层消失后,即使释放资源,剪断引用链条就可以解决内存泄漏问题了。
那么现在的问题点空就变成
1.view层什么时候资源释放? (找到剪断时机)
2.presenter层怎么释放?(找到剪断方法)

第二步:通过Lifecycle来传递生命周期

说到这里,我不得不说一下。很多人喜欢让Presenter层也拥有onCreate、onDestroy 等等方法,然后让view层在自己的生命周期方法时候去调用Presenter层的方法。且不说Presenter作为一个业务处理层拥有这些方法是否美观。 就仅仅主动调用这一点其实就非常的麻烦,可以主动调用,那么就得承担“被多次调用的风险”和“未被调用的风险”。 在阅读代码的时候,我承认我的拳头硬了

说到Lifecyle就是简单的原理就是让P层去观察V层的生命周期,当V层生命周期发送改变,便可以监听到对应的状态。这样就将显式调用变成了隐式监听。(这里我就不展开说了)
1.首先是让Presenter继承LifecycleObserver接口
2.view层提供getLifecycle方法,让Presenter去观察。

第三步:通过autodispose来进行网络反注册,剪断引用链

简单的讲下个组件的原理:
AutoDispose在内部创建了ArchLifecycleObserver,采用Event.ON_ANY注解监听Lifecyc的生命周期

当Lifecycle发出ON_DESTROY事件时,ArchLifecycleObserver转发该事件给特定observer,该observer通过filter限定Event.ON_DESTROY事件通过
随后当Activity销毁时,Lifecycle发送事件给ArchLifecycleObserver,并调用onDispose方法取消对Lifecycle的监听。
最后回调至ObservableCreate在订阅时创建的CreateEmitter的dispose方法,将CreateEmitter本身赋值为DISPOSED,销毁observer实例.

三、简单粗暴,照着抄就完事了

第一步:搭建Mvp的底层抽象类

1.通过底层架构,把mvp的接口类搭建起来

View层的抽象类

interface IBaseView<T : IBasePresenter> {
    fun getPresenter(): T

    fun getLifecycle(): Lifecycle

    /**
     * 判断Activity、Fragment是否回收
     */
    fun isRecycled(): Boolean
}

Presenter层的抽象类

interface IBasePresenter
2.通过做一个简单的Activity抽象类为例
abstract class MvpBaseActivity<T : BasePresenter<*>> : Activity(), IBaseView<T> {
    private lateinit var mPresenter: T

    override fun getLifecycle(): Lifecycle {
        return super.getLifecycle()
    }

    @LayoutRes
    protected abstract fun getLayoutResId(): Int

    protected abstract fun initAll(savedInstanceState: Bundle?)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(getLayoutResId())
        initPresenter()?.let { mPresenter = it }
        initAll(savedInstanceState)
    }


    override fun isRecycled(): Boolean {
        return isFinishing || isDestroyed
    }

    //有的页面不需要Mvp所以不去实现
    open fun initPresenter(): T? {
        return null
    }

    override fun getPresenter(): T {
        if (!::mPresenter.isInitialized) throw IllegalStateException("please override initPresenter()")
        return mPresenter
    }
}
3.做一个简单的Presenter层抽象类
abstract class BasePresenter<T : IBaseView<*>>() : IBasePresenter, LifecycleObserver {
    lateinit var view: T
    lateinit var scopeProvider: LifecycleScopeProvider<Lifecycle.Event>

    constructor(view: T) : this() {
        this.view = view
        view.getLifecycle().addObserver(this)
        scopeProvider = AndroidLifecycleScopeProvider.from(view.getLifecycle(), Lifecycle.Event.ON_DESTROY)
        release(view)
    }

    private fun release(view: T) {
        view.getLifecycle().addObserver(object : TempLifecycleObserver() {
            override fun onDestroy() {
                view.getLifecycle().removeObserver(this)
            }
        })
    }
}

这样 我们就已经把生命周期悄悄的传递给了presenter,下面是如何简单使用(这里网络请求以Retrofit网络请求为例,不了解retrofit网络请求的同学请坐等我更新,或者去隔壁了解一下)

第二步:使用我们定义好的类

1.首先是契约层

class HomeContract {

    interface View : IBaseView<Presenter> {
        fun responseButtonText(isSuccess: Boolean, buttonText:String)
    }


    abstract class Presenter(view: View) : BasePresenter<View>(view) {
        abstract fun requestButtonText()
    }
}

2.下来是view层

class HomActivity : MvpBaseActivity<HomeContract.Presenter>(), HomeContract.View{
    
    private var demo_tv: TextView? = null

    override fun getLayoutResId(): Int {
        return R.layout.activity_home_live_channel
    }

    override fun initAll(savedInstanceState: Bundle?) {
        initView()
        initData()
    }

    override fun initPresenter(): HomeContract.Presenter? {
        return HomePresenter(this)
    }

    fun initView() {
        demo_tv = findViewById(R.id.demo_tv)
    }

    fun initData() {
        getPresenter().requestButtonText()
    }

    override fun responseButtonText(isSuccess: Boolean,buttonText:String) {
        if(isSuccess){
            demo_tv.text = buttonText;
        }else{
            Toast.makeText(context,"网络请求失败",Toast.LENGTH_LONG)
        }
    }
}

3.接下来是Presenter层

class HomePresenter(view: HomeContract.View) : HomeContract.Presenter(view) {

    override fun requestButtonText(isRefresh: Boolean, categoryId: String, type: String) {
        RequestManager.getInstance()
                .observableApiService
                .getButtonText()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .autoDisposable(scopeProvider)   //重点是这个
                .subscribe(object : SingleObserver<BaseResponse<ButtonData>> {
                    override fun onSubscribe(d: Disposable) {}

                    override fun onSuccess(response: BaseResponse<ButtonData>) {
                        if (response.state == 200) {
                            view.responseButtonText(true,response.buttonText)
                        }
                    }

                    override fun onError(e: Throwable) {
                         view.responseButtonText(false,"")
                    }
                })
    }
}

4.网络层回调定义
这里我们为啥用Single,因为这里只有单次的网络请求,如果回调后无二次网络请求,则使用Single即可

Single<BaseResponse<ButtonData>> getButtonText();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容