预加载资源地图集服务(Asset Atlas Service)分析

本文详细分析了Android系统中的预加载资源地图集服务,探讨了其如何通过Asset Atlas Service在GPU级别实现预加载资源的共享,减少GPU内存浪费。内容涉及Zygote进程预加载资源、System进程合成纹理并上传到GPU,以及应用程序进程如何使用上传后的纹理。预加载资源地图集服务旨在优化内存使用和提高渲染效率。
摘要由CSDN通过智能技术生成

我们知道,Android系统在启动的时候,会对一些系统资源进行预加载。这样不仅使得应用程序在需要时可以快速地访问这些资源,还使得这些资源能够在不同应用程序之间进行共享。在硬件加速渲染环境中,这些预加载资源还有进一步优化的空间。Android系统提供了一个地图集服务,负责将预加载资源合成为一个纹理上传到GPU去,并且能够在所有的应用程序之间进行共享。

资源预加载是发生在Zygote进程的,然后Zygote进程fork了应用程序进程,于是就使得预加载的资源可以在Zygote进程与所有的应用程序进程进行共享。这种内存共享机制是由Linux进程创建方式决定的。也就是说,父进程fork子进程之后,只要它们都不去修改某一块内存,那么这块内存就可以在父进程和子进程之间进行共享。一旦父进程或者子进程修改了某一块内存,那么Linux内核就会通过一种称为COW(Copy On Wrtie)的技术为要修改的进程创建一块内存拷贝出来,这时候被修改的内存就不再可以共享。

对于预加载资源来说,它们都是只读的,因此就可以保证它们在Zygote进程与所有的应用程序进程进行共享。这在应用程序UI使用软件方式渲染时可以工作得很好。但是当应用程序UI使用硬件加速渲染时,情况就发生了变化。资源一般是作为纹理来使用的。这意味着每一个应用程序都会将它要使用的预加载资源作为一个纹理上传到GPU去,如图所示:

在这里插入图片描述
因此,这种做法会浪费GPU内存。为了节省GPU内存,Android系统在System进程中运行了一个Asset Atlas Service。这个Asset Atlas Service将预加载资源合成为一个纹理,并且上传到GPU去。应用程序进程可以向Asset Atlas Service请求上传后的纹理,从而使得它们不需要再单独去上传一份,这样就可以起到在GPU级别共享的作用,如图所示:

在这里插入图片描述
最右侧显示的是应用程序进程的Render Thread,它们通过Asset Atlas Service获得已经上传到GPU的预加载资源纹理,这样就可以直接使用它们,而不再需要独立上传。

接下来,我们从Zygote进程预加载资源、System进程合成资源为纹理并且上传到GPU,以及应用程序使用上传后的纹理三个过程来描述预加载资源地图集机制,以便可以更好地理解应用程序是如何做到在GPU级别共享预加载资源的。

我们首先看Zygote进程预加载资源的过程。Zygote进程在Java层的入口点为ZygoteInit类的静态成员函数main,它的实现如下所示:

public class ZygoteInit {
     
    ......  
  
    public static void main(String argv[]) {
     
        try {
     
            ......  
  
            registerZygoteSocket(socketName);  
            ......  
            preload();  
            ......  
  
            if (startSystemServer) {
     
                startSystemServer(abiList, socketName);  
            }  
  
            ......  
            runSelectLoop(abiList);  
  
            ......  
        } catch (MethodAndArgsCaller caller) {
     
            ......  
        } catch (RuntimeException ex) {
     
            ......  
        }  
    }  
  
    ......  
}  

ZygoteInit类的静态成员函数main的执行流程如下所示:

  1. 调用成员函数registerZygoteSocket创建一个Server端的Socket,用来与运行在System进程中的ActivityManagerService服务通信,也就是用来接收ActivityManagerService发送过来的创建应用程序进程的请求。
  2. 调用成员函数preload执行预加载资源的操作。
  3. 调用成员函数startSystemServer启动System进程。
  4. 调用成员函数runSelectLoop进入一个循环中等待和处理ActivityManagerService服务发送过来的创建应用程序进程的请求。

这里我们只关注资源预加载的过程,即ZygoteInit类的成员函数preload的实现,如下所示:

public class ZygoteInit {
     
    ......  
  
    static void preload() {
     
        Log.d(TAG, "begin preload");  
        preloadClasses();  
        preloadResources();  
        preloadOpenGL();  
        preloadSharedLibraries();  
        // Ask the WebViewFactory to do any initialization that must run in the zygote process,  
        // for memory sharing purposes.  
        WebViewFactory.prepareWebViewInZygote();  
        Log.d(TAG, "end preload");  
    }  
  
    ......  
}  

这个函数定义在文件frameworks/base/core/java/com/android/internal/os/ZygoteInit.java中。

Zygote进程需要预加载的东西很多,包括:

  1. 预加载系统类,这是通过调用ZygoteInit类的静态成员函数preloadClasses实现的。
  2. 预加载系统资源,这是通过调用ZygoteInit类的静态成员函数preloadResources实现的。
  3. 预加载Open GL资源,这是通过调用ZygoteInit类的静态成员函数preloadOpenGL实现的。
  4. 预加载一些共享库,这是通过调用ZygoteInit类的静态成员函数preloadSharedLibraries实现的。
  5. 预加载WebView库,这是通过调用WebViewFactory类的静态成员函数prepareWebViewInZygote实现的。

