防猎豹垃圾清理(实现原理+源码)

防猎豹垃圾清理(实现原理+源码)



转载请注明出处: 防猎豹垃圾清理(实现原理+源码)


前几天无意打开猎豹内存大师, 发现它的垃圾清理很强大, 效果也不错, 闲着就研究了下。 不过.. 结果貌似和我想象的不太一样。怎么说呢, 听我下文一一分析。

效果图:

从效果图, 我们可以看出它有以下几个功能:

  1. 获取设备上已安装的所有App
  2. 获取App的信息, 包括图标和名称
  3. 获取当前已用存储和可用存储
  4. 扫描App动画效果
  5. 清除所有App垃圾文件

看到这里, 你是不是也觉得很强大?

然后然后, 感叹的同时, 我有几点疑惑。

  1. 获取到所有已安装的App, 这个功能能通过审核?(我是去年在App Store上下载的这个App)
  2. App的图标如何获取到的? (因为扫描到的App包括我自己没上架的demo, icon只能是本地获取, 从其他App沙盒拿?)
  3. 垃圾清理过程, 为什么会出现“存储容量已满”这个提示? 明明是清理垃圾, 中途还会出现存储满的情况?

困惑, 不解..~ 于是乎, 折腾呗。 花了两天时间。写了个小demo。

效果如下:

接下去, 我会介绍以下各个功能的实现过程, 包括:

  1. 获取设备已安装App列表已经App信息
  2. 扫描动画的实现
  3. 获取已用存储和可用存储
  4. 垃圾清理

不过, 分析之前, 说明一下, 该功能不能够上传到App Store上! 也就是说, 它通不过审核的

原因有二:

1. 使用了私有API

2. 苹果不允许App有处理内存相关功能

至于猎豹内存大师这个App、它也早已经被下架了。我怀疑它利用混淆代码通过的审核。至于功能的实现, 我觉得和猎豹的实现思路应该是一样的。

至此, 如果你还对这篇文章感兴趣, 欢迎继续往下阅读。

本文参考源码: CSDN下载_防猎豹垃圾清理


获取设备已安装App列表已经App信息

不越狱, 非私有API

没有越狱的设备,官方没有提供api,所以只能用一些技巧,但是获取内容不全。

这里主要有两种办法:

方法一:利用URL scheme,看对于某一应用特有的url scheme,有没有响应。如果有响应,就说明安装了这个特定的app。

说实在.. 这个办法比较傻。 App Store几百万的App, 如何枚举的过来? 并且, 也无法扫描到自己的demo。 不过, 还真有人这么干..

这是对应的demo, 感兴趣可以看看。 iHasApp

官方教程: iPhoneURLScheme_Reference

方法二:利用一些方法获得当前正在运行的进程信息,从进程信息中获得安装的app信息。

参考: UIDevice_Category_For_Processes

总的来说, 不越狱, 非私有API, 想获得完整列表, 基本没什么可能。

不越狱, 私有API。

这里就是我demo所采用的办法, 比较简单。

#include <objc/runtime.h>

Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)];
NSLog(@"apps: %@", [workspace performSelector:@selector(allApplications)]);  

返回结果


    "LSApplicationProxy: com.qunar.iphoneclient8",
    "LSApplicationProxy: com.apple.mobilemail",
    "LSApplicationProxy: com.apple.mobilenotes",
    "LSApplicationProxy: com.apple.compass",
    "LSApplicationProxy: com.tencent.happymj",
    "LSApplicationProxy: com.apple.mobilesafari",
    "LSApplicationProxy: com.apple.reminders"

返回的是个数据, 每个元素都是LSApplicationProxy.它的description只返回了 它的bundle id。然而这并不是我们想要的。

接下去我们看

LSApplicationProxy.h

形如:

@class LSApplicationProxy, NSArray, NSDictionary, NSProgress, NSString, NSURL, NSUUID;

@interface LSApplicationProxy : LSResourceProxy <NSSecureCoding> {
    NSArray *_UIBackgroundModes;
    NSString *_applicationType;
    NSArray *_audioComponents;
    unsigned int _bundleFlags;
    NSURL *_bundleURL;
    NSString *_bundleVersion;
    NSArray *_directionsModes;
    NSDictionary *_entitlements;
    NSDictionary *_envi

