内存泄漏分析

近些年在上学MVP架构情势中,一贯模糊与MVP格局真的能带来什么样,大家能从中获得什么。浅显地以来代码分层,易于重构和掩护,代码结构解析。但着实在利用进度中,感觉又是可有可无的东西。接下来是本人这一天学习查阅资料的体味。

Android项目性质优化中有个必须关怀的点是内存泄漏,下边篇幅不在细说内存泄漏工具检测,而在首要表明在实际上情形中造成内存泄漏的原由以及解决方案和MVP方式的利益。

  • 内存泄漏常见场景
    1.资源对象没停歇
    比如Cursor游标File文件等,我们在不行使的时候应该关闭它,以便它们的缓冲及时回收内存。面对这一状态自然要养成关闭资源的习惯,因为那种造成泄漏的是长日子多量操作意况下才会复现,会为之后的测试和排查带来不便软危机
    2.构造Adapter适配器时,没有行使缓存的contentView
    3.试着使用Application的Context代替Activity的Context
    Application的Context的生命周期维持整个应用,若在运用进程中有着了该Context,那么在回收进程中因为该Context的生命周期过长,会造成持有对象无法回收
    4.挂号没裁撤
    如广播、服务等,尽管程序甘休了,不过其余引用程序或然依旧对我们的次第的某部对象的引用,泄漏的内存仍不可以被回收。调用广播registerReceiver后回忆要调用unregisterReceiver
    5.汇集对象没清空
    若在单例中爱护集合对象,大家普通把部分对象引用参与到集结中,当我们不须要该对象时,须求把它的引用从集合中移除,防止集合对象占用过多内存
    6.内部类具有外部类导致
    在外表类中定义内部类,如定义一个线程或许Handler,当线程执行耗时操作时关闭Activity,重复该操作,由于线程持有Activity对象,导致Activity对象不能被回收。

线程耗时那种操作是心有余而力不足防止的,那时就可以选择MVP格局了,把耗时操作放入Presenter中实践,可以定义static静态Presenter,让其不持有Activity对象,那是一种缓解思路,但大家能够透过别人对MVP情势的包裹来优化内存泄漏的标题。

备注:AsyncTask和RxJava处理异步时,cancle恐怕unsubscribe仅是不触发onPostExecute或onNext。异步操作照旧在跑的,只是没打招呼回调而已,那些是原先的误区。所以那种内存消耗是无能为力幸免的,大家的优化点就在于幸免不须求的异步耗时请求

常备意义上Presenter都有持有View对象,而View常常是Activity和Fragment来饰演,那那不就是争论了啊?在Presenter会执行互连网请求那一个耗时操作,请求为止后会让View作出报告,那当Activity或然Fragment释放时由于Presenter持有对象那就会Acitivity只怕Fragment释放不了大概导致内存败露的暴发。那么怎么化解这一个标题吗?能够在View
onDestroy销毁时实施Presenter解绑View操作,让View置null,布告GC释放View。当View重新置于前台时让Presenter重新绑定。

文章转发自:Hi大头鬼_深刻浅出RxJava四-在Android中使用响应式编程
个人感觉CSDN上看起来字体不是很爽快,所以转发一下。

Nucles框架

上面会花一定篇幅来介绍MVP封装库Nucles,而Nucles是怎么啊,有哪些利益?

  • 特点计算
    1.它接济在View/Fragment/Activity的Bundle中保存/复苏Presenter的图景,一个Presenter可以保留它的伸手参数到bundles中,以便之后重启它们
    2.它同意一个View实例持有多少个Presenter对象
    3.飞跃达成View和Presenter的绑定
    4.提供线程的基类以便复用
    5.支撑在进程重启后,自动重新发起呼吁,在onDestroy方法中,自动退订RxJava订阅
    6.一定不难
  • 代码层计算

Paste_Image.png

RequiesPresenter:自定义注脚,方便工厂加工Presenter实例
PresenterStorage:Presenter存储单例,方便View重启复苏Presenter
RxPresenter:落成对事情的卷入,对事情做解绑操作等
PresenterLifecycleDelegate:Presenter生命周期委托,其中的法门对应View的生命周期。如onSaveInstanceState保存Presenter相关至View的bundlestate,以便onRestoreInstanceState时上涨
NucleusActivity/NucleusFragment/NucleusLayout:持有PresenterLifecycleDelegate对象,统一PresenterLifecycleDelegate管理Presenter生命周期
Delivery相关:涉及RxJava部分,平时是提议Presenter不直接操作View,Delivery落成Observable<数据源>->Observable<View,数据源>转换,动态操作View

