前言
經(jīng)過(guò)上一篇文章的探索和學(xué)習(xí),相信大家對(duì)Hal 層的運(yùn)作原理以及SF如何監(jiān)聽(tīng)Hal層返回的回調(diào)有一定的了解挤茄。
原本應(yīng)該是聊如何申請(qǐng)圖元的,但是感覺(jué)和前文的邏輯割裂有點(diǎn)大,還是繼續(xù)按照SF初始化晴圾,開(kāi)機(jī)的邏輯順序繼續(xù)走下去。這一次就讓我們聊聊系統(tǒng)啟動(dòng)動(dòng)畫(huà)吧噪奄。
帶著這個(gè)疑問(wèn)去閱讀死姚,開(kāi)機(jī)的時(shí)候沒(méi)有Activity為什么可以播放開(kāi)機(jī)動(dòng)畫(huà)呢?注意Android系統(tǒng)中存在幾個(gè)開(kāi)機(jī)動(dòng)畫(huà)勤篮,這里不去聊Linux開(kāi)機(jī)動(dòng)畫(huà)都毒,我們只聊Android渲染系統(tǒng)中的開(kāi)機(jī)動(dòng)畫(huà),也就是我們打開(kāi)手機(jī)時(shí)候的那個(gè)動(dòng)畫(huà)叙谨。
如果遇到問(wèn)題温鸽,可以到本文來(lái)討論http://www.reibang.com/p/a79de4a6d83c
正文
解析init.rc service的原理
文件:/frameworks/base/cmds/bootanimation/bootanim.rc
service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics audio
disabled
oneshot
writepid /dev/stune/top-app/tasks
開(kāi)機(jī)就啟動(dòng)進(jìn)程,那肯定就要從rc里面找。負(fù)責(zé)開(kāi)機(jī)動(dòng)畫(huà)的進(jìn)程是bootanimation涤垫。上面是他的rc文件姑尺。值得注意的是,設(shè)置了disable標(biāo)志位蝠猬。
我們翻翻看在init進(jìn)程,是怎么解析service的切蟋。在Android9.0中實(shí)際上會(huì)把service,action榆芦,import等都會(huì)進(jìn)行字符串解析額柄粹,最后分散在三個(gè)鏈表等待執(zhí)行。
文件:/system/core/init/service.cpp
const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map option_parsers = {
{"capabilities",
{1, kMax, &Service::ParseCapabilities}},
{"class", {1, kMax, &Service::ParseClass}},
{"console", {0, 1, &Service::ParseConsole}},
{"critical", {0, 0, &Service::ParseCritical}},
{"disabled", {0, 0, &Service::ParseDisabled}},
{"enter_namespace",
{2, 2, &Service::ParseEnterNamespace}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
{"interface", {2, 2, &Service::ParseInterface}},
{"ioprio", {2, 2, &Service::ParseIoprio}},
{"priority", {1, 1, &Service::ParsePriority}},
{"keycodes", {1, kMax, &Service::ParseKeycodes}},
{"oneshot", {0, 0, &Service::ParseOneshot}},
{"onrestart", {1, kMax, &Service::ParseOnrestart}},
{"override", {0, 0, &Service::ParseOverride}},
{"oom_score_adjust",
{1, 1, &Service::ParseOomScoreAdjust}},
{"memcg.swappiness",
{1, 1, &Service::ParseMemcgSwappiness}},
{"memcg.soft_limit_in_bytes",
{1, 1, &Service::ParseMemcgSoftLimitInBytes}},
{"memcg.limit_in_bytes",
{1, 1, &Service::ParseMemcgLimitInBytes}},
{"namespace", {1, 2, &Service::ParseNamespace}},
{"rlimit", {3, 3, &Service::ParseProcessRlimit}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
{"shutdown", {1, 1, &Service::ParseShutdown}},
{"socket", {3, 6, &Service::ParseSocket}},
{"file", {2, 2, &Service::ParseFile}},
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
// clang-format on
return option_parsers;
}
在這個(gè)map中寫(xiě)好了每一個(gè)命令對(duì)應(yīng)的解析方法指針匆绣。我們直接看看disable做了什么:
Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
flags_ |= SVC_DISABLED;
flags_ |= SVC_RC_DISABLED;
return Success();
}
很簡(jiǎn)單就是設(shè)置了DISABLED的flag驻右。
當(dāng)解析完畢將會(huì)嘗試著執(zhí)行解析好的service的命令。
Result<Success> Service::Start() {
bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
if (flags_ & SVC_RUNNING) {
if ((flags_ & SVC_ONESHOT) && disabled) {
flags_ |= SVC_RESTART;
}
// It is not an error to try to start a service that is already running.
return Success();
}
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console) {
if (console_.empty()) {
console_ = default_console;
}
int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
if (console_fd < 0) {
flags_ |= SVC_DISABLED;
return ErrnoError() << "Couldn't open console '" << console_ << "'";
}
close(console_fd);
}
...
pid_t pid = -1;
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork();
}
if (pid == 0) {
...
}
if (pid < 0) {
pid_ = 0;
return ErrnoError() << "Failed to fork";
}
...
return Success();
}
能看到如果沒(méi)有disable崎淳,reset這些標(biāo)志位阻礙堪夭,將會(huì)通過(guò)fork系統(tǒng)生成一個(gè)新的進(jìn)程。但是這里disable了拣凹,因此不會(huì)走下來(lái)森爽,而是會(huì)把解析結(jié)果保存起來(lái)。保存在ServiceList對(duì)象中的services_ vector集合嚣镜。
到這里似乎邏輯斷開(kāi)了爬迟,我們先把思路阻塞到這里。稍后就能看到菊匿。
SF啟動(dòng)開(kāi)機(jī)動(dòng)畫(huà)
在SF的init方法中付呕,我為了獨(dú)立出一節(jié)出來(lái)藻雪,故意有一段沒(méi)有解析池颈。如下:
文件:/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
if (getHwComposer().hasCapability(
HWC2::Capability::PresentFenceIsNotReliable)) {
mStartPropertySetThread = new StartPropertySetThread(false);
} else {
mStartPropertySetThread = new StartPropertySetThread(true);
}
if (mStartPropertySetThread->Start() != NO_ERROR) {
ALOGE("Run StartPropertySetThread failed!");
}
這個(gè)方法是干什么的呢?我第一次看的時(shí)候差點(diǎn)看漏了豌鸡,差點(diǎn)找不到哪里啟動(dòng)開(kāi)機(jī)動(dòng)畫(huà)疹蛉,其實(shí)是把事情交給了StartPropertySetThread完成活箕。
文件:/frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp
StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}
status_t StartPropertySetThread::Start() {
return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}
bool StartPropertySetThread::threadLoop() {
// Set property service.sf.present_timestamp, consumer need check its readiness
property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
// Clear BootAnimation exit flag
property_set("service.bootanim.exit", "0");
// Start BootAnimation if not started
property_set("ctl.start", "bootanim");
// Exit immediately
return false;
}
能看到在這里設(shè)置了兩個(gè)系統(tǒng)屬性,一個(gè)是service.bootanim.exit設(shè)置為0可款,另一個(gè)則是開(kāi)機(jī)的關(guān)鍵育韩,設(shè)置了ctl.start中的參數(shù)為bootanim。這樣就能啟動(dòng)開(kāi)機(jī)動(dòng)畫(huà)的進(jìn)程闺鲸。
為什么如此呢筋讨?我們還是要回到init進(jìn)程的main函數(shù)中。
文件:/system/core/init/init.cpp
int main(int argc, char** argv) {
....
...
start_property_service();
...
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
subcontexts = InitializeSubcontexts();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
...
return 0;
}
我們忽律掉最下面init.cpp監(jiān)聽(tīng)epoll消息的邏輯摸恍,其實(shí)在這個(gè)過(guò)程中還通過(guò)start_property_service啟動(dòng)了一個(gè)檢測(cè)Android全局配置屬性變化服務(wù)悉罕。
文件:http://androidxref.com/9.0.0_r3/xref/system/core/init/property_service.cpp
void start_property_service() {
selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr);
if (property_set_fd == -1) {
...
}
listen(property_set_fd, 8);
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
能看到在這個(gè)過(guò)程中赤屋,設(shè)置了版本號(hào)為2,啟動(dòng)了一個(gè)名字為property_service的socket服務(wù)壁袄。然后對(duì)這個(gè)服務(wù)進(jìn)行監(jiān)聽(tīng)类早,把property_set_fd注冊(cè)到poll中,注冊(cè)一個(gè)handle_property_set_fd回調(diào)事件嗜逻。
我們先來(lái)看看property_set中做了什么事情:
文件:/bionic/libc/bionic/system_property_set.cpp
int __system_property_set(const char* key, const char* value) {
if (key == nullptr) return -1;
if (value == nullptr) value = "";
if (g_propservice_protocol_version == 0) {
detect_protocol_version();
}
if (g_propservice_protocol_version == kProtocolVersion1) {
....
} else {
if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
// Use proper protocol
PropertyServiceConnection connection;
if (!connection.IsValid()) {
errno = connection.GetLastError();
...
return -1;
}
SocketWriter writer(&connection);
if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {
errno = connection.GetLastError();
...
return -1;
}
int result = -1;
if (!connection.RecvInt32(&result)) {
errno = connection.GetLastError();
...
return -1;
}
...
return 0;
}
}
因?yàn)榘姹咎?hào)為2.其實(shí)它就會(huì)走下面的分之涩僻,此時(shí)能看到這不是簡(jiǎn)單的寫(xiě)入文件,而是寫(xiě)到了一個(gè)socket 中栈顷。
而這個(gè)socket是什么逆日?
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
PropertyServiceConnection() : last_error_(0) {
socket_ = ::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (socket_ == -1) {
last_error_ = errno;
return;
}
const size_t namelen = strlen(property_service_socket);
sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
addr.sun_family = AF_LOCAL;
socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
if (TEMP_FAILURE_RETRY(connect(socket_, reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
last_error_ = errno;
close(socket_);
socket_ = -1;
}
}
我們能看到他的地址其實(shí)是dev/socket下的property_service。也就是剛好是上面注冊(cè)的socket萄凤。
關(guān)鍵來(lái)看這個(gè)方法:
static void handle_property_set_fd() {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
if (s == -1) {
return;
}
ucred cr;
socklen_t cr_size = sizeof(cr);
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
close(s);
return;
}
SocketConnection socket(s, cr);
uint32_t timeout_ms = kDefaultSocketTimeout;
uint32_t cmd = 0;
if (!socket.RecvUint32(&cmd, &timeout_ms)) {
socket.SendUint32(PROP_ERROR_READ_CMD);
return;
}
switch (cmd) {
case PROP_MSG_SETPROP: {
...
}
case PROP_MSG_SETPROP2: {
std::string name;
std::string value;
if (!socket.RecvString(&name, &timeout_ms) ||
!socket.RecvString(&value, &timeout_ms)) {
socket.SendUint32(PROP_ERROR_READ_DATA);
return;
}
const auto& cr = socket.cred();
std::string error;
uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
if (result != PROP_SUCCESS) {
...
}
socket.SendUint32(result);
break;
}
default:
socket.SendUint32(PROP_ERROR_INVALID_CMD);
break;
}
}
從上面得知室抽,首先會(huì)寫(xiě)入PROP_MSG_SETPROP2一個(gè)命令,此時(shí)就會(huì)走到下面這個(gè)分之蛙卤,接著通過(guò)RecvString讀取數(shù)據(jù)內(nèi)容狠半,執(zhí)行HandlePropertySet。
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error) {
...
if (StartsWith(name, "ctl.")) {
if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
*error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
HandleControlMessage(name.c_str() + 4, value, cr.pid);
return PROP_SUCCESS;
}
...
return PropertySet(name, value, error);
}
在SF中輸送了兩個(gè)屬性過(guò)來(lái)颤难,其中使用HandleControlMessage對(duì)ctl.進(jìn)行了特殊處理。
static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
// clang-format off
static const std::map<std::string, ControlMessageFunction> control_message_functions = {
{"start", {ControlTarget::SERVICE, DoControlStart}},
{"stop", {ControlTarget::SERVICE, DoControlStop}},
{"restart", {ControlTarget::SERVICE, DoControlRestart}},
{"interface_start", {ControlTarget::INTERFACE, DoControlStart}},
{"interface_stop", {ControlTarget::INTERFACE, DoControlStop}},
{"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}},
};
// clang-format on
return control_message_functions;
}
static Result<Success> DoControlStart(Service* service) {
return service->Start();
}
void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
const auto& map = get_control_message_map();
const auto it = map.find(msg);
if (it == map.end()) {
...
return;
}
std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
std::string process_cmdline;
if (ReadFileToString(cmdline_path, &process_cmdline)) {
std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' ');
process_cmdline = Trim(process_cmdline);
} else {
process_cmdline = "unknown process";
}
...
const ControlMessageFunction& function = it->second;
if (function.target == ControlTarget::SERVICE) {
Service* svc = ServiceList::GetInstance().FindService(name);
if (svc == nullptr) {
...
return;
}
if (auto result = function.action(svc); !result) {
...
}
return;
}
....
}
這里面的邏輯十分簡(jiǎn)單已维,本質(zhì)上就是繼續(xù)獲取從property_set傳遞過(guò)來(lái)后續(xù)的字符串行嗤。也就是ctl.xxx點(diǎn)后面的命令對(duì)應(yīng)的方法。并且先通過(guò)serviceList找到解析好的服務(wù)垛耳,調(diào)用緩存在map中命令start對(duì)應(yīng)的方法指針栅屏,也就是service的start。就重新走到上面Service::Start fork出進(jìn)程的邏輯中堂鲜。
BootAnimation進(jìn)程啟動(dòng)
int main()
{
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
bool noBootAnimation = bootAnimationDisabled();
ALOGI_IF(noBootAnimation, "boot animation disabled");
if (!noBootAnimation) {
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
waitForSurfaceFlinger();
// create the boot animation object
sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
ALOGV("Boot animation set up. Joining pool.");
IPCThreadState::self()->joinThreadPool();
}
ALOGV("Boot animation exit");
return 0;
}
首先默認(rèn)當(dāng)前noBootAnimation是false栈雳。因此會(huì)初始化Binder的驅(qū)動(dòng),調(diào)用waitForSurfaceFlinger從serviceManager中查找SF進(jìn)程缔莲,找到才生成一個(gè)BootAnimation對(duì)象準(zhǔn)備執(zhí)行開(kāi)機(jī)動(dòng)畫(huà)哥纫,并設(shè)置了一個(gè)音軌的回調(diào)。
BootAnimation 初始化
接下來(lái)就會(huì)揭開(kāi)本片文章的秘密痴奏,為什么沒(méi)有Activity還是能夠顯示界面蛀骇。其核心原理是什么。
文件:/frameworks/base/cmds/bootanimation/BootAnimation.cpp
BootAnimation::BootAnimation(sp<Callbacks> callbacks)
: Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
mTimeFormat12Hour(false), mTimeCheckThread(NULL), mCallbacks(callbacks) {
mSession = new SurfaceComposerClient();
...
}
在這個(gè)構(gòu)造函數(shù)中读拆,生成了一個(gè)十分重要的對(duì)象SurfaceComposerClient擅憔。因?yàn)镾urfaceComposerClient是一個(gè)sp強(qiáng)智能指針,會(huì)繼續(xù)走到onFirstRef中檐晕。
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != 0 && mStatus == NO_INIT) {
auto rootProducer = mParent.promote();
sp<ISurfaceComposerClient> conn;
conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) :
sf->createConnection();
if (conn != 0) {
mClient = conn;
mStatus = NO_ERROR;
}
}
}
先拿到一個(gè)單例的ComposerService 服務(wù)對(duì)象暑诸,接著通過(guò)ISurfaceComposer通過(guò)createScopedConnection通信創(chuàng)建一個(gè)ISurfaceComposerClient。
ISurfaceComposer指代的是什么,ISurfaceComposerClient又是指什么呢个榕?
ComposerService的初始化
/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
ComposerService& instance = ComposerService::getInstance();
Mutex::Autolock _l(instance.mLock);
if (instance.mComposerService == NULL) {
ComposerService::getInstance().connectLocked();
assert(instance.mComposerService != NULL);
ALOGD("ComposerService reconnected");
}
return instance.mComposerService;
}
能看到它實(shí)際上是一個(gè)單例設(shè)計(jì),返回了ISurfaceComposer對(duì)象篡石。
ComposerService::ComposerService()
: Singleton<ComposerService>() {
Mutex::Autolock _l(mLock);
connectLocked();
}
void ComposerService::connectLocked() {
const String16 name("SurfaceFlinger");
while (getService(name, &mComposerService) != NO_ERROR) {
usleep(250000);
}
assert(mComposerService != NULL);
// Create the death listener.
class DeathObserver : public IBinder::DeathRecipient {
ComposerService& mComposerService;
virtual void binderDied(const wp<IBinder>& who) {
ALOGW("ComposerService remote (surfaceflinger) died [%p]",
who.unsafe_get());
mComposerService.composerServiceDied();
}
public:
explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
};
mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));
IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
}
能看到在這個(gè)過(guò)程中,會(huì)從ServiceManager查找SF在Binder驅(qū)動(dòng)中映射的服務(wù)笛洛。并且綁定上另一個(gè)死亡代理夏志,用于銷(xiāo)毀ComposerService中的資源。
到此時(shí)SF的binder接口已經(jīng)對(duì)著B(niǎo)ootAnimation暴露了苛让。
了解ISurfaceComposer 本質(zhì)上就是SF的遠(yuǎn)程端口沟蔑,接下來(lái)再看看ISurfaceComposerClient是什么。
ISurfaceComposer createConnection
實(shí)際調(diào)用的是SF的createConnection
文件:/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
return initClient(new Client(this));
}
文件:/frameworks/native/services/surfaceflinger/Client.cpp
class Client : public BnSurfaceComposerClient
Client::Client(const sp<SurfaceFlinger>& flinger)
: Client(flinger, nullptr)
{
}
本質(zhì)上就是一個(gè)實(shí)現(xiàn)了BnSurfaceComposerClient的Client對(duì)象狱杰。之后關(guān)于渲染的對(duì)象將會(huì)從中生成瘦材。
BootAnimation onFirstRef
void BootAnimation::onFirstRef() {
status_t err = mSession->linkToComposerDeath(this);
ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
if (err == NO_ERROR) {
run("BootAnimation", PRIORITY_DISPLAY);
}
}
當(dāng)通過(guò)SurfaceComposerClient鏈接到遠(yuǎn)程Binder服務(wù)后,就會(huì)執(zhí)行run方法仿畸。
文件:/frameworks/native/libs/gui/SurfaceComposerClient.cpp
status_t SurfaceComposerClient::linkToComposerDeath(
const sp<IBinder::DeathRecipient>& recipient,
void* cookie, uint32_t flags) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
return IInterface::asBinder(sf)->linkToDeath(recipient, cookie, flags);
}
能看到本質(zhì)上是生成一個(gè)ComposerService BpBinder對(duì)象食棕,并且進(jìn)行Binder的死亡代理綁定。
BootAnimation run
本質(zhì)上BootAnimation還是一個(gè)線(xiàn)程:
class BootAnimation : public Thread, public IBinder::DeathRecipient
因此執(zhí)行run之后错沽,會(huì)先執(zhí)行readyToRun簿晓,接著執(zhí)行treadLoop方法。注意這兩個(gè)方法都已經(jīng)在線(xiàn)程中了千埃。
BootAnimation readyToRun 準(zhǔn)備渲染流程
status_t BootAnimation::readyToRun() {
mAssets.addDefaultAssets();
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
DisplayInfo dinfo;
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
if (status)
return -1;
// create the native surface
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::Transaction t;
t.setLayer(control, 0x40000000)
.apply();
sp<Surface> s = control->getSurface();
// initialize opengl and egl
const EGLint attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_NONE
};
EGLint w, h;
EGLint numConfigs;
EGLConfig config;
EGLSurface surface;
EGLContext context;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, 0, 0);
eglChooseConfig(display, attribs, &config, 1, &numConfigs);
surface = eglCreateWindowSurface(display, config, s.get(), NULL);
context = eglCreateContext(display, config, NULL, NULL);
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
return NO_INIT;
mDisplay = display;
mContext = context;
mSurface = surface;
mWidth = w;
mHeight = h;
mFlingerSurfaceControl = control;
mFlingerSurface = s;
// If the device has encryption turned on or is in process
// of being encrypted we show the encrypted boot animation.
char decrypt[PROPERTY_VALUE_MAX];
property_get("vold.decrypt", decrypt, "");
bool encryptedAnimation = atoi(decrypt) != 0 ||
!strcmp("trigger_restart_min_framework", decrypt);
if (!mShuttingDown && encryptedAnimation) {
static const char* encryptedBootFiles[] =
{PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE};
for (const char* f : encryptedBootFiles) {
if (access(f, R_OK) == 0) {
mZipFileName = f;
return NO_ERROR;
}
}
}
static const char* bootFiles[] =
{PRODUCT_BOOTANIMATION_FILE, OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
static const char* shutdownFiles[] =
{PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE};
for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
if (access(f, R_OK) == 0) {
mZipFileName = f;
return NO_ERROR;
}
}
return NO_ERROR;
}
在準(zhǔn)備渲染流程中憔儿,做的事情有如下幾個(gè)步驟:
- 1.SurfaceComposerClient::getBuiltInDisplay 從SF中查詢(xún)可用的物理屏幕
- 2.SurfaceComposerClient::getDisplayInfo 從SF中獲取屏幕的詳細(xì)信息
- 3.session()->createSurface 通過(guò)Client創(chuàng)建繪制平面控制中心
- 4.t.setLayer(control, 0x40000000) 設(shè)置當(dāng)前l(fā)ayer的層級(jí)
- 5.control->getSurface 獲取真正的繪制平面對(duì)象
- 6.eglGetDisplay 獲取opengl es的默認(rèn)主屏幕,加載OpenGL es
- 7.eglInitialize 初始化屏幕對(duì)象和著色器緩存
- 8.eglChooseConfig 自動(dòng)篩選出最合適的配置
- 9.eglCreateWindowSurface 從Surface中創(chuàng)建一個(gè)opengl es的surface
- 10.eglCreateContext 創(chuàng)建當(dāng)前opengl es 的上下文
- 11.eglQuerySurface 查找當(dāng)前環(huán)境的寬高屬性
- 12.eglMakeCurrent 把上下文Context放可,屏幕display還有渲染面surface,線(xiàn)程關(guān)聯(lián)起來(lái)谒臼。
- 13.從如下幾個(gè)目錄查找zip文件,分為兩種模式耀里,一種是加密文件的動(dòng)畫(huà)蜈缤,一種是普通壓縮文件動(dòng)畫(huà):
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
static const char PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[] = "/product/media/bootanimation-encrypted.zip";
static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip";
static const char PRODUCT_SHUTDOWNANIMATION_FILE[] = "/product/media/shutdownanimation.zip";
static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip";
在這些zip包中其實(shí)就是一張張圖片。播放的時(shí)候冯挎,解壓這些zip包底哥,一張張的圖片想動(dòng)畫(huà)一樣播出。
從第6點(diǎn)開(kāi)始就是opengl es開(kāi)發(fā)初四話(huà)流程的套路织堂。其實(shí)真的核心的準(zhǔn)備核心還是前5點(diǎn)叠艳。opengles的環(huán)境真正和Android系統(tǒng)關(guān)聯(lián)起來(lái)是是第9點(diǎn)創(chuàng)建opengles的surface時(shí)候和Android的本地窗口互相綁定。
BootAnimation threadLoop
bool BootAnimation::threadLoop()
{
bool r;
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZipFileName.isEmpty()) {
r = android();
} else {
r = movie();
}
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(mDisplay, mContext);
eglDestroySurface(mDisplay, mSurface);
mFlingerSurface.clear();
mFlingerSurfaceControl.clear();
eglTerminate(mDisplay);
eglReleaseThread();
IPCThreadState::self()->stopProcess();
return r;
}
在這里如果設(shè)定了bootanimation的zip壓縮包則會(huì)走movie解壓播放zip中的動(dòng)畫(huà)易阳,否則就會(huì)走android默認(rèn)動(dòng)畫(huà)附较。
BootAnimation moive
既然老羅解析了Android默認(rèn)的動(dòng)畫(huà),我就去解析Android自定義動(dòng)畫(huà)的核心原理潦俺。
bool BootAnimation::movie()
{
Animation* animation = loadAnimation(mZipFileName);
if (animation == NULL)
return false;
...
mUseNpotTextures = false;
String8 gl_extensions;
const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
if (!exts) {
glGetError();
} else {
gl_extensions.setTo(exts);
if ((gl_extensions.find("GL_ARB_texture_non_power_of_two") != -1) ||
(gl_extensions.find("GL_OES_texture_npot") != -1)) {
mUseNpotTextures = true;
}
}
// Blend required to draw time on top of animation frames.
//設(shè)置混合顏色模式 后面是1 - 原圖的四個(gè)顏色分量
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//設(shè)置顏色過(guò)度模式拒课,非平滑模式
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
//綁定紋理
glBindTexture(GL_TEXTURE_2D, 0);
//啟動(dòng)紋理
glEnable(GL_TEXTURE_2D);
//控制紋理如何與片元顏色進(jìn)行計(jì)算的 設(shè)置紋理環(huán)境徐勃,紋理代替片元
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
//設(shè)置橫縱兩軸的邊界外以重復(fù)模式繪制紋理
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//為線(xiàn)性過(guò)濾
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
bool clockFontInitialized = false;
if (mClockEnabled) {
clockFontInitialized =
(initFont(&animation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
mClockEnabled = clockFontInitialized;
}
if (mClockEnabled && !updateIsTimeAccurate()) {
mTimeCheckThread = new TimeCheckThread(this);
mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
}
playAnimation(*animation);
if (mTimeCheckThread != nullptr) {
mTimeCheckThread->requestExit();
mTimeCheckThread = nullptr;
}
releaseAnimation(animation);
if (clockFontInitialized) {
glDeleteTextures(1, &animation->clockFont.texture.name);
}
return false;
}
大致分為三步驟:
- 1.loadAnimation 解析zip包的動(dòng)畫(huà)數(shù)據(jù)
- 2.初始化紋理設(shè)置
- 3.playAnimation 播放解析好的紋理數(shù)據(jù)
- 4.releaseAnimation 播放完畢釋放資源
loadAnimation
BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
{
if (mLoadedFiles.indexOf(fn) >= 0) {
ALOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
fn.string());
return NULL;
}
ZipFileRO *zip = ZipFileRO::open(fn);
if (zip == NULL) {
ALOGE("Failed to open animation zip \"%s\": %s",
fn.string(), strerror(errno));
return NULL;
}
Animation *animation = new Animation;
animation->fileName = fn;
animation->zip = zip;
animation->clockFont.map = nullptr;
mLoadedFiles.add(animation->fileName);
parseAnimationDesc(*animation);
if (!preloadZip(*animation)) {
return NULL;
}
mLoadedFiles.remove(fn);
return animation;
}
關(guān)鍵方法有兩個(gè),第一個(gè)是parseAnimationDesc早像,第二個(gè)是preloadZip僻肖。
parseAnimationDesc 解析zip包中的描述文件
bool BootAnimation::parseAnimationDesc(Animation& animation)
{
String8 desString;
if (!readFile(animation.zip, "desc.txt", desString)) {
return false;
}
char const* s = desString.string();
// Parse the description file
for (;;) {
const char* endl = strstr(s, "\n");
if (endl == NULL) break;
String8 line(s, endl - s);
const char* l = line.string();
int fps = 0;
int width = 0;
int height = 0;
int count = 0;
int pause = 0;
char path[ANIM_ENTRY_NAME_MAX];
char color[7] = "000000"; // default to black if unspecified
char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
char pathType;
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
// ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
animation.width = width;
animation.height = height;
animation.fps = fps;
} else if (sscanf(l, " %c %d %d %s #%6s %16s %16s",
&pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) {
//ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
// pathType, count, pause, path, color, clockPos1, clockPos2);
Animation::Part part;
part.playUntilComplete = pathType == 'c';
part.count = count;
part.pause = pause;
part.path = path;
part.audioData = NULL;
part.animation = NULL;
if (!parseColor(color, part.backgroundColor)) {
ALOGE("> invalid color '#%s'", color);
part.backgroundColor[0] = 0.0f;
part.backgroundColor[1] = 0.0f;
part.backgroundColor[2] = 0.0f;
}
parsePosition(clockPos1, clockPos2, &part.clockPosX, &part.clockPosY);
animation.parts.add(part);
}
else if (strcmp(l, "$SYSTEM") == 0) {
// ALOGD("> SYSTEM");
Animation::Part part;
part.playUntilComplete = false;
part.count = 1;
part.pause = 0;
part.audioData = NULL;
part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
if (part.animation != NULL)
animation.parts.add(part);
}
s = ++endl;
}
return true;
}
想要正常解析開(kāi)機(jī)動(dòng)畫(huà),需要有一個(gè)desc.txt卢鹦。有一個(gè)例子如下:
241 63 60
c 1 30 part0
c 1 0 part1
c 0 0 part2
c 1 64 part3
c 1 15 part4
該文件頭三個(gè)字符串定義了寬高臀脏,和幀數(shù)。找到\n結(jié)束后就到下一行中去冀自。
接下來(lái)的部分就是動(dòng)畫(huà)每一部分揉稚,第一個(gè)字符串c是指是否一直播放到結(jié)束;第而個(gè)則是指pause熬粗,是暫停的幀數(shù)搀玖;第三個(gè)int是指當(dāng)前幀數(shù)中有加載目錄下多少畫(huà)面,最后一個(gè)代表資源路徑驻呐。
把每一部分當(dāng)成一個(gè)part保存在Animation中灌诅。
當(dāng)然在Android 9.0版本中還能設(shè)置更多的選項(xiàng)如音頻等。
preloadZip預(yù)加載zip
bool BootAnimation::preloadZip(Animation& animation)
{
// read all the data structures
const size_t pcount = animation.parts.size();
void *cookie = NULL;
ZipFileRO* zip = animation.zip;
if (!zip->startIteration(&cookie)) {
return false;
}
ZipEntryRO entry;
char name[ANIM_ENTRY_NAME_MAX];
while ((entry = zip->nextEntry(cookie)) != NULL) {
const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
continue;
}
const String8 entryName(name);
const String8 path(entryName.getPathDir());
const String8 leaf(entryName.getPathLeaf());
if (leaf.size() > 0) {
if (entryName == CLOCK_FONT_ZIP_NAME) {
FileMap* map = zip->createEntryFileMap(entry);
if (map) {
animation.clockFont.map = map;
}
continue;
}
for (size_t j = 0; j < pcount; j++) {
if (path == animation.parts[j].path) {
uint16_t method;
// supports only stored png files
if (zip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
if (method == ZipFileRO::kCompressStored) {
FileMap* map = zip->createEntryFileMap(entry);
if (map) {
Animation::Part& part(animation.parts.editItemAt(j));
if (leaf == "audio.wav") {
// a part may have at most one audio file
part.audioData = (uint8_t *)map->getDataPtr();
part.audioLength = map->getDataLength();
} else if (leaf == "trim.txt") {
part.trimData.setTo((char const*)map->getDataPtr(),
map->getDataLength());
} else {
Animation::Frame frame;
frame.name = leaf;
frame.map = map;
frame.trimWidth = animation.width;
frame.trimHeight = animation.height;
frame.trimX = 0;
frame.trimY = 0;
part.frames.add(frame);
}
}
} else {
...
}
}
}
}
}
}
for (Animation::Part& part : animation.parts) {
const char* trimDataStr = part.trimData.string();
for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) {
const char* endl = strstr(trimDataStr, "\n");
// No more trimData for this part.
if (endl == NULL) {
break;
}
String8 line(trimDataStr, endl - trimDataStr);
const char* lineStr = line.string();
trimDataStr = ++endl;
int width = 0, height = 0, x = 0, y = 0;
if (sscanf(lineStr, "%dx%d+%d+%d", &width, &height, &x, &y) == 4) {
Animation::Frame& frame(part.frames.editItemAt(frameIdx));
frame.trimWidth = width;
frame.trimHeight = height;
frame.trimX = x;
frame.trimY = y;
} else {
break;
}
}
}
mCallbacks->init(animation.parts);
zip->endIteration(cookie);
return true;
}
這里面做了兩件事情:
- 1.首先含末,解壓zip包中所有的資源猜拾,注入到每一個(gè)Animation parts中。讓其擁有真正的資源地址佣盒。
- 2.根據(jù)每一個(gè)parts中的資源數(shù)據(jù)和頭信息关带,生成一個(gè)個(gè)Frame保存在Animation中。
playAnimation 播放動(dòng)畫(huà)
bool BootAnimation::playAnimation(const Animation& animation)
{
const size_t pcount = animation.parts.size();
nsecs_t frameDuration = s2ns(1) / animation.fps;
const int animationX = (mWidth - animation.width) / 2;
const int animationY = (mHeight - animation.height) / 2;
ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
elapsedRealtime());
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
glBindTexture(GL_TEXTURE_2D, 0);
// Handle animation package
if (part.animation != NULL) {
playAnimation(*part.animation);
if (exitPending())
break;
continue; //to next part
}
for (int r=0 ; !part.count || r<part.count ; r++) {
// Exit any non playuntil complete parts immediately
if(exitPending() && !part.playUntilComplete)
break;
mCallbacks->playPart(i, part, r);
glClearColor(
part.backgroundColor[0],
part.backgroundColor[1],
part.backgroundColor[2],
1.0f);
for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
const Animation::Frame& frame(part.frames[j]);
nsecs_t lastFrame = systemTime();
if (r > 0) {
glBindTexture(GL_TEXTURE_2D, frame.tid);
} else {
if (part.count != 1) {
glGenTextures(1, &frame.tid);
glBindTexture(GL_TEXTURE_2D, frame.tid);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
int w, h;
initTexture(frame.map, &w, &h);
}
const int xc = animationX + frame.trimX;
const int yc = animationY + frame.trimY;
Region clearReg(Rect(mWidth, mHeight));
clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight));
if (!clearReg.isEmpty()) {
Region::const_iterator head(clearReg.begin());
Region::const_iterator tail(clearReg.end());
glEnable(GL_SCISSOR_TEST);
while (head != tail) {
const Rect& r2(*head++);
glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height());
glClear(GL_COLOR_BUFFER_BIT);
}
glDisable(GL_SCISSOR_TEST);
}
glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight),
0, frame.trimWidth, frame.trimHeight);
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
}
eglSwapBuffers(mDisplay, mSurface);
nsecs_t now = systemTime();
nsecs_t delay = frameDuration - (now - lastFrame);
//ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
lastFrame = now;
if (delay > 0) {
struct timespec spec;
spec.tv_sec = (now + delay) / 1000000000;
spec.tv_nsec = (now + delay) % 1000000000;
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
} while (err<0 && errno == EINTR);
}
checkExit();
}
usleep(part.pause * ns2us(frameDuration));
// For infinite parts, we've now played them at least once, so perhaps exit
if(exitPending() && !part.count)
break;
}
}
// Free textures created for looping parts now that the animation is done.
for (const Animation::Part& part : animation.parts) {
if (part.count != 1) {
const size_t fcount = part.frames.size();
for (size_t j = 0; j < fcount; j++) {
const Animation::Frame& frame(part.frames[j]);
glDeleteTextures(1, &frame.tid);
}
}
}
return true;
}
能看到在這個(gè)過(guò)程中沼撕,如果沒(méi)有設(shè)置c標(biāo)志為則不會(huì)播放。否則將會(huì)循環(huán)每一個(gè)每一個(gè)part中對(duì)應(yīng)frame的個(gè)數(shù)芜飘。當(dāng)是frame的第一幀的時(shí)候?qū)?huì)初始化紋理务豺,設(shè)置好紋理需要加載的數(shù)據(jù)以及寬高,并且綁定當(dāng)前frame的id為渲染紋理的句柄嗦明。
找到動(dòng)畫(huà)的動(dòng)畫(huà)的啟動(dòng)位置笼沥,其位置就是整個(gè)屏幕的寬高-動(dòng)畫(huà)繪制區(qū)域?qū)捀叩?/2,保證整個(gè)動(dòng)畫(huà)剛好在大小適應(yīng)的位置娶牌。
獲取到區(qū)域之后奔浅,通過(guò)glScissor進(jìn)行裁剪。glDrawTexiOES繪制紋理位置诗良,最后進(jìn)行opengles 緩沖區(qū)交換汹桦。計(jì)算當(dāng)前已經(jīng)消耗的時(shí)間和每一幀進(jìn)行比對(duì),再進(jìn)行延時(shí)處理鉴裹。
最后處理pause的數(shù)據(jù)舞骆,查看需要暫停沉睡多少幀钥弯。
最后的最后,銷(xiāo)毀每一個(gè)綁定在frame.id上的紋理督禽。以及調(diào)用checkExit檢測(cè)是否需要退出該進(jìn)程脆霎。
releaseAnimation
void BootAnimation::releaseAnimation(Animation* animation) const
{
for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
e = animation->parts.end(); it != e; ++it) {
if (it->animation)
releaseAnimation(it->animation);
}
if (animation->zip)
delete animation->zip;
delete animation;
}
銷(xiāo)毀animation中part以及zip映射的內(nèi)存。
那么什么時(shí)候才是整個(gè)進(jìn)程真正銷(xiāo)毀呢狈惫?那一般就是桌面進(jìn)程準(zhǔn)備顯示了睛蛛,那就應(yīng)該銷(xiāo)毀BootAnimation進(jìn)程了。我們可以猜測(cè)就在桌面的Activity 進(jìn)入了Resume之后進(jìn)行銷(xiāo)毀的胧谈。
BootAnimation 進(jìn)程的銷(xiāo)毀
文件:/frameworks/base/core/java/android/app/ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
Looper.myQueue().addIdleHandler(new Idler());
}
在執(zhí)行完Resume之后忆肾,會(huì)添加一次IdleHandler對(duì)象。讓進(jìn)程空閑執(zhí)行第岖。
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
boolean stopProfiling = false;
...
if (a != null) {
mNewActivities = null;
IActivityManager am = ActivityManager.getService();
ActivityClientRecord prev;
do {
if (a.activity != null && !a.activity.mFinished) {
try {
am.activityIdle(a.token, a.createdConfig, stopProfiling);
a.createdConfig = null;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
prev = a;
a = a.nextIdle;
prev.nextIdle = null;
} while (a != null);
}
...
return false;
}
該方法將會(huì)通信到AMS中难菌。
文件:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
ActivityRecord r =
mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
false /* processPausingActivities */, config);
if (stopProfiling) {
if ((mProfileProc == r.app) && mProfilerInfo != null) {
clearProfilerLocked();
}
}
}
}
Binder.restoreCallingIdentity(origId);
}
文件:/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
ArrayList<ActivityRecord> finishes = null;
ArrayList<UserState> startingUsers = null;
int NS = 0;
int NF = 0;
boolean booting = false;
boolean activityRemoved = false;
ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
....
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
r.finishLaunchTickingLocked();
if (fromTimeout) {
reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
}
if (config != null) {
r.setLastReportedGlobalConfiguration(config);
}
// We are now idle. If someone is waiting for a thumbnail from
// us, we can now deliver.
r.idle = true;
if (isFocusedStack(r.getStack()) || fromTimeout) {
booting = checkFinishBootingLocked();
}
}
....
}
....
return r;
}
能看到此時(shí)將會(huì)執(zhí)行checkFinishBootingLocked檢測(cè)BootAnimation是否關(guān)閉。
private boolean checkFinishBootingLocked() {
final boolean booting = mService.mBooting;
boolean enableScreen = false;
mService.mBooting = false;
if (!mService.mBooted) {
mService.mBooted = true;
enableScreen = true;
}
if (booting || enableScreen) {
mService.postFinishBooting(booting, enableScreen);
}
return booting;
}
這里很簡(jiǎn)單蔑滓,就是一個(gè)全局的標(biāo)志位判斷,接下來(lái)就回到AMS中
void postFinishBooting(boolean finishBooting, boolean enableScreen) {
mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,
finishBooting ? 1 : 0, enableScreen ? 1 : 0));
}
進(jìn)入AMS的Handler中
case FINISH_BOOTING_MSG: {
if (msg.arg1 != 0) {
finishBooting();
}
if (msg.arg2 != 0) {
enableScreenAfterBoot();
}
break;
}
會(huì)執(zhí)行finishBooting繼續(xù)設(shè)置一個(gè)標(biāo)志位郊酒。這個(gè)標(biāo)志用來(lái)判斷不需要其他時(shí)候有其他進(jìn)程來(lái)執(zhí)行結(jié)束B(niǎo)ootAnimation進(jìn)程的操作。
void enableScreenAfterBoot() {
mWindowManager.enableScreenAfterBoot();
...
}
此時(shí)就會(huì)走到WMS中的enableScreenAfterBoot键袱。因?yàn)閃MS作為窗體管理服務(wù)燎窘,肯定有渲染相關(guān)的服務(wù)在里面。
文件:/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public void enableScreenAfterBoot() {
synchronized(mWindowMap) {
...
if (mSystemBooted) {
return;
}
mSystemBooted = true;
hideBootMessagesLocked();
mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);
}
mPolicy.systemBooted();
performEnableScreen();
}
private void performEnableScreen() {
synchronized(mWindowMap) {
...
try {
IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
if (surfaceFlinger != null) {
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) {
...
}
...
try {
mActivityManager.bootAnimationComplete();
} catch (RemoteException e) {
}
...
}
關(guān)鍵是通信到SF中蹄咖,傳入了FIRST_CALL_TRANSACTION命令褐健,而這個(gè)命令實(shí)際上就是BOOT_FINISHED。
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
我們?nèi)F的基類(lèi)看看做了什么澜汤。
文件:/frameworks/native/libs/gui/ISurfaceComposer.cpp
case BOOT_FINISHED: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
bootFinished();
return NO_ERROR;
}
其實(shí)就是SF中的bootFinished方法蚜迅。
void SurfaceFlinger::bootFinished()
{
if (mStartPropertySetThread->join() != NO_ERROR) {
ALOGE("Join StartPropertySetThread failed!");
}
const nsecs_t now = systemTime();
const nsecs_t duration = now - mBootTime;
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
// wait patiently for the window manager death
const String16 name("window");
sp<IBinder> window(defaultServiceManager()->getService(name));
if (window != 0) {
window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
}
....
property_set("service.bootanim.exit", "1");
const int LOGTAG_SF_STOP_BOOTANIM = 60110;
LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
sp<LambdaMessage> readProperties = new LambdaMessage([&]() {
readPersistentProperties();
});
postMessageAsync(readProperties);
}
在此刻,SF綁定了對(duì)WMS的Binder死亡代理俊抵。不過(guò)關(guān)鍵還是設(shè)置了service.bootanim.exit這個(gè)全局屬性谁不。而這個(gè)屬性剛好就是BootAnimation在checkExit方法中不斷循環(huán)檢測(cè)。
void BootAnimation::checkExit() {
// Allow surface flinger to gracefully request shutdown
char value[PROPERTY_VALUE_MAX];
property_get(EXIT_PROP_NAME, value, "0");
int exitnow = atoi(value);
if (exitnow) {
requestExit();
mCallbacks->shutdown();
}
}
requestExit其實(shí)就是退出該BootAnimation線(xiàn)程的threadLoop方法徽诲,這樣整個(gè)main方法就不會(huì)阻塞住刹帕,就會(huì)一直運(yùn)行整個(gè)main到底結(jié)束整個(gè)進(jìn)程。
總結(jié)
用一幅圖總結(jié):
這些其實(shí)都是普通的OpenGL es的操作谎替。但是似乎把Android渲染系統(tǒng)部分給屏蔽掉了偷溺。我們似乎沒(méi)有辦法繼續(xù)探索下去了?不得不說(shuō)封裝的太棒了钱贯,壓根沒(méi)有感受到OpenGL es適配了平臺(tái)的特性挫掏。
回答開(kāi)頭的疑問(wèn),為什么我們不需要Activity也能渲染呢喷舀,由始至終都沒(méi)有看到Activity的存在砍濒?其實(shí)Activity本身并不是負(fù)責(zé)渲染淋肾,它不過(guò)是集UI交互的大成者。真正負(fù)責(zé)渲染的爸邢,其實(shí)是由Surface聯(lián)通SF進(jìn)行交互渲染的樊卓。我們?nèi)粘i_(kāi)發(fā)(不打開(kāi)硬件加速)似乎有沒(méi)有看到OpenGL es的身影。
其實(shí)我們可以做一個(gè)大膽的猜測(cè)杠河,其實(shí)Android的渲染很可能分為2個(gè)部分碌尔,一個(gè)是借助OpenGL es進(jìn)行渲染,另一個(gè)則是通過(guò)Skia畫(huà)筆繪制好像素之后進(jìn)行渲染券敌。究竟是不是這樣呢唾戚?不妨留一個(gè)懸念,下一篇文章待诅,將會(huì)帶領(lǐng)大家閱讀Android在封裝OpenGL es上做了什么努力叹坦。