当前位置: 首页>前端>正文

Android达到内存阈值时开始回收内存到zram android内存溢出和内存泄漏

Activity生命周期

onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy()

Android内存泄露及管理

(1)内存溢出(OOM)和内存泄露(对象无法被回收)的区别。 

(2)引起内存泄露的原因

(3) 内存泄露检测工具 ------>LeakCanary

内存溢出 out of memory:是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。内存溢出通俗的讲就是内存不够用。

内存泄露 memory leak:是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光

内存泄露原因:

一、Handler 引起的内存泄漏

解决:将Handler声明为静态内部类,就不会持有外部类SecondActivity的引用,其生命周期就和外部类无关,

如果Handler里面需要context的话,可以通过弱引用方式引用外部类

二、单例模式引起的内存泄漏。

解决:Context是ApplicationContext,由于ApplicationContext的生命周期是和app一致的,不会导致内存泄漏

三、非静态内部类创建静态实例引起的内存泄漏。

解决:把内部类修改为静态的就可以避免内存泄漏了

四、非静态匿名内部类引起的内存泄漏。

解决:将匿名内部类设置为静态的。

五、注册/反注册未成对使用引起的内存泄漏。

注册广播接受器、EventBus等,记得解绑。

六、资源对象没有关闭引起的内存泄漏。

在这些资源不使用的时候,记得调用相应的类似close()、destroy()、recycler()、release()等方法释放。

七、集合对象没有及时清理引起的内存泄漏。

通常会把一些对象装入到集合中,当不使用的时候一定要记得及时清理集合,让相关对象不再被引用。

Fragment与Fragment、Activity通信的方式

1.直接在一个Fragment中调用另外一个Fragment中的方法

2.使用接口回调

3.使用广播

4.Fragment直接调用Activity中的public方法

Android系统启动流程

  • 1、电源和系统:当电源时引导芯片从预定义的地方启动引导到ROM启动程序,开始执行加载,然后启动执行。
  • 2、引导程序BootLoader:BootLoader是在Android系统开始运行前的一个小程序,主要用于把系统OS拉起来并运行。
  • 3、Linux内核:当内核启动时,启动缓存、被告知、计划。当其完成系统设置时,会先在系统文件中寻找init.rc文件,并启动init进程。
  • 4、init进程启动:和启动属性服务,并且启动Zygote进程。
  • 5、 Zy得到了启动进程:创建JVM并自己注册Socket,创建服务器端,启动SystemServer进程。
  • 6、System进程启动:启动Binder线程池和SystemServiceManager,启动各种系统服务。
  • 7、Launcher启动:SystemServer启动Launcher启动后启动系统启动AMS应用程序的启动启动到系统安装应用的快捷方式被显示到桌面上。

打开activity流程

Android达到内存阈值时开始回收内存到zram android内存溢出和内存泄漏,Android达到内存阈值时开始回收内存到zram android内存溢出和内存泄漏_外部类,第1张

 

View的事件分发机制

点击事件产生后,首先传递给Activity的dispatchTouchEvent方法,通过PhoneWindow传递给DecorView,然后再传递给根ViewGroup,进入ViewGroup的dispatchTouchEvent方法,执行onInterceptTouchEvent方法判断是否拦截,再不拦截的情况下,此时会遍历ViewGroup的子元素,进入子View的dispatchToucnEvent方法,如果子view设置了onTouchListener,就执行onTouch方法,并根据onTouch的返回值为true还是false来决定是否执行onTouchEvent方法,如果是false则继续执行onTouchEvent,在onTouchEvent的Action Up事件中判断,如果设置了onClickListener ,就执行onClick方法。

View的加载流程

View随着Activity的创建而加载,startActivity启动一个Activity时,在ActivityThread的handleLaunchActivity方法中会执行Activity的onCreate方法,这个时候会调用setContentView加载布局创建出DecorView并将我们的layout加载到DecorView中,当执行到handleResumeActivity时,Activity的onResume方法被调用,然后WindowManager会将DecorView设置给ViewRootImpl,这样,DecorView就被加载到Window中了,此时界面还没有显示出来,还需要经过View的measure,layout和draw方法,才能完成View的工作流程。我们需要知道View的绘制是由ViewRoot来负责的,每一个DecorView都有一个与之关联的ViewRoot,这种关联关系是由WindowManager维护的,将DecorView和ViewRoot关联之后,ViewRootImpl的requestLayout会被调用以完成初步布局,通过scheduleTraversals方法向主线程发送消息请求遍历,最终调用ViewRootImpl的performTraversals方法,这个方法会执行View的measure layout 和draw流程

Activity与Fragment之间生命周期比较

Fragment生命周期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach

切换到该Fragment:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume

按下Power键:onPause->onSaveInstanceState->onStop

点亮屏幕解锁:onStart->onRestoreInstanceState->onResume

切换到其他Fragment: onPause->onStop->onDestoryView

切回到该Fragment: onCreateView->onActivityCreated->onStart->onResume

退出应用:onPause->onStop->onDestoryView->onDestory->onDetach

Activity的四种启动模式对比

1)standard:标准启动模式(默认),每启动一次Activity,都会创建一个实例,即使从ActivityA startActivity ActivityA,也会再次创建A的实例放于栈顶,当回退时,回到上一个ActivityA的实例。

2) singleTop:栈顶复用模式,每次启动Activity,如果待启动的Activity位于栈顶,则不会重新创建Activity的实例,即不会走onCreate->onStart,会直接进入Activity的onPause->onNewIntent->onResume方法

3) singleInstance: 单一实例模式,整个手机操作系统里只有一个该Activity实例存在,没有其他Actvity,后续请求均不会创建新的Activity。若task中存在实例,执行实例的onNewIntent()。应用场景:闹钟、浏览器、电话

  1. singleTask:栈内复用,启动的Activity如果在指定的taskAffinity的task栈中存在相应的实例,则会把它上面的Activity都出栈,直到当前Activity实例位于栈顶,执行相应的onNewIntent()方法。如果指定的task不存在,创建指定的taskAffinity的task,taskAffinity的作用,进入指写taskAffinity的task,如果指定的task存在,将task移到前台,如果指定的task不存在,创建指定的taskAffinity的task. 应用场景:应用的主页面

内存泄漏的场景和解决办法

  1. 非静态内部类的静态实例
    非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,就会长期的维持着外部类的引用,组织被系统回收,解决办法是使用静态内部类
    2.多线程相关的匿名内部类和非静态内部类
    匿名内部类同样会持有外部类的引用,如果在线程中执行耗时操作就有可能发生内存泄漏,导致外部类无法被回收,直到耗时任务结束,解决办法是在页面退出时结束线程中的任务
    3.Handler内存泄漏
    Handler导致的内存泄漏也可以被归纳为非静态内部类导致的,Handler内部message是被存储在MessageQueue中的,有些message不能马上被处理,存在的时间会很长,导致handler无法被回收,如果handler是非静态的,就会导致它的外部类无法被回收,解决办法是1.使用静态handler,外部类引用使用弱引用处理2.在退出页面时移除消息队列中的消息
    4.Context导致内存泄漏
    根据场景确定使用Activity的Context还是Application的Context,因为二者生命周期不同,对于不必须使用Activity的Context的场景(Dialog),一律采用Application的Context,单例模式是最常见的发生此泄漏的场景,比如传入一个Activity的Context被静态类引用,导致无法回收
    5.静态View导致泄漏
    使用静态View可以避免每次启动Activity都去读取并渲染View,但是静态View会持有Activity的引用,导致无法回收,解决办法是在Activity销毁的时候将静态View设置为null(View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity)
    6.WebView导致的内存泄漏
    WebView只要使用一次,内存就不会被释放,所以WebView都存在内存泄漏的问题,通常的解决办法是为WebView单开一个进程,使用AIDL进行通信,根据业务需求在合适的时机释放掉
    7.资源对象未关闭导致
    如Cursor,File等,内部往往都使用了缓冲,会造成内存泄漏,一定要确保关闭它并将引用置为null
    8.集合中的对象未清理
    集合用于保存对象,如果集合越来越大,不进行合理的清理,尤其是入股集合是静态的
    9.Bitmap导致内存泄漏
    bitmap是比较占内存的,所以一定要在不使用的时候及时进行清理,避免静态变量持有大的bitmap对象
    10.监听器未关闭
    很多需要register和unregister的系统服务要在合适的时候进行unregister,手动添加的listener也需要及时移除

说下冷启动与热启动是什么,区别,如何优化,使用场景等。

app冷启动: 当应用启动时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用, 这个启动方式就叫做冷启动(后台不存在该应用进程)。冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。

app热启动: 当应用已经被打开, 但是被按下返回键、Home键等按键时回到桌面或者是其他程序的时候,再重新打开该app时, 这个方式叫做热启动(后台已经存在该应用进程)。热启动因为会从已有的进程中来启动,所以热启动就不会走Application这步了,而是直接走MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个MainActivity就行了,而不必创建和初始化Application

冷启动的流程
当点击app的启动图标时,安卓系统会从Zygote进程中fork创建出一个新的进程分配给该应用,之后会依次创建和初始化Application类、创建MainActivity类、加载主题样式Theme中的windowBackground等属性设置给MainActivity以及配置Activity层级上的一些属性、再inflate布局、当onCreate/onStart/onResume方法都走完了后最后才进行contentView的measure/layout/draw显示在界面上

冷启动的生命周期简要流程:
Application构造方法 –> attachBaseContext()–>onCreate –>Activity构造方法 –> onCreate() –> 配置主体中的背景等操作 –>onStart() –> onResume() –> 测量、布局、绘制显示

冷启动的优化主要是视觉上的优化,解决白屏问题,提高用户体验,所以通过上面冷启动的过程。能做的优化如下:

  1. 减少onCreate()方法的工作量
  2. 不要让Application参与业务的操作
  3. 不要在Application进行耗时操作
  4. 不要以静态变量的方式在Application保存数据
  5. 减少布局的复杂度和层级
  6. 减少主线程耗时

ANR的原因

1.耗时的网络访问
2.大量的数据读写
3.数据库操作
4.硬件操作(比如camera)
5.调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候
6.service binder的数量达到上限
7.system server中发生WatchDog ANR
8.service忙导致超时无响应
9.其他线程持有锁,导致主线程等待超时
10.其它线程终止或崩溃导致主线程一直等待

Serializable与Parcable的区别?

1.Serializable (java 自带)
方法:对象继承 Serializable类即可实现序列化,就是这么简单,也是它最吸引我们的地方
2.Parcelable(Android专用):Parcelable方式的实现原理是将一个完整的对象进行分解,用起来比较麻烦

1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。

2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。

3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性,在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。

4)android上应该尽量采用Parcelable,效率至上,效率远高于Serializable


https://www.xamrdz.com/web/2mh1957623.html

相关文章: