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

前言

日常开发中,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.mfbz.cn/a/45849.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C进阶】指针进阶(1)_二次复习版

目录 1. 字符指针 1.1常量字符串的修改 加上const解决问题 打印常量字符串 1.2数组存放的字符串 1.3例题:数组创建与常量池的区别 2. 指针数组 2.1字符指针数组 2.2整型指针数组 2.3使用3个一维数组,模拟实现一个二维数组 2.4例题: 3.数组指针 3.1 数组指针的定义…

同步网盘使用中的五大突出优势

同步网盘是一种流行的云存储解决方案&#xff0c;它可以将您本地计算机上的文件与云端存储空间同步&#xff0c;以保证文件的备份和访问。那么&#xff0c;同步网盘使用中的突出优势是什么呢&#xff1f;下面就为您详细介绍。 一、数据备份 同步网盘最大的优势之一就是可以自动…

错误解决:Failed to create Spark client for Spark session

错误解决&#xff1a;Failed to create Spark client for Spark session "Failed to create Spark client for Spark session"的错误通常表示无法为Spark会话创建Spark客户端。这可能是由于以下一些常见问题导致的&#xff1a; Spark配置错误&#xff1a;请检查Spar…

智慧园区楼宇合集:数字孪生管控系统

智慧园区是指将物联网、大数据、人工智能等技术应用于传统建筑和基础设施&#xff0c;以实现对园区的全面监控、管理和服务的一种建筑形态。通过将园区内设备、设施和系统联网&#xff0c;实现数据的传输、共享和响应&#xff0c;提高园区的管理效率和运营效益&#xff0c;为居…

ubuntu开机自启动

ubuntu开机自启动 1、建一个test.sh脚本&#xff0c;并写入 #!/bin/sh gnome-terminal -x bash -c ‘cd /home/文件路径/;python3 main.py’ exit 0 2、:wq!保存 3、创建rc-local.service文件&#xff08;sudo vim /etc/systemd/system/rc-local.service&#xff09;&#xf…

一次线上OOM问题的个人复盘

我们一个java服务上线后&#xff0c;偶尔会发生内存OOM(Out Of Memory)问题&#xff0c;但由于OOM导致服务不响应请求&#xff0c;健康检查多次不通过&#xff0c;最后部署平台kill了java进程&#xff0c;这导致定位这次OOM问题也变得困难起来。 最终&#xff0c;在多次review代…

shell实现数据库分库分表备份

#!/bin/bash2 3 backup/backup/db #存放数据库的位置4 nodatabasesinformation_schema|mycat|performance_schema|sys|mysql #要过滤的数据库5 6 mysql -uroot -predhat -e "show databases" -N | egrep -v "${nodatabases}" > dbname #将数据库存放在…

跨境出海企业,如何防范恶意退货欺诈

很多出海企业遭遇到过恶意退货事件。 2021年&#xff0c;某跨境商家在海外电商平台运营超过13年。有一次&#xff0c;有个海外买家买了一台二手的数码摄像机。在买家收到货后&#xff0c;却声称商品备在使用了45分钟之后便自动关机&#xff0c;且不能继续充电。该商家很肯定产…

裁员 10%,暴跌 14%,这家 IT 独角兽正在被抛弃!

流量一跌再跌&#xff0c;Stack Overflow 简直被狠狠地上了一课&#xff01; 3 月份 Stack Overflow 的流量下降了近 14%。该公司的 CEO 压力空前&#xff0c;甚至昨天决定裁员 10%&#xff01; 平均每月下降6%&#xff0c;上月直接跌了近14% 开发人员越来越多地从 AI 聊天机器…

Audio2Face

1:下载链接。 Omniverse Enterprise 许可和定价 | NVIDIA 2:安装。 audio2face ue插件 教程&#xff1a; 1&#xff1a;【青松微课堂】Audio2Face数字人工作流&#xff1a;软件的下载安装与UI介绍 【青松微课堂】Audio2Face数字人工作流&#xff1a;软件的下载安装与UI介绍_…

