(原创)自定义DialogFragment以及解决其内存泄漏问题

news2024/11/29 4:50:43

前言

日常开发中,dialog是常见的功能,我们时常需要弹出来一些弹框提示用户
今天就定义了一个方便的dialog基类BaseSimpleDialogFragment,
支持快速地显示一个dialog
主要功能有:
initAnimation:设置入场和出场动画
getGravity:设置dialog显示位置(屏幕上,中,下)
getCanceledOnTouchOutside:点击空白处关闭
getWindowWidth
getWindowHeight
getPaddingLeft:动态设置宽高和间距
整体来说比较简单,也方便扩展
创建dialog的时候只需要实现BaseSimpleDialogFragment即可
比如这样:

class MyDialog: BaseSimpleDialogFragment()  {

  override val layoutId: Int = R.layout.dialog_my_show

  companion object {
    @JvmStatic
    fun newInstance(): MyDialog {
      val dialog = MyDialog()
      dialog.arguments = Bundle().apply {
//      putParcelableArrayList(DATA, data)
      }
      return dialog
    }
  }


  override fun initData() {
//    data = arguments?.getParcelableArrayList(DATA) ?: return
  }

  override fun initView(view: View) {
    layoutView.findViewById<Button>(R.id.cancle).setOnClickListener {
      dismissAllowingStateLoss()
    }
  }
}

展示的时候几行代码就可以了:

      MyDialog.newInstance().apply {
        //传递数据
      }.show(supportFragmentManager)

源码

源码这边先贴出来:

abstract class BaseSimpleDialogFragment : DialogFragment() {

  abstract val layoutId: Int

  protected open fun initView(view: View) {
    //sonar
  }

  protected open fun initData() {
    //sonar
  }

  protected open fun initListener() {
    //sonar
  }

  lateinit var layoutView: View

  protected lateinit var mContext: Context


  override fun onAttach(context: Context) {
    super.onAttach(context)
    mContext = context
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    initAnimation()
  }


  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ): View? {
    layoutView = inflater.inflate(layoutId, container, false)
    return layoutView
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    initView(view)
    initListener()
    initData()
  }


  override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = super.onCreateDialog(savedInstanceState).apply {
      window?.run {
        decorView.setPadding(
          getPaddingLeft(),
          getPaddingTop(),
          getPaddingRight(),
          getPaddingBottom()
        )
        val wlp = attributes.apply {
          gravity = getGravity()
          width = getWindowWidth()
          height = getWindowHeight()
        }
        attributes = wlp
        setWindowParam(this)
      }

      setCanceledOnTouchOutside(getCanceledOnTouchOutside())
    }

    isCancelable = getCancelable()
    return dialog
  }

  protected open fun setWindowParam(window: Window) {
    //sonar 
  }

  protected open fun initAnimation() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      setStyle(STYLE_NORMAL, R.style.FragmentDialogStyleWithAni)
    } else {
      setStyle(STYLE_NORMAL, R.style.FragmentDialogStyle_Low_Level_WithAni)
    }
  }

  protected open fun getGravity(): Int {
    return Gravity.CENTER
  }

  protected open fun getWindowWidth(): Int {
    return WindowManager.LayoutParams.MATCH_PARENT
  }

  protected open fun getWindowHeight(): Int {
    return WindowManager.LayoutParams.WRAP_CONTENT
  }

  protected open fun getPaddingLeft(): Int {
    return 0
  }

  protected open fun getPaddingRight(): Int {
    return 0
  }

  protected open fun getPaddingTop(): Int {
    return 0
  }

  protected open fun getPaddingBottom(): Int {
    return 0
  }

  protected open fun getCanceledOnTouchOutside(): Boolean {
    return false
  }

  protected open fun getCancelable(): Boolean {
    return true
  }

  override fun dismiss() {
    dismissAllowingStateLoss()
  }

  open fun show(manager: FragmentManager) {
    show(manager, javaClass.simpleName)
  }


  override fun show(manager: FragmentManager, tag: String?) {
    try {
      super.show(manager, tag)
    } catch (e: Exception) {
      Log.e("print", "show: $e")
    }
  }

}

用到的style:


  <style name="FragmentDialogStyleWithAni" parent="FragmentDialogStyle">
    <item name="android:windowAnimationStyle">@style/DialogAnimation</item>
  </style>


  <style name="FragmentDialogStyle_Low_Level_WithAni" parent="FragmentDialogStyle_Low_Level">
    <item name="android:windowAnimationStyle">@style/DialogAnimation</item>
  </style>

  <style name="DialogAnimation" parent="@android:style/Animation.Dialog">
    <item name="android:windowEnterAnimation">@anim/push_ani_up_in</item>
    <item name="android:windowExitAnimation">@anim/push_ani_down_out</item>
  </style>

  <style name="FragmentDialogStyle_Low_Level" parent="android:Theme.Holo.Light.Dialog">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowFrame">@null</item>
    <item name="android:backgroundDimEnabled">true</item>
    <item name="android:windowIsTranslucent">false</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowContentOverlay">@null</item>
  </style>

  <style name="FragmentDialogStyle" parent="Base.AlertDialog.AppCompat.Light">
    <!--点击窗口外是否消失-->
    <item name="android:windowCloseOnTouchOutside">true</item>
    <!-- 背景颜色及透明程度 -->
    <item name="android:windowBackground">@android:color/transparent</item>

    <!-- 是否半透明 -->
    <item name="android:windowIsTranslucent">false</item>
    <!-- 是否没有标题 -->
    <item name="android:windowNoTitle">true</item>
    <!-- 是否浮现在activity之上 设置成false则match_parent可以全屏-->
    <item name="android:windowIsFloating">true</item>
  </style>

