红橙Darren视频笔记 换肤框架4 换肤的功能完善 内存泄漏分析

上一篇完成了换肤框架的基本搭建,这一次 我们继续补完上一次遗留的一些可以完善的部分

1.完善换肤

1.1退出后再进入应用 不会丢失上一次保存的皮肤
基本原理:将上一次切换的皮肤path保存在SharedPreference中,下一次进入应用读取该数值
同时在BaseSkinActivity创建view时应该加载一下皮肤

            public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {//在这里拦截view创建//可以在这里进行换肤Log.e(TAG, "onCreateView: 拦截了view " + name);// 换肤框架从这里开始搭建// 1.创建View 目的是替换原先的view的一些属性// createView走的代码流程和AppCompatDelegateImpl createView源码没有二致// 即先走源码的流程 让源码帮我们创建好view 我们再检查这些view中的属性 看是否需要换肤View view = createView(parent, name, context, attrs);// 在拦截后与返回前进行所有可以进行换肤view的存储// 2.解析属性  src  textColor  background textHintColor TODO 自定义属性先不考虑if (view != null) {// 获取当前activity中一个view中所有换肤的属性List<SkinAttr> skinAttrs = SkinAttrSupport.getSkinAttrs(context, attrs);// 创建skinView  skinView中可能包含多个需要换肤的属性SkinView skinView = new SkinView(view, skinAttrs);// 3.交给SkinManager统一存储管理managerSkinView(skinView);// 4.换肤 !!!!!!多了这一步!!!!!!!skinView.applySkin();}return view;}

1.2.换肤时如果已经换肤 不再调用换肤的一系列接口 避免无效调用
1.3.切换皮肤前判断是否是有效切换
比如 当前如果已经是那个皮肤 则不应该调用任何切换的代码
比如 切换皮肤前应该判断皮肤包是否存在 是否格式正确(可以获取包名)
上面提到的几点 实现如下

/*** Created by hjcai on 2021/4/21.*/
public class SkinConfig {// SharedPreferences xml文件的文件名public static final String SHARED_PREFERENCE_FILE_NAME_SKIN = "skinInfo";// SharedPreferences中保存皮肤文件路径的keypublic static final String SKIN_PATH_NAME_KEY = "skinPath";// 不需要改变任何东西public static final int SKIN_CHANGE_NOTHING = -1;// 换肤成功public static final int SKIN_CHANGE_SUCCESS = 1;// 皮肤文件不存在public static final int SKIN_FILE_NO_EXIST = -2;// 皮肤文件有错误可能不是一个apk文件public static final int SKIN_FILE_ERROR = -3;// 皮肤文件状态OKpublic static final int SKIN_FILE_OK = 2;}
/*** Created by hjcai on 2021/4/19.*/
public class SkinUtil {private static SkinUtil mInstance;private final WeakReference<Context> contextWeakRef;private SkinUtil(Context context) {contextWeakRef = new WeakReference<>(context.getApplicationContext());}public static SkinUtil getInstance(Context context) {if (mInstance == null) {synchronized (SkinUtil.class) {if (mInstance == null) {mInstance = new SkinUtil(context);}}}return mInstance;}// return /storage/emulated/0/light.skinpublic String getLightSkinPath() {return Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator + "light.skin";}// 保存当前皮肤路径public void saveSkinPath(String skinPath) {contextWeakRef.get().getSharedPreferences(SkinConfig.SHARED_PREFERENCE_FILE_NAME_SKIN, Context.MODE_PRIVATE).edit().putString(SkinConfig.SKIN_PATH_NAME_KEY, skinPath).apply();}// 清空皮肤路径public void clearSkinInfo() {saveSkinPath("");}public String getSkinPathFromSP() {return contextWeakRef.get().getSharedPreferences(SkinConfig.SHARED_PREFERENCE_FILE_NAME_SKIN, Context.MODE_PRIVATE).getString(SkinConfig.SKIN_PATH_NAME_KEY, "");}// 检查皮肤有效性public int checkSkinFileByPath(String skinPath) {File file = new File(skinPath);if (!file.exists()) {// 不存在,清空皮肤SkinUtil.getInstance(contextWeakRef.get()).clearSkinInfo();return SkinConfig.SKIN_FILE_NO_EXIST;}// 检查是否是apkString packageName = Objects.requireNonNull(contextWeakRef.get().getPackageManager().getPackageArchiveInfo(skinPath, PackageManager.GET_ACTIVITIES)).packageName;if (TextUtils.isEmpty(packageName)) {SkinUtil.getInstance(contextWeakRef.get()).clearSkinInfo();return SkinConfig.SKIN_FILE_ERROR;}return SkinConfig.SKIN_FILE_OK;}
}

上述工具类提供检查皮肤有效性 以及 保存皮肤路径到SharePreference以及从SP读取皮肤路径的方法

public class SkinManager {
...public void init(Context context) {mContextWeakRef = new WeakReference<>(context);// 每一次打开应用都会到这里来,防止皮肤被任意删除 检查皮肤有效性String currentSkinPath = SkinUtil.getInstance(context).getSkinPathFromSP();int checkRes = SkinUtil.getInstance(context).checkSkinFileByPath(currentSkinPath);if (checkRes != SkinConfig.SKIN_FILE_OK && checkRes != SkinConfig.SKIN_FILE_ERROR) {return;}// 最好校验签名  增量更新再说// 做一些初始化的工作mSkinResource = new SkinResource(context, currentSkinPath);}public int loadSkin(String skinPath) {// 1.加载皮肤前检查有效性if (SkinUtil.getInstance(mContextWeakRef.get()).checkSkinFileByPath(skinPath) != SkinConfig.SKIN_FILE_OK) {return SkinUtil.getInstance(mContextWeakRef.get()).checkSkinFileByPath(skinPath);}// 2.当前皮肤如果一样不要换String currentSkinPath = SkinUtil.getInstance(mContextWeakRef.get()).getSkinPathFromSP();if (skinPath.equals(currentSkinPath)) {return SkinConfig.SKIN_CHANGE_NOTHING;}// 3.初始化资源管理并换肤mSkinResource = new SkinResource(mContextWeakRef.get(), skinPath);changeSkin();// 4.保存皮肤的状态saveSkinStatus(skinPath);return SkinConfig.SKIN_CHANGE_SUCCESS;}private void saveSkinStatus(String skinPath) {SkinUtil.getInstance(mContextWeakRef.get()).saveSkinPath(skinPath);}// 恢复默认皮肤public int restoreDefault() {// 判断当前SP如果没有存储皮肤 什么都不做String currentSkinPath = SkinUtil.getInstance(mContextWeakRef.get()).getSkinPathFromSP();if (TextUtils.isEmpty(currentSkinPath)) {return SkinConfig.SKIN_CHANGE_NOTHING;}// 当前手机运行的app的路径apk路径String skinPath = mContextWeakRef.get().getPackageResourcePath();// 初始化资源管理mSkinResource = new SkinResource(mContextWeakRef.get(), skinPath);// 改变皮肤changeSkin();// 把皮肤信息清空SkinUtil.getInstance(mContextWeakRef.get()).clearSkinInfo();return SkinConfig.SKIN_CHANGE_SUCCESS;}    
...    
}

2.内存泄漏的分析与完善

2.1Android Studio 4.1.1的内存分析工具使用

点击Android studio左下角的Profiler出现如图的效果
在这里插入图片描述
点击+ 选择设备 选择想查看的app
在这里插入图片描述
完了之后如下
在这里插入图片描述
点击memory部分 如下
在这里插入图片描述
在这里插入图片描述
我们点击导出堆栈 发现左边多出一个堆栈结果
在这里插入图片描述
例如我输入
baseSkin
在这里插入图片描述
如图只有一个BaseSkinActivity实例 说明没有内存泄漏

其实有更简便的方式看是否内存泄漏
在这里插入图片描述
如图 即选中show activity/fragments leaks, Android studio 则会智能的过滤出内存泄漏的activity/fragment
**注意:**我们分析内存泄漏时 假设从activity A->B->C->D
我们分析时要退回到A 再GC 然后还要等一段时间 因为GC不是即时的,等一段时间如果发现BCD的实例仍然没有释放,则说明存在内存泄漏

2.2 具体分析项目中的内存泄漏

    // 缓存当前activity的所有换肤的viewpublic void cache(ISkinChangeListener skinChangeListener, List<SkinView> skinViews) {mAllSkinViewsInActivity.put(skinChangeListener, skinViews);}

之前我们的代码只在activity创建的时候缓存了Activity的实例 却没有提供方法将其remove 自然会造成内存泄漏
比如我们在切换皮肤界面写一个button自己跳转自己的activity ,退出到最开始的界面 然后调用GC 发现不管多久 内存中始终存在多个BaseSkinActivity对象 勾选show activity/fragments leaks后就会发现BaseSkinActivity已经导致泄漏了
因此我们需要提供unCache方法 并在activity销毁时调用

    public void unCache(ISkinChangeListener skinChangeListener) {mAllSkinViewsInActivity.remove(skinChangeListener);}

然后再BaseSkinActivity中添加如下代码

    @Overrideprotected void onDestroy() {// 避免内存泄漏super.onDestroy();SkinManager.getInstance().unCache(this);}

3.自定义view 如何切换皮肤

如今 我们的换肤仅仅适用于Android原生的view 如果是自定义的view换肤则可以通过换肤的时候,由SkinManager通知给Activity 各个Activity在各自的回调中处理自定义view的换肤
原理也很简单 即,让Activity实现接口

public interface ISkinChangeListener {void changeSkin(SkinResource skinResource);
}

而SkinManager则存储了这些接口(实际是Activity对象)

private final Map<ISkinChangeListener, List<SkinView>> mAllSkinViewsInActivity = new HashMap<>();

最后在SkinManager中发生皮肤切换的时候 通知各Activity

    private void changeSkin() {Set<ISkinChangeListener> keys = mAllSkinViewsInActivity.keySet();// 遍历存储的所有需要换肤的Activityfor (ISkinChangeListener key : keys) {List<SkinView> skinViews = mAllSkinViewsInActivity.get(key);// 更新所有Activity中的viewfor (SkinView skinView : skinViews) {skinView.applySkin();}// 通知Activitykey.changeSkin(mSkinResource);}}

至此 换肤框架的搭建就告一段落了,写的比较乱,读者见谅 完整的代码如下,本节主要重点还是分析内存泄漏
https://github.com/caihuijian/learn_darren_eassy_joke

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

红橙Darren视频笔记 热更新 bsdiff bspatch 在Android设备上的应用 架构篇1完结篇

概述 当时红橙的视频讲解就差不多90分钟&#xff0c;但是真正自己做出来热更新的demo还是花了八九个晚上&#xff0c;期间遇到各种各样的问题&#xff0c;什么叫台上一分钟 台下十年功是深有体会了。 本节会涉及一部分NDK的知识 推荐阅读Android官方的NDK简介 https://develope…

飞机大战学习心得(Dev-C++)

在c语言中,我们要用代码来做游戏就必须得去实现代码与图形界面接口的互联&#xff0c;也就是要通过按键的输入与输出来控制游戏的进行&#xff0c;所以以下几个头文件是必不可少的 stdio函数就不用我多说了。 windows.h函数可以获得程序的权柄&#xff0c;涉及到了Windows内核…

网易传媒签约法国队、阿根廷队 世界杯独家内容盛宴即将开启

NEW 关注Tech逆向思维视频号 最新视频→【耗资一百亿美元的韦伯望远镜到底观测什么&#xff1f;】 2022年卡塔尔世界杯进入开幕倒计时。7月14日、15日&#xff0c;网易传媒相继宣布与顶级夺冠热门球队法国队、阿根廷队签约&#xff0c;成为两支队伍的中国独家网络媒体合作伙伴&…

连续两届世界杯与冠军同行 网易传媒创造内容赛道卫冕传奇

2022年12月19日凌晨&#xff0c;阿根廷队战胜法国队&#xff0c;夺得了卡塔尔世界杯冠军&#xff0c;如愿捧回了队史上第三座大力神杯&#xff0c;梅西也以最完美的方式完成了他的世界杯谢幕战&#xff0c;成就球王传奇。 对内容公司来说&#xff0c;世界杯是专业内容、创意灵感…

java 上传Excel文件 使用easyExcel读取文件内容

记录Java接口上传Excel文件&#xff0c;及使用阿里的easyExcel读取excel文件内容&#xff0c;进行内容校验的过程。文中贴出代码是精简后代码&#xff0c;仅供参考。 引入依赖 在pom.xml中引入easyExcel需要的依赖&#xff0c;具体参考官网&#xff1a;EasyExcel文档 上传文…

使用TDM-GCC(mingw)搭建EasyX图形库开发环境

首先去EasyX官网&#xff1a;EasyX Graphics Library for C 然后看这个文章只把 EasyX 拷贝进项目里&#xff0c;方便发给没有安装 EasyX 的人编译&#xff08;以 MinGW 为例&#xff09; - CodeBus 请使用TDM-GCC进行编译&#xff0c;其他mingw(GNU)编译器可能有问题。 在 …

SpringBoot项目使用EasyExcel读取上传Excel

SpringBoot项目使用EasyExcel读取上传Excel 1、EasyExcel简介 EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。 EasyExcel读取Excel的解析原理: 2、导入相应的依赖文件 <dependency><groupId>com.a…

EasyV数据可视化|精选2022多行业可视化作品合集,看这篇就够了

​​时光荏苒 2022进度条■■■■■■■■□□ 80% 又是一年金秋时节&#xff0c;又是一年硕果累累&#xff0c;在这丰收的时节里&#xff0c;工作也陆续到了冲刺的阶段&#xff0c;准备开干的你&#xff0c;是否有这些疑惑 想要的效果构思不出来&#xff0c;需要点小灵感&am…

英文Essay各部分的写作攻略

以下是英文Essay各部分的写作攻略&#xff1a; 引言部分&#xff1a;在引言中&#xff0c;应该清晰明了地陈述你的主题和目的&#xff0c;以及你将要探讨的问题和论点。你可以使用一个有趣的事实、例子、名言、问题或者比喻来引起读者的兴趣。同时&#xff0c;你也可以在引言中…

从头到尾写SQL(二)

二 数据表的创建和管理 数据库是我们用的别人的。。数据表就该我们自己创建了吧。。 1.数据基本类型 建表的时候&#xff0c;需要我们确定好这个表有多少字段&#xff0c;这些字段又都是什么类型的(其实创建完以后这些东西也能改)。那么数据库接纳的数据类型有哪些呢&#…

暑假用chatgpt肝了一篇顶会...

小时候特别羡慕大雄&#xff0c;因为他有可以帮他写作业的机器猫小叮当。 熬夜读论文&#xff0c;却无法得出一个好的idea&#xff0c;深夜薅头发苦恼的时候&#xff0c;就会想如果有一个机器人&#xff0c;给出一串指令就能马上生成一篇质量上佳的论文就好了。 现在&#xff0…

Unity Open Day 北京站-技术专场:生成式AI技术助力Unity创作

【获取 2023 Unity Open Day 北京站演讲 PPT】 伴随Unity中国版引擎的布局以及AI技术的爆发式发展&#xff0c;ChatGPT、StableDiffusion等生成式AI技术受到广大Unity创作者的关注。Unity已经在AI领域探索多年&#xff0c;一直致力于利用人工智能技术以及工具&#xff0c;帮助创…

3D引擎龙头Unity:元宇宙和AI活跃玩家

Unity是用于创建和操作交互式实时3D内容的世界领先平台。凭借灵活的编辑器、友好的开发环境、丰富的工具套件&#xff0c;Unity吸引了大量开发者&#xff0c;全球排名前1000的移动游戏70%以上使用了Unity的创作和运营解决方案&#xff0c;如今&#xff0c;Unity引擎在工业场景、…

城市的 “数字生命” 是怎么被赋予的?|场景创新公司是这样开会的

ChatGPT 为我们带来了巨大便利的同时&#xff0c;也让更多人茫然、焦虑甚至恐惧。对于这一重塑我们生活的技术&#xff0c;每个渺小的个体或许都在心里有过类似的疑问&#xff1a;我&#xff0c;还能做什么&#xff1f; 当体力劳动、内容生产都能够以技术的方式进行翻天覆地的变…

关于ChatGPT应用于软件测试中的思考

前言 关于最近大火的ChatGPT相信各位也听过不同渠道听说过他的厉害&#xff0c;目前发展趋势比较火热&#xff0c;科技公司都有在考虑怎么使用ChatGPT进行提高研发效率以及办公效率&#xff0c;最近我所在的公司也有在要求大家使用ChatGPT进行改善工作效率&#xff0c;所以引发…

借助 AI 我为 Raycast 制作了一个可以 OCR 中文的插件

本文速通&#xff1a;最近用了 Raycast&#xff0c;真的很棒&#xff01;但是没有好用的中文 OCR 插件&#xff0c;不怎么会写 Typescript 的我&#xff0c;在 ChatGPT 的指导下&#xff0c;整了一个小插件。开发过程非常不严肃&#xff0c;充满了野路子&#xff0c;不敢妄称指…

ChatGPT令人触目惊心的地方

前两天&#xff0c;马斯克联合数千名AI科技人员&#xff0c;要求暂停巨型AI实验的新闻&#xff0c;想必大家都知道了。 这个消息挺爆炸&#xff0c;一度加剧了人们的恐慌情绪。 鸣哥当时灵光一闪&#xff0c;这件事ChatGPT是如何看待的&#xff1f; 于是我输入这一新闻事件&a…

卷积神经网络与循环神经网络实战 --- 手写数字识别及诗词创作

卷积神经网络与循环神经网络实战 — 手写数字识别及诗词创作 文章目录 卷积神经网络与循环神经网络实战 --- 手写数字识别及诗词创作一、神经网络相关知识1. 深度学习2. 人工神经网络回顾3. 卷积神经网络&#xff08;CNN&#xff09;3.1 卷积层3.2 汇集&#xff08;池化&#x…

chatgpt赋能python:如何在Python中中断程序并退出程序

如何在Python中中断程序并退出程序 在Python编程的过程中&#xff0c;有时候我们需要中断程序的执行&#xff0c;并让程序退出。这在调试程序时尤为重要。本文将介绍Python中如何中断程序并退出程序。 使用sys.exit()函数 在Python中&#xff0c;我们可以使用sys.exit()函数…

常见快捷令与指令

快捷指令 ctrlc:复制 ctrlv:粘贴 ctrla:全选 ctrlx:剪切 ctrlz:撤销 ctrls:保存 altF4:关闭窗口 打开cmd的方式 开始Windows系统找到命令提示符 win键R 输入cmd打开控制台&#xff08;推荐使用&#xff09; 管理员身份运行&#xff1a;右键选择以管理员方式运行即可 …