Android研究-Android的init啓動到launcher啓動-主要分析zygote服務

Android啓動過程簡單概括爲:

init進程 –> Zygote進程 –> SystemServer 進程 –>各種應用進程

init進程:linux的根進程,android系統是基於linux系統的,因此可以算作是整個android操作系統的第一個進程;

Zygote進程:android系統的根進程,主要作用:可以作用Zygote進程fork出SystemServer進程和各種應用進程;


SystemServer 進程:主要是在這個進程中啓動系統的各項服務,比如ActivityManagerService,PackageManagerService,WindowManagerService服務等等;

各種應用進程:啓動自己編寫的客戶端應用時,一般都是重新啓動一個應用進程,有自己的虛擬機與運行環境;


下面這邊文章個人認爲寫得非常好,因爲版本原因,可能跟新的Android版本代碼有不一樣的地方,但是大的架構沒有變化,值得仔細閱讀,最好跟着系統源碼碼一起看!



轉自: Android研究-Android的init啓動到launcher啓動-主要分析zygote服務 


Android的啓動過程是從進程init開始的,所以它是後續所有進程的祖先進程。

一、init進程

源碼位於system/core/init目錄。主要做了以下事情:

1.     重新設置子進程終止時信號SIGCHLD的處理函數。

act.sa_handler = sigchld_handler;  //調用了wait函數等待子進程退出。

act.sa_flags = SA_NOCLDSTOP;

act.sa_mask = 0;

act.sa_restorer = NULL;

sigaction(SIGCHLD, &act, 0);

 

2. 將kernel啓動過程中建立好的文件系統框架mount到相應目錄。

    mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");

   …

    mount("devpts", "/dev/pts", "devpts", 0, NULL);

    mount("proc", "/proc", "proc", 0, NULL);

mount("sysfs", "/sys", "sysfs", 0, NULL);

   

3.     open_devnull_stdio(),將init進程的標準輸入、輸出、出錯設備設置爲新建的設備節點/dev/__null__。

4.     log_init(),創建並打開設備節點/dev/__kmsg__。

5.     讀取並解析rc配置文件。

5.1 先從文件/sys/class/BOOT/BOOT/boot/boot_mode讀出啓動方式:Factory Mode, '4';ATE Factory Mode, '6'。看是否是facatory模式。

5.2 如果是的話,需要讀取並解析兩個文件:init.factory.rc和init.rc。

5.3 如果是正常啓動,則暫時先讀取init.rc。

這裏在讀取解析文件的時候,是以行爲最小可執行單位在解析。關於書寫init.rc文件的初始化腳本語言的規則,可以上網查找。解析之後並不會馬上執行,而是在init進入服務循環之前統一根據其命令本身所帶的條件來執行。

   

6.     導入kernel的cmdline,也就是u-boot傳遞給kernel的參數,查看其中是否具有 androidboot.xxx(androidboot.mode、androidboot.hardware等)參數,如果有,將其保存在同名字的 xxx(mode、hardware)全局變量中。這裏特別說明的是hardware這個參數,從kernel中導出一部分之後,又要從/proc /cpuinfo中導出一部分來組合成完整的hardware參數,因爲後面接下來會讀取並解析以特定平臺的rc文件。

7.     讀取特定平臺相關的initrc文件,如:init.mt6516.rc。

需要注意的是:對於service,這裏會給每個服務建立一個struct service的結構體,全部掛入鏈表service_list之中,在init最後才啓動。

   

8.     檢查解析出來的所有命令行當中是否有屬於early-init的,如果有,將其提出來加入到鏈表action_queue之中,馬上將其執行掉。

 

9.     device_init()函數將會打開uevent的netlink socket,遍歷/sys/class、/sys/block、/sys/devices目錄,檢查各級目錄的uevent文件,處理在vold服務起 來之前由kernel所發出來的device add, remove等事件。

 

10.  property_init(), 顧名思義,是屬性初始化。首先創建一個名字爲system_properties的匿名共享內存區域,對並本init進程做mmap讀寫映射,其餘共享它 的進程只有讀的權限。然後將這個prop_area結構體通過全局變量__system_property_area__傳遞給property services。

