轉(zhuǎn)載請(qǐng)注明出處:
仿獵豹垃圾清理(實(shí)現(xiàn)原理+源碼)
前幾天無(wú)意打開(kāi)獵豹內(nèi)存大師, 發(fā)現(xiàn)它的垃圾清理很強(qiáng)大, 效果也不錯(cuò),
閑著就研究了下乏奥。 不過(guò).. 結(jié)果貌似和我想象的不太一樣。怎么說(shuō)呢,
聽(tīng)我下文一一分析判呕。
效果圖:
從效果圖, 我們可以看出它有以下幾個(gè)功能:
獲取設(shè)備上已安裝的所有App
獲取App的信息, 包括圖標(biāo)和名稱
獲取當(dāng)前已用存儲(chǔ)和可用存儲(chǔ)
掃描App動(dòng)畫(huà)效果
清除所有App垃圾文件
看到這里, 你是不是也覺(jué)得很強(qiáng)大?
然后然后, 感嘆的同時(shí), 我有幾點(diǎn)疑惑猾蒂。
獲取到所有已安裝的App, 這個(gè)功能能通過(guò)審核?(我是去年在App Store上下載的這個(gè)App)
App的圖標(biāo)如何獲取到的? (因?yàn)閽呙璧降腁pp包括我自己沒(méi)上架的demo, icon只能是本地獲取, 從其他App沙盒拿幌羞?)
垃圾清理過(guò)程, 為什么會(huì)出現(xiàn)“存儲(chǔ)容量已滿”這個(gè)提示? 明明是清理垃圾, 中途還會(huì)出現(xiàn)存儲(chǔ)滿的情況?
困惑, 不解..~ 于是乎, 折騰唄晚顷。 花了兩天時(shí)間峰伙。寫(xiě)了個(gè)小demo。
效果如下:
接下去, 我會(huì)介紹以下各個(gè)功能的實(shí)現(xiàn)過(guò)程, 包括:
獲取設(shè)備已安裝App列表已經(jīng)App信息
掃描動(dòng)畫(huà)的實(shí)現(xiàn)
獲取已用存儲(chǔ)和可用存儲(chǔ)
垃圾清理
不過(guò), 分析之前, 說(shuō)明一下,
該功能不能夠上傳到App Store上! 也就是說(shuō), 它通不過(guò)審核的
该默。
原因有二:\
- 使用了私有API\
- 蘋(píng)果不允許App有處理內(nèi)存相關(guān)功能
至于獵豹內(nèi)存大師這個(gè)App瞳氓、它也早已經(jīng) 被下架
了。我懷疑它利用混淆代碼通過(guò)的審核栓袖。至于功能的實(shí)現(xiàn),
我覺(jué)得和獵豹的實(shí)現(xiàn)思路應(yīng)該是一樣的匣摘。
至此, 如果你還對(duì)這篇文章感興趣, 歡迎繼續(xù)往下閱讀。
本文參考源碼:
CSDN下載_防獵豹垃圾清理
獲取設(shè)備已安裝App列表已經(jīng)App信息
不越獄, 非私有API
沒(méi)有越獄的設(shè)備裹刮,官方?jīng)]有提供api音榜,所以只能用一些技巧,但是獲取內(nèi)容不全捧弃。
這里主要有兩種辦法:
方法一:利用URL scheme赠叼,看對(duì)于某一應(yīng)用特有的urlscheme,有沒(méi)有響應(yīng)违霞。如果有響應(yīng)嘴办,就說(shuō)明安裝了這個(gè)特定的app。
說(shuō)實(shí)在.. 這個(gè)辦法比較傻买鸽。 App Store幾百萬(wàn)的App, 如何枚舉的過(guò)來(lái)? 并且,
也無(wú)法掃描到自己的demo涧郊。 不過(guò), 還真有人這么干..
這是對(duì)應(yīng)的demo, 感興趣可以看看。
iHasApp
官方教程:
iPhoneURLScheme_Reference
方法二:利用一些方法獲得當(dāng)前正在運(yùn)行的進(jìn)程信息眼五,從進(jìn)程信息中獲得安裝的app信息妆艘。
參考:
UIDevice_Category_For_Processes
總的來(lái)說(shuō), 不越獄, 非私有API, 想獲得完整列表, 基本沒(méi)什么可能。
不越獄, 私有API看幼。
這里就是我demo所采用的辦法, 比較簡(jiǎn)單批旺。
include <objc/runtime.h>Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace"); NSObject* workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)]; NSLog(@"apps: %@", [workspace performSelector:@selector(allApplications)]);
返回結(jié)果
"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"
返回的是個(gè)數(shù)據(jù), 每個(gè)元素都是 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
對(duì)應(yīng)的屬性和方法朱沃。
我們可以用如下代碼, 打印下每個(gè)屬性的值, 找出我們想要的。
2、/* 獲取對(duì)象的所有屬性 以及屬性值 */- (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
遍歷未知對(duì)象的屬性和方法
然后我們提取出我們需要的, 圖標(biāo)和應(yīng)用名逗物。
[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
中存儲(chǔ)的就是我們需要的App數(shù)據(jù)了。
越獄
.. 這里我也不懂, 也沒(méi)去研究瑟俭。 感興趣的可以看看
MobileInstallation.framework
掃描動(dòng)畫(huà)的實(shí)現(xiàn)
這里主要有兩個(gè)動(dòng)畫(huà)翎卓。
利用UIScrollView, 實(shí)現(xiàn)每個(gè)App自動(dòng)滾動(dòng)。
Animation動(dòng)畫(huà), 中間掃描線的往返運(yùn)動(dòng)摆寄。
至于動(dòng)畫(huà), 這里我不想介紹太多失暴。 源碼里面都寫(xiě)清楚了。(當(dāng)然, 寫(xiě)的比較粗糙…)
簡(jiǎn)單帶一下掃描線的動(dòng)畫(huà)實(shí)現(xiàn):
/* 向左移動(dòng) */ CABasicAnimation animationLeft = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; // 動(dòng)畫(huà)選項(xiàng)的設(shè)定 animationLeft.duration = 0.5f; // 持續(xù)時(shí)間 animationLeft.beginTime = 0.0f; animationLeft.autoreverses = YES; // 結(jié)束后執(zhí)行逆動(dòng)畫(huà) // 動(dòng)畫(huà)先加速后減速 animationLeft.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; // 終了幀 animationLeft.toValue = [NSNumber numberWithFloat:-40];; / 向右移動(dòng) */ CABasicAnimation animationRight = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"]; // 動(dòng)畫(huà)選項(xiàng)的設(shè)定 animationRight.duration = 0.5f; // 持續(xù)時(shí)間 animationRight.beginTime = 1.0f; animationRight.autoreverses = YES; // 結(jié)束后執(zhí)行逆動(dòng)畫(huà) // 動(dòng)畫(huà)先加速后減速 animationRight.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; // 終了幀 animationRight.toValue = [NSNumber numberWithFloat:40];; / 動(dòng)畫(huà)組 */ CAAnimationGroup *group = [CAAnimationGroup animation]; group.delegate = self; group.duration = 2.0; group.repeatCount = 15; // 動(dòng)畫(huà)結(jié)束后不變回初始狀態(tài) group.removedOnCompletion = NO; group.fillMode = kCAFillModeForwards; // 添加動(dòng)畫(huà) group.animations = [NSArray arrayWithObjects:animationLeft, animationRight, nil]; [mySL.layer addAnimation:group forKey:@"moveLeft-moveRight-layer"];
獲取已用存儲(chǔ)和可用存儲(chǔ)
這個(gè)沒(méi)什么好說(shuō)的了.. Apple提供了API, 直接用就是了微饥。
// 獲取占用內(nèi)存-(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);}
垃圾清理
這里我本來(lái)是不想提的逗扒,畢竟這個(gè)功能,蘋(píng)果是不能接受的欠橘。
之前提到了, 獵豹在清理過(guò)程中,
會(huì)出現(xiàn) “存儲(chǔ)已滿的提示”
矩肩。然后我開(kāi)始考慮了。
為什么要彈出提示肃续?
存儲(chǔ)真的在某一刻滿了嗎黍檩?
它清理的時(shí)候, QQ直接被殺死, 應(yīng)用名變成”正在清理…”(和安裝中一個(gè)狀態(tài))。 真有這么厲害? !!!!!!
這個(gè)好像在哪里見(jiàn)過(guò)…
最后,我確定了獵豹的實(shí)現(xiàn)方式始锚。它只不過(guò)是觸發(fā)了Apple自己的垃圾回收機(jī)制而已刽酱。
當(dāng)存儲(chǔ)滿的時(shí)候, 系統(tǒng)會(huì)自動(dòng)幫我們進(jìn)行垃圾清理, 并彈出提示說(shuō)明存儲(chǔ)已滿。
所以, 獵豹只不過(guò)是計(jì)算了剩余多少存儲(chǔ),
然后制造了一個(gè)與之差不多大小的垃圾文件瞧捌。
然后觸發(fā)蘋(píng)果的清理機(jī)制棵里。清理完后,
刪除之前生成的垃圾文件。再次統(tǒng)計(jì)當(dāng)前可用存儲(chǔ),
差值即為本次清理的垃圾大小姐呐。
是吧, 其實(shí)也沒(méi)那么神~
至于如何快速制造幾百M(fèi), 甚至幾G的垃圾文件?
// 將文件的長(zhǎng)度設(shè)定為offset -(void)truncateFileAtOffset:offset
truncateFileAtOffset:offset
就能搞定了殿怜。 感興趣的可以自己研究下。
至此, 獵豹垃圾清理分析完畢皮钠。
當(dāng)然, 這只是我個(gè)人的看法稳捆。如果有更好的方式, 或者文章中存在任何錯(cuò)誤。歡迎交流指正麦轰。