C++类——Vector容器的模拟实现

目录 一.vector类的成员变量&#xff1a; 二.Vector类的初始化方式&#xff1a; 三.vector的基本成员函数 四.vector类的增删查改&#xff1a; 指针失效问题&#xff1a; insert(): 代码解析&#xff1a; erase(): 代码解析&#xff1a; 所以erase()函数的正确写法:…

苹果iOS 16.6 RC发布:或为iPhone X/8系列养老版本

今天苹果向iPhone用户推送了iOS 16.6 RC更新(内部版本号&#xff1a;20G75)&#xff0c;这是时隔两个月的首次更新。 按照惯例RC版基本不会有什么问题&#xff0c;会在最近一段时间内直接变成正式版&#xff0c;向所有用户推送。 需要注意的是&#xff0c;鉴于iOS 17正式版即将…

Linux系列---【Ubuntu 20.04安装KVM】

Ubuntu 20.04安装KVM 一、安装kvm 1.安装kvm sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils 2. 将当前用户添加至libvirt 、 kvm组 sudo adduser $USER libvirt sudo adduser $USER kvm 3.验证安装 virsh list --all 4.启动libvert sudo syst…

el-select和el-checkBox实现下拉菜单全选功能

el-select 和 el-checkbox 实现下拉菜单全选功能 示例代码&#xff1a; <el-selectpopper-class"select-container"v-model"ids"placeholder"请选择目标":multiple-limit"20"multiplefilterablecollapse-tagsclass"wd400&qu…

自动驾驶数据标注有哪些?

自动驾驶汽车&#xff1a;人工智能(AI)的焦点 人工智能驱动汽车解决方案的市场规模预计到 2025年将增长十倍以上&#xff0c;提升车内体验的商机领域以及 AI 模型的无偏见训练数据的重要性。在本篇中&#xff0c;我们将介绍车外体验的关键组成部分&#xff0c;以及自动驾驶数据…

【Zerotier】通过docker自建PLANET服务器

在如今全球互联的时代&#xff0c;我们对于互联网的依赖程度越来越高。然而&#xff0c;传统的网络连接方式在某些情况下可能会受到一些限制&#xff0c;例如局域网的范围限制、防火墙的阻断或者设备所处的多层NAT等。但是&#xff0c;现在有一个名为ZeroTier的工具出现了&…

工程安全监测无线振弦采集仪在建筑物的应用分析

工程安全监测无线振弦采集仪在建筑物的应用分析 工程安全监测无线振弦采集仪是一种在建筑物中应用的重要设备。它通过无线采集建筑物内部的振动信息&#xff0c;对建筑物的安全性进行监测和评估&#xff0c;为建筑物的施工和使用提供了可靠的技术支持。本文将详细介绍工程安全…

了解 3DS MAX 3D摄像机跟踪设置:第 2 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 转到合成>新合成以创建新合成。 将“宽度”和“高度”值分别设置为 1280 和 720。将帧速率设置为 25&#xff0c;将持续时间设置为 12 秒。单…

C#中未能找到为main方法指定的XXX.Program怎么解决

有时在修改项目名称后&#xff0c;报错未能找到为main方法指定的XXX.Program 解决办法&#xff1a; 点击进入项目属性&#xff0c;将启动对象设置为空或者你要指定的XXX.Program&#xff08;改名后的&#xff09;

Redis两种持久化机制RDB和AOF详解(面试常问,工作常用)

redis是一个内存数据库&#xff0c;数据保存在内存中&#xff0c;但是我们都知道内存的数据变化是很快的&#xff0c;也容易发生丢失。幸好Redis还为我们提供了持久化的机制&#xff0c;分别是RDB(Redis DataBase)和AOF(Append Only File)。 在这里假设你已经了解了redis的基础…