还有两个默认的进入和退出动画

<?xml version="1.0" encoding="UTF-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
  <translate
    android:duration="500"
    android:fromYDelta="0"
    android:toYDelta="100%p" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
  <translate
    android:duration="500"
    android:fromYDelta="100%p"
    android:toYDelta="0" />
</set>

内存泄漏

在使用这个自定义的dialog的时候
我发现退出页面时
LeakCanary 会在dialog dismiss后报内存泄漏
大概像这样:
在这里插入图片描述
代码很简单,贴出来:

class MainActivity : AppCompatActivity() {

  lateinit var mydialog: MyDialog
  
  @SuppressLint("MissingInflatedId")
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    findViewById<Button>(R.id.showbtn).setOnClickListener {
      mydialog = MyDialog.newInstance().apply {
        //传递数据
      }
      mydialog.show(supportFragmentManager)
    }
  }
}

dialog内部有按钮,点击就关闭dialog:

    layoutView.findViewById<Button>(R.id.cancle).setOnClickListener {
      dismissAllowingStateLoss()
    }

不知道大家看出来原因没有
看LeakCanary 日志,告诉我的是dialogFragment 收到了onDestroy的回调了。
也就是被销毁,那么gc就应该回收掉该fragment对象。
但是呢当前界面还持有该对象的引用造成了内存泄漏。

我们点进dismissAllowingStateLoss的源码
在这里插入图片描述
可以看到:dismissAllowingStateLoss应该是要去remove这个fragment,
但是如果activity持有的话,就无法被内存回收了,从而导致了内存泄漏

解决

解决办法也很简单,提供几种方法:
1:简单粗暴,dismiss的时候,把Activity的引用置位null
首先我们的kotlin代码就要改下:

  var mydialog: MyDialog?=null

然后dismiss的时候,把Activity的引用置位null

    mydialog=null
    mydialog?.dismiss()

2:创建一个一次性的对象来使用,也就是局部变量,让当前界面不再全局持有该dialog对象。

    findViewById<Button>(R.id.showbtn).setOnClickListener {
      var mydialog = MyDialog.newInstance().apply {
        //传递数据
      }
      mydialog.show(supportFragmentManager)
    }

3:弱引用dialog,利用弱引用的特性,确保内存可以顺利回收

class MainActivity : AppCompatActivity() {

  lateinit var mydialog: WeakReference<MyDialog>

  @SuppressLint("MissingInflatedId")
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    findViewById<Button>(R.id.showbtn).setOnClickListener {
      mydialog = WeakReference(MyDialog.newInstance().apply {
        //传递数据
      })
      mydialog.get()?.show(supportFragmentManager)
    }

  }
}

关于自定义DialogFragment以及解决内存泄漏的问题,就介绍到这了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/793052.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【青书学堂】管理学基础(直播课) 第一学期 考试

【青书学堂】计算机组装与维护(直播课) 第一学期 考试 标题最终成绩:83.34 分 注意:答案仅供参考 第1题 单选题 梅奥的霍桑试验表明( )。 A:非正式组织对组织目标的达成是有害的 B:非正式组织对组织目标的达成是有益的 C:企业应采取一切措施来取缔非正式组织 D:企业应该…

【Unity3D日常开发】Unity3D中比较string字符串的常用方法

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 字符串string的比较有很多方法&#xff0c;比如&#xff1a; …

【动态规划】简单多状态

文章目录 动态规划&#xff08;简单多状态&#xff09;1. 按摩师2. 打家劫舍 ||3. 删除并获得点数4. 粉刷房子5. 最佳买卖股票时机含冷冻期6. 买卖股票的最佳时机含手续费7. 买卖股票的最佳时机 |||8. 买卖股票的最佳时机 IV 动态规划&#xff08;简单多状态&#xff09; 1. 按…

dp算法篇Day12

“我悲喜都&#xff0c;只换来这一场无声的野火。” 56、完全平方数 (1) 题目解析 ​​​​​​ 把题目解释到了这个份上&#xff0c;你很难不把思路转移到考虑 "背包问题上"。 (2) 算法原理 class Solution { public:int numSquares(int n) {int m sqrt(n);vec…

【Python基础】VS2019中使用Python及安装Python包

【Python基础】VS2019中使用Python及安装Python包 文章目录 前言一、VS2019中安装Python环境二、Python环境变量配置三、安装Python包总结 前言 要使用Python语言来写一些程序&#xff0c;使用哪个IDE是个问题&#xff0c;若是专业开发Python&#xff0c;PyCharm无疑是最佳选择…

RTI无线电层析成像Matlab仿真数据生成

文章目录 概述初始化环境参数逆面积椭圆模型 概述 无线电层析成像是一种通过获取一定区域内多对相对固定的无线通信节点间的某种测量数据后,按照一定的数学处理方法,对区域内的障碍物目标以图像的形式 展现出来的成像技术。 开山之作&#xff1a; J. Wilson and N. Patwari, …

【小白必看】Python爬虫实战:获取阴阳师网站图片并自动保存

文章目录 前言导入模块伪装自己发送请求获取地址列表获取所有背景的地址创建文件夹保存图片文件完整代码运行效果部分图片展示结束语 前言 本文介绍了一个使用Python编写的程序&#xff0c;用于获取指定网页的背景图片并保存到本地。在程序中使用了requests模块发送HTTP请求&a…

FCPX插件-15组金色华丽粒子特效闪耀动画 Awards Backgrounds

Awards Backgrounds是fcpx上一个很棒的电影级效果插件&#xff0c;Awards Backgrounds 包含15组金色华丽粒子特效闪耀动画&#xff0c;可以为您的作品创建豪华的背景或叠加特效&#xff01;包含各种带有可编辑颜色的下落闪闪发光粒子的场景。用于展示奖项提名者、优雅的表演、祝…

【C++】开源:Redis数据库配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Redis数据库配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c…

Emacs之改造最快文本搜索工具ripgrep(一百一十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

微服务远程调用openFeign简单回顾(内附源码示例)

目录 一. OpenFeign简介 二. OpenFeign原理 演示使用 provider模块 消费者模块 配置全局feign日志 示例源代码: 一. OpenFeign简介 OpenFeign是SpringCloud服务调用中间件&#xff0c;可以帮助代理服务API接口。并且可以解析SpringMVC的RequestMapping注解下的接口&#x…

Android12之快速查找静态注册jni函数方法(一百六十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

C++初阶--C++入门

目录 前言C关键字命名空间命名空间的定义命名空间的使用加命名空间名称及作用域限定符使用using namespace 命名空间名称引入使用using将命名空间中的成员引入 C的输入与输出缺省参数全缺省半缺省参数 函数重载参数类型不同参数个数不同参数类型顺序不同 引用引用特性 常引用使…

可视化bi工具datart部署到k8s

目录 1. 前言 2. 安装 2.1 mysql初始化 2.2 datart配置文件持久化 2.3 文件挂载 2.4 部署 3. 登录及创建管理帐号 1. 前言 datart 是新一代数据可视化开放平台&#xff0c;支持各类企业数据可视化场景需求&#xff0c;如创建和使用报表、仪表板和大屏&#xff0c;进行可视…

Python面向对象(三)(继承、封装)

面向对象的三大特性 面向对象编程&#xff0c;是许多编程语言都支持的一种编程思想。 简单理解是&#xff1a;基于模板&#xff08;类&#xff09;去创建实体&#xff08;对象&#xff09;&#xff0c;使用对象完成功能开发。 面向对象包含3大主要特性&#xff1a; 封装 封…

ssl证书安装后还是显示不安全怎么办?解决办法来了

ssl证书安装后还是显示不安全怎么办&#xff1f;一般网站在部署安装ssl证书之后&#xff0c;浏览器的不安全显示就会消除。但有时候&#xff0c;在证书安装完成后&#xff0c;浏览器仍然会出现不安全提示&#xff0c;这时候我们可以按照以下的步骤一一排查解决。 1、确认安装的…

numpy广播机制介绍

广播 广播机制的意义&#xff1a;广播描述了在算术运算期间NumPy如何处理具有不同形状的数组。受某些约束条件的限制&#xff0c;较小的数组会在较大的数组中“广播”&#xff0c;以便它们具有兼容的形状。 在对两个数组进行操作时&#xff0c;NumPy按元素对它们的形状进行比…

UE5.1移动端PreintegratedSkinBxDF解析

Part 1 头文件 MobileBasePassPixelShader.usf 主要看Main函数&#xff1a; #if MOBILE_MULTI_VIEWResolvedView ResolveView(BasePassInterpolants.MultiViewId); #elseResolvedView ResolveView(); #endif这玩意Shader文件找不到&#xff0c;感觉是个全局变量的东西。万幸…

【JDK新特性】必会_Stream API

【必看】原创声明&#xff1a;转载请注明作者 & 文章来源&#xff1a;都在用Stream流&#xff1f; ​​​​​​​ hello&#xff0c;我是小索奇&#xff0c;这次讲解JDK 8新特性的重点&#xff01;Stream流&#xff0c;到后期学习框架时候你会发现大量的Stream流出现&…

波奇学C++:写实拷贝,_buf数组

我们知道当自定义对象如果在堆上开空间&#xff0c;那么拷贝构造时会深拷贝&#xff0c;深拷贝会加大内存开销&#xff0c;用写实拷贝(也叫延迟拷贝)可以在深浅拷贝中取得平衡。 浅拷贝问题&#xff1a; 析构两次 一个对象修改会影响另一个 用引用计数来解决析构问题 拷贝构造时…