简单介绍了安卓开机动画流程
// Bootanim.rc (frameworks\base\cmds\bootanimation)
service bootanim /system/bin/bootanimation
class core
user graphics
group graphics audio
disabled
oneshot
在 init 程序中注册了这样一个服务,那它在哪启动的?
答案见 老罗UI相关/Android系统的开机动画显示
在是 SurfaceFlinger 初始化时启动了,通过如下命令:
SurfaceFlinger::readyToRun()
{
property_set("ctl.start",bootanim);
}
上面的以 ctl 开头的是一个控制型属性,会让 init.cpp 启动 bootanim 服务。
// Z:\work\E262L_WW_0911_eng\frameworks\base\cmds\bootanimation\Android.mk
LOCAL_SRC_FILES:= \
bootanimation_main.cpp \
AudioPlayer.cpp \
BootAnimation.cpp
LOCAL_MODULE:= bootanimation
//Bootanimation_main.cpp (frameworks\base\cmds\bootanimation)
int main(int argc, char** argv)
{
// 首先检查系统属性“debug.sf.nobootnimaition”的值是否不等于0。
// 如果不等于的话,那么接下来就会启动一个Binder线程池,并且创建一个BootAnimation对象
// 这个BootAnimation对象就是用来显示第三个开机画面的。
// 由于BootAnimation对象在显示第三个开机画面的过程中,需要与SurfaceFlinger服务通信
// 因此,应用程序bootanimation就需要启动一个Binder线程池
property_get("debug.sf.nobootanimation", value, "0");
int noBootAnimation = atoi(value);
if (!noBootAnimation) {
sp proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// BootAnimation类间接地继承了RefBase类,并且重写了RefBase类的成员函数onFirstRef,
// 因此,当一个BootAnimation对象第一次被智能指针引用的时,这个BootAnimation对象的成员函数onFirstRef就会被调用
sp boot = new BootAnimation(setBoot,sePaly,setRotated);
/
// BootAnimation.cpp (frameworks\base\cmds\bootanimation)
BootAnimation::onFirstRef()
// 由于BootAnimation类引用了SurfaceFlinger服务,因此,当SurfaceFlinger服务意外死亡时,BootAnimation类就需要得到通知
// 通过调用成员变量mSession的成员函数linkToComposerDeath来注册SurfaceFlinger服务的死亡接收通知来实现的
status_t err = mSession->linkToComposerDeath(this);
// BootAnimation类继承了Thread类,因此,当BootAnimation类的成员函数onFirstRef调用了父类Thread的成员函数run之后
// 系统就会创建一个线程,这个线程在第一次运行之前,会调用BootAnimation类的成员函数readyToRun来执行一些初始化工作
// 后面再调用BootAnimation类的成员函数htreadLoop来显示第三个开机画面
run("BootAnimation", PRIORITY_DISPLAY);
//
// 首先会进行一些初始化操作:
// 获取相关动画资源
// 申请一些 Surface 资源进行显示
BootAnimation::readyToRun()
// 获得开关机动画 .zip 文件,相关资源路径为:
// char mResourcePath_gb[PATH_COUNT_USP][PROPERTY_VALUE_MAX] =
// {
// "/custom/media/", // 0
// "/system/media/" // 1
// };
// 或者:
// static const char* mResourcePath[MNC_COUNT][PATH_COUNT] =
// {{"/system/media/bootanimation1.zip", "/custom/media/bootanimation1.zip", "/data/local/bootanimation1.zip"}, // 0
// {"/system/media/bootanimation2.zip", "/custom/media/bootanimation2.zip", "/data/local/bootanimation2.zip"} // 1
// };
if (bBootOrShutDown) {
initBootanimationZip();
} else {
initShutanimationZip();
}
// 获得开关机动车里面的 desc.txt 文件
ZipEntryRO desc = mZip->findEntryByName("desc.txt");
mZip->getEntryInfo(desc, &method, NULL, NULL, NULL, NULL, NULL);
// 进行 Surface 资源获得
// 进行 EGL 相关操作
//
// 再进行动画显示
BootAnimation::threadLoop()
// 首先进行开机声音播放
if (resourcePath != NULL) {
bPlayMP3 = true;
ALOGD("sound file path: %s", resourcePath);
mediaplayer = new MediaPlayer();
mediastatus = mediaplayer->setDataSource(NULL, resourcePath, NULL);
sp listener = new BootVideoListener(this);
mediaplayer->setListener(listener);
if (mediastatus == NO_ERROR) {
ALOGD("mediaplayer is initialized");
Parcel* attributes = new Parcel();
attributes->writeInt32(AUDIO_USAGE_MEDIA); //usage
attributes->writeInt32(AUDIO_CONTENT_TYPE_MUSIC); //audio_content_type_t
attributes->writeInt32(AUDIO_SOURCE_DEFAULT); //audio_source_t
attributes->writeInt32(0); //audio_flags_mask_t
attributes->writeInt32(1); //kAudioAttributesMarshallTagFlattenTags of mediaplayerservice.cpp
attributes->writeString16(String16("BootAnimationAudioTrack")); // tags
mediaplayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *attributes);
mediaplayer->setAudioStreamType(AUDIO_STREAM_MUSIC);
mediastatus = mediaplayer->prepare();
}
if (mediastatus == NO_ERROR) {
ALOGD("media player is prepared");
mediastatus = mediaplayer->start();
}
}else{
bPlayMP3 = false;
}
// 如果没有客制化开机动画,就使用 android() 显示默认开机动画
if ((mZip == NULL)&&(mZipFileName.isEmpty())) {
r = android();
// 就是一个安卓字然后亮边滚动
// 原理如下:
// Android 系统默认的开机动画是由两张图片 android-logo-mask.png 和 android-logo-shine.png中。
// 这两张图片保存在 frameworks/base/core/res/assets/images 目录中,它们最终会被编译在 framework-res
// 模块(frameworks/base/core/res)中,即编译在framework-res.apk文件中。编译在 framework-res 模块中的
// 资源文件可以通过 AssetManager 类来访问。
//
// BootAnimation 类的成员函数 android 首先调用另外一个成员函数 initTexture 来将根据图片 android-logo-mask.png
// 和 android-logo-shine.png 的内容来分别创建两个纹理对象,这两个纹理对象就分别保存在 BootAnimation 类的成员变量
// mAndroid 所描述的一个数组中。通过混合渲染这两个纹理对象,我们就可以得到一个开机动画,这是通过中间的 while 循环语句来实现的。
//
// 图片 android-logo-mask.png 用作动画前景,它是一个镂空的“ANDROID”图像。图片android-logo-shine.png用作动画背
// 景,它的中间包含有一个高亮的呈 45 度角的条纹。在每一次循环中,图片 android-logo-shine.png 被划分成左右两部分内容
// 来显示。左右两个部分的图像宽度随着时间的推移而此消彼长,这样就可以使得图片android-logo-shine.png中间高亮的条纹好
// 像在移动一样。另一方面,在每一次循环中,图片android-logo-shine.png都作为一个整体来渲染,而且它的位置是恒定不变的。
// 由于它是一个镂空的“ANDROID”图像,因此,我们就可以通过它的镂空来看到它背后的图片android-logo-shine.png的条纹一闪一闪地划过。
//
// 这个while循环语句会一直被执行,直到应用程序/system/bin/bootanimation被结束为止
} else {
// 否则就使用 movie() 来显示客制化的开机动画
if (!bETC1Movie) {
ALOGD("threadLoop() movie()");
r = movie();
// 显示用户自定义的动画:
相关原理如下:
从前面 BootAnimation 类的成员函数 readyToRun 的实现可以知道,如果目标设备上存在压缩文件 /data/local/bootanimation.zip,
那么BootAnimation类的成员变量mZip就会指向它,否则的话,就会指向目标设备上的压缩文件/system/media/bootanimation.zip。无论
BootAnimation类的成员变量mZip指向的是哪一个压缩文件,这个压缩文件都必须包含有一个名称为“desc.txt”的文件,用来描述用户自
定义的开机动画是如何显示的。
文件desc.txt的内容格式如下面的例子所示:
600 480 24
p 1 0 part1
p 0 10 part2
第一行的三个数字分别表示开机动画在屏幕中的显示宽度、高度以及帧速(fps)。剩余的每一行都用来描述一个动画片断,
这些行必须要以字符“p”来开头,后面紧跟着两个数字以及一个文件目录路径名称。第一个数字表示一个片断的循环显示次数,
如果它的值等于0,那么就表示无限循环地显示该动画片断。第二个数字表示每一个片断在两次循环显示之间的时间间隔。这个时
间间隔是以一个帧的时间为单位的。文件目录下面保存的是一系列png文件,这些png文件会被依次显示在屏幕中。
以上面这个desct.txt文件的内容为例:
它描述了一个大小为600 x 480的开机动画,动画的显示速度为24帧每秒。
这个开机动画包含有两个片断 part1 和 part2。
片断part1只显示一次,它对应的png图片保存在目录part1中。
片断part2无限循环地显示,其中,每两次循环显示的时间间隔为10 x (1 / 24)秒,它对应的png图片保存在目录part2中。
上面的for循环语句分析完成desc.txt文件的内容后,就得到了开机动画的显示大小、速度以及片断信息。这些信息都保存在Animation
对象animation中,其中,每一个动画片断都使用一个Animation::Part对象来描述,并且保存在Animation对象animation的成员变量parts所
描述的一个片断列表中。
接下来,BootAnimation类的成员函数movie再断续将每一个片断所对应的png图片读取出来,每一个png图片都表示一个动画帧,使用一个
Animation::Frame对象来描述,并且保存在对应的Animation::Part对象的成员变量frames所描述的一个帧列表中。
获得了开机动画的所有信息之后,接下来BootAnimation类的成员函数movie就准备开始显示开机动画了
前面的一系列gl函数首先用来清理屏幕,接下来的一系列gl函数用来设置OpenGL的纹理显示方式。
变量 xc 和 yc 的值用来描述开机动画的显示位置,即需要在屏幕中间显示开机动画,另外一个变量frameDuration的值
用来描述每一帧的显示时间,它是以纳秒为单位的。
Region对象clearReg用来描述屏幕中除了开机动画之外的其它区域,它是用整个屏幕区域减去开机动画所点据的区域来得到的。
准备好开机动画的显示参数之后,最后就可以执行显示开机动画的操作了
} else {
ALOGD("threadLoop() ETC1movie()");
r = ETC1movie();
}
}
IPCThreadState::self()->joinThreadPool();
}
}
从前面Android系统默认Home应用程序(Launcher)的启动过程源代码分析一文可以知道,当System进程将系统中的关键服务启动起来之后,就会
将应用程序启动器(Launcher)启动起来。从Android应用程序启动过程源代码分析一文又可以知道,Android应用程序的启动过程实际上就是它的根
Activity组件的启动过程。对于应用程序Launcher来说,它的根Activity组件即为Launcher组件。
一个Activity组件在启动起来之后,就会被记录起来,等到它所运行在的主线程空闲的时候,这个主线程就会向ActivityManagerService发送一
个Activity组件空闲的通知。由于应用程序Launcher是系统中第一个被启动的应用程序,即它的根Activity组件是系统中第一个被启动的Activity组件,
因此,当ActivityManagerService接收到它的空闲通知的时候,就可以知道系统是刚刚启动起来的。在这种情况下,ActivityManagerService就会停
止显示开机动画,以便可以在屏幕中显示应用程序 Lancher 的界面。
相关流程如下:具体参见<<老罗 UI相关/Android系统的开机动画显示>>
ActivityManagerService
=>WindowManagerService
=>SurfaceFlinger
frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp
property_set("ctl.stop", "bootanim");