接着調用函數load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT)從/default.prop文件中加載編譯時生成的屬性。

       

11.  如果在root目錄下有initlogo.rle文件存在,這個是兩張android字樣的縷空圖片,將其讀入fb中顯示到LCD上。同時也要往串口上輸出"             A N D R O I D "。如果圖片不存在,就沒有這兩項的輸出。

 

12.  設置相應的屬性:

property_set("ro.factorytest", "0")

property_set("ro.serialno", serialno[0] ? serialno : "");

property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");

property_set("ro.baseband", baseband[0] ? baseband : "unknown");

property_set("ro.carrier", carrier[0] ? carrier : "unknown");

property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");

 

property_set("ro.hardware", hardware);

snprintf(tmp, PROP_VALUE_MAX, "%d", revision);

property_set("ro.revision", tmp);

 

13.  開始執行以init爲trigger的命令行:

action_for_each_trigger("init", action_add_queue_tail);

drain_action_queue();

        前面有執行過eraly-init的。

       

14.  啓動屬行服務:property_set_fd = start_property_service();

先讀取剩餘三個文件中的屬性:/system/build.prop、/system/default.prop、/system /default.prop,然後用函數load_persistent_properties()加載persist.開始的屬性,這種屬性都是保存在 目錄/data/property下的以屬性名爲文件名的中。

接下來創建一個名爲property_service的socket接口(SOCK_STREAM),然後進入監聽狀態,等待屬性事件到來。

   

15.  創建一對socket,用來做信號方面的處理。

socketpair(AF_UNIX, SOCK_STREAM, 0, s),signal_fd = s[0],signal_recv_fd = s[1]。

 

    16.執行eraly-boot和boot爲trigger的命令

        action_for_each_trigger("early-boot", action_add_queue_tail);

        action_for_each_trigger("boot", action_add_queue_tail);

        drain_action_queue();

       

17.執行init.rc中以property:開頭的屬性設置語句,同時使能屬性觸發方式。

queue_all_property_triggers();

drain_action_queue();

       

        property_triggers_enabled = 1;  //  可以執行那些以屬性爲條件的init語句。

       

1.     接下來就是利用poll機制監聽前面創建的幾個fd的動態。

struct pollfd ufds[4];

ufds[0].fd = device_fd;

ufds[0].events = POLLIN;

ufds[1].fd = property_set_fd;

ufds[1].events = POLLIN;

ufds[2].fd = signal_recv_fd;

ufds[2].events = POLLIN;

 

for(;;) {

     int nr, i, timeout = -1;

           

    for (i = 0; i < fd_count; i++)

        ufds[i].revents = 0;

    

     drain_action_queue(); //執行action_queue鏈表中後來新出現的command。

     restart_processes();  // 第一次啓動所有服務,也包括後來restart這些

服務。restart_service_if_needed() à service_start(svc, NULL) à fork()

    

     …

     nr = poll(ufds, fd_count, timeout);

     if (nr <= 0)

            continue;

    

     if (ufds[2].revents == POLLIN) {

            /* we got a SIGCHLD - reap and restart as needed */

            read(signal_recv_fd, tmp, sizeof(tmp));

            while (!wait_for_one_process(0))

                ;

            continue;

     }

    

     if (ufds[0].revents == POLLIN)

          handle_device_fd(device_fd);  // Vold的netlink類型的socket

    

     if (ufds[1].revents == POLLIN)

          handle_property_set_fd(property_set_fd);//屬(行服務)的socket

     if (ufds[3].revents == POLLIN)

            handle_keychord(keychord_fd);

}

到這裏init就進入了死循環中一直在監聽ufds中的4個文件描述符的動靜,如果有POLLIN的事件,就做相應的處理,所以init並沒有退出 或者進入idle,而是被當做一個服務在運行。第4個文件描述符是keychord_fd,暫時不清楚這個怎麼用,不過通過它也可以啓動服務,可參考源 碼。

下面是init.rc的例子,見附件init.rc

       

二、init中啓動的各種服務

在init中啓動起來的服務按照init.rc中的先後順序,大致有:

console: start a shell,code path: system/bin/sh,其源碼中包含常用的shell命令,如ls,cd等。

