開機畫面,按照國際慣例,一般是分為2屏,當然也有3屏的說法,不管怎樣,我這里說得就是最后的一屏,按照bootanimation的字面意思翻譯,大概也就是開機動畫的意思,那這就不說第幾屏了,直接用“開機動畫”這詞。
首先,開機動畫的地址:system\media\bootanimation.zip。。。要修改開機動畫就是修改bootanimation這個文件。如果說你的手機里沒有這個文件,那就是說明你用的是官方原始版本的rom,開機動畫沒有被修改過,但這不礙事,沒有就放一個進去,到時候想換回最原始的開機畫面,也可以把bootanimation這個文件刪了。官方最原始的開機動畫在system\ framework\framework-res.apk\assets\images里面,里面主要是2張圖片,高手面可以去研究一下,說不定通過這個文件可以用代碼實現動畫的過程。
接下來,說說bootanimation.zip這個文件的結構。bootanimation里面主要包含一個desc.txt以及N個文件夾。而文件夾里面放着的就是你要的開機動畫的圖片。decs.txt的作用就是指導系統如何去執行你的開機畫面。
下面繼續說說desc.txt這個文檔要怎么寫。
先看例子,如果我的開機動畫要用到2個文件夾,分別是part1和part2,。。。。我希望開機的時候,先把part1里面的圖片都播放一遍,然后再循環播放part2里面的文件,直到進入系統。那么decs.txt文檔的內容應該如下:
302 480 5
p 1 0 part1
p 0 0 part2
就這么多,現在解釋一下這些命令吧。
第一行,320 480是代表你屏幕的分辨率,因為我們的胖6是320*480的,這就不多說了。后面的5是說,5幀每秒,不懂的可以這么理解,5是代表一秒鍾播放5張圖片。就這么簡單。
第二行,p 1 0 part1。p應該是play吧,也就是播放的意思。1是播放一次。0是空指令,就是什么都不做。part1就是說,這條指令是針對part1這個文件夾的。整條指令的意思就是,播放part1里面的圖片1次,然后什么都不做,馬上進入下一條指令。
第三行,p 0 0 part2。p依然是播放。第一個0這里是代表循環播放,第二個0和上面第二條指令一樣,代表什么也不做。part2也就不多說了。
至於為什么第一個0為什么是代表循環,只能這么說,這事國際慣例,就像用迅雷的時候,0代表不限制速度一樣,系統規定的,你知道就好,別糾結。
總結公式如下
第一條指令:屏幕的分辨率(空格)播放頻率
第二條指令:p(空格)播放次數(空格)后續操作(空格)作用文件夾
第三條指令:同上
第N條指令:同上
屏幕分辨率的格式是:寬(空格)高,胖6的就是320 480。。。播放頻率,上面已經說清楚了,至於到底要用多大的數字,視你開機動畫的張數而定,比如說,一共有100張圖片,開機時間是20秒,那么100/20就是5。
播放次數,1是代表一次,0代表循環播放。如果要播放2次的話,就是就是打個2上去,應該是這樣吧,反正我是沒試過,只試過0和1.
后續操作,0代表什么都不做,直接進入下一條指令。10是代表稍作停頓。
至於文件夾里面的圖片,比較簡單,首先,糾正一個說法,之前說必須要是png格式,經過我試驗證明,jpg格式的也可以,其他的有待試驗,不過只支持靜態圖片這點應該是真的,動態圖片同樣有待試驗。其次,關於圖片的尺寸,是不做要求的,只要在txt文檔里寫好手機的分辨率即可,開機的時候系統會自動適配。其其次要按播放循序命名,建議用5位數字,比如說,第一張圖片是00010,第二張就是00020.。。。。為什么要從00010開始,而不是00001?因為這事預防以后要在第一張和第二張中間再插一張圖片進去的話,就可以把插進去的圖片命名為00011,在00010之后,00020之前。就這么簡單。至於圖片那里來嘛,當然是自己網上找啦,也可以下載別人的bootanimation,然后修改一下。
最后就是壓縮包了,把需要用到的part文件夾跟decs.txt打包成zip格式,必須是zip,不能是rar,而且打包的時候壓縮方式要選擇“存儲”模式。然后改名成為bootanimation.zip,復制到SD卡里,再通過RE文件管理器復制到system\media\下面,重啟。
要注意一下,bootanimation不能太大,大了會卡,一般不要超過3M,。
————————————————————————————————————————————————————————————————————————————————————
1. Linux 系統啟動,出現Linux小企鵝畫面(reboot)(Android 1.5及以上版本已經取消加載圖片);
2. Android平台啟動初始化,出現"A N D R I O D"文字字樣畫面;
3. Android平台圖形系統啟動,出現含閃動的ANDROID字樣的動畫圖片(start)。
現在我們說的是第三種方式(基於模擬器):
android開機動畫叫源碼位於frameworks/base/cmds/bootanimation下,這個程序會將/data/local/bootanimation.zip或/system/media/bootanimation.zip里面的png圖片以動畫的形式播放出來。
首先,我們先來分析一下源碼:
frameworks/base/cmds/bootanimation/BootAnimation.cpp
首先看一下定義的常量:
#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip" #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip" #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
BootAnimation::readyToRun()
進入一個if判斷語句
if ((encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) && (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) || ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) && (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) || ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) { mAndroidAnimation = false; }
BootAnimation::threadLoop()
if (mAndroidAnimation) { r = Android(); // 執行android字體閃動的圖片 } else { r = movie(); // 執行bootanimation.zip中提供的動畫圖片 }
==> BootAnimation::Android()會加載"images/android-logo-mask.png"和"images/android-logo-shine.png"
==> BootAnimation::movie()會加載bootanimation.zip中的內容
我們下載的源碼里默認是沒有那些個.zip動畫的,所以總會跳到android字體閃動的畫面
所以如果你系那個用.zip的動畫那么把你做好的動畫拷貝到編譯好對應的目錄下即可,然后執行make snod整合進img包就可以看到效果了
如果你想修改android閃動的那兩張圖片的話,最簡單的方法是直接替換圖片,如果你懂openGL的話也可以自己做酷炫的動畫
那兩張圖片放在./frameworks/base/core/res/assets/images 目錄下,一張鏤空的android圖,一張發光效果,動畫效果就是下面那張發光的效果圖不斷左右移動。
我是用Photoshop直接修改的
修改完后直接替換,然后再 mmm frameworks/base , make snod 即可
以下是我修改后的效果圖:
看樣子還想那么回事
接下來我們給系統添加開機聲音
由於動畫是在BootAnimation播放的,所以我們的聲音肯定也在這個類中做,照貓畫虎
首先在BootAnimation.h添加方法的聲明和頭文件的引用
#include <media/AudioSystem.h> #include <media/mediaplayer.h>
添加方法 void bootMusic();
然后在BootAnimation.cpp中實現這個方法:
void BootAnimation::bootMusic() { int index; MediaPlayer* mp = new MediaPlayer(); if (mp->setDataSource("/system/etc/poweron.wav", NULL) == NO_ERROR) { mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE); mp->prepare(); } AudioSystem::getStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, &index); if (index != 0) { mp->seekTo(0); mp->start(); } }
請注意這個目錄setDataSource("/system/etc/poweron.wav", NULL)
其實這個目錄是隨便寫的,你想從哪個目錄讀這個文件就從哪個目錄讀好了,但是你要把這個文件放在對應的目錄下
比如,這個目錄是system/etc (這個目錄是android系統的目錄,不是源碼的目錄),這個文件夾是只有讀權限而沒有寫權限的,所有你不用試adb shell了,沒用的
其實system/etc這個目錄對應的是我們編譯后out文件夾下的xxx,然后在system/etc,所以你把poweron.wav放到這里就行了,當然如果你這時再emulaor 也同樣是看不到效果的,因為你修改完還沒有編譯,和上面修改圖片一樣,make snod一下
然后和圖上一樣,你就會生成新的system.img,當然你之前添加的poweron.wav也會自動被編譯進去了
然后再修改bootanimation_main.cpp這個文件,因為方法也申明了,也實現了,就是還沒有調用,所以調用就在這里調
//play boot music -yp BootAnimation *animation = new BootAnimation(); animation->bootMusic();
修改完這寫以后還需要修改Android.mk文件
因為播放聲音還需要引入庫
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libbinder \
libui \
libskia \
libEGL \
libGLESv1_CM \
libgui \
libmedia
請注意,libmedia是新添加的;
完事后就ok了,當然還是需要編譯的
mmm frameworks/base/cmds/bootanimation/
make snod
大功告成,這樣你就成功的添加了開機音樂
也許有人會問,那android系統自帶的那些音樂和鈴聲在什么地方呢?
源碼目錄在framworks/base/data/sounds
至於編譯完成后放到什么地方了那是Android.mk文件上配置的
不過這里叫AllAudio.mk
我們可以看到他又包含了很多mk文件,在同一目錄下就可以找到,上圖中就可以,我們隨便打開一個看看
這下你們頓時明白了吧,這些資源編譯后都去了神馬地方
所以,在剛才添加poweron.wav的時候,其實可以直接把聲音資源丟到framworks/base/data/sounds這個目錄下
然后再mk文件中這樣修改:
$(LOCAL_PATH)/poweron.wav:system/etc/poweron.wav \
不用我說,當然是執行mmm還有make snod命令了
其實還有一種辦法,如果你不想這么麻煩,你可以之際把poweron.wav 文件丟到./frameworks/base/core/res/assets/sounds下,用的時候怎么用呢?
加載的時候路徑是什么呢?我們看看源碼
我們可以看到之前我們修改的那兩張圖片是怎么被加載進來的
而這個方法的兩個參數是:
是不是頓時又明白了?
原來精髓所在是有個Asset可以使用,這個和上層開發中是一樣的,這個文件夾中的東西是不編譯的。
Asset* asset = assets.open(name, Asset::ACCESS_BUFFER); if (!asset) return NO_INIT; SkBitmap bitmap; SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode); asset->close(); delete asset;
所以按照這個方法就可以找到poweron.wav 這個文件了,然后修改下bootMusic的代碼就可以了。
——————————————————————————————————————————————————————————————————————————————————
系統通常默認支持開機動畫,開機關機鈴聲服務倒是預先定義了,不過使用時需要在/sysytem/media/下添加相應的音頻文件。長按Power鍵彈出關鍵選項,關機時也只是顯示進度條,而不是我們期望的關機動畫同時伴有關機鈴聲,當然在做二次開發的時候,這些都是可以定制的。下面將逐步分析如何實現這些定制化的需求。
一. 制作開機動畫
開機畫面是保存在/system/media的bootanimation.zip,下面分析系統開機畫面的構成
文件名為bootanimation.zip 一定是這個不能修改,里面包含part1 part2文件夾 和desc.txt,part 文件夾里面放的是動畫拆分的圖片,格式為png, desc.txt里面是播放的設置。
格式是這樣的
480 800 15
p 1 0 part0
p 0 0 part1
480 800是指顯示的分辨率 寬 高
15是一秒的幀數
p后面的數字是指播放次數 0為循環,N為播放N次
后面那位數字 一般為0(空指令), 表示播放一遍后稍作停頓的時間
part0,part1 為文件夾名
一般我們只使用part0目錄
圖片准備好了,desc.txt里面的命令也准備好了,就可以把他們一起打包壓縮成zip格式了。記住,是zip格式,而不是rar格式。另外壓縮的時候注意一點,壓縮的時候壓縮方式要選擇存儲,否則開機時手機會不認的。當然不至於變磚,只是開機的時候會黑着沒動畫一直到進入桌面為止。
開機動畫的替換也很簡單,我們把做好的bootanimation.zip替換掉原來的/system/media/bootanimation.zip文件即可。關機動畫其實和開機動畫的制作一樣,我們命名為shutdownanimation.zip,和bootanimation.zip放在同一目錄下。
二. 開關機動畫實現原理
Android系統在init.rc中定義了很多Servic,具體定義格式可以參考《Android Platform Developer’s Guide》中的“Android Init Language”。Init.rc中定義的Service將會被Init進程創建,其中已經定義的服務就包含了開機動畫,但沒有關機動畫的定義
service bootsound /system/bin/mplayer /system/media/bootsound
user media
group audio
oneshot
//開機鈴聲服務 /system/media/startupsound是鈴聲文件
service startupsound /system/bin/mplayer /system/media/startupsound
user media
group audio
disabled
oneshot
//關機鈴聲服務 /system/media/shutdownsound是關機鈴聲
service shutdownsound /system/bin/mplayer /system/media/shutdownsound
user media
group audio
disabled
oneshot
//定義了一個bootanim的服務,對應執行/system/bin/bootanimation
//disabled 表示init進程創建只是創建它,但不立刻執行
//oneshot 表示該服務只執行一次
service bootanim /system/bin/bootanimation
user graphics
group graphics
disabled
oneshot
當android系統boot時,開始加載動畫和開機鈴聲,其代碼位於
//源文件SurfaceFlinger.cpp
status_t SurfaceFlinger::readyToRun() {
// start boot animation service
property_set("ctl.start", "bootanim");//注
{
char value[PROPERTY_VALUE_MAX];
property_get("persist.sys.profile.silent", value, "0");
if (atoi(value)== 0){
LOGI("start:persist.sys.profile.silent is soundable");
// start startupsound service
property_set("ctl.start","startupsound");//注
} else {
LOGI("start:persist.sys.profile.silent is silent");
}
}
return NO_ERROR;
}
當Android完成boot后,關閉動畫和開機鈴聲,代碼位於
//源文件SurfaceFlinger.cpp
void SurfaceFlinger::bootFinished() {
const nsecs_t now = systemTime();
const nsecs_t duration = now - mBootTime;
LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mBootFinished = true;
//stop bootanim service
property_set("ctl.stop", "bootanim");
char value[PROPERTY_VALUE_MAX];
property_get("persist.sys.profile.silent", value, "0");
if (atoi(value)== 0){
LOGI("stop:persist.sys.profile.silent is soundable");
//stop startupsound service
property_set("ctl.stop","startupsound");
} else {
LOGI("stop:persist.sys.profile.silent is silent");
}
}
如何理解ctr.start和ctr.stop系統屬性?
每一項服務必須在/init.rc中定義.Android系統啟動時,init守護進程將解析init.rc和啟動屬性服務,屬性“ ctl.start ”和“ ctl.stop ”是用來啟動和停止服務的。一旦收到設置“ ctrl.start ”屬性的請求,屬性服務將使用該屬性值作為服務名找到該服務,啟動該服務。這項服務的啟動結果將會放入“ init.svc.<服務名>“屬性中 。客戶端應用程序可以輪詢那個屬性值,以確定結果。想更深入了解Android property系統可以參考博文《(翻譯)Android屬性系統》。
property_set("ctl.start",ServiceName);就是啟動ServiceName服務(在init.rc中定義);
property_set("ctl.stop",ServiceName)相對的是關閉ServiceName服務。
A啟動動畫服務
由於開機動畫和關機動畫除了播放的動畫文件不同,其他的完全一致,這里重復利用/system/bin/bootanimation代碼, 仿照開機動畫服務,我們新定義關機動畫
service shutdownanim /system/bin/bootanimation -shutdown
user graphics
group graphics
disabled
oneshot
唯一要注意的是關機動畫使用的/system/bin/bootanimation帶了-shutdown參數,這個參數用來區分加載的動畫文件為開機還是關機動畫。當bootanimation服務啟動時,進入/frameworks/base/cmds/bootanimation/bootanimation_main.cpp主函數main,
int main(int argc, char** argv)
{
#if defined(HAVE_PTHREADS)
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
#endif
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.nobootanimation", value, "0");
int noBootAnimation = atoi(value);
LOGI_IF(noBootAnimation, "boot animation disabled");
if (!noBootAnimation) {
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// create the animation object
sp<BootAnimation> boot = new BootAnimation();
//根據是否有參數,來設置動畫對象的isShutdown屬性
if (argc > 1) {
if (strcmp(argv[1], "-shutdown")==0) {
boot->isShutdown(true);
}
}
IPCThreadState::self()->joinThreadPool();
}
return 0;
}
new BootAnimation();時,如果有參數-shutdown, 則boot->isShutdown(true);方法isShutdown和isShutdown使我們新加的,所以先要在frameworks/base/cmds/bootanimation/bootanimation/BootAnimation.h頭文件中申明為public,如下:
class BootAnimation : public Thread, public IBinder::DeathRecipient
{
public:
BootAnimation();
virtual ~BootAnimation();
sp<SurfaceComposerClient> session() const;
bool mShutdown;
void isShutdown(bool shutdown);
…………………………………………….
}
之后修改BootAnimation.cpp
//源碼位於frameworks/base/cmds/bootanimation/bootanimation/BootAnimation.cpp
首先在構造函數中初始化mShutdown = false;表示默認是開機標志
BootAnimation::BootAnimation() : Thread(false)
{
mSession = new SurfaceComposerClient();
mShutdown = false;
}
實現isShutdown(bool shutdown);方法
void BootAnimation::isShutdown(bool shutdown)
{
mShutdown = shutdown;
}
修改status_t BootAnimation::readyToRun() 方法來根據mShutdown值加載動畫文件,如
status_t BootAnimation::readyToRun() {
……………………………………
mAndroidAnimation = false;
if (!mShutdown) {
status_t err = mZip.open("/data/local/bootanimation.zip");
if (err != NO_ERROR) {
err = mZip.open("/system/media/bootanimation.zip");
if (err != NO_ERROR) {
mAndroidAnimation = true;
}
}
} else {
status_t err = mZip.open("/data/local/shutdownanimation.zip");
if (err != NO_ERROR) {
err = mZip.open("/system/media/shutdownanimation.zip");
if (err != NO_ERROR) {
mAndroidAnimation = true;
}
}
mShutdown = false;
}
return NO_ERROR;
}
以上是動畫定制化C代碼部分的全部修改。
B啟動開機鈴聲服務
startupsound開機鈴聲服務,shutdownsound關機鈴聲服務
init.rc定義,service startupsound /system/bin/mplayer /system/media/startupsound
相當於用/system/bin/mplayer對象播放媒體文件/system/media/startupsound,
init.rc定義,service shutdownsound /system/bin/mplayer /system/media/shutdownsound
相當於用/system/bin/mplayer對象播放媒體文件/system/media/ shutdownsound.
到這里,開機動畫和開機鈴聲是正常的,我們還需要修改的是關機時候的執行流程,因為默認的操作時關機確認后顯示進度條。
C定制Android關機過程
在Android系統中,長按Power鍵默認會彈出對話框讓你選擇“飛行模式”,“靜音”,“關機”等功能。
我的目標是長按Power鍵,將會關機,彈出“設備將要關機”選擇對話框。如果可以選擇“是”關機,和“否”返回系統。
彈出對話框的代碼位於:
frameworks\policies\base\phone\com\android\internal\policy\impl\PhoneWindowManager.java
長按Power鍵顯示對話框的代碼如下:
Runnable mPowerLongPress = new Runnable() {
public void run() {
mShouldTurnOffOnKeyUp = false;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
showGlobalActionsDialog();
}
};
調用showGlobalActionsDialog方法將會顯示上面提到的顯示“飛行模式”,“靜音”,“關機”,選項的對話框。
關機的代碼位於:
frameworks\policies\base\phone\com\android\internal\policy\impl\GlobalActions.java
文件的createDialog方法中,有如下代碼:
mItems = Lists.newArrayList(
// silent mode
mSilentModeToggle,
// next: airplane mode
mAirplaneModeOn,
// last: power off
new SinglePressAction(
com.android.internal.R.drawable.ic_lock_power_off,
R.string.global_action_power_off) {
public void onPress() {
// shutdown by making sure radio and power are handled accordingly.
ShutdownThread.shutdown(mContext, true);
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return true
}
});
從代碼中我們可以看出,如果選擇上述對話框的“關機”選項之后,將會調用ShutdownThread的shutdown方法來關機。shutdown方法的第二個參數標識是否彈出詢問對話框。
ShutdownThread代碼位於
frameworks/base/core/java/com/android/internal/app/ShutdownThread.java
shutdown方法中有如下代碼
public static void shutdown(final Context context, boolean confirm) {
// ensure that only one thread is trying to power down.
// any additional calls are just returned
synchronized (sIsStartedGuard){
if (sIsStarted) {
Log.d(TAG, "Request to shutdown already running, returning.");
return;
}
}
Log.d(TAG, "Notifying thread to start radio shutdown");
if (confirm) {
final AlertDialog dialog = new AlertDialog.Builder(context)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(com.android.internal.R.string.power_off)
.setMessage(com.android.internal.R.string.shutdown_confirm)
.setPositiveButton(com.android.internal.R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
beginShutdownSequence(context);
}
})
.setNegativeButton(com.android.internal.R.string.no, null)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
if (!context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_slowBlur)) {
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
}
dialog.show();
} else {
beginShutdownSequence(context);
}
}
確認關機后開始執行beginShutdownSequence(context),這里就是我們定制關機過程的關鍵。
修改beginShutdownSequence(context)方法,在方法開頭打印Log之后增加如下代碼:
//我們判斷如果有關機動畫文件,即顯示關機動畫,否則顯示默認的進度條。
boolean showShutdownAnim = new File("/system/media/shutdownanimation.zip").exists();
if (showShutdownAnim) {
Log.d(TAG, "shutdownanim");
//設置前面說的android系統屬性ctr.start,即通知屬性服務啟動關機動畫服務
android.os.SystemProperties.set("ctl.start", "shutdownanim");
} else {
// throw up an indeterminate system dialog to indicate radio is
// shutting down.
ProgressDialog pd = new ProgressDialog(context);
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
if (!context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_slowBlur)) {
pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
}
pd.show();
}
boolean playShutdownSound = new File("/system/media/shutdownsound").exists();
android.os.SystemProperties.getBoolean("ro.config.enable_shutdownshound", false);
if (playShutdownSound) {
// play shutdown sound
Log.d(TAG, "shutdownsound");
android.os.SystemProperties.set("ctl.start", "shutdownsound");
}
以上就是定制化實現的所有需要修改的地方,想更深入了解Android property系統可以參考博文《(翻譯)Android屬性系統。