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

news/2024/7/24 15:05:54/

前言

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

class MyDialog: BaseSimpleDialogFragment()  {override val layoutId: Int = R.layout.dialog_my_showcompanion object {@JvmStaticfun 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: Intprotected open fun initView(view: View) {//sonar}protected open fun initData() {//sonar}protected open fun initListener() {//sonar}lateinit var layoutView: Viewprotected lateinit var mContext: Contextoverride 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 = wlpsetWindowParam(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"><translateandroid: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"><translateandroid: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=nullmydialog?.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.ppmy.cn/news/982093.html

相关文章

汇编调用C语言定义的全局变量

在threadx移植中&#xff0c;系统的systick通过了宏定义的方式定义&#xff0c;很难对接库函数的时钟频率&#xff0c;不太利于进行维护 所以在C文件中自己定义了一个systick_Div的变量&#xff0c;通过宏定义方式设定systick的时钟频率 在汇编下要加载这个systick分频系数 …

Spring AOP 中,切点有多少种定义方式?

在 Spring AOP 中&#xff0c;我们最常用的切点定义方式主要是两种&#xff1a; 使用 execution 进行无侵入拦截。使用注解进行拦截。 这应该是是小伙伴们日常工作中使用最多的两种切点定义方式了。但是除了这两种还有没有其他的呢&#xff1f;今天松哥就来和大家聊一聊这个话…

KBYCMS模板文件创建操作说明

创建模板 首先安装“一键生成应用”插件,来帮助我们快速的创建插件。 操作说明 1.安装 2.进入生成界面,填写模板信息,点击提交 3. 生成效果 静态资源目录 当然你也可以手动创建 模板目录 响应式主题 template 模板安装目录 ├─admin 后台模板目录 ├─index 前台…

如何正确使用npm常用命令

npm常用命令&#xff1a; 官方文档&#xff1a;CLI Commands | npm Docs 1. npm -v&#xff1a;查看 npm 版本 2. npm init&#xff1a;初始化后会出现一个 Package.json 配置文件&#xff0c;可以在后面加上 -y&#xff0c;快速跳到问答界面 3. npm install&#xff1a;会…

自定义数据类型

一、结构体的定义与使用 1. 定义结构体类型 结构体允许将不同类型的数据元素组合在一起形成一种新的数据类型 结构体类型声明一般放在程序文件开头&#xff0c;此时这个声明是全局的。 结构体类型声明也可以放到函数内部&#xff0c;此时这个声明是局部的。 &#xff08;1&…

【MySQL】SQL优化(九)

&#x1f697;MySQL学习第九站~ &#x1f6a9;本文已收录至专栏&#xff1a;MySQL通关路 ❤️文末附全文思维导图&#xff0c;感谢各位点赞收藏支持~ 一.插入数据 (1) 小规模数据 如果我们需要一次性往数据库表中插入多条记录: -- 例如我们需要插入大量数据 insert into t…

Linux--进程池

1.一个父进程生成五个子进程且分别建立与子进程管道 ①用for循环&#xff0c;结束条件为<5 ②father父进程每次都要离开for循环&#xff0c;生成下一个子进程和管道 2.#include <cassert>和#include <assert.h>的区别 assert.h 是 C 标准库的头文件&#xff…

gin框架内容(二)

上一篇过于gin的内容 https://mp.csdn.net/mp_blog/creation/editor/131953861 CSDNhttps://mp.csdn.net/mp_blog/creation/editor/131953861 一、路由组 为了管理具有相同前缀的URL, 将拥有URL共同前缀的路由划分为一组 为了代码的阅读性&#xff0c;使用{}包裹相同组的路由…

【算法题】2681. 英雄的力量

题目&#xff1a; 给你一个下标从 0 开始的整数数组 nums &#xff0c;它表示英雄的能力值。如果我们选出一部分英雄&#xff0c;这组英雄的 力量 定义为&#xff1a; i0 &#xff0c;i1 &#xff0c;… ik 表示这组英雄在数组中的下标。那么这组英雄的力量为 max(nums[i0],n…

编写LED驱动,创建三个设备文件,每个设备文件绑定一个设备

驱动代码&#xff1a; #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/io.h> #include <linux/device.h> #include <linux/uaccess.h> #include <linux/slab.h> #include <linux/cd…

[ 容器 ] Harbor 私有仓库的部署与管理

目录 一、什么是Harbor二、Harbor的特性三、Harbor的构成四、Harbor 部署五、关于 Harbor.cfg 配置文件中有两类参数&#xff1a;所需参数和可选参数六、维护管理Harbor 一、什么是Harbor Harbor 是 VMware 公司开源的企业级 Docker Registry 项目&#xff0c;其目标是帮助用户…

redis数据库

目录 1.关系型数据库与非关系型数据库 关系型数据库 非关系型数据库 区别 2.redis 3.安装redis 1.关系型数据库与非关系型数据库 关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向…

汽车分析,随时间变化的燃油效率

简述 今天我们来分析一个汽车数据。 数据集由以下列组成&#xff1a; 名称&#xff1a;每辆汽车的唯一标识符。MPG&#xff1a;燃油效率&#xff0c;以英里/加仑为单位。气缸数&#xff1a;发动机中的气缸数。排量&#xff1a;发动机排量&#xff0c;表示其大小或容量。马力&…

Python Web 开发及 Django 总结

title: Python Web 开发及 Django 总结 date: 2023-07-24 17:26:26 tags: PythonWeb categories:Python cover: https://cover.png feature: false Python 基础部分见&#xff1a;Python 基础总结 1. 创建项目 1.1 命令行 1、下载安装 Django 在终端输入 pip install djan…

JVM详解(超详细)

目录 JVM 的简介 JVM 执行流程 JVM 运行时数据区 由五部分组成 JVM 的类加载机制 类加载的过程(五个) 双亲委派模型 类加载器 双亲委派模型的优点 JVM 中的垃圾回收策略 GC GC 中主要分成两个阶段 死亡对象的判断算法 引用计数算法 可达性分析算法 垃圾回收算…

Vmware vSphere 5.0系列

Vmware vSphere 5.0 我们都用过 vmware workstation 这款产品&#xff0c;可以使我们安装很多虚拟机&#xff0c;但是 vmware 的核心产品远非局限于 workstation。 vSphere 是 VMware 推出的基于云的新一代数据中心虚拟化套件&#xff0c;提供了虚拟化基础架构、高可用性、集…

生信学院|07月28日《企业制造研发一体化解决方案》

课程主题&#xff1a;企业制造研发一体化解决方案 课程时间&#xff1a;2023年07月28日 14:00-14:30 主讲人&#xff1a;周可 生信科技 售前技术顾问 1、企业面临的挑战与痛点 2、达索系统研发制造一体化解决方案 3、DELMIAworks&#xff08;DMW&#xff09;制造运营管理价…

62 | Python 操作 PDF

文章目录 Python 操作 PDF 教程1. 安装 PyPDF22. 读取 PDF 文件3. 创建 PDF 文件4. 修改 PDF 文件练习题1. 创建一个新的 PDF 文件,其中包含两个页面。第一个页面包含一段文本和一张图片,第二个页面包含一个表格。2. 打开练习题中创建的 PDF 文件,并将第一个页面中的文本修改…

【Docker--harbor私有仓库部署与管理】

目录 一、Harbor 部署1. 部署 Docker-Compose 服务2. 部署 Harbor 服务&#xff08;1&#xff09;下载或上传 Harbor 安装程序&#xff08;2&#xff09;修改harbor安装的配置文件 3. 启动 Harbor4. 查看 Harbor 启动镜像5. 创建一个新项目1、在虚拟上进行登录 Harbor2、下载镜…

rust reborrow - 重借用

两个知识点: 第一:对于不可变借用&T,它的传递属于Copy语意。对于可变借用&mut T它的传递属于Move语意或reborrow。 第二:可变引用在同一个时刻只能拥有一个,但是有一个重借用(reborrow)的方式,可以让借用重新获得可变引用。 下面为reborrow的三种方式 明确写出…