通过一张图来作分析:

Paste_Image.png

<View,T>分别对应View对象和数据源。Observable<T>是如通过Retrofit互联网请求到的数据源操作。我们必要将数据源操作转换成对View和多少源两者的操作。从中而知大家须要封装个Delivery对象存放View和数据源,然后将互连网请求转换成对Delivery。最后subscribe订阅后让Delivery内部处理特定逻辑。那么View对象是从哪里获得到的啊,答案就在布局参数Observable<View>
view对象中,这一个目的是宣称在RxPresenter中的private final
BehaviorSubject<View> views = BehaviorSubject.create();

最后附上例子:
https://github.com/hhhhskfk/oschina-mvp

英文原稿
在第123篇中,我几乎介绍了RxJava是怎么采纳的。上边我会介绍怎么样在Android中动用RxJava。

RxJava介绍

BehaviorSubject:相当于Observable或许Subscriber,那么些效应是当被订阅后执行onNext执行具体操作后,会优首发送一个暗许值
SubscriptionList:Subscription列表,管理列表中订阅的铲除
Observable.first():仅在率先次订阅中实施
combineLatest():功效于眼下发射的数目项:即使Observable1发射了A并且Observable2发射了B和C,combineLatest()将会分组处理AB和AC

具体RxJava干货请看那里

RxAndroid


RxAndroid是RxJava的一个对准Android平台的增加。它含有了有的可见简化Android开发的工具。

率先,AndroidSchedulers提供了针对性Android的线程系统的调度器。需求在UI线程中运行某些代码?很粗略,只需要采纳AndroidSchedulers.mainThread():

retrofitService.getImage(url)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

一旦你早已创办了温馨的Handler,你可以采用HandlerThreadScheduler将一个调度器链接到你的handler上。

接着要介绍的就是AndroidObservable,它提供了跟多的意义来协作Android的生命周期。bindActivity()bindFragment()方式暗许使用AndroidSchedulers.mainThread()来施行观望者代码,那五个方法会在Activity可能Fragment停止的时候文告被观看者为止爆发新的音信。

AndroidObservable.bindActivity(this, retrofitService.getImage(url))
    .subscribeOn(Schedulers.io())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap);

自个儿要好也很欣赏AndroidObservable.fromBroadcast()主意,它同意你创设一个类似BroadcastReceiverObservable目的。上边的事例突显了什么样在互连网转移的时候被通报到:

IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
AndroidObservable.fromBroadcast(context, filter)
    .subscribe(intent -> handleConnectivityChange(intent));

最终要介绍的是ViewObservable,使用它可以给View添加了一些绑定。假若你想在历次点击view的时候都吸纳一个事件,可以运用ViewObservable.clicks(),可能你想监听TextView的始末变更,可以选取ViewObservable.text()

ViewObservable.clicks(mCardNameEditText, false)
    .subscribe(view -> handleClick(view));

Awesome-RxJava

RxJava resources

Retrofit


盛名的Retrofit库内置了对RxJava的协助。寻常调用发可以由此选拔一个Callback对象来得到异步的结果:

@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);

接纳RxJava,你可以一向重临一个Observable对象。

@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);

近来您能够轻易动用Observable对象了。你不但可以获取数据,还足以开展转换。
Retrofit对Observable的支撑使得它可以很简单的将多个REST请求结合起来。比如大家有一个伸手是得到照片的,还有一个请求是赢得元数据的,我们就可以将那多少个请求并发的发出,并且等待多少个结实都回到之后再做拍卖:

Observable.zip(
    service.getUserPhoto(id),
    service.getPhotoMetadata(id),
    (photo, metadata) -> createPhotoWithData(photo, metadata))
    .subscribe(photoWithData -> showPhoto(photoWithData));

在其次篇里本人显得过一个好像的例子(使用flatMap())。那里自个儿只是想浮现以下使用RxJava

  • Retrofit可以多多简单地组成多少个REST请求。

Blog

遗留代码,运行极慢的代码


Retrofit可以回去Observable对象,可是假设你使用的其他库并不支持那样如何做?可能说一个里边的内码,你想把她们转移成Observable的?有如何简单的格局没?

绝大部分时候Observable.just()Observable.from()
可以扶助您从遗留代码中创立 Observable 对象:

private Object oldMethod() { ... }

public Observable<Object> newMethod() {
    return Observable.just(oldMethod());
}

