背景
在修改開機音量的時候,發現找不到對應的聲音功能調用。
因此了解了一下安卓的開機聲音是如何實現的。
安卓4~安卓7 都可以這么做。
參考:
- https://blog.csdn.net/chen825919148/article/details/19626241
- https://www.dazhuanlan.com/2019/08/21/5d5d166b2ed1a/
前言
關於安卓的開機效果(動畫、logo、聲音),有下面幾個階段:
- 老的方案1:Linux 系統啟動,出現Linux小企鵝畫面(reboot)(Android 1.5及以上版本已經取消加載圖片);
- 沿用至今的方案2:Lndroid平台圖形系統啟動,出現含閃動的ANDROID字樣的動畫圖片(start)並加載Android系統。
我們這里說的是沿用至今的方案2。
源碼分析
android開機動畫叫源碼位於frameworks/base/cmds/bootanimation/BootAnimation.cpp
中,會將/data/local/bootanimation.zip
或/system/media/bootanimation.zip
里面的圖片(支持png、jpeg、bmp)以動畫的形式播放出來:
在源碼樹中搜索
bootanimation.zip
即可找到可供修改的開機動畫。
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
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 andrew.hu modify
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
dinfo.orientation == 1 ? dinfo.h : dinfo.w,
dinfo.orientation == 1 ? dinfo.w : dinfo.h, PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::openGlobalTransaction();
control->setLayer(0x40000000);
SurfaceComposerClient::closeGlobalTransaction();
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 (encryptedAnimation && (access(getAnimationFileName(IMG_ENC), R_OK) == 0)) {
mZipFileName = getAnimationFileName(IMG_ENC);
}
else if (access(getAnimationFileName(IMG_OEM), R_OK) == 0) {
mZipFileName = getAnimationFileName(IMG_OEM);
}
else if (access(getAnimationFileName(IMG_SYS), R_OK) == 0) {
mZipFileName = getAnimationFileName(IMG_SYS);
}
return NO_ERROR;
}
循環:
bool BootAnimation::threadLoop()
{
bool r;
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZipFileName.isEmpty()) {
r = android(); // 執行android字體閃動的圖片 會加載"images/android-logo-mask.png"和"images/android-logo-shine.png"
} else {
r = movie(); // 執行bootanimation.zip中提供的動畫圖片
}
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(mDisplay, mContext);
eglDestroySurface(mDisplay, mSurface);
mFlingerSurface.clear();
mFlingerSurfaceControl.clear();
eglTerminate(mDisplay);
IPCThreadState::self()->stopProcess();
return r;
}
所以如果你想修改動畫,那么把你做好的動畫拷貝到編譯好對應的目錄下即可,然后編譯刷入整合進system鏡像包就可以看到效果了
如果你想修改android閃動的那兩張圖片的話,最簡單的方法是直接替換圖片,如果你懂openGL的話也可以自己做酷炫的動畫
另外如果需要修改開機logo動畫,可以這么做(參考BootAnimation.cpp
中的android
函數):
1、將圖片文件xx.png
放置到 frameworks/base/core/res/assets/images
中。
2、再在函數中完成對應的調用:
- 聲明對應的類型:
Texture mTexture
- 加載
initTexture(&mTexture, mAssets, "images/xx.png");
- 使用openGL來繪制界面(略)
添加開機聲音
既然我們已經知道了關於開機效果顯示的大致流程。
那么添加開機聲音也很簡單,我們可以使用MediaPlayer
這個類來完成我們的需求。
聲明與實現
首先在BootAnimation.h添加方法的聲明和頭文件的引用
#include <media/AudioSystem.h>
#include <media/mediaplayer.h>
添加: void bootMusic();
的聲明以及對應的實現
// frameworks/base/cmds/bootanimation/BootAnimation.h
class BootAnimation : public Thread, public IBinder::DeathRecipient
{
//...
private:
void bootMusic();
}
// frameworks/base/cmds/bootanimation/BootAnimation.cpp
void BootAnimation::bootMusic()
{
int index;
const char *fileName = "/system/media/boot.wav";
MediaPlayer* mp = new MediaPlayer();
audio_devices_t device = AudioSystem::getDevicesForStream(AUDIO_STREAM_ENFORCED_AUDIBLE);
if (mp->setDataSource(NULL, fileName, NULL) == NO_ERROR)
{
mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE/*AudioSystem: :ENFORCED_AUDIBLE*/);
mp->prepare();
}
LOGE ("bootMusic\n");
AudioSystem::initStreamVolume(AUDIO_STREAM_ENFORCED_AUDIBLE, 0,7);
AudioSystem::setStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, 7, device);
AudioSystem::getStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE/*AudioSystem::ENFORCED_AUDIBLE*/, &index, device);
LOGE ("index %d",index);
if (index != 0)
{
LOGD("playing %s", fileName);
mp->setVolume(0.4f, 0.4f);
mp->seekTo(0);
mp->start();
}
}
只要
fileName
對應的聲音文件存在而且有權限訪問到,那么無所謂放在哪里。
此后,在合適的地方(android ()
或者movie ()
)調用即可,例如:
bool BootAnimation::movie()
{
Animation* animation = loadAnimation(mZipFileName);
// ...
glEnable(GL_TEXTURE_2D);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// ...
bootMusic(); // 播放聲音 (只要在播放動畫之前播放即可)
playAnimation(*animation); // 播放動畫
// ...
return false;
}
Android.mk
光有頭文件還不夠,播放聲音還需要引入對應的庫:
# frameworks/base/cmds/bootanimation/Android.mk
# ...
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
libandroidfw \
libutils \
libbinder \
libui \
libskia \
libEGL \
libGLESv1_CM \
libgui \
libOpenSLES \
libtinyalsa \
libregionalization \
libmedia # 注意,libmedia是新添加的;
# ...
大功告成,這樣就成功的添加了開機音樂。
修改鈴聲
也許有人會問,那android系統自帶的那些音樂和鈴聲在什么地方呢?
系統默認了很多的聲音,如果有需要,可以修改frameworks/base/data/sounds
下面文檔及文檔夾中的聲音文檔。
至於編譯完成后放到什么分區那是由AllAudio.mk
決定的。
AllAudio.mk
的作用:是將這些音樂文檔全部打包到系統system/media/audio
下面各個模塊的文檔,然后在系統開機的時候,掃描這些文檔,將其加入到數據庫中,之后在設置中更換聲音時,則直接從數據庫中查詢這些音樂文檔,然后供用戶選擇。
如果需要添加某個鈴聲,可以這么做:
1、在AllAudio.mk
添加一列類似這樣的內容:$(LOCAL_PATH)/aaa.ogg:system/media/audio/bbb/xxx.ogg
2、再將aaa.ogg
拷貝到frameworks/base/data/sounds
中
修改也是類似的原理。
當然,也可以通過修改mk
文件中指定的音樂文檔名來實現靜音等目的。
例如,
ro.config.notification_sound
(通知默認的音樂文檔文檔名)在build/target/product/full_base.mk
中定義,如果我們不想有聲音那么我們可以將默認值改為不存在的文檔,則不會播放通知聲音了。當然我們也可以在客戶定義的mk中使用PRODUCT_PROPERTY_OVERRIDES
去復寫此屬性,將其指定為不存在文檔或者為空,這樣就不會有通知聲音響了。
修改開機動畫
這里介紹bootanimation.zip如何制作。
開機動畫包內容
先看看bootanimation.zip的內容:解壓.zip文件后,會有n個存放圖片的文件夾(part
n
)+1個desc.txt
文件。
開機動畫壓縮包不能包含bootanimation文件夾,必現是如下格式
├── desc.txt
├── part0
│ ├── 00013.png
│ ├── 00014.png
│ ├── 00052.png
└── part1
├── 00013.png
├── 00015.png
├── 00019.png
└── 00069.png
上圖中的文件夾名字可隨便命名(對應desc.txt
即可),里面存放的就是開機要顯示的圖片。
desc.txt解析
打開以后可以看到是類似這樣的內容。
240 320 1
p 2 0 part0
p 0 0 part1
第一行代表幀率以及動畫大小:
240 | 320 | 1 |
---|---|---|
寬 | 高 | 每秒1幀 |
此后的每一行代表每一段動畫具體的效果:
p | 2 | 0 | part0 |
---|---|---|---|
代表一段動畫 | 代表文件夾播放的次數,0代表永遠循環 | 表示這部分播完后下部分開始的間隔 | 代表從哪個文件夾中尋找動畫 |
一般來說,將最后的那段動畫設置成循環播放,就可以在進入系統界面之前一直有畫面,不會出現黑屏的情況(所有動畫都做完了,但是還沒進入到系統界面就會出現黑屏)。
最后一行是空行,代表文件結束;很多人在改動時候會把這一行刪掉,如果制作以后失敗了,那么可以將這一行加上去試試。
圖片替換
圖片需要統一格式為png,且各圖片的大小需要相同。
圖片的命名可以隨意,但是最后的數字要有順序。
zip制作
開機動畫壓縮格式必須為存儲方式。
在Windows中可以在壓縮時,指定壓縮級別為
不壓縮
/存檔
。由於Windows下壓縮軟件良莠不齊,因此建議在Linux中 制作。
Linux下可以使用下列命令:
cd bootanimation
zip -0 -r ../bootanimation.zip ./*