【转】app瘦身

iPhone经过这几年的发展,已经发生了很大的变化,例如屏幕变得更加多样,尺寸更多,内存变得更大,CPU的架构也在变化。伴随着iPhone的变化,iOS也在变化,例如AutoLayout、size classes、split view controller等。这些技术及设备的变化给我在开发的过程中也造成了许多的问题,不仅如此苹果通过不断推出新的技术,努力在帮助我们使用同一套代码开发适应多个设备的Universal的App。另一方面Universal App虽然在开发的过程中,方便了我们开发人员,可是对于用户来说就不那么友好了,由于需要适配多种设备,所以里包含所有设备的代码,但真正的在运行的时候,我们并不需要那么多相关的代码及资源。

例如下面的一张图,是一个App运行在iPhone 6+上,使用的各个资源相关的情况: 

上图中对勾标出来的是在iPhone 6+上真实运行的时候使用到的相关的资源及代码,对比有对勾的部分,更多的是没有被对勾标出来的部分。可以想象我们下载了一个App(前提这个App是Universal的),然后至少一半的代码及资源是我们不需要的,白白占用着我们的空间。这样对用户体验也不好。为了解决这个问题苹果在iOS 9给出了新的解决方案:

  • App Slicing 当你提交你的iOS 9 打包文件到App Store的时候,苹果编译你的资源和可执行文件,然后为每个设备生成一个特定的可执行文件。最终,设备只会下载适应与其特性的,并且它使用到的内容。这些特性包含显卡性能(原文单词:graphics capabilities)、内存级别、CPU架构、size classes、屏幕 scaling等。
  • On Demand Resouces 应用程序的资源只有在需要使用的时候才会下载,并且如果其他资源需要空间这些资源可以被移除。
  • Bitcode 在你提交App到App Store的时候,Bitcode可以作为中间产物一起提交。包含bitcode配置的程序将会在App store上被编译和链接。bitcode允许苹果在后期重新优化我们程序的二进制文件,而不需要我们重新提交一个新的版本到App store上。

这三个技术加起来,统一称为App Thinning。

Getting started

打开本章节的初始项目,然后选在iPad Air 2运行,这时候运行效果如下: 

伴随着模拟器启动起来的还打开了一个Finder窗口: 

这个Finder窗口能够打开,是因为在程序中添加了一个脚本,每次运行的时候都会执行,脚本所在地方如下: 

echo "App Size in KB:  `du -sk \"${CONFIGURATION_BUILD_DIR}/${EXECUTABLE_NAME}.app\"`"
if [ "${CONFIGURATION}" = "Debug" ]; then
open ${CONFIGURATION_BUILD_DIR}
fi
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

在Finder的Old CA Maps点击右键,选择显示包内容,如下: 

上图中标注的说明如下: 
1. Assets.car是Assets.xcassets被Xcode进行编译后的文件。 
2. Old CA Maps是真实运行在设备上的可执行文件。 
3. Santa Cruz PNGs 这个是图片文件,但是没有被编译到Assets.car文件中,这是因为它并没有放到Assets.xcassets中,而是放到了工程的顶层文件中。 
4. SD_Map.bundle 这个就是地图图片文件,但是将近120MB。

Measuring your work

本章介绍一些App瘦身相关的东西,所以我们必须能够测量App是否减少了。工程里面已经内置了一个脚本(上面代码里面有),能够在build的过程中输出App的大小。查看的位置如下: 

Slicing up app slicing

App slicing包含两部分内容:可执行文件分片(Executable slicing)和资源分片(resource slicing)。

Executable slicing 指的是在设备下载App的时候会根据设备的相关信息只是下载对应该设备的相关的可执行文件,并不会包含其他设备及架构的可执行文件,达到App安装包的缩小。并且这个功能并不需要我们做太多,App Store默认支持的。

默认情况下提交到App Store的包是包含所有的内容的,这些都在配置文件里面,App Store会自动创建对应于每个类型的可执行文件。这个在iOS9+上支持。

Being smart with resources

Resource slicing 需要我们一小部分简单的工作就能实现。如果使用Resource slicing,则要保证我们的资源都被Asset Catalogs管理。在Xcode 7中,能够标记资源被使用设备的 Memory 和 Graphics ,如下: 

Your first fix

在开始的时候介绍过Santa Cruz PNGs这个文件因为被放到Main bundle中,所以不能被编译进入到Assets.car,进而也不能使用Resource slicing。下面看一下我们怎么修改,使其能够使用: 

选择New Image Set后,将新加入的set命名为Santa Cruz,紧接着做如下操作: 

纠正一下 上图左边的内容应该是删除,包括在Finder内也应该删除

然后在不同的设备上运行App,最后发现Asset.car文件的大小并不一致。这个是因为在安装的时候,会根据设备安装对应的资源。

Lazily (down)loading content

苹果提供On-Demand Resources技术,简称ODR。ODR允许你将资源存储在苹果的服务器上,然后在你App使用的时候再去下载。NSBundleResourceRequest是处理ODR的类,使用这个类能够通过tag下载对应的资源。images, data, OpenGL shaders, SpriteKit Particles, Watchkit Complications等都可以使用ODR。