adbd: start adb daemon,通常帶有disabled的選項,表明需要按名字啓動,code path:system/bin/adb。

servicemanager:這個服務管理着系統內所有binder services。code path: frameworks/base/cmds/servicemanager。

Vold: android 的udev,code path: system/vold。

Netd: start ntd daemon, code path: system/netd。

Debuggerd: start debug system, code path: system/core/debuggerd。

zygote: ['zaigut]這是一個非常重要的服務,稍後詳解。start Android  Java Runtime  and start systemserver。code path:frameworks/base/cmds/app_process。

media: add AudioFlinger,AudioPolicyService,MediaPlayerService and CameraService to servicemanager,同時啓動管理binder通訊的機制,依靠這兩個類來完成binder機制在android中間層所體現的功 能:ProcessState 和IPCThreadState。Code path:frameworks/base/media/mediaserver。

bootanim: 開機動畫和鈴聲,code path:frameworks/base/cmds/bootanimation。

 

接下來就是關於modem的服務,如:ccci_fsd、ccci_mdinit、pppd_gprs、pppd、gsm0710muxd、 muxtestapp、sockcli、socksrv、muxreport、ril-daemon等,除了前面2個,後面的都帶有disabled的參 數,需要按名啓動。

 

Installd: start install package daemon, code path:

frameworks/base/cmds/installd。

後面還有很多關於其他硬件的服務,比如BT、WIFI等。

 

2.1 servicemanager

    這個服務進程代碼比較簡單,功能也簡單,c實現的,用來管理系統中所有的binder service,不管是本地的c++實現的還是java語言實現的都需要這個進程來統一管理,最主要的管理就是,註冊添加服務,獲取服務。

    這些binder服務在對外公開之前都必須將自身擁有的binder實體註冊到SMgr中,而其他進程如果需要使用binder service的功能,也必須先經過SMgr取得 binder service中的binder實體在SMgr中對應的引用號(binder的引用號和進程中文件描述符概念類似,有效範圍只限於本進程)。

 

#define BINDER_SERVICE_MANAGER ((void*) 0)  

int main(int argc, char **argv) 

    struct binder_state *bs; 

    void *svcmgr = BINDER_SERVICE_MANAGER;  

    bs = binder_open(128*1024);   

    // 打開binder設備,mmap映射當前進程的binder接收緩衝區,返回一個binder_state結構體  。

    if (binder_become_context_manager(bs)) {     

    // 通過這個函數將當前進程設置成服務管理進程MSgr,整個系統就這一個。 

       …

    } 

    svcmgr_handle = svcmgr;    

    binder_loop(bs, svcmgr_handler);  

 /*svcmgr_handler作爲處理函數,所能完成的操作有:獲取service,查看service是否存在,添加service ,列出service清單。其中用的最多的就是獲取、添加。*/ 

    return 0; 

}

   

    2.2 zygote

        zygote服務進程也叫做孵化進程,在linux的用戶空間,進程app_process會做

一些zygote進程啓動的前期工作,如,啓動runtime運行時環境(實例),參數分解,設置startSystemServer標誌,接着用 runtime.start()來執行zygote服務的代碼,其實說簡單點,就是zygote搶了app_process這個進程的軀殼,改了名字,將 後面的代碼換成zygote的main函數,這樣順利地過度到了zygote服務進程。這樣我們在控制檯用ps看系統所有進程,就不會看到 app_process,取而代之的是zygote。

       

        而前面runtime.start()這個函數實際上是類函數AndroidRuntime::start(),在

這個函數中,會新建並啓動一個虛擬機實例來執行com.android.internal.os.ZygoteInit這個包的main函數。這個 main函數中會fork一個子進程來啓動systemserver,父進程就作爲真正的孵化進程存在了,每當系統要求執行一個 Android應用程序,Zygote就會收到socket消息FORK出一個子進程來執行該應用程序。因爲Zygote進程是在系統啓動時產生的,它會 完成虛擬機的初始化,庫的加載,預置類庫的加載和初始化等操作,而在系統需要一個新的虛擬機實例時可以快速地製造出一個虛擬機出來。

   

    每一個Android應用都運行在一個Dalvik虛擬機實例裏,而每一個虛擬機實例都是一個獨立的進程空間。虛擬機的線程機制,內存分配和管 理,Mutex等等都是依賴底層linux實現的。所以android應用程序中創建了線程將會實際調用到linux的線程創建接口,和虛擬機所在進程共 享一個虛擬機實例對java代碼執行。

   