    ...
    ...

这里列举了LSApplicationProxy对应的属性和方法。

我们可以用如下代码, 打印下每个属性的值, 找出我们想要的。

2、/* 获取对象的所有属性 以及属性值 */
- (NSDictionary *)properties_aps
{
   NSMutableDictionary *props = [NSMutableDictionary dictionary];
   unsigned int outCount, i;
   objc_property_t *properties = class_copyPropertyList([self class], &outCount);
   for (i = 0; i<outCount; i++)
    {
       objc_property_t property = properties[i];
       const char* char_f =property_getName(property);
       NSString *propertyName = [NSString stringWithUTF8String:char_f];
       id propertyValue = [self valueForKey:(NSString *)propertyName];
       if (propertyValue) [props setObject:propertyValue forKey:propertyName];
    }
   free(properties);
   return props;
} 

参考: IOS 遍历未知对象的属性和方法

然后我们提取出我们需要的, 图标和应用名。

[appsInfoArr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
        {
            NSDictionary *boundIconsDictionary = [obj performSelector:@selector(boundIconsDictionary)];

            NSString *iconPath = [NSString stringWithFormat:@"%@/%@.png", [[obj performSelector:@selector(resourcesDirectoryURL)] path], [[[boundIconsDictionary objectForKey:@"CFBundlePrimaryIcon"] objectForKey:@"CFBundleIconFiles"]lastObject]];

             UIImage *image = [[[UIImage alloc]initWithContentsOfFile:iconPath] TransformtoSize:CGSizeMake(65, 65)];
            if (image)
            {
                [self.appsIconArr addObject:image];
                [self.appsNameArr addObject:[obj performSelector:@selector(localizedName)]];
            }
        }];

如此, _self.appsIconArr_appsNameArr中存储的就是我们需要的App数据了。

越狱

.. 这里我也不懂, 也没去研究。 感兴趣的可以看看 MobileInstallation.framework


扫描动画的实现

这里主要有两个动画。

  1. 利用UIScrollView, 实现每个App自动滚动。
  2. Animation动画, 中间扫描线的往返运动。

至于动画, 这里我不想介绍太多。 源码里面都写清楚了。(当然, 写的比较粗糙…)

简单带一下扫描线的动画实现:

/* 向左移动 */
    CABasicAnimation *animationLeft = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];

    // 动画选项的设定
    animationLeft.duration = 0.5f; // 持续时间
    animationLeft.beginTime = 0.0f;
    animationLeft.autoreverses = YES; // 结束后执行逆动画
    // 动画先加速后减速
    animationLeft.timingFunction =
    [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];

    // 终了帧
    animationLeft.toValue = [NSNumber numberWithFloat:-40];;

    /* 向右移动 */
    CABasicAnimation *animationRight = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];

    // 动画选项的设定
    animationRight.duration = 0.5f; // 持续时间
    animationRight.beginTime = 1.0f;
    animationRight.autoreverses = YES; // 结束后执行逆动画
    // 动画先加速后减速
    animationRight.timingFunction =
    [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];

    // 终了帧
    animationRight.toValue = [NSNumber numberWithFloat:40];;

    /* 动画组 */
    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.delegate = self;
    group.duration = 2.0;
    group.repeatCount = 15;

    // 动画结束后不变回初始状态
    group.removedOnCompletion = NO;
    group.fillMode = kCAFillModeForwards;

    // 添加动画
    group.animations = [NSArray arrayWithObjects:animationLeft, animationRight, nil];
    [mySL.layer addAnimation:group forKey:@"moveLeft-moveRight-layer"];

获取已用存储和可用存储

这个没什么好说的了.. Apple提供了API, 直接用就是了。

// 获取占用内存
-(void)usedSpaceAndfreeSpace
{
    NSString* path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] ;
    NSFileManager* fileManager = [[NSFileManager alloc ]init];
    NSDictionary *fileSysAttributes = [fileManager attributesOfFileSystemForPath:path error:nil];
    NSNumber *freeSpace = [fileSysAttributes objectForKey:NSFileSystemFreeSize];
    NSNumber *totalSpace = [fileSysAttributes objectForKey:NSFileSystemSize];
    NSString  * str= [NSString stringWithFormat:@"已占用%0.1f G / 剩余%0.1f MB",([totalSpace longLongValue] - [freeSpace longLongValue])/1024.0/1024.0/1024.0,[freeSpace longLongValue]/1024.0/1024.0];
    NSLog(@"--------%@",str);
}

垃圾清理

这里我本来是不想提的,毕竟这个功能,苹果是不能接受的。

之前提到了, 猎豹在清理过程中, 会出现“存储已满的提示”。然后我开始考虑了。

  1. 为什么要弹出提示?
  2. 存储真的在某一刻满了吗?
  3. 它清理的时候, QQ直接被杀死, 应用名变成”正在清理…”(和安装中一个状态)。 真有这么厉害? !!!!!!
  4. 这个好像在哪里见过…

最后, 我确定了猎豹的实现方式。它只不过是触发了Apple自己的垃圾回收机制而已。

当存储满的时候, 系统会自动帮我们进行垃圾清理, 并弹出提示说明存储已满。

所以, 猎豹只不过是计算了剩余多少存储, 然后制造了一个与之差不多大小的垃圾文件。 然后触发苹果的清理机制。清理完后, 删除之前生成的垃圾文件。再次统计当前可用存储, 差值即为本次清理的垃圾大小。