所有的这些预加载行为都是为了实现内存共享目的的,也就是在Zygote进程和所有应用程序进程之间进行内存共享。这里我们只关注系统资源的预加载过程,即ZygoteInit类的静态成员函数preloadResources的实现,如下所示:

public class ZygoteInit {
     
    ......  
  
    private static void preloadResources() {
     
        ......  
  
        try {
     
            ......  
            mResources = Resources.getSystem();  
            mResources.startPreloading();  
            if (PRELOAD_RESOURCES) {
     
                ......  
                TypedArray ar = mResources.obtainTypedArray(  
                        com.android.internal.R.array.preloaded_drawables);  
                int N = preloadDrawables(runtime, ar);  
                ......  
  
                ar = mResources.obtainTypedArray(  
                        com.android.internal.R.array.preloaded_color_state_lists);  
                N = preloadColorStateLists(runtime, ar);  
                ......  
            }  
            mResources.finishPreloading();  
        } catch (RuntimeException e) {
     
            ......  
        } finally {
     
            ......  
        }  
    }  
  
    ......  
}  

从这里就可以看到,预加载的系统资源有两类,一类是Drawable资源,另一类是Color State List资源,分别通过调用ZygoteInit类的静态成员函数preloadDrawables和preloadColorStateLists实现。这两类资源所包含的具体资源列表分别由frameworks/base/core/res/res/values/arrays.xml文件里面的数组preloaded_drawables和preloaded_color_state_lists定义。

接下来我们只关注Drawable资源的预加载过程,即ZygoteInit类的静态成员函数preloadDrawables的实现,如下所示:

public class ZygoteInit {
     
    ......  
  
    private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
     
        int N = ar.length();  
        for (int i=0; i<N; i++) {
     
            ......  
            int id = ar.getResourceId(i, 0);  
            ......  
            if (id != 0) {
     
                if (mResources.getDrawable(id, null) == null) {
     
                    throw new IllegalArgumentException(  
                            "Unable to find preloaded drawable resource #0x"  
                            + Integer.toHexString(id)  
                            + " (" + ar.getString(i) + ")");  
                }  
            }  
        }  
        return N;  
    }  
  
    ......  
}  

ZygoteInit类的静态成员函数preloadDrawables通过调用静态成员变量mResources指向的一个Resources对象的成员函数getDrawable来依次读取由参数ar描述的一系列Drawable资源。ZygoteInit类的静态成员变量mResources指向的Resources对象就是用来描述系统资源的,从前面Android资源管理框架(Asset Manager)简要介绍和学习计划这个系列的文章可以知道,当我们调用它的成员函数getXXX来获取指定的资源时,如果该资源还没有加载,那么就会被加载。

这些被预加载的Drawable将会被运行在System进程里面的Asset Atlas Service合成一个地图集,并且作为纹理上传到GPU去,因此,接下来我们就继续分析Asset Atlas Service的实现。

前面提到,System进程是由Zygote进程启动的。System进程启动之后,就会加载系统服务,其中就包括Asset Atlas Service,如下所示:

public final class SystemServer {
     
    ......  
  
    public static void main(String[] args) {
     
        new SystemServer().run();  
    }  
      
    ......  
  
    private void run() {
     
        ......  
  
        Looper.prepareMainLooper();  
  
        ......  
  
        // Start services.  
        try {
     
            ......  
            startOtherServices();  
        } catch (Throwable ex) {
     
            ......  
        }  
  
        ......  
  
        // Loop forever.  
        Looper.loop();  
        ......  
    }  
  
    ......  
  
   private void startOtherServices() {
     
        ......  
  
        AssetAtlasService atlas = null;  
        ......  
  
        if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
     
            ......  
  
            if (!disableNonCoreServices) {
     
                try {
     
                    ......  
                    atlas = new AssetAtlasService(context);  
                    ServiceManager.addService(AssetAtlasService.ASSET_ATLAS_SERVICE, atlas);  
                } catch (Throwable e) {
     
                    .......  
                }  
            }  
          
            ......  
        }  
  
        ......  
    }  
  
    ......  
} 

Asset Atlas Service是一个非系统核心服务,当设备启动在非工厂模式,并且在没有禁用非系统核心服务的条件下,就会启动Asset Atlas Service。

Asset Atlas Service的启动过程如下所示:

public class AssetAtlasService extends IAssetAtlas.Stub {
     
    ......  
  
    public AssetAtlasService(Context context) {
     
        ......  
  
        ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>(300);  
        int totalPixelCount = 0;  
  
        // We only care about drawables that hold bitmaps  
        final Resources resources = context.getResources();  
        final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();  
  
        final int count = drawables.size();  
  
        ......  
  
        for (int i = 0; i < count; i++) {
     
            final Bitmap bitmap = drawables.valueAt(i).getBitmap();  
            if (bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888) {
     
                bitmaps.add(bitmap);  
                totalPixelCount += bitmap.getWidth() * bitmap.getHeight();  
            }  
        }  
  
        // Our algorithms perform better when the bitmaps are first sorted  
        // The comparator will sort the bitmap by width first, then by height  
        Collections.sort(bitmaps, new Comparator<Bitmap>(<
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值