Wire things up to use tags

下面我们修改代码,实现资源的下载,修改MapChromeViewController.Swift对应方法如下:

  private func downloadAndDisplayMapOverlay() {
//    displayOverlayFromBundle(NSBundle.mainBundle())
    guard let bundleTitle = mapOverlayData?.bundleTitle else {
      return
    }

    let bundleResource = NSBundleResourceRequest(tags: [bundleTitle])

    bundleResource.beginAccessingResourcesWithCompletionHandler { [weak self] (error) -> Void in
      NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
        if error == nil {
          self?.displayOverlayFromBundle(bundleResource.bundle)
        }
      })
    }

  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这时候我们运行代码,可能会在控制台输出错误,这是因为我们对应的bundle并没有tag,我们需要给bundle添加tag: 

然后我们重新编译运行我们的程序,然后按照上面的查看编译运行的程序的大小,发现小了许多。对比之前的编译生成的文件,发现运行文件里面不包含bundle了。 

如果你的App在App Store上可能这个资源文件下载的很慢。但是在开发的过程中,Xcode会利用本地网络作为服务器,然后在设备上能够下载到,所以在开发的过程中如果电脑关了,那ODR也就不能使用了。

Make it download faster

在我们使用ODR的过程中,如果bundle比较大,可能再下载的过程中就会比较耗时,并且在下载过程中用户不知道,这样用户体验就不好。我们可以再Resource下载的过程中给用户一些提示,修改下面的代码:

// add 为新添加的 ProgressView是程序已经添加上的
private func downloadAndDisplayMapOverlay() {
//    displayOverlayFromBundle(NSBundle.mainBundle())
  guard let bundleTitle = mapOverlayData?.bundleTitle else {
    return
  }

  let bundleResource = NSBundleResourceRequest(tags: [bundleTitle])

  bundleResource.loadingPriority = NSBundleResourceRequestLoadingPriorityUrgent  //add

  loadingProgressView.observedProgress = bundleResource.progress // add

  loadingProgressView.hidden = false // add
  UIApplication.sharedApplication().networkActivityIndicatorVisible = true // add

  bundleResource.beginAccessingResourcesWithCompletionHandler { [weak self] (error) -> Void in
    NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
      self?.loadingProgressView.hidden = true // add
      UIApplication.sharedApplication().networkActivityIndicatorVisible = false // add
      if error == nil {
        self?.displayOverlayFromBundle(bundleResource.bundle)
      }
    })
  }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

如果用户已经下载过某个bundle,下次在使用的时候就不会再去下载了。

The many flavors of tagging

虽然添加了ProgressView,在体验是好了一点,但是需要注意测试的时候是使用的本地的网络,所以比较快,但是如果要是提交到App Store上,那可能下载就是比较慢了,如果再配上用户没有WiFi那可能就没法用了,所以我们还需要做其他的一些调整。

Initial install tags

使用Initial install tags,我们可以设置哪些bundle会在我们App初始化安装的时候就会被下载。 下面下介绍一下ODR三种下载的时机吧: 
Initial Install Tags 在ipa下载的时候一同下载 
Prefetched Tag Order 在程序下载完成后,下载对应的资源,然后按顺序排列。 
Prefetched Tag Order 按需下载 
下面是配置的地方: 

Purging content

应用程序在使用的过程中通过ODR下载了对应的bundle,但是有时候我们需要清理一些已经下载过的并且不使用的bundle。在介绍怎么删除之前先看一下怎么查看下载的ODR: 

Set a resource to be purged

