編輯:關於Android編程
在Android中所有的應用程序進程,以及用來運行系統關鍵服務的System進程都是有Zygote進程負責創建的,因此我們就叫他受精卵,畢竟它真的和受精卵很類似。
我們知道,Android的應用程序是由Java編寫的,它們不能直接以本地進程的形態運行在Linux上,只能運行在Dalvik/ART虛擬機中。每個應用程序都運行在各自的虛擬機中,應用程序每次運行都要重新初始化並啟動虛擬機,這個過程會耗費相當長時間,是導致應用程序啟動慢的原因之一。
為了很好地解決這個問題,在Android中,應用程序運行前,Zygote進程通過共享已運行的虛擬機的代碼與內存信息,縮短應用程序運行所耗費的時間。並且,它會預先將應用程序要使用的Android Framework中的類與資源加載到內存中,並組織形成所用資源的鏈接信息。因此,新運行的應用程序在使用所需資源時不必每次重新形成資源的鏈接信息,從而提高運行速度。
下面我們來先分析一下Zygote的原理和源碼分析
Zygote是由Android中第一個進程init啟動完所需要的一系列後台程序後才啟動起來的,利用adb命令可以看到手機上運行的進程間的派生關系,如下圖所示:
vcq9ttTUy9DQ1NrE2rTm1tC1xL34s8zKtc/WwcvX7rTzs8y2yLXEuLTTw6OssqLNqLn9v+K5ss/t09DQp7XYvbW1zcHLxNq05rXEyrnTw8G/oaM8YnIgLz4NCtPJ09paeWdvdGXKx9PJSmF2YdaxvdOx4NC0tcSjrLK7xNzWsb3T08lpbml0vfizzMb0tq/Uy9DQoaPI9M/r1MvQ0Fp5Z290ZdTy0OjSqs/IyfqzyURhbHZpay9BUlTQ6cTiu/qjrNa00NDV4tK7yM7O8b7NysfU2mFwcF9wcm9jZXNzwO/D5qGjPGJyIC8+DQq0+sLryOfPwjwvcD4NCjxwcmUgY2xhc3M9"brush:java;">
int main(int argc, const char* const argv[])
{
...
AppRuntime runtime;
...
// Next arg is startup classname or "--zygote"
if (i < argc) {
arg = argv[i++];
if (0 == strcmp("--zygote", arg)) {
bool startSystemServer = (i < argc) ?
strcmp(argv[i], "--start-system-server") == 0 : false;
setArgv0(argv0, "zygote");
set_process_name("zygote");
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer);
} else {
set_process_name(argv0);
runtime.mClassName = arg;
// Remainder of args get passed to startup class main()
runtime.mArgC = argc-i;
runtime.mArgV = argv+i;
LOGV("App process is starting with pid=%d, class=%s.\n",
getpid(), runtime.getClassName());
runtime.start();
}
}
...
}
這裡面先創建了一個AppRuntime對象,其中AppRuntime類繼承自AndroidRuntime類,而AndroidRuntime類的作用是初始化並運行Dalvik虛擬機,為運行Android應用程序做好准備。
虛擬機是怎麼創建起來的本文就不關心了
接著來看AndroidRuntime.start()的主要代碼
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
jclass startClass;
jmethodID startMeth;
slashClassName = strdup(className);
for (cp = slashClassName; *cp != '\0'; cp++)
if (*cp == '.')
*cp = '/';
startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
LOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
這裡一開始先調用成員函數starVM創建一個虛擬機實例,接著就獲得main的ID並來調用main方法它。繼續看main()
public static void main(String argv[]) {
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
//綁定socket,接收新Android應用程序運行請求
registerZygoteSocket();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
//加載Android Application Framework使用的類與資源
preloadClasses();
//cacheRegisterMaps();
preloadResources();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
if (SamplingProfilerIntegration.isEnabled()) {
SamplingProfiler sp = SamplingProfiler.getInstance();
sp.pause();
SamplingProfilerIntegration.writeZygoteSnapshot();
sp.shutDown();
}
// Do an initial gc to clean up after startup
gc();
// If requested, start system server directly from Zygote
if (argv.length != 2) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
//運行SystemServer
if (argv[1].equals("true")) {
startSystemServer();
} else if (!argv[1].equals("false")) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
Log.i(TAG, "Accepting command socket connections");
if (ZYGOTE_FORK_MODE) {
runForkMode();
} else {
//處理新Android應用程序請求
runSelectLoopMode();
}
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
概括起來,主要完成了以下4個工作:
1)registerZygoteSocket(); 語句用來綁定socket,以便接收新的Android應用程序的運行請求。為了從ActivityManager接收新的Android應用程序的運行請求,Zygote使用UDS(Unix Domain Socket),init進程在運行app_process時,使用init.rc文件中以”/dev/zygote”形式注冊的socket;
2)該行用於將應用程序框架中的類、平台資源(圖像、XML信息、字符串等)預先加載到內存中。新進程直接使用這些類與資源,而不需要重新加載它們,這大大加快了程序的執行速度;
3)通過app_process運行zygote時,參數”–start-system-server”會調用startSystemServer()方法啟動系統服務器,系統服務器用來運行Android平台需要的一些主要的本地服務(主要是SurfaceFlinger,AudioFlinger,MediaPlayerService,CameraService等);
4)runSelectLoopMode();的作用是監視UDS,若收到新的Android應用程序生成請求,則進入處理循環.
我們來一一分析這四項工作是如何完成的。
registerZygoteSocket()方法的代碼如下:
/**
* Registers a server socket for zygote command connections
*
* @throws RuntimeException when open fails
*/
private static void registerZygoteSocket() {
if (sServerSocket == null) {
int fileDesc;
try {
String env = System.getenv(ANDROID_SOCKET_ENV);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(
ANDROID_SOCKET_ENV + " unset or invalid", ex);
}
try {
sServerSocket = new LocalServerSocket(
createFileDescriptor(fileDesc));
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
主要就是先獲取socket的文件描述符,然後據此創建一個LocalServerSocket對象並將其賦值給sServerSocket靜態變量。在System.getenv(ANDROID_SOCKET_ENV); 中的套接字由init進程記錄在ANDROID_SOCKET_ENV環境變量中,在init.rc中有生成該socket的相關內容。
這個過程主要是由proloadClasses()與preloadResources()兩個方法來完成,分別將Framework中的類和資源加載到內存中,並對裝載的類與資源生成鏈接信息。新生成的Android應用程序在使用這些已經裝載的類或資源時,直接使用即可,不需要重新生成鏈接信息。
此外,還加載許多其他的類,比如圖形相關的類andriod.graphics,以及通信相關的類android.net等。
preloadClasses()方法的主要代碼如下:
private static final String PRELOADED_CLASSES="preloaded-classes";
...
private static void preloadClasses(){
...
//code_1
InputStream is=ZygoteInit.class.getClassLoader().getResourceAsStream(PRELOADED_CLASSES);
...
BufferedReader br=new BufferedReader(new InputStreamReader(is),256);
int count=0;
String line;
while((line=br.readLine())!=null){
line=line.trim();
if(line.startsWith("#")||line.equals("")){
continue;
}
Class.forName(line);
...
}
}
在code_1處獲取一個輸入流,以便讀取”preloaded-classes”文件(frameworks/base/preloaded-classes)中記錄的類,總共有1265個類會被預加載,如果每啟動一個應用程序,就加載一遍這些類,那麼將會耗費巨大的時間。
與預加載類相似,Android應用程序也會預先加載使用的資源,這樣在使用這些資源時的效率會大大提高。
preloadResources()方法的代碼如下所示:
/**
* Load in commonly used resources, so they can be shared across
* processes.
*
* These tend to be a few Kbytes, but are frequently in the 20-40K
* range, and occasionally even larger.
*/
private static void preloadResources() {
final VMRuntime runtime = VMRuntime.getRuntime();
Debug.startAllocCounting();
try {
runtime.gcSoftReferences();
runtime.runFinalizationSync();
mResources = Resources.getSystem();
mResources.startPreloading();
if (PRELOAD_RESOURCES) {
Log.i(TAG, "Preloading resources...");
long startTime = SystemClock.uptimeMillis();
TypedArray ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
//code_1
int N = preloadDrawables(runtime, ar);
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
//code_2
N = preloadColorStateLists(runtime, ar);
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
}
mResources.finishPreloading();
} catch (RuntimeException e) {
Log.w(TAG, "Failure preloading resources", e);
} finally {
Debug.stopAllocCounting();
}
}
這裡定義了一個成員變量,用來記錄資源加載的狀態為了避免因重復調用而造成資源重復加載的情形。若指定資源已經被加載,就會拋出IllegalStateException,並終止方法的執行。
startSystemServer()的代碼如下所示:
private static boolean startSystemServer()
throws MethodAndArgsCaller, RuntimeException {
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
"--capabilities=130104352,130104352",
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
int debugFlags = parsedArgs.debugFlags;
if ("1".equals(SystemProperties.get("ro.debuggable")))
debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
/* Request to fork the system server process */
//code_2
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, debugFlags, null);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
//code_3
handleSystemServerProcess(parsedArgs);
}
return true;
}
1)args[]數組中保存了SystemServer的啟動參數,參數的含義也很好理解,無非是為SystemServier設置名稱、uid和gid,以及進程分組等。在字符串數組中,最後一個參數com.android.server.SystemServer用於指定SystemServer類;
2)與運行其他應用程序不同,startSystemServer()方法會調用forkSystemServer()方法來創建新進程,並運行SystemServer.系統在運行普通的Android應用程序時,只負責創建應用程序進程,至於進程是否創建成功則並不檢查。但是SystemServer則是必須運行的,因此在forkSystemServer()方法中必須檢查生成SystemServer進程工作是否正常;
3)handleSystemServerProcess()是用來啟動System進程,這個等後面再來分析
public class ZygoteInit {
......
private static void runSelectLoopMode() 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;
......
try {
fdArray = fds.toArray(fdArray);
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 newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done;
done = peers.get(index).runOnce();
if (done) {
peers.remove(index);
fds.remove(index);
}
}
}
}
......
}
這就是在等待ActivityManagerService來連接這個Socket,然後調用ZygoteConnection.runOnce函數來創建新的應用程
這樣,Zygote進程就啟動完成了
到這裡,我們可以歸納出Android Framework的啟動過程:
可能我們在開發中會時常用到計時器這玩意兒,比如在錄像的時候,我們可能需要在右上角顯示一個計時器。這個東西其實實現起來非常簡單。只需要用一個控件Chronometer,是的
上一篇我們說到的逐幀動畫和補間動畫,這篇我們著重說下屬性動畫:先看下面兩幅動畫: 上面兩幅動畫就是通過屬性動畫做出來的,是不是比較炫呢!不僅有顯示,也有交互點擊事件。下面
android Fragments詳解 Fragment是activity的界面中的一部分或一種行為。你可以把多個Fragment們組合到一個activity中
OTTO是一個EventBus類型的事件傳輸總線,它可以提供“存儲轉發”的功能,讓你APP中各個組件的交流更加便利,讓你的程序分層更加清晰。使用場景OTTO基於Obser