十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
一、为什么要使用DialogFragment:
创新互联是一家集网站建设,延吉企业网站建设,延吉品牌网站建设,网站定制,延吉网站建设报价,网络营销,网络优化,延吉网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
在程序开发中我们经常要做一些弹窗提醒,如果用自带的Dialog虽然能解决一部分,但强大的UI是不会给你这个机会的,各种自定义UI,时间选择器弹窗,巴拉巴拉~~~
使用DialogFragment就是能配合一些工具进行自定义,同时官方也是比较推荐使用,极大方便了开发,比如自定义一个baseDialogFragment.
二、内存泄漏的原因:
源码如下:
但一般在DialogFragment的源码默认实现了对dialog的取消和结束的监听:
根据源码分析,DialogFragment的dialog的变量Handler对DialogFragment持有。
网上有些分析是直接置为空,但是根本不能解决问题,在super.onActivityCreated(savedInstanceState)中仍然会handler先持有DialogFragment。
最终解决方案,重写onActivityCreated方法,并在super.onActivityCreated(savedInstanceState),修改dialog显示.
区别:
内存溢出就是要求分配的内存超出了系统能给的,系统不能满足需求,于是产生溢出。
内存泄漏是指向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果申请到的那块内存自己也不能再访问(也许把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。
一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出.
定义:
1.内存溢出 out of memory
是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
2.内存泄露 memory leak
是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
3.二者的联系
内存泄露最终会导致内存溢出
不是成功来得太慢,只是下的功夫还不够
最近在写一个关于贝塞尔曲线水波纹的小DEMO,发现关闭当前视图时报出内存泄漏的问题,检查下内存泄漏信息,经过排查发现是ValueAnimator的监听事件引起的,
先上解决方法:
在Activity/Fragment销毁时调用如下方法:
找到问题就好办了,查源码
检查发现在ValueAnimator start()方法中有个 addAnimationCallback(0); 调用,继续检查发现
这里就发现问题了, AnimationHandler.getInstance(); 这个是单例获取,所以在退出Activity时,ValueAnimator没有被释放,找到问题就好办了,就在Activity或者Fragment被销毁时,主动把动画监听取消掉。最后上一下我Demo的效果图 \得意
泄露信息如下:
====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS
References underlined with "~~~" are likely causes.
Learn more at .
Displaying only 1 leak trace out of 4 with the same signature
Signature: 645952c17f5aa992b39740a17b9afc2f3ccf5619
┬───
│ GC Root: System class
│
├─ android.os.AsyncTask class
│ Leaking: NO (a class is never leaking)
│ ↓ static AsyncTask.SERIAL_EXECUTOR
│ ~~~~~~~~~~~~~~~
├─ android.os.AsyncTask$SerialExecutor instance
│ Leaking: UNKNOWN
│ ↓ AsyncTask$SerialExecutor.mTasks
│ ~~~~~~
├─ java.util.ArrayDeque instance
│ Leaking: UNKNOWN
│ ↓ ArrayDeque.elements
│ ~~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ ↓ Object[].[0]
│ ~~~
├─ android.os.AsyncTask$SerialExecutor$1 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ AsyncTask$SerialExecutor$1.val$r
│ ~~~~~
├─ android.widget.TextView$3 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ TextView$3.this$0
│ ~~~~~~
├─ androidx.appcompat.widget.AppCompatEditText instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ View not part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mID = R.id.input_edit
│ View.mWindowAttachCount = 1
│ mContext instance of RoomMainActivity with mDestroyed = true
│ ↓ View.mContext
╰→ RoomMainActivity instance
Leaking: YES (ObjectWatcher was watching this because RoomMainActivity
received Activity#onDestroy() callback and Activity#mDestroyed is true)
key = cb388a5c-0ff2-452f-a9b0-6ea7d03a5105
watchDurationMillis = 37631
retainedDurationMillis = 32630
mApplication instance of ChatApplication
mBase instance of androidx.appcompat.view.ContextThemeWrapper
====================================
原因分析:
1、引用链结构:AsyncTask的SerialExecutor执行器引用了EditText对象,而EditText对象中的mContext引用到了RoomMainActivity中context,导致RoomMainActivity 无法销毁。
查看源码得知在TextView中有updateTextServicesLocaleAsync()方法,调用了AsyncTask.execute()向其中传入了匿名内部类Runnable,而持有了控件对象。
2、解决方法:因为在源码层面无法修改源码,在引用端切断引用链。
给EditText使用Application的上下文,在EditText使用的页面退出销毁时移除EditText控件,包括置空它的监听器、清除它的焦点。
import android.content.Context;
import android.os.Build;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.ViewGroup;
import androidx.appcompat.widget.AppCompatEditText;
/**
* 关于Android EditText导致的内存泄漏的问题
*/
public class NoMemoryLeakEditTextextends AppCompatEditText {
public NoMemoryLeakEditText(Context context) {
super(context.getApplicationContext());
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs) {
super(context.getApplicationContext(), attrs);
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context.getApplicationContext(), attrs, defStyleAttr);
}
public void clearMemoryLeak(TextWatcher watcher, ViewGroup container) {
clearFocus();
setOnTouchListener(null);
setOnClickListener(null);
setOnDragListener(null);
setOnKeyListener(null);
setOnLongClickListener(null);
setOnEditorActionListener(null);
if (Build.VERSION.SDK_INT = Build.VERSION_CODES.KITKAT_WATCH) {
setOnApplyWindowInsetsListener(null);
}
if (Build.VERSION.SDK_INT = Build.VERSION_CODES.M) {
setOnScrollChangeListener(null);
}
setOnFocusChangeListener(null);
removeTextChangedListener(watcher);
container.removeView(this);
}
}
最后在页面销毁的地方调用clearMemoryLeak方法
内存泄漏是指分配出去的内存无法回收了
内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存。应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。
内存溢出是指程序要求的内存,超出了系统所能分配的范围,从而发生溢出。
内存溢是指在一个域中输入的数据超过它的要求而且没有对此作出处理引发的数据溢出问题,多余的数据就可以作为指令在计算机上运行。
依赖库即可,重点在分析工具和分析方法:
debugImplementation'com.squareup.leakcanary:leakcanary-android:2.8.1'
分析工具:MAT 、AndroidStudioProfiler 和 自带分析工具;
这里先看一下Leaking的状态(YES、NO、UNKNOWN),NO表示没泄露、YES表示出现泄漏、UNKNOW表示可能泄漏。
具体学习资料: 学习资料
首先了解下Android中最重要的四大内存指标的概念
我们主要使用USS和PSS来衡量进程的内存使用情况
dumpsys meminfo命令展示的是系统整体内存情况,内存项按进程进行分类
查看单个进程的内存信息,命令如下
adb shell dumpsys meminfo [pid | packageName]
Objects中Views、Activities、AppContexts的异常可以判断有内存泄露,比如刚退出应用,查看Activites是否为0,如果不为0,则有Activity没有销毁。
具体用法直接参考大佬的资源即可,不赘述。
android studio 中Memory Profile的用法
接入LeakCanary,监控所有Activity和Fragment的释放,App所有功能跑一遍,观察是否有抓到内存泄露的地方,分析引用链找到并解决问题,如此反复,直到LeakCanary检查不到内存泄露。
adb shell dumpsys meminfo命令查看退出界面后Objects的Views和Activities数目,特别是退出App后数目为否为0。
打开Android Studio Memory Profiler,反复打开关闭页面多次,点击GC,如果内存没有恢复到之前的数值,则可能发生了内存泄露。再点击Profiler的垃圾桶图标旁的heap dump按钮查看当面内存堆栈情况,按包名找到当前测试的Activity,如果存在多份实例,则很可能发生了内存泄露。