在MapChromeViewController.swift添加如下代码:

  // new add 是新加的代码
  var overlayBundleResource: NSBundleResourceRequest? // new add
  private func downloadAndDisplayMapOverlay() {
//    displayOverlayFromBundle(NSBundle.mainBundle())
    guard let bundleTitle = mapOverlayData?.bundleTitle else {
      return
    }

    let bundleResource = NSBundleResourceRequest(tags: [bundleTitle])
    overlayBundleResource = bundleResource // new add

    bundleResource.loadingPriority = NSBundleResourceRequestLoadingPriorityUrgent  //add

    loadingProgressView.observedProgress = bundleResource.progress // add

    loadingProgressView.hidden = false // add
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true // add

    bundleResource.beginAccessingResourcesWithCompletionHandler { [weak self] (error) -> Void in
      NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
        self?.loadingProgressView.hidden = true // add
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false // add
        if error == nil {
          self?.displayOverlayFromBundle(bundleResource.bundle)
        }
      })
    }

  }

  // new add
  override func viewDidDisappear(animated: Bool) {
    super.viewDidDisappear(animated)
    // 告诉系统结束了对资源的访问
    overlayBundleResource?.endAccessingResources()
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

上面的代码,我做测试的时候不清楚会在什么时候会删除,我也模拟了内存警告,如果谁清楚,还请告诉我,谢谢。

坚持了好几天中午写完了,这篇笔记,一篇笔记13张截图,好累。

时间: 2024-12-28 23:38:05

【转】app瘦身的相关文章

iOS 9 App 瘦身功能

App 瘦身简介 当前市场上存在大量的 iOS 设备,因此有多种屏幕尺寸和分辨率,要想保证一个 App 在多种屏幕下的展示效果需要大量的资源(比如 png.jpeg 以及二进制的 PDF).不幸的是,这导致用户需要下载一个巨大的程序包(之前的 iOS 版本强制用户下载全部 App 文件,包括他们在用 iPhone 时永远也不会用到的 iPad 适配图片).16G 的 iPhone 仍然有不少人在用(并且可能短时间内不会消失),所以你要缩小 App 体积从而可以快速下载并且保证用户有足够的空间.A

Android App瘦身实战

随着业务的快速迭代增长,不断引入新的业务逻辑代码.图片资源和第三方SDK等,很多app都面临一个一个结果,app越来越大,甚至很多无用的代码,包体积的增大带来了很多问题,诸如app启动更慢,代码维护越来越困难.公司业务发展到一定程度之后,重构,代码优化,app瘦身成为不得不做的一个任务.这里以xx外卖app为例给大家讲讲app瘦身过程中常用的几种方法(也都是网上老生常谈的). apk文件构成 我们可以用Zip工具打开APK,一个常见的APK结构如下: 可以看到APK由以下主要部分组成: 文件/目

安卓-APP瘦身优化

APP优化 APP瘦身 #1. 图片的优化 ☆☆☆☆☆ s1.png图片压缩裁剪,打包APP后资源文件分50k+大小图片重点优化,10k+图片选择优化: s2.图片资源格式处理(大小):.png > .jpg > .webp,条件合适选择应选择.webp: s3.使用矢量图: #2.开启混淆与压缩 ☆☆☆☆☆ s1.gradle shrinkResources设置为true,minifyEnabled设置为true: s2.gradle minifyEnabled可单独开启,shrinkRes

App瘦身、性能优化总结

App瘦身 资源瘦身 使用tinypng压缩PNG图片.视频可以通过 Final cut等软件进行分辨率压缩.音频则降低码率即可. 非必须资源文件可以放到自己服务器上 启动图使用 LaunchScreen.storyboard,启动图在一个项目资源中占比其实蛮大的,但是使用 LaunchScreen.storyboard 只需要设置一张ImageView即可. IconFont的使用很方便,项目中图标太多或者随时需要转换图标颜色的话,建议使用 放弃使用 Realm Realm,据说是目前是性能最

iOS App 瘦身方案

缩减iOS安装包大小是很多中大型APP都要做的事,一般首先会对资源文件下手,压缩图片/音频,去除不必要的资源.这些资源优化做完后,我们还可以尝试对可执行文件进行瘦身,项目越大,可执行文件占用的体积越大,又因为AppStore会对可执行文件加密,导致可执行文件的压缩率低,压缩后可执行文件占整个APP安装包的体积比例大约有80%~90%,还是挺值得优化的.下面介绍一下在研究可执行文件过程中发现的可以优化的点.研究的过程使用了linkmap,linkmap的介绍跟生成可以参考另一篇文章—iOS可执行文

App瘦身

http://www.zoomfeng.com/blog/ipa-size-thin.html https://github.com/ming1016/SMCheckProject

【直播】APP全量混淆和瘦身技术揭秘

[直播]APP全量混淆和瘦身技术揭秘 近些年来移动APP数量呈现爆炸式的增长,黑产也从原来的PC端转移到了移动端,通过逆向手段造成数据泄漏.源码被盗.APP被山寨.破解后注入病毒或广告现象让用户苦不堪言. 为了解决安卓APP容易被逆向的问题,除了对产品进行加固处理,代码混淆技术是对抗逆向攻击最有效的方式之一.本直播会分享阿里聚安全带来的APP全量混淆技术.此外越来越多的新特性正在啃蚀着大型APP的用户体验,APP瘦身减肥也成了亟待解决的问题,如何能在使用安全功能同时瘦身,也将是本期主题所带来的内

iOS安装包瘦身的那些事儿

在我们提交安装包到App Store的时候,如果安装包过大,有可能会收到类似如下内容的一封邮件: 收到这封邮件的时候,意味着安装包在App Store上下载的时候,有的设备下载的安装包大小会超过100M.对于超过100M的安装包,只能在WIFI环境下下载,不能直接通过4G网络进行下载. 在这里,我们提交App Store的安装包大小为67.6MB,在App Store上显示的下载大小和实际下载下来的大小,我们通过下表做一个对比: iPhone型号 系统 AppStore 显示大小 下载到设备大小

Android APK瘦身全面总结——如何从32.6M到13.6M

前言 之前我简单介绍了关于svg图片瘦身的问题,在公司,瘦身这个问题是我提出来的,所以这锅我背了.公司项目是32.6M,我给自己的要求就是低于20M.上周花了一个星期瘦身,至于为什么花了一周,主要是svg适配问题我被搞蒙蔽了.然后发现还要改大量代码,想想也就算了,又换了另一种瘦身方法. 很多人是因为这标题而来的,怎么可能,32.6M的居然可以变成13.6M.下面容我慢慢道来. APK结构介绍 classes.dex classes.dex是Java源码编译后生成的java字节码文件.但由于And