是吧, 其实也没那么神~

至于如何快速制造几百M, 甚至几G的垃圾文件?

// 将文件的长度设定为offset
 -(void)truncateFileAtOffset:offset 

truncateFileAtOffset:offset就能搞定了。 感兴趣的可以自己研究下。



至此, 猎豹垃圾清理分析完毕。

当然, 这只是我个人的看法。如果有更好的方式, 或者文章中存在任何错误。 欢迎交流指正。

时间: 2024-08-15 16:32:47

防猎豹垃圾清理(实现原理+源码)的相关文章

Java HashMap实现原理 源码剖析

HashMap是基于哈希表的Map接口实现,提供了所有可选的映射操作,并允许使用null值和null建,不同步且不保证映射顺序.下面记录一下研究HashMap实现原理. HashMap内部存储 在HashMap内部,通过维护一个 瞬时变量数组table (又称:桶) 来存储所有的键值对关系,桶 是个Entry对象数组,桶 的大小可以按需调整大小,长度必须是2的次幂.如下代码: /** * 一个空的entry数组,桶 的默认值 */ static final Entry<?,?>[] EMPTY

Mybatis Interceptor 拦截器原理 源码分析

Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件. 代理链的生成 Mybatis支持对Executor.StatementHandler.PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理. 通过查看Configuration类的源代码我们可以看到,每次都

Windows虚拟地址转物理地址(原理+源码实现,附简单小工具)

                                                                                                     By Lthis 上个月就想写了,一直没时间...网上大概搜了一下,原理与操作倒是一大堆,一直没看到源码实现,总得有人动手,这回轮到我了.东西写得很烂,请大牛勿喷.一直觉得靠源码的方式驱动学习是非常好的一种学习方法,比较直观!声明一下,本教程只有讨论开启PAE与关闭PAE两种,至于PSE是否开启没

AtomicInteger原理&amp;源码分析

转自https://www.cnblogs.com/rever/p/8215743.html 深入解析Java AtomicInteger原子类型 在进行并发编程的时候我们需要确保程序在被多个线程并发访问时可以得到正确的结果,也就是实现线程安全.线程安全的定义如下: 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的. 举个线程不安全的例子.假如我们想实现一个功能来统计

# 【nodejs原理&amp;源码赏析(3)】欣赏手术级的原型链加工艺术

目录 一. 概述 二. 原型链基础知识 三. Worker类的原型链加工 四. 实例的生成 五. 最后一个问题 六. 一些心得 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 华为云社区地址:[你要的前端打怪升级指南] 好的代码都差不多,烂的代码却各有各的烂法. 一. 概述 原型链是javascript非常重要的基础知识.最近在阅读node.js,发现许多代码乍一看会觉得很费解,但细细品味之后会觉得非常

开源词袋模型DBow3原理&amp;源码

前人摘树,后人乘凉. 源码在github有CMakeLists,代码下下来可以直接编译. 泡泡机器人有个很详细的分析,结合浅谈回环检测中的词袋模型,配合高翔的回环检测应用,基本上就可以串起来了. tf-idf的概念,表达方式不唯一,这里的定义是这样: tf表示词频,这个单词在图像中出现的次数/图像单词总量 idf表示单词在整个训练语料库中的常见程度:idf=log(N/Ni),N是训练语料库中的图片总数,Ni是训练语料库中包含这个单词的图像数 由于Ni<=N,idf>=0,当Ni=N时,idf

[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的

Launcher启动类 本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是非常重要的 不过源码其实比较简单,接下来简单介绍一下 我们先从启动类说起 有一个Launcher类   sun.misc.Launcher; 仔细看下这简短的几行注释,可以得到有用的信息 ps:直接IDE里面查看反编译的,看不到注释的,可以下载openJDK查看源码,我的这个版本是openjdk-8-src-b132-03_mar_2014 sun.misc.Launcher这个类是系统用于

【nodejs原理&amp;源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)

目录 一. 概述 二. 线程与进程 三. cluster模块源码解析 3.1 起步 3.2 入口 3.3 主进程模块master.js 3.4 子进程模块child.js 四. 小结 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 华为云社区地址:[你要的前端打怪升级指南] 一. 概述 cluster模块是node.js中用于实现和管理多进程的模块.常规的node.js应用程序是单线程单进程的,这也意味

开源词袋模型DBow3原理&amp;源码(二)ORB特征的保存和读取

util里提供了create_voc_step0用于批量生成features并保存,create_voc_step1读入features再生成聚类中心,比较适合大量语料库聚类中心的生成. 提取一张图的特征如下: 第一行是文件头,分别用32bit表示特征来自几张图(1).特征描述子长度(128bit,=32B), 特征长度(89), 特征类型(cv8u) ./utils/create_voc_step0 orb fea0 zs00.jpg Extracting features... readin