Android 性能优化—开机动画优化

Android 手机的分辨率越来越大,从矮穷锉到百富美都是如此.现在矮穷锉也是HD的屏而百富美一般是2K, 8CPU, 3GRAM.然并卵,我们依然会发现大多数的开机动画并不是很流畅,而比较流畅的,大都要么画面简单有的只是showlogo, 要么降低动画的分辨率/FPS.比如本来是百富美的HW(2K),却用矮穷锉的开机动画(HD/10fps开机动画).当然,这里有androiddesign 的问题,也有手机厂商的设置animation内容的问题.本文将提供如何在矮穷锉HW上实现真正百富美HW才有的功能(FHD,或者2K 30FPS).

1.1 Bootanimation架构

android ,开机动画并不仅仅是开机动画,而还包含开机铃声.开机动画都压缩在bootanimation.zip,而开机铃声L以及以前的版本中可以指定某一目录下的audio 文件也可以把开机铃声放到bootanimation.zip就只能放到bootanimation.zip中除非修改androidcode.本文只讨论动画.事实上所谓的开机动画其实就是几十张或上百张的PNG或者JPG 图片按照一定的顺序, FPS播放出来所有的开机动画都压缩在bootanimation.zip,其位置是/system/media/bootanimation.zip. 现在我们打开bootanimation.zip来看看究竟有些什么内容.

+bootanimation.zip

| | +part0

| |480_854_00000.png

| |480_854_00001.png

| |480_854_00002.png

| |480_854_00003.png

| |480_854_00004.png

| |480_854_00005.png

|+------------------

| +part1

| | 480_854_00000.png

| |480_854_00001.png

| |480_854_00002.png

| |480_854_00003.png

| |480_854_00004.png

| |480_854_00005.png

……

|+------------------

| +partn

| | 480_854_00000.png

| |480_854_00001.png

| |480_854_00002.png

| |480_854_00003.png

| |480_854_00004.png

| |480_854_00005.png

|+------------------

| | audio.wav

|+------------------

| desc.txt

+--------------------

|+------------------

| audio_conf.txt

+--------------------

这里我们可以看到在bootanimation.zip中由part1,part2,part3 … partn, audio.wav

, desc.txtaudio_conf.txt 构成.其中audio.wavaudio_conf.txt是和开机动画的audio相关我们在本文略去不表.partdesc.txt与开机动画有关.每一个part 又由一系列的pngjpg格式的图片按照数字或字母的大小顺序排列而成. desc.txt则是描述怎样处理这些图片.

desc.txt

#typicaldesc.txt

480854 10

p1 0 part0

p0 0 part1

这里是典型的矮穷锉的配置.但是百富美的架构也和这个相同只是内容不同而已

“480” 
表示这些图片的宽度是480.
“854” 表示这些图片的高度是854.
“10” 表示开机动画的目标fps 10. 

“p” 
这个flag表示某一个part的图片的处理方式,bootanimation 程序在处理某part的图片时会参考这个标志.P表示bootanimation可以随时退出当处理完一张图片后,如果bootanimation可以退出. ”C” 则表示必须把这部分的图片处理完毕才可以退出.事实上这个标识只要不为C, 则其行为完全一致.

“1”这个flag表示这part的图片需要循环处理多少次. 1表示处理循环处理一次.”0” 表示无限循环.
“0” 
这个flag 表示播放完这部分所有图片后需要pause多久时间.”0” 表示不需要pause. “part0”表示动画的第一部分内容.
“part1” 表示动画的第二部分内容.

“partn” 表示动画的第n+1部分内容.

这里我们讨论bootanimation程序的退出时机,正如前面讨论的,bootanimation程序会根据part 描述的第一个标识来处理怎样退出bootanimation.我们先假定此时bootanimation满足退出条件(propservice.bootanim.exit 1) bootanimation 在三种条件下可以退出.

  • 开始处理part的新循环或新part. 如果这部分part的处理标识不是”c”则可以退出.否则不能退出.

  • 开始处理新的一帧如果这部分part的处理标识不是”c”则可以退出.否则不能退出.

  • 处理完一次循环后如果这部分part的处理标识是”c”,并且这部分是无限循环,则可以退出.如果不是无限循环则不能退出需要完全处理完这部分的图片才可能退出.

总之如果我们设置part的处理标识设置为”c”,则需要好好考虑和组织我们animation的图片.一步小心可能会把百富美的HW的性能搞成矮穷锉HW的性能.(开机时间过长).不建议设置此标识位为”C”.

frameworks\base\cmds\bootanimation\bootAnimation.cpp

bool BootAnimation::movie()

{

….

elseif (sscanf(l, " %c %d %d %s #%6s", &pathType, &count,&pause, path, color) >= 4)

Animation::Part part;

part.playUntilComplete = pathType == 'c';

}

