編輯:關於Android編程
當長按手機的power鍵,Android手機就會開機,那麼Android系統的開機啟動過程到底是怎麼樣的呢,本文將要介紹這一過程。簡單來說,Android系統的開機啟動過程大致是這樣的:首先linux系統會啟動一個叫做zygote(可以稱為受精卵、母體)的linux程序,這個程序實際上就是android系統的內核,zygote啟動的時候會建立socket服務端並加載大量的類和資源。接著zygote會孵化第一個dalvik進程SystemServer,在SystemServer中會創建一個socket客戶端,後續AMS(ActivityManagerService)會通過此客戶端和zygote通信,zygote再根據請求孵化出新的dalvik進程即啟動一個新的apk同時把新進程的socket連接關閉。SystemServer初始化完畢後會啟動一個位於桟頂的activity,由於系統剛開機,所以task桟頂沒有activity,於是接著它會發送一個隱式的intent(category:CATEGORY_HOME),也就是launcher了,即Android系統的桌面程序,launcher啟動以後,我們就可以通過桌面啟動各種應用了,可以發現,launcher可是不是一個,第三方應用只要加入launcher所需要的intent-filter即可。下面一一分析各個流程。(注:本文分析基於Android4.3源碼)
zygote是一個linux程序,其對應的可執行文件位於/system/bin/app_process,它在/init.rc中定義,如下
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
可以發現,zygote創建了一個流式套接字(即采用TCP協議),並監聽660端口,並且當zygote重啟的時候需要對喚醒電源並重啟Media、netd服務。下面看zygote的源碼,其路徑為frameworksasecmdsapp_processapp_main.cpp中:
int main(int argc, char* const argv[])
{
#ifdef __arm__
/*
* b/7188322 - Temporarily revert to the compat memory layout
* to avoid breaking third party apps.
*
* THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.
*
* http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466
* changes the kernel mapping from bottom up to top-down.
* This breaks some programs which improperly embed
* an out of date copy of Android's linker.
*/
char value[PROPERTY_VALUE_MAX];
property_get(ro.kernel.qemu, value, );
bool is_qemu = (strcmp(value, 1) == 0);
if ((getenv(NO_ADDR_COMPAT_LAYOUT_FIXUP) == NULL) && !is_qemu) {
int current = personality(0xFFFFFFFF);
if ((current & ADDR_COMPAT_LAYOUT) == 0) {
personality(current | ADDR_COMPAT_LAYOUT);
setenv(NO_ADDR_COMPAT_LAYOUT_FIXUP, 1, 1);
execv(/system/bin/app_process, argv);
return -1;
}
}
unsetenv(NO_ADDR_COMPAT_LAYOUT_FIXUP);
#endif
// These are global variables in ProcessState.cpp
mArgC = argc;
mArgV = argv;
mArgLen = 0;
for (int i=0; i說明:這句代碼runtime.start(com.android.internal.os.ZygoteInit, startSystemServer ? start-system-server : )在AndroidRuntime中實現,其最終會調用ZygoteInit的main方法,請看env->CallStaticVoidMethod(startClass, startMeth, strArray);這裡的startClass就是com.android.internal.os.ZygoteInit,而startMeth就是main,所以,我們直接看ZygoteInit的main方法,代碼路徑為:frameworksasecorejavacomandroidinternalosZygoteInit.java:
public static void main(String argv[]) {
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
//這裡注冊流式socket,以便於fork新的dalvik進程
registerZygoteSocket();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
//這裡預先加載一些類和資源
preload();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
// Finish profiling the zygote initialization.
SamplingProfilerIntegration.writeZygoteSnapshot();
// Do an initial gc to clean up after startup
gc();
// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
Trace.setTracingEnabled(false);
// If requested, start system server directly from Zygote
if (argv.length != 2) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
if (argv[1].equals(start-system-server)) {
//啟動SystemServer,zygote通過SystemServer和上層服務進行交互
startSystemServer();
} else if (!argv[1].equals()) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
Log.i(TAG, Accepting command socket connections);
//通過Select方式監聽端口,即異步讀取消息,死循環,沒有消息則一直阻塞在那裡
runSelectLoop();
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, Zygote died with exception, ex);
closeServerSocket();
throw ex;
}
}
下面看一下runSelectLoop方法,看看它是如何fork產生一個新的進程的:
/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
*
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
*/
private static void runSelectLoop() throws MethodAndArgsCaller {
ArrayList fds = new ArrayList();
ArrayList peers = new ArrayList();
FileDescriptor[] fdArray = new FileDescriptor[4];
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
int loopCount = GC_LOOP_COUNT;
//死循環,沒有消息則一直阻塞在這裡
while (true) {
int index;
/*
* Call gc() before we block in select().
* It's work that has to be done anyway, and it's better
* to avoid making every child do it. It will also
* madvise() any free memory as a side-effect.
*
* Don't call it every time, because walking the entire
* heap is a lot of overhead to free a few hundred bytes.
*/
if (loopCount <= 0) {
gc();
loopCount = GC_LOOP_COUNT;
} else {
loopCount--;
}
try {
fdArray = fds.toArray(fdArray);
//通過select()函數來讀取新的socket消息,其返回值有<0、0、>0三種
//分別代表:發生異常、繼續讀取新消息、首先處理當前消息
index = selectReadable(fdArray);
} catch (IOException ex) {
throw new RuntimeException(Error in select(), ex);
}
if (index < 0) {
throw new RuntimeException(Error in select());
} else if (index == 0) {
//構造一個ZygoteConnection對象,並將其加入到peers列表中
ZygoteConnection newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done;
//這裡處理當前socket消息,ZygoteConnection的runOnce會被調用,一個新的dalvik進程會被創建
done = peers.get(index).runOnce();
if (done) {
//處理完了以後刪除此socket消息
peers.remove(index);
fds.remove(index);
}
}
}
}
接著,我們還需要看下ZygoteConnection的runOnce方法,看看一個dalvik進程到底是如何產生的,我們知道每個apk都運行在一個獨立的dalvik進程中,所以當啟動一個apk的時候,zygote會孵化出一個新的進程,在這個進程中運行此apk。 在ZygoteConnection中,新進程是通過Zygote的靜態方法forkAndSpecialize來產生的:
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName);
具體的我們就不用多看了,內部肯定是通過linux系統的fork()函數來產生一個新進程的。當一個新的dalvik進程產生了以後,還需要做一些清場的工作,由於新進程是由zygote程序fork出來的,所以子進程具有zygote的一份拷貝,我們知道,zygote啟動的時候創建了一個socket服務端,這個服務端只能有一個,由zygote孵化的子進程是不應該有的,所以子進程孵化出來以後,還必須關閉拷貝的socket服務端,這些操作在handleChildProc方法中完成:
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
//關閉本地和服務端(如果有)的socket
closeSocket();
ZygoteInit.closeServerSocket();
if (descriptors != null) {
try {
ZygoteInit.reopenStdio(descriptors[0],
descriptors[1], descriptors[2]);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
newStderr = System.err;
} catch (IOException ex) {
Log.e(TAG, Error reopening stdio, ex);
}
}
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
if (parsedArgs.runtimeInit) {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs);
}
} else {
String className;
try {
className = parsedArgs.remainingArgs[0];
} catch (ArrayIndexOutOfBoundsException ex) {
logAndPrintError(newStderr,
Missing required class name argument, null);
return;
}
String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
System.arraycopy(parsedArgs.remainingArgs, 1,
mainArgs, 0, mainArgs.length);
if (parsedArgs.invokeWith != null) {
WrapperInit.execStandalone(parsedArgs.invokeWith,
parsedArgs.classpath, className, mainArgs);
} else {
ClassLoader cloader;
if (parsedArgs.classpath != null) {
cloader = new PathClassLoader(parsedArgs.classpath,
ClassLoader.getSystemClassLoader());
} else {
cloader = ClassLoader.getSystemClassLoader();
}
try {
//這裡子進程的main方法被調用,此時,子進程完全從zygote(母體)上脫離出來了
ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
} catch (RuntimeException ex) {
logAndPrintError(newStderr, Error starting., ex);
}
}
}
}
同時在ZygoteInit中會預先加載一些類和資源,具體代碼在preload方法中:
static void preload() {
preloadClasses();
preloadResources();
}
SystemServer的創建
SystemServer作為zygote孵化的第一個dalvik進程,其孵化過程在上面已經進行了描述,但是其和普通進程的啟動略有不同,普通進程由Zygote.forkAndSpecialize來啟動,而SystemServer由Zygote.forkSystemServer來啟動,其次是SystemServer內部多創建了一個socket客戶端。關於SystemServer內部的本地socket客戶端,本文前面已經說過,外圍的Service都是通過SystemServer和zygote交互的,比如要啟動一個apk,首先AMS會發起一個新進程的創建請求,在startProcessLocked方法中會調用Process的start方法,其內部會調用startViaZygote方法,而在startViaZygote內部會創建一個本地socket和zygote通信,我們要知道,AMS是在SystemServer進程中創建的,所以說在SystemServer中創建一個本地socket和zygote通信是有道理的。SystemServer的一個很重要的作用是創建各種服務,包括大家常見的WindowManagerService 、AlarmManagerService、ActivityManagerService等,然後上層的各種manager通過binder和service進行交互,關於SystemServer創建各種服務的過程以及和binder的交互,請參考我之前寫的一篇博客的其中一節,這裡就不重復了:各種Manager和Binder服務的對應關系。
系統桌面的啟動
當SystemServer創建各種服務完畢後,其中的一個服務ActivityManagerService由於也創建完成,所以其事件回調方法systemReady會被調用,這個方法很長,注意到在這個方法的倒數第二句是mMainStack.resumeTopActivityLocked(null),它的意思是將桟頂的activity復位,看它的代碼
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
// Find the first activity that is not finishing.
//找到桟頂的activity記錄
ActivityRecord next = topRunningActivityLocked(null);
// Remember how we'll process this pause/resume situation, and ensure
// that the state is reset however we wind up proceeding.
final boolean userLeaving = mUserLeaving;
mUserLeaving = false;
//由於系統剛啟動,桟頂肯定沒有activity,所以next為null
if (next == null) {
// There are no more activities! Let's just start up the
// Launcher...
if (mMainStack) {
ActivityOptions.abort(options);
//程序執行到這裡,桌面就會被調起來
return mService.startHomeActivityLocked(mCurrentUser);
}
}
...此處省略
}
最後看看桌面是如何被調起來的:
boolean startHomeActivityLocked(int userId) {
if (mHeadless) {
// Added because none of the other calls to ensureBootCompleted seem to fire
// when running headless.
ensureBootCompleted();
return false;
}
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
// the factory test app, so just sit around displaying the
// error message and don't try to start anything.
return false;
}
Intent intent = new Intent(
mTopAction,
mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
//其實就是為intent加上CATEGORY_HOME這個Category,接著就發送隱式intent來調起所有滿足條件的桌面
//這也是第三方桌面存在的價值
intent.addCategory(Intent.CATEGORY_HOME);
}
ActivityInfo aInfo =
resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
//這裡啟動桌面activity,到此為止,桌面被啟動了,我們就可以認為手機開機完成了
mMainStack.startActivityLocked(null, intent, null, aInfo,
null, null, 0, 0, 0, null, 0, null, false, null);
}
}
return true;
}
到此為止,桌面已經啟動了,也就意味著手機的開機啟動過程已經完成,後續我們就可以通過桌面來啟動各個應用了,根據本文的介紹,我們已經知道apk啟動時dalvik進程的創建過程,關於單個activity的啟動過程,請參看我之前寫的另一篇文章Android源碼分析-Activity的啟動過程。到此為止,本文結束了,相信大家對Android系統的開機啟動過程應該有了一個感性的認識了。
Android pendingInten 用法詳解
pendingIntent字面意義:等待的,未決定的Intent。要得到一個pendingIntent對象,使用方法類的靜態方法getActivity(Context,
Android開發之自定義圓角矩形進度對話框
方式一:自定義對話框 public class ProgersssDialog extends Dialog { private ImageView img;
Android Studio安裝指南及genymotion配置
第一次安裝Java JDK ,要大於1.7版本,不安裝的話就會出現如下提示:這時點擊上面的JDK鏈接,跳轉到甲骨文的JDK下載頁面,根據操作系統,選擇下載版本,如下:下載
淺談android適配(下)
上一篇我們主要了解了為什麼適配,以及怎麼適配,同時給出了部分切圖規范,和在開發過程中需要的一些注意事項,這一遍主要從官方給出的指導建議出發,從視覺的角度來說說怎麼適配。度