地点的例证中一经oldMethod()足足快是没有何难点的,然则假诺很慢呢?调用oldMethod()将会阻塞住他所在的线程。
为了缓解那么些难题,可以参见我一贯利用的章程 –
使用defer()来包装缓慢的代码:

private Object slowBlockingMethod() { ... }

public Observable<Object> newMethod() {
    return Observable.defer(() -> Observable.just(slowBlockingMethod()));
}

现在,newMethod()的调用不会阻塞了,除非你订阅再次来到的observable对象。

开发者前线翻译的一多重很赞的科目

生命周期


自家把最难的不分留在了最后。怎么样处理Activity的生命周期?首要就是三个难题:

  1. configuration改变(比如转屏)之后继续在此以前的Subscription
    比如您利用Retrofit发出了一个REST请求,接着想在listview中显示结果。就算在互联网请求的时候用户旋转了屏幕如何做?你当然想延续刚才的呼吁,不过怎么搞?
  2. Observable持有Context导致的内存走漏
    那几个标题是因为创设subscription的时候,以某种格局有所了context的引用,尤其是当你和view交互的时候,那太不难发生!如若Observable没有当即竣事,内存占用就会愈来愈大。
    噩运的是,没有银弹来解决那七个难题,不过那里有部分引导方案你可以参照。

第四个难题的化解方案哪怕使用RxJava内置的缓存机制,那样你就可以对同一个Observable对象实施unsubscribe/resubscribe,却毫无再行运行获得Observable的代码。cache()
(或者replay())会继续执行网络请求(甚至你调用了unsubscribe也不会终止)。那就是说你可以在Activity重新成立的时候从cache()的重回值中创立一个新的Observable对象。

Observable<Photo> request = service.getUserPhoto(id).cache();
Subscription sub = request.subscribe(photo -> handleUserPhoto(photo));

// ...When the Activity is being recreated...
sub.unsubscribe();

// ...Once the Activity is recreated...
request.subscribe(photo -> handleUserPhoto(photo));

瞩目,一遍sub是拔取的同一个缓存的伏乞。当然在哪个地方去存储请求的结果恐怕要你自个儿来做,和装有其余的生命周期相关的缓解方案一延虎,必须在生命周期外的某部地点贮存。(retained
fragment可能单例等等)。

其次个难题的化解方案即使在生命周期的某个时刻裁撤订阅。一个很普遍的情势就是应用CompositeSubscription来持有所有的Subscriptions,然后在onDestroy()或者onDestroyView()里打消所有的订阅。

private CompositeSubscription mCompositeSubscription
    = new CompositeSubscription();

private void doSomething() {
    mCompositeSubscription.add(
        AndroidObservable.bindActivity(this, Observable.just("Hello, World!"))
        .subscribe(s -> System.out.println(s)));
}

@Override
protected void onDestroy() {
    super.onDestroy();

    mCompositeSubscription.unsubscribe();
}

您可以在Activity/Fragment的基类里成立一个CompositeSubscription对象,在子类中应用它。

注意! 一旦你调用了
CompositeSubscription.unsubscribe(),那些CompositeSubscription对象就不可用了,
倘若你还想行使CompositeSubscription,就务须在成立一个新的对象了。

四个问题的化解方案都亟待添加额外的代码,要是何人有更好的方案,欢迎告诉本身。

有些科学的牵线操作符的稿子

总结


RxJava仍然一个很新的门类,RxAndroid更是。RxAndroid近年来还在外向开发中,也尚未多少好的事例。我打赌一年将来我的一部分提出就会被看做过时了。

一对毋庸置疑的翻译小说

部分规律分析的小说

Test

App

  • android-gfycat
    -Android application that loads gifs via gfycat for efficiency’s
    sake

  • JakeWharton/u2020
    -Jake大神的种类,里面有RxJava和Retrofit一起使用的例子

  • Avengers
    一个利用Retrofit+RxJava+MVP的app

  • TranslateApp
    一个运用
    MVP+Dagger2+RxJava+Retrofit的贯彻手机端『划词翻译』成效的App –
    咕咚翻译

  • AppPlus
    一个足以用于传送Apk文件,提取APK文件等的工具软件。

  • rx-android-architecture
    -Android中行使Rx的一种架构

  • android-boilerplate
    -使用RxJava+Retrofit+MVP的app,并了组合详细的测试用例

  • RxJavaApp
    -用于学习RxJava操作符的APP

Example

Library

Stackoverflow

Video

相关文章