….

for(int r=0 ; !part.count || r<part.count ; r++) {

// Exitany non playuntil complete parts immediately

if(exitPending() &&!part.playUntilComplete) //check exit condition 1

break;

….

for(size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) //check exit condition 2

{

……

}

// For infinite parts, we've now played them at least once,so perhaps exit

if(exitPending()&& !part.count) //check exit condition 3

break;

}

我们知道boot animation在开机中占有重要地位,它也会影响开机时间.下面我们来讨论开机动画的工作流程.

1.2 开机动画的流程

在开机动画的生命周期中,至少有5个不同的进程参与其中.他们分别是:init ,surfaceflinger, system server,bootanimation Lancher.


  1. Kernel将会启动init 进程当kernel启动完成后.

  2. Init进程将会启动那些定义在init.rc 中的进程.zygote,surfaceflinger.

  3. Sufacefinger进程会启动bootanimation 进程当surfaceflinger 进程启动时.(SurfaceFlinger::startBootAnim()).

  4. Bootanimation进程处理开机动画.同时zygote 进程启动systemserver 进程. AMS ,WMS 将会在systemserver 进程里启动,进而启动launcher 进程.

  5. Launcher进程的UI线程将会call AMSactivityIdle() Launcher 进程完成了启动,并且没有其它event 需要处理.注意这是Android framwork的功能.每一个app启动完成后,在其主线程有空闲的时候(没有需要处理的event)都会call AMSactivityIdle().这个idle handler 是在handleResumeActivity中注册的.所以当app 启动完成后都会call AMSactivityIdle(). AMSactivityIdle()中会调用WindowManagerService. performEnableScreen() call.

    frameworks\base\services\core\java\com\android\server\am\ActivityThread.java

    final voidhandleResumeActivity(IBinder token,

    boolean clearHide, booleanisForward, boolean reallyResume)

    {

    ….

    f(!r.onlyLocalRequest) {

    r.nextIdle = mNewActivities;

    mNewActivities = r;

    if (localLOGV) Slog.v(

    TAG, "Scheduling idlehandler for " + r);

    Looper.myQueue().addIdleHandler(new Idler());

    }

    }

    Idler 定义.

    private classIdler implements MessageQueue.IdleHandler {

    @Override

    public final boolean queueIdle() {

    ActivityClientRecord a =mNewActivities;

    boolean stopProfiling = false;

    if (mBoundApplication != null&& mProfiler.profileFd != null

    &&mProfiler.autoStopProfiler) {

    stopProfiling = true;

    }

    if (a != null) {

    mNewActivities = null;

    IActivityManager am =ActivityManagerNative.getDefault();

    ActivityClientRecord prev;

    do {

    if (localLOGV) Slog.v(

    TAG, "Reportingidle of " + a +

    " finished=" +

    (a.activity != null&& a.activity.mFinished));

    if (a.activity != null&& !a.activity.mFinished) {

    try {

    am.activityIdle(a.token,a.createdConfig, stopProfiling); //这里会callAMS. activityIdle by binder

    a.createdConfig =null;

    } catch(RemoteException ex) {

    // Ignore

    }

    }

    prev = a;

    a = a.nextIdle;

    prev.nextIdle = null;

    } while (a != null);

    }

    if (stopProfiling) {

    mProfiler.stopProfiling();

    }

    ensureJitEnabled();

    return false;

    }

    }

  6. WindowManagerService.performEnableScreen()将通过发送IBinder.FIRST_CALL_TRANSACTION.从而callsurfaceFlinger. bootFinished().如果比较顺利的话.注意这里也是很容易由白富美变为矮穷锉如果KeyguardWallpaper处理得不好的话.开机动画和开机时间会边长.

    frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

    public voidperformEnableScreen() {

    synchronized(mWindowMap) {

    ……

    // Don't enable the screen untilall existing windows have been drawn.

    //这里会check KeyguardWallpaper.

    if (!mForceDisplayEnabled &&checkWaitingForWindowsLocked()) {

    return;

    }

    if (!mBootAnimationStopped) {

    // Do this one time.

    try {

    IBinder surfaceFlinger =ServiceManager.getService("SurfaceFlinger");

    if (surfaceFlinger != null){

    //Slog.i(TAG,"******* TELLING SURFACE FLINGER WE ARE BOOTED!");

    Parcel data = Parcel.obtain();

    data.writeInterfaceToken("android.ui.ISurfaceComposer");

    surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION,// BOOT_FINISHED

    data, null, 0);

    data.recycle();

    }

    } catch (RemoteException ex) {

    Slog.e(TAG, "Bootcompleted: SurfaceFlinger is dead!");

    }

    mBootAnimationStopped = true;

    }

    ….

    }

    private booleancheckWaitingForWindowsLocked(){

    …..

    final WindowList windows =getDefaultWindowListLocked();

    final int N = windows.size();

    for (int i=0; i<N; i++) {

    WindowState w = windows.get(i);

    if (w.isVisibleLw() &&!w.mObscured && !w.isDrawnLw()) {

    return true;

    }

    if (w.isDrawnLw()) {

    if (w.mAttrs.type ==TYPE_BOOT_PROGRESS) {

    haveBootMsg = true;

    } else if (w.mAttrs.type ==TYPE_APPLICATION) {

    haveApp = true;

    } else if (w.mAttrs.type ==TYPE_WALLPAPER) {

    haveWallpaper = true;

    } else if (w.mAttrs.type ==TYPE_STATUS_BAR) {

    haveKeyguard =mPolicy.isKeyguardDrawnLw();

    }

    }

    }

    // If we are turning on the screen toshow the boot message,

    // don't do it until the boot messageis actually displayed.

    if (!mSystemBooted &&!haveBootMsg) {

    return true;

    }

    ….

    // If we are turning on the screenafter the boot is completed

    // normally, don't do so until we havethe application and

    // wallpaper.

    if (mSystemBooted && ((!haveApp&& !haveKeyguard) ||

    (wallpaperEnabled &&!haveWallpaper))) {

    return true;

    }

    return false;

    }

  7. 如前所述Bootanimation 会在关键节点去check 是否需要exit. 当且仅当属性“service.bootanim.exit“ , bootanimation 会退出.

1.3 矮穷锉逆袭百富美

前面我们分析了开机动画的组织架构以及工作流程现在就到了最重要的时刻了.我们来看看矮穷锉如何逆袭百富美.那么哪些是矮穷锉的行为典型的行为有如下两点:

  • Kernel logo 的时间太长 (10s以上), 而开机动画的时间太短.

    如果出现这种现象,给人的第一印象便是这台手机的CPUTM 矮穷锉了我们来看看问题到底在哪.我们知道android 原来的开机动画design 为只有开机动画而没有开机铃声或者开机铃声不在bootanimation里处理所以其bootanimation 进程的user 定义为Graphic ,随着消费者要求的提高就出现了需要在bootanimation进程中处理audio,这样就需要修改bootanimation进程的用户为media. 因为auido service 只能media root 用户能访问.

    那么问题到底出现在哪里?我们来看看bootanimation 进程的定义大多数手机的定义如下:

    service bootanim/system/bin/bootanimation

    class core

    user media

    group graphics audio

    disabled

    oneshot

    user media.我们接着看其Bootanimation构造函数的定义.

  1. Bootanimationnew SurfaceComposerClient().

frameworks\base\cmds\bootanimation\bootAnimation.cpp

BootAnimation::BootAnimation(): Thread(false), mZip(NULL)

{

mSession = new SurfaceComposerClient();

}

b.SurfaceComposerClient的定义如下:

frameworks\native\libs\gui\SurfaceComposerClient.cpp

SurfaceComposerClient::SurfaceComposerClient()

: mStatus(NO_INIT),mComposer(Composer::getInstance())

{

}

c.在onFirstRef()会去连接surfacefinger.

voidSurfaceComposerClient::onFirstRef() {

sp<ISurfaceComposer>sm(ComposerService::getComposerService());

if (sm != 0) {

sp<ISurfaceComposerClient> conn =sm->createConnection();

if (conn != 0) {

mClient = conn;

mStatus = NO_ERROR;

}

}

}

d.连接surfaceflinger,check permission.现在**部分来了

frameworks\native\services\surfaceflinger\ SurfaceFlinger.cpp

status_tSurfaceFlinger::onTransact(

uint32_t code, constParcel& data, Parcel* reply, uint32_t flags)

{

switch (code) {

case CREATE_CONNECTION:

case CREATE_DISPLAY:

case SET_TRANSACTION_STATE:

case BOOT_FINISHED:

case CLEAR_ANIMATION_FRAME_STATS:

case GET_ANIMATION_FRAME_STATS:

case SET_POWER_MODE:

{

// codes that require permissioncheck

IPCThreadState* ipc =IPCThreadState::self();

const int pid =ipc->getCallingPid();

const int uid = ipc->getCallingUid();

if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) &&

!PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)){

ALOGE("Permission Denial:"

"can't accessSurfaceFlinger pid=%d, uid=%d", pid, uid);

return PERMISSION_DENIED;

}

break;

…..

}

}

e.我们从上面的代码中看到在surfaceflinger的很多操作例如连接,创建display和设置power 等都需要检查permission。除非进程的user graphics system。然而几乎在android的所有版本中,media 用户都是有ACCESS_SURFACE_FLINGER permission的。其定义在平台的permission文件中。

frameworks\base\data\etc\platform.xml

<assign-permissionname="android.permission.ACCESS_SURFACE_FLINGER"uid="media" />

<assign-permissionname="android.permission.ACCESS_SURFACE_FLINGER"uid="graphics" />

另一方面,permissioncheck是在javaservice permission service。当bootanimation开始运行的时候permission 还没有看是运行。Permisison service运行起来还需一段时间(89s)。因此开机动画在这里被阻塞了。其结果就是百富美变成了矮穷锉。如果我们要从矮穷锉完美的逆袭为百富美,只需做如下改动即可。我们为什么我们不把bootanimation 改为root 用户,我想你懂的。

if ((uid !=AID_GRAPHICS && uid != AID_SYSTEM) &&

!PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid))

if ((uid != AID_GRAPHICS && uid != AID_SYSTEM&&uid != AID_MEDIA) &&

!PermissionCache::checkPermission(sAccessSurfaceFlinger,pid, uid))

  • 开机动画fps 太低,不流程。

    我们知道很多开机动画要么是手机厂商logo或者手机的宣传片,要么是运行商的宣传片。可以说是手机的脸面,在现如今的刷脸时代,没有一个好的面孔是很难找到高大帅的。那么是什么原因导致矮穷锉满天下。我们还是看看code吧。

    frameworks\base\cmds\bootanimation\bootAnimation.cpp

    boolBootAnimation::movie()

    {

    ….

    for (size_t i=0 ; i<pcount ; i++) {

    for (int r=0 ; !part.count ||r<part.count ; r++) {

    …..

    for (size_t j=0 ; j<fcount&& (!exitPending() || part.playUntilComplete) ; j++) {

    ….

    initTexture(frame);

    }

    return false;

    }

    status_t BootAnimation::initTexture(Texture* texture, AssetManager&assets,

    const char* name) {

    SkBitmap bitmap;

    SkImageDecoder::DecodeMemory(asset->getBuffer(false),asset->getLength(),

    &bitmap, kUnknown_SkColorType,SkImageDecoder::kDecodePixels_Mode);

    asset->close();

    delete asset;

    ….

    glTexParameteriv(GL_TEXTURE_2D,GL_TEXTURE_CROP_RECT_OES, crop);

    glTexParameterx(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    glTexParameterx(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexParameterx(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_REPEAT);

    glTexParameterx(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_REPEAT);

    return NO_ERROR;

    }

    看出点什么名堂出来没。好像没有什么问题吧?是的,功能上是没有什么问题。那么从性能上看是否有问题的?对的,这里用的是SW decoder SkImageDecoder::DecodeMemory(),而且是单线程那么其解码效率如何?如果你巧合用了百富美平台,又巧合的对SW decoder进行了优化如neon指令做了优化,那么恭喜你。你已经没有此问题的烦恼。对此问题如何解决呢?聪明的你一定想到两种方案。一种是比较low的方案,继续往矮穷锉的方向发展,那就是降低开机动画的分辨率来提高解码速度从而提高开机动画的fps。另一种就是向百富美看其,用多线程来进行解码,把解码好的数据放到一个buffer queue中,需要用到解码的frame时直接从bufferqueue里拿而不是解一个frame,画一个frame。充分利用多核的计算能力。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android性能优化是为了提高Android应用程序的运行效率和响应速度,以提升用户体验。有多个方面需要考虑和优化,如布局优化、绘制优化、网络优化、安装包优化、内存优化、卡顿优化、耗电优化、列表和图片优化、数据库优化、启动优化、数据结构优化和稳定性优化等。 在布局优化方面,可以通过减少布局层次、使用ConstraintLayout等来提高布局效率。绘制优化可以通过使用ViewStub延迟加载视图、使用ViewHolder模式优化列表视图等来加快绘制速度。网络优化可以通过合理使用缓存、减少网络请求次数等来提高网络传输效率。安装包优化可以通过混淆代码、删除无用资源等来减小APK大小。 内存优化可以通过及时释放不再使用的资源、避免内存泄漏等措施来减少内存占用。卡顿优化可以通过使用异步加载、优化耗时操作等来提高应用的流畅度。耗电优化可以通过合理管理后台任务、优化网络请求等来降低电量消耗。 列表和图片优化可以通过使用分页加载、缓存图片等方式来提高列表和图片的加载速度和效率。数据库优化可以通过使用合适的索引、批量操作等来提高数据库的查询和写入性能。启动优化可以通过延迟初始化、减少启动时的资源加载等来加快应用的启动速度。数据结构优化可以通过选择合适的数据结构来提高数据的存储和检索效率。稳定性优化则需要通过全面的测试和错误处理来保证应用的稳定性。 综上所述,Android性能优化是一个多方面的工作,需要从各个方面入手,以提高应用程序的性能和用户体验。<span class="em">1</span><span class="em">2</span><span class="em">3</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值