2.2.1 app_process

下面重點討論zygote服務,源碼位於frameworks/base/cmds/app_process。

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

參數:/system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

 

int main(int argc, const char* const argv[])

{

    …

    AppRuntime runtime; // 這裏啓動runtime運行時環境(實例),AppRuntime是AndroidRuntime的子類,在創建這個對象的時候會依次調用基類和子類的構造函數。

    const char *arg;

    const char *argv0;

    …

    argv0 = argv[0];

    // ignore argv[0]

    argc--;

argv++;

/*

    argc = 4;

argv[0] = “-Xzygote”;

argv[1] = “/system/bin”;

argv[2] = “--zygote”;

argv[3] = “–start-system-server”;

*/

    int i = runtime.addVmArguments(argc, argv); // 找到參數中第一個不是以單個-開始的參數,這裏很明顯是第二個參數:/system/bin

    if (i < argc) {

        runtime.mParentDir = argv[i++];  // 將命令目錄保存在mParentDir中,之後i = 2。

    }

    if (i < argc) {

        arg = argv[i++];

        if (0 == strcmp("--zygote", arg)) { // 通常這個分支是成立的

            bool startSystemServer = (i < argc) ?

                    strcmp(argv[i], "--start-system-server") == 0 : false;

// startSystemServer = true ,這個bool變量決定着後面執行runtime.start時是否啓動systemserver。

            setArgv0(argv0, "zygote");

            set_process_name("zygote");

            runtime.start("com.android.internal.os.ZygoteInit",

                startSystemServer);

        } else {

            … // 只啓動AndroidRuntime

        }

    }else{

        … // 錯誤處理

    }

} // main()

   

    2.2.2 AndroidRuntime

    下面進一步分析

    runtime.start("com.android.internal.os.ZygoteInit",startSystemServer);

    位於文件frameworks/base/core/jni/ AndroidRuntime.cpp,類定義於:

    frameworks/base/include/android_runtime/AndroidRuntime.h

    void AndroidRuntime::start(const char* className,

const bool startSystemServer){

LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n");

    JNIEnv* env;

    …

    /* start the virtual machine , mJavaVM是類AndroidRuntime的私有變量,env該函數中局部變量 */

    if (startVm(&mJavaVM, &env) != 0)

        goto bail;

 

    /* Register android functions.向剛剛新建的虛擬機註冊JNI本地接口。frameworks/base/core/jni/這個目錄下的所有jni接口。*/

    if (startReg(env) < 0) {

        …

goto bail;

}

 

//啓動虛擬機之前需要構造java形式的參數數組,如下:

jclass stringClass;

jobjectArray strArray;

    jstring classNameStr;

    jstring startSystemServerStr;

 

stringClass = env->FindClass("java/lang/String");

strArray = env->NewObjectArray(2, stringClass, NULL);

classNameStr = env->NewStringUTF(className);

env->SetObjectArrayElement(strArray, 0, classNameStr);

startSystemServerStr = env->NewStringUTF(startSystemServer ?

                                                 "true" : "false");

env->SetObjectArrayElement(strArray, 1, startSystemServerStr);

/*  strArray[0] = “com.android.internal.os.ZygoteInit”

    strArry[1] = “true”*/

 

/*

 * 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); // 根據路徑找到這個包

            // com.android.internal.os.ZygoteInit,這個類位於文件

            // com/ndroid/nternal/os/ZygoteInit.java

if (startClass == NULL) {

     LOGE("JavaVM unable to locate class '%s'\n", slashClassName);

     /* keep going */

 } else {

        /*這個參數決定了我們接下來執行的是zygoteInit.java的main函數。*/

        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);

                // 虛擬機啓動,將會調用到com.android.internal.os.ZygoteInit包的main函數。

}

}

 

LOGD("Shutting down VM\n");

    if (mJavaVM->DetachCurrentThread() != JNI_OK)

        LOGW("Warning: unable to detach main thread\n");

    if (mJavaVM->DestroyJavaVM() != 0)

        LOGW("Warning: VM did not shut down cleanly\n");

 

bail:

    free(slashClassName);

}

} // start()

   

    2.2.3 ZygoteInit

    下面進入com.android.internal.os.ZygoteInit 包的main函數:

    frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    public static void main(String argv[]) {

        try {

            …

            registerZygoteSocket(); // Registers a server socket for zygote command

            // load classs and resources

    preloadClasses();

    preloadResources();

    …

    // Do an initial gc to clean up after startup

gc();/*初始化GC垃圾回收機制*/

 

/* 通過從前面傳遞過來的第二個參數startsystemserver=”true” 啓動systemserver, 在startSystemServer()中會fork一個新的進程命名爲system_server, 執行的是com.android.server包中的SystemServer.java文件中的main函數, 源碼位置:frameworks/base/services/java/com/android/server/ SystemServer.java。*/

if (argv[1].equals("true")) {

                startSystemServer(); ///*************

} else if(…)

 

if (ZYGOTE_FORK_MODE) {

    runForkMode();      /* ZYGOTE_FORK_MODE 永遠爲flase */

} else {

    runSelectLoopMode();/* Zygote進程進入無限循環,不再返回。接下來的zygote將會作爲一個孵化服務進程來運行。*/

}

 

closeServerSocket();

}

        …

    } // end main()

   

從這裏開始android啓動分爲兩條線走,分別是:

startSystemServer(); ---------- Zygote的子進程

runSelectLoopMode(); /* Zygote進程進入無限循環,不再返回,執行孵化工作。*/

 

2.2.4 systemserver

    上面的startSystemServer()函數中,做了如下的調用:

    startSystemServer()

    à Zygote.forkSystemServer()

    à 父進程直接返回true,子進程執行handleSystemServerProcess()

        à RuntimeInit.zygoteInit(parsedArgs.remainingArgs);

        /*

         * Pass the remaining arguments to SystemServer.

         * "--nice-name=system_server com.android.server.SystemServer"

         */

/*上面這個RuntimeInit包定於於文件frameworks\base\core\java\com\android\internal\os\RuntimeInit.java */

à invokeStaticMain(startClass, startArgs)

/* 通過該函數調用執行startClass類的main函數,帶參數 system_server 。*/

à ZygoteInit.MethodAndArgsCaller(m, argv)

/* 得到包com.android.server.SystemServer的main()函數。然後執行。*/

   

    下面就開始調用到com.android.server.SystemServer類的main函數,源碼位於:

    frameworks/base/services/java/com/android/server/SystemServer.java 

   

This method is called from Zygote to initialize the system. This will cause the nativeservices (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call backup into init2() to start the Android services.

    // main--->init1(system_init)--->init2(systemThread)

native public static void init1(String[] args);

       

public static void main(String[] args) {

        ...

System.loadLibrary("android_servers");// libandroid_servers.so是由目錄frameworks/base/services/jni下的源碼編譯所得

init1(args); // init1實際上是一個jni本地接口,位於文件frameworks\base\services\jni\com_android_server_SystemServer.cpp文件中system_init()函數

}

   

init1接口位於com_android_server_SystemServer.cpp中:

extern "C" int system_init();

static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz){

    system_init();

}

 

static JNINativeMethod gMethods[] = {

    /* name, signature, funcPtr */

{"init1","([Ljava/lang/String;)V",(void*)

android_server_SystemServer_init1 },

};

而函數system_init()位於文件

frameworks\base\cmds\system_server\library\System_init.cpp

extern "C" status_t system_init()

{

    …

    char propBuf[PROPERTY_VALUE_MAX];

    property_get("system_init.startsurfaceflinger", propBuf, "1");

    if (strcmp(propBuf, "1") == 0) {

        // Start the SurfaceFlinger

        SurfaceFlinger::instantiate();

    }

    if (!proc->supportsProcesses()) {

       

        // Start the AudioFlinger

        AudioFlinger::instantiate();

       

        // Start the media playback service

        MediaPlayerService::instantiate();

       

        // Start the camera service

        CameraService::instantiate();

       

        // Start the audio policy service

        AudioPolicyService::instantiate();

       

        //start appc service

        APPCService::instantiate();

    }

    …

    AndroidRuntime* runtime = AndroidRuntime::getRuntime();

    runtime->callStatic("com/android/server/SystemServer", "init2");

    // 執行com.android.server.SystemServer類的init2函數

}

com.android.server.SystemServer包的init2函數開啓一個ServerThread線程:

public static final void init2() {

    Thread thr = new ServerThread();

    thr.setName("android.server.ServerThread");

    thr.start();

}

ServerThread線程的run函數會啓動系統中絕大部分的android service,並最後進入Loop.loop(),,,,(SystemServer.java)

public void run() {

    …

    // Critical services...

    try {

        …

        Slog.i(TAG, "Power Manager");

        power = new PowerManagerService();

        ServiceManager.addService(Context.POWER_SERVICE, power);

       

        Slog.i(TAG, "Activity Manager");

        context = ActivityManagerService.main(factoryTest);

       

        …

        Slog.i(TAG, "Package Manager");

        pm = PackageManagerService.main(context,

                factoryTest != SystemServer.FACTORY_TEST_OFF);

       

        …

        Slog.i(TAG, "Content Manager");

        ContentService.main(context,

             factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);

       

        …

        Slog.i(TAG, "Battery Service");

        battery = new BatteryService(context);

        ServiceManager.addService("battery", battery);

        …

        其餘addservice的過程類似,只是啓動的不同服務罷了,後面還啓動了很多

服務,如:Lights、Vibrator、Alarm、Sensor、Bluetooth、Input Method、NetStat、NetworkManagement、Connectivity、Mount、Notification、Audio等。 在這些服務都啓動完了之後。

        …

        …

        … // run()函數的後半部分

        // It is now time to start up the app processes...

        使用xxx.systemReady()通知各個服務,系統已經就緒。

        …

        ((ActivityManagerService)ActivityManagerNative.getDefault())

                .systemReady(new Runnable() {

         public void run() {

                …

                if (batteryF != null) batteryF.systemReady();

                if (connectivityF != null) connectivityF.systemReady();

                if (dockF != null) dockF.systemReady();

                if (uiModeF != null) uiModeF.systemReady();

                if (recognitionF != null) recognitionF.systemReady();

                Watchdog.getInstance().start();

                …

            }

        });

        …

        Looper.loop(); // Run the message queue in this thread。

        …

}

 

2.2.5 home界面啓動

    Home在

((ActivityManagerService)ActivityManagerNative.getDefault()).systemReady(.)

函數調用的過程中啓動,其中systemReady()的參數是一段callback代碼,如上面灰色顯示的部分。

這個函數的實現部分在文件:ActivityManagerService.java中。

public void systemReady(final Runnable goingCallback) {

    …

   

    if (mSystemReady) {

        if (goingCallback != null) goingCallback.run();

        return; // 執行回調

    }

    …

    resumeTopActivityLocked(null);

}

private final boolean resumeTopActivityLocked(HistoryRecord prev) {

    …

    if (next == null) {

        // There are no more activities!  Let's just start up the

        // Launcher...

        return startHomeActivityLocked();

    }

    …

}

private boolean startHomeActivityLocked() {

    …

    if (aInfo != null) {

        …

        if (app == null || app.instrumentationClass == null) {

                intent.setFlags(intent.getFlags() |

                                Intent.FLAG_ACTIVITY_NEW_TASK);

                startActivityLocked(null, intent, null, null, 0, aInfo,

                        null, null, 0, 0, 0, false, false); // 這裏啓動home

         }

     }

    …

}

三、android啓動圖示

參考網址:

1.        http://blog.csdn.net/cuijianzhongswust/article/details/6612624

2.        http://blog.csdn.net/sustzombie/article/details/6659622

3.        http://www.docin.com/p-191202348.html

4.        http://blog.csdn.net/maxleng/article/details/5508372

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章