1. 程式人生 > >Android 淘氣三千傳之 —— 外掛化的一點理解(上)

Android 淘氣三千傳之 —— 外掛化的一點理解(上)

外掛化

這一篇主要是個人對外掛化涉及到的一些基礎知識的理解,內容都比較簡單:

包括以下內容:

目錄:

1、類載入機制

2、Binder機制

3、APP、四大元件的啟動流程

4、APK安裝過程

5、資源的載入過程

6、Hook機制

7、OOP

8、面向切面程式設計

9、代理模式

10、外掛化框架

外掛化的發展已經越來越成熟,Android 9.0 之後的 @hide API 對一些外掛化的做法也有越來越大的限制,當然也有人找出了一些解決的方法,
比如這篇:https://juejin.im/post/5acf3be5f265da23a40534f4


總體來說,瞭解一下外掛化也是挺不錯的,瞭解過程中可以對其它方面程式碼順帶了解下:

要檢視 Android 原始碼的話,比較好是有一份編譯好的原始碼,匯入到android studio 裡面,ubuntu、window都可以檢視,主要是需要有 android.ipr 檔案,這裡在android studio 除錯的是 android-6.0.1_r72 的原始碼,文中一些也有 5.0 的程式碼 ~~~,或者 在 http://androidxref.com/ 以及 https://www.androidos.net.cn/ 也可以檢視,或者谷歌的 AOSP裡面也可以檢視,AOSP 目前有8.1的版本,AndroidXRef 最新只到了8.0

1、類載入機制:

Module First : java 虛擬機器的類載入機制

類載入機制是老生常談的了,很多大神都寫了非常優秀的文章,小蝦米這裡簡單再捋一遍,當作一個記錄,

那麼,首先記住這5個詞語就好了:

載入、驗證、準備、解析、初始化

然後,腦海裡默默讀十遍:

載入、驗證、準備、解析、初始化

載入、驗證、準備、解析、初始化

。。。

好了,已經掌握了,本文結束,over!

剩下的就是這5個詞語展開了,載入什麼呢?怎麼載入?驗證什麼呢?怎麼驗證?等等問題,就像腦海裡有一個xmind圖一樣,一點點地展開,具體可以參考 geeksforgeeks 這個網站jvm的講解,貼個網址:

https://www.geeksforgeeks.org/jvm-works-jvm-architecture/ ,這個網站也有很多其他方面的知識,演算法、c++等,或者 深入jvm這本書 的介紹。

這裡簡單過一下:

(1) 五個過程:

Class檔案:二進位制位元組流

a、載入

  • 通過類的全限定名來獲取定義此類的二進位制位元組流
  • 將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構
  • 在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的各種資料的訪問入口。

b、驗證

  • 檔案格式驗證
  • 元資料驗證
  • 位元組碼驗證
  • 符號引用驗證。

c、準備

略。。。

d、解析

略。。。

e、初始化

略。。。

問題1:五個階段的開始順序是什麼:

載入 -> 驗證 ->準備 -> 初始化

這四個階段“開始”的順序是固定的,“進行”過程順序不定,交叉混合進行

解析有時候在初始化之後再開始,這是為了支援Java的動態繫結

(2) 類載入器:

a、對於任意一個類,都需要由載入它的類載入器和這個類本身一同確立其在Java虛擬機器中的唯一性,每一個類載入器,都擁有一個獨立的類名稱空間,也就是說,即使兩個類來源於同一個Class檔案,並且被同一個虛擬機器載入,只要載入它們的類載入器不同,那麼這兩個類就必定不相等。

b、雙親委派模型:組合關係複用父載入器的程式碼

這裡寫圖片描述

程式碼可以對比下 Android 的 DexClassLoader 的 findClass 過程,過程都是一樣的

Module Second : android虛擬機器的類載入機制

PathClassLoader

DexClassLoader

這兩個類進行流程一步步除錯就可以知道了,注意原始碼裡這兩個類的構造方法對應引數的解釋。

ok,玩一把遊戲休息下~~~~

這裡寫圖片描述

2、Binder

首先簡單瞭解下 Linux 的 IPC,Linux核心是超級優秀的 c 程式,常說的 fork()、pipe() 等也是這段程式裡面的一個函式

Module First:linux 的 程序間通訊 (IPC)

IPC是程序間通訊的縮寫,通常指允許使用者態程序執行下列操作的一組機制:

  • 通過訊號量與其它程序進行同步
  • 向其它程序傳送訊息或者從其它程序接收訊息
  • 和其它程序共享一段記憶體區

IPC資料結構是在程序請求IPC資源(訊號量、訊息佇列或者共享記憶體區)時動態建立的,每個IPC資源都是持久的,除非被程序顯示地釋放,否則永遠駐留在記憶體中,知道系統關閉。

linux使用者態的程序之間進行同步和交換資料,有以下幾種程序間通訊的基本機制:

管道

管道是程序之間的一個單向資料流:一個程序寫入管道的所有資料都由核心定向到另一個程序,另一個程序由此就可以從管道中讀取資料

可以使用 pipe()來建立一個新的管道,這個系統呼叫返回一對檔案描述符,然後程序通過 fork() 把這兩個描述符傳遞給它的子程序,由此與子程序共享管道。程序可以在 read() 系統呼叫中使用第一個檔案描述符從管道中讀取資料,同樣也可以在 write() 系統呼叫中使用第二個檔案描述符向管道中寫入資料。

一個管道可以供任意個程序使用,當然,如果兩個或者多個程序對同一個管道進行讀寫,那麼這些程序必須使用檔案加鎖或者IPC訊號量進行顯示同步

管道有一個主要缺點:無法開啟已經存在的管道,這就使得任意的兩個程序不可能共享同一個管道,除非管道是由一個共同的祖先程序建立

FIFO(命名管道)

由於管道的缺點,引入了命名管道FIFO,FIFO是一種雙向通訊管道

訊號量

IPC訊號量和核心訊號量非常類似,都是計數器,用來為多個程序共享的資料結構提供受控訪問。要訪問資源的程序試圖把訊號量的值減1,但是,核心阻塞這個程序,知道在這個訊號量上的操作產生一個正值。核心給每個IPC訊號量都分配了一個掛起請求對壘,用來標識正在等待陣列中的一個(或多個)訊號量的程序

訊息

程序彼此之間可以通過IPC訊息進行通訊。程序產生的每條訊息都被髮送到一個IPC訊息佇列中,這個訊息一直存放在佇列中直到另一個程序將其讀走為止。

Linux核心提供兩種不同的訊息版本:System V IPC 訊息 和 POSIX 訊息

共享記憶體區

最有用的IPC機制。允許兩個或多個程序通過把公共資料結構放入一個共享記憶體區來訪問它們,如果程序要訪問這種存放在共享記憶體區的資料結構,就必須在自己的地址空間中增加一個新記憶體區,它將對映與這個共享記憶體區相關的頁框,這樣的頁框可以很容易地由核心通過請求調頁進行處理。

套接字(socket)

Module Second:Android 的 Binder

除了 Socket、匿名管道以外,傳統的 IPC 如命名管道、訊號量、訊息佇列等已經從Android中去除掉了,而在網路通訊,socket很適合,在程序通訊效率就不太高。並且在安全的要求上,linux 原有的通訊不能很好滿足。Android 需要建立一套新的IPC 機制來滿足系統對通訊方式,傳輸效能和安全性的要求,這就是Binder。
關於 Binder 的 種種,
推薦一篇非常牛的文章:https://blog.csdn.net/universus/article/details/6211589 ,講得非常詳細。

a、這裡是AIDL的一點基本理解:

b、 接下來梳理一下 binder 的應用層
用 Android Studio 建立一個預設的AIDL檔案,編譯成功之後生成了 IMyAidlInterface.java,進入這個類檢視,檢視一下檔案格式:

這裡寫圖片描述

可以看到,IMyAidlInterface 有一個內部類 Stub,Stub 也有一個內部類 Proxy

首先關注這裡:

這裡寫圖片描述

queryLocalInterface(String params) 方法:

這裡寫圖片描述
方法的引數是一個類的全限定類名,可用於唯一標識,這個方法相當於解釋了 返回 null 和 不返回 null 的區別,首先嚐試檢索介面的本地實現,返回 null 會 走Stub 的代理 類Proxy 裡面的 transact 方法,否則不執行

asInterface 方法:
通過 queryLocalInterface 來確定是否要執行 Proxy

接下來看下 Proxy:

private static class Proxy implements com.example.z.ipctest.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}

Proxy 是 Stub 的內部類,asBinder 返回一個 Binder 物件,getInterfaceDescriptor 返回唯一標識,basicTypes 在寫入引數之後執行 mRemote.transact,這個是發生在對應程序的事務執行緒池中,並且是同步的。執行之後,該執行緒被掛起直到回調回來再繼續執行,後面可以看下IBinder.java 的解釋。

讀一下 IBinder.java

這個類看一下挺好的,讀這個類實際主要是讀它的註釋,整個類的程式碼實際也不多:

package android.os;

import java.io.FileDescriptor;

/**
 * Base interface for a remotable object, the core part of a lightweight
 * remote procedure call mechanism designed for high performance when
 * performing in-process and cross-process calls.  This
 * interface describes the abstract protocol for interacting with a
 * remotable object.  Do not implement this interface directly, instead
 * extend from {@link Binder}.
 * 
 * <p>The key IBinder API is {@link #transact transact()} matched by
 * {@link Binder#onTransact Binder.onTransact()}.  These
 * methods allow you to send a call to an IBinder object and receive a
 * call coming in to a Binder object, respectively.  This transaction API
 * is synchronous, such that a call to {@link #transact transact()} does not
 * return until the target has returned from
 * {@link Binder#onTransact Binder.onTransact()}; this is the
 * expected behavior when calling an object that exists in the local
 * process, and the underlying inter-process communication (IPC) mechanism
 * ensures that these same semantics apply when going across processes.
 * 
 * <p>The data sent through transact() is a {@link Parcel}, a generic buffer
 * of data that also maintains some meta-data about its contents.  The meta
 * data is used to manage IBinder object references in the buffer, so that those
 * references can be maintained as the buffer moves across processes.  This
 * mechanism ensures that when an IBinder is written into a Parcel and sent to
 * another process, if that other process sends a reference to that same IBinder
 * back to the original process, then the original process will receive the
 * same IBinder object back.  These semantics allow IBinder/Binder objects to
 * be used as a unique identity (to serve as a token or for other purposes)
 * that can be managed across processes.
 * 
 * <p>The system maintains a pool of transaction threads in each process that
 * it runs in.  These threads are used to dispatch all
 * IPCs coming in from other processes.  For example, when an IPC is made from
 * process A to process B, the calling thread in A blocks in transact() as
 * it sends the transaction to process B.  The next available pool thread in
 * B receives the incoming transaction, calls Binder.onTransact() on the target
 * object, and replies with the result Parcel.  Upon receiving its result, the
 * thread in process A returns to allow its execution to continue.  In effect,
 * other processes appear to use as additional threads that you did not create
 * executing in your own process.
 * 
 * <p>The Binder system also supports recursion across processes.  For example
 * if process A performs a transaction to process B, and process B while
 * handling that transaction calls transact() on an IBinder that is implemented
 * in A, then the thread in A that is currently waiting for the original
 * transaction to finish will take care of calling Binder.onTransact() on the
 * object being called by B.  This ensures that the recursion semantics when
 * calling remote binder object are the same as when calling local objects.
 * 
 * <p>When working with remote objects, you often want to find out when they
 * are no longer valid.  There are three ways this can be determined:
 * <ul>
 * <li> The {@link #transact transact()} method will throw a
 * {@link RemoteException} exception if you try to call it on an IBinder
 * whose process no longer exists.
 * <li> The {@link #pingBinder()} method can be called, and will return false
 * if the remote process no longer exists.
 * <li> The {@link #linkToDeath linkToDeath()} method can be used to register
 * a {@link DeathRecipient} with the IBinder, which will be called when its
 * containing process goes away.
 * </ul>
 * 
 * @see Binder
 */
public interface IBinder {
    /**
     * The first transaction code available for user commands.
     */
    int FIRST_CALL_TRANSACTION  = 0x00000001;
    /**
     * The last transaction code available for user commands.
     */
    int LAST_CALL_TRANSACTION   = 0x00ffffff;

    /**
     * IBinder protocol transaction code: pingBinder().
     */
    int PING_TRANSACTION        = ('_'<<24)|('P'<<16)|('N'<<8)|'G';

    /**
     * IBinder protocol transaction code: dump internal state.
     */
    int DUMP_TRANSACTION        = ('_'<<24)|('D'<<16)|('M'<<8)|'P';

    /**
     * IBinder protocol transaction code: execute a shell command.
     * @hide
     */
    int SHELL_COMMAND_TRANSACTION = ('_'<<24)|('C'<<16)|('M'<<8)|'D';

    /**
     * IBinder protocol transaction code: interrogate the recipient side
     * of the transaction for its canonical interface descriptor.
     */
    int INTERFACE_TRANSACTION   = ('_'<<24)|('N'<<16)|('T'<<8)|'F';

    /**
     * IBinder protocol transaction code: send a tweet to the target
     * object.  The data in the parcel is intended to be delivered to
     * a shared messaging service associated with the object; it can be
     * anything, as long as it is not more than 130 UTF-8 characters to
     * conservatively fit within common messaging services.  As part of
     * {@link Build.VERSION_CODES#HONEYCOMB_MR2}, all Binder objects are
     * expected to support this protocol for fully integrated tweeting
     * across the platform.  To support older code, the default implementation
     * logs the tweet to the main log as a simple emulation of broadcasting
     * it publicly over the Internet.
     * 
     * <p>Also, upon completing the dispatch, the object must make a cup
     * of tea, return it to the caller, and exclaim "jolly good message
     * old boy!".
     */
    int TWEET_TRANSACTION   = ('_'<<24)|('T'<<16)|('W'<<8)|'T';

    /**
     * IBinder protocol transaction code: tell an app asynchronously that the
     * caller likes it.  The app is responsible for incrementing and maintaining
     * its own like counter, and may display this value to the user to indicate the
     * quality of the app.  This is an optional command that applications do not
     * need to handle, so the default implementation is to do nothing.
     * 
     * <p>There is no response returned and nothing about the
     * system will be functionally affected by it, but it will improve the
     * app's self-esteem.
     */
    int LIKE_TRANSACTION   = ('_'<<24)|('L'<<16)|('I'<<8)|'K';

    /** @hide */
    int SYSPROPS_TRANSACTION = ('_'<<24)|('S'<<16)|('P'<<8)|'R';

    /**
     * Flag to {@link #transact}: this is a one-way call, meaning that the
     * caller returns immediately, without waiting for a result from the
     * callee. Applies only if the caller and callee are in different
     * processes.
     */
    int FLAG_ONEWAY             = 0x00000001;

    /**
     * Limit that should be placed on IPC sizes to keep them safely under the
     * transaction buffer limit.
     * @hide
     */
    public static final int MAX_IPC_SIZE = 64 * 1024;

    /**
     * Get the canonical name of the interface supported by this binder.
     */
    public String getInterfaceDescriptor() throws RemoteException;

    /**
     * Check to see if the object still exists.
     * 
     * @return Returns false if the
     * hosting process is gone, otherwise the result (always by default
     * true) returned by the pingBinder() implementation on the other
     * side.
     */
    public boolean pingBinder();

    /**
     * Check to see if the process that the binder is in is still alive.
     *
     * @return false if the process is not alive.  Note that if it returns
     * true, the process may have died while the call is returning.
     */
    public boolean isBinderAlive();

    /**
     * Attempt to retrieve a local implementation of an interface
     * for this Binder object.  If null is returned, you will need
     * to instantiate a proxy class to marshall calls through
     * the transact() method.
     */
    public IInterface queryLocalInterface(String descriptor);

    /**
     * Print the object's state into the given stream.
     * 
     * @param fd The raw file descriptor that the dump is being sent to.
     * @param args additional arguments to the dump request.
     */
    public void dump(FileDescriptor fd, String[] args) throws RemoteException;

    /**
     * Like {@link #dump(FileDescriptor, String[])} but always executes
     * asynchronously.  If the object is local, a new thread is created
     * to perform the dump.
     *
     * @param fd The raw file descriptor that the dump is being sent to.
     * @param args additional arguments to the dump request.
     */
    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException;

    /**
     * Execute a shell command on this object.  This may be performed asynchrously from the caller;
     * the implementation must always call resultReceiver when finished.
     *
     * @param in The raw file descriptor that an input data stream can be read from.
     * @param out The raw file descriptor that normal command messages should be written to.
     * @param err The raw file descriptor that command error messages should be written to.
     * @param args Command-line arguments.
     * @param shellCallback Optional callback to the caller's shell to perform operations in it.
     * @param resultReceiver Called when the command has finished executing, with the result code.
     * @hide
     */
    public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
            String[] args, ShellCallback shellCallback,
            ResultReceiver resultReceiver) throws RemoteException;

    /**
     * Perform a generic operation with the object.
     * 
     * @param code The action to perform.  This should
     * be a number between {@link #FIRST_CALL_TRANSACTION} and
     * {@link #LAST_CALL_TRANSACTION}.
     * @param data Marshalled data to send to the target.  Must not be null.
     * If you are not sending any data, you must create an empty Parcel
     * that is given here.
     * @param reply Marshalled data to be received from the target.  May be
     * null if you are not interested in the return value.
     * @param flags Additional operation flags.  Either 0 for a normal
     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
     */
    public boolean transact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException;

    /**
     * Interface for receiving a callback when the process hosting an IBinder
     * has gone away.
     * 
     * @see #linkToDeath
     */
    public interface DeathRecipient {
        public void binderDied();
    }

    /**
     * Register the recipient for a notification if this binder
     * goes away.  If this binder object unexpectedly goes away
     * (typically because its hosting process has been killed),
     * then the given {@link DeathRecipient}'s
     * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
     * will be called.
     * 
     * <p>You will only receive death notifications for remote binders,
     * as local binders by definition can't die without you dying as well.
     * 
     * @throws RemoteException if the target IBinder's
     * process has already died.
     * 
     * @see #unlinkToDeath
     */
    public void linkToDeath(DeathRecipient recipient, int flags)
            throws RemoteException;

    /**
     * Remove a previously registered death notification.
     * The recipient will no longer be called if this object
     * dies.
     * 
     * @return {@code true} if the <var>recipient</var> is successfully
     * unlinked, assuring you that its
     * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
     * will not be called;  {@code false} if the target IBinder has already
     * died, meaning the method has been (or soon will be) called.
     * 
     * @throws java.util.NoSuchElementException if the given
     * <var>recipient</var> has not been registered with the IBinder, and
     * the IBinder is still alive.  Note that if the <var>recipient</var>
     * was never registered, but the IBinder has already died, then this
     * exception will <em>not</em> be thrown, and you will receive a false
     * return value instead.
     */
    public boolean unlinkToDeath(DeathRecipient recipient, int flags);
}

首先,通過閱讀整個類頭的註釋,可以獲取一些資訊:

1) IBinder 的關鍵是 匹配 transact() 和 onTransact() 方法,可以通過這些方法分別傳送一個IBinder物件並接收一個Binder物件,並且是同步的。

2)系統在每個程序中維護一個事務執行緒池,這些執行緒用於分派所有來自其他程序的IPC。例如:

從 程序 A 到 程序 B,

I、A塊中開啟一個執行緒,執行 transact() 併發送一些事務 transaction 給 B

II 、此時 B中下一個可用的執行緒池接收到 A 傳送過來的事務,通過目標物件回撥用 Binder.onTransact() 方法,並返回結果給 A

III、A 直到收到結果反饋,該執行緒才可以繼續執行,從而也說明了同步執行。

3) Binder系統還支援跨程序的遞迴

4)使用遠端物件時,通過以下三個方法可以確認不再有效:

I、transact() : 如果在IBinder上呼叫它,則會發生 RemoteException 異常,說明其程序不再存在。

II、pingBinder() : 返回一個布林值,如果遠端程序不再存在則返回 false。

III、linkToDeath() :通過 DeathRecipient 註冊一個死亡代理,當遠端程序消失的時候裡面的方法會被回撥。

其次,除了 queryLocalInterface() 這個類提供了一些方法,通過方法名也可以看出具體作用,具體也可以看方法的註釋:

public boolean isBinderAlive();
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException;
public boolean unlinkToDeath(DeathRecipient recipient, int flags);

c、上面是 binder 應用層的簡單梳理,稍微看下 c++ 的binder,這裡是 Android 5.0 的

/frameworks/native/libs/binder/IServiceManager.cpp , 擷取一段 onTransact 的:

status_t BnServiceManager::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    //printf("ServiceManager received: "); data.print();
    switch(code) {
        case GET_SERVICE_TRANSACTION: {
            CHECK_INTERFACE(IServiceManager, data, reply);
            String16 which = data.readString16();
            sp<IBinder> b = const_cast<BnServiceManager*>(this)->getService(which);
            reply->writeStrongBinder(b);
            return NO_ERROR;
        } break;
        case CHECK_SERVICE_TRANSACTION: {
            CHECK_INTERFACE(IServiceManager, data, reply);
            String16 which = data.readString16();
            sp<IBinder> b = const_cast<BnServiceManager*>(this)->checkService(which);
            reply->writeStrongBinder(b);
            return NO_ERROR;
        } break;
       default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

再看下 /frameworks/native/libs/binder/BpBinder.cpp,BpBinder 是 IBinder 的子類

這裡寫圖片描述
這裡寫圖片描述

這裡還要注意另外的一個 BBinder,這個類和 BpBinder 都是 IBinder 的子類,類的內容初看也是一模一樣,不同的是,BBinder 有 onTransact() 方法,但 BpBindr 沒有,。。。

c++ 的binder 先暫時到這裡,可以看到,c++ 和 Java 使用Binder服務方式基本一樣,函式的介面型別都相同。

ok,玩一把遊戲休息下~~~~

這裡寫圖片描述

3、APP、四大元件的啟動流程

四大元件的工作流程梳理之前,有幾個概念可以先了解下,基本上在除錯四大元件啟動過程都會看到這些方法或者這些類裡面的方法的呼叫:

1、ContextWrappr.java 和 ContextImpl.java 這兩個類的關係

這兩個類都是Context的子類,從名字上可以看出,ContextWraper 是Context的包裝類,實際上也是如此,並且實際執行中,ContextWrapper 會通過 attachBaseContext 先例項化一個 ContextImpl 的物件 mBase,並持有這個 mBase,並通過這個 mBase 進行一系列實際的操作。

2、ActivityManagerNative.getDefault():

這裡寫圖片描述

看下 gDefault:

這裡寫圖片描述

可以看到,Singleton 是一個單例類,而對於四大元件的啟動過程,在啟動過程中最終都會呼叫到 ActivityManagerNative.getDefault() 裡面對應的啟動方法。

這裡寫圖片描述
3、ContextImpl 的 startActivity 、ContextWrapper 的 startActivity 以及 Activity 的 startActivity 關係
4、LoadedApk.java、PackageManagerService.java、ActivityThread.java、ActivityManagerService.java

LoadedApk 包含了應用的一系列資訊:

public final class LoadedApk {
    ...
    private static final String TAG = "LoadedApk";

    private final ActivityThread mActivityThread;
    private ApplicationInfo mApplicationInfo;
    final String mPackageName;
    private final String mAppDir;
    private final String mResDir;
    private final String[] mSplitAppDirs;
    private final String[] mSplitResDirs;
    private final String[] mOverlayDirs;
    private final String[] mSharedLibraries;
    private final String mDataDir;
    private final String mLibDir;
    private final File mDataDirFile;
    private final ClassLoader mBaseClassLoader;
    private final boolean mSecurityViolation;
    private final boolean mIncludeCode;
    private final boolean mRegisterPackage;
    private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
    Resources mResources;
    private ClassLoader mClassLoader;
    private Application mApplication;

    ...
}

PackageManagerService 可用於元件安裝時候的註冊:

/**
 * Keep track of all those .apks everywhere.
 *
 * This is very central to the platform's security; please run the unit
 * tests whenever making modifications here:
 */
 public class PackageManagerService extends IPackageManager.Stub {
 }

而關於 ActivityManagerService,是Binder的實現類,元件的啟動最後需要經過 ActivityManagerService 裡面的對應方法處理排程。

ActivityThread.java 用於管理主執行緒的執行,管理 activities、broadcasts 以及其它在 atcivityThread 的操作

5、ProcessRecord.java 的各種 Record,用於記錄對應的元件:

...

// all activities running in the process
final ArrayList<ActivityRecord> activities = new ArrayList<>();
// all ServiceRecord running in this process
final ArraySet<ServiceRecord> services = new ArraySet<>();
// services that are currently executing code (need to remain foreground).
final ArraySet<ServiceRecord> executingServices = new ArraySet<>();
// All ConnectionRecord this process holds
final ArraySet<ConnectionRecord> connections = new ArraySet<>();
// all IIntentReceivers that are registered from this process.
final ArraySet<ReceiverList> receivers = new ArraySet<>();
// class (String) -> ContentProviderRecord
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();

...

那麼開始瞧一瞧:
這裡寫圖片描述

Part 1 : Activity啟動流程

1、涉及到的類和方法

2、流程

3、看下程式碼

4、總結

1、涉及到的類和方法:

涉及到的類不多,只要跟著原始碼一步步跟進,很快的最後可以理清楚脈絡:

  • Activity.java
    • startActivity
    • startActivityForResult
    • performCreate
  • Context.java
    • startActivity
  • Instrumentation.java
    • execStartActivity
    • callActivityOnCreate
  • ActivityManagerNative.java
  • ActivityManagerService.java
    • startActivity
    • startActivityAsUser
  • ActivityStackSupervisor.java
    • startActivityMayWait
    • startActivityLocked
    • startActivityUncheckedLocked
    • startSpecificActivityLocked
    • realStartActivityLocked
  • ActivityStack.java
    • resumeTopActivityLocked
    • resumeTopActivityInnerLocked
    • startSpecificActivityLocked
    • realStartActivityLocked
  • IApplicationThread.java
  • ApplicationThreadNative.java
    • scheduleLaunchActivity
    • handleLaunchActivity
    • performLaunchActivity

2、流程:
用 visio 畫了下流程圖

這裡寫圖片描述

3、看下程式碼:

首先在 Context.java 中,有 startActivity 方法
這裡寫圖片描述

這是一個抽象方法,需要看具體實現

選中該方法,然後在 Android Studio 中 按 Ctrl + Alt + Shift + F7,出現如下,勾選 implementing methods

這裡寫圖片描述

接下來可以看到具體實現:

這裡寫圖片描述

可以看到過載了 startActivity 方法,繼續跟進,程式碼如下:

/**
 * Launch a new activity.  You will not receive any information about when
 * the activity exits.  This implementation overrides the base version,
 * providing information about
 * the activity performing the launch.  Because of this additional
 * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not
 * required; if not specified, the new activity will be added to the
 * task of the caller.
 *
 * <p>This method throws {@link android.content.ActivityNotFoundException}
 * if there was no Activity found to run the given Intent.
 *
 * @param intent The intent to start.
 * @param options Additional options for how the Activity should be started.
 * See {@link android.content.Context#startActivity(Intent, Bundle)
 * Context.startActivity(Intent, Bundle)} for more details.
 *
 * @throws android.content.ActivityNotFoundException
 *
 * @see {@link #startActivity(Intent)}
 * @see #startActivityForResult
 */
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        // Note we want to go through this call for compatibility with
        // applications that may have overridden the method.
        startActivityForResult(intent, -1);
    }
}

稍微看下英文註釋,意思就是如果是預設的activity啟動,那麼新的activity將會被新增到呼叫的棧,除非用了 FLAG_ACTIVITY_NEW_TASK 這個標誌。可以看到,最後都會呼叫 startActivityForResult,並且傳入的 requestCode 都是 -1,那麼看下startActivityForResult:

/**
 * Launch an activity for which you would like a result when it finished.
 * When this activity exits, your
 * onActivityResult() method will be called with the given requestCode.
 * Using a negative requestCode is the same as calling
 * {@link #startActivity} (the activity is not launched as a sub-activity).
 *
 * <p>Note that this method should only be used with Intent protocols
 * that are defined to return a result.  In other protocols (such as
 * {@link Intent#ACTION_MAIN} or {@link Intent#ACTION_VIEW}), you may
 * not get the result when you expect.  For example, if the activity you
 * are launching uses the singleTask launch mode, it will not run in your
 * task and thus you will immediately receive a cancel result.
 *
 * <p>As a special case, if you call startActivityForResult() with a requestCode
 * >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your
 * activity, then your window will not be displayed until a result is
 * returned back from the started activity.  This is to avoid visible
 * flickering when redirecting to another activity.
 *
 * <p>This method throws {@link android.content.ActivityNotFoundException}
 * if there was no Activity found to run the given Intent.
 *
 * @param intent The intent to start.
 * @param requestCode If >= 0, this code will be returned in
 *                    onActivityResult() when the activity exits.
 * @param options Additional options for how the Activity should be started.
 * See {@link android.content.Context#startActivity(Intent, Bundle)
 * Context.startActivity(Intent, Bundle)} for more details.
 *
 * @throws android.content.ActivityNotFoundException
 *
 * @see #startActivity
 */
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
    if (mParent == null) {
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
        if (requestCode >= 0) {
            // If this start is requesting a result, we can avoid making
            // the activity visible until the result is received.  Setting
            // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
            // activity hidden during this time, to avoid flickering.
            // This can only be done when a result is requested because
            // that guarantees we will get information back when the
            // activity is finished, no matter what happens to it.
            mStartedActivity = true;
        }

        cancelInputsAndStartExitTransition(options);
        // TODO Consider clearing/flushing other event sources and events for child windows.
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            // Note we want to go through this method for compatibility with
            // existing applications that may have overridden it.
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

startActivityForResult 的程式碼不多,注意方法前面註釋第二段的 Note that this method should only be used with Intent protocols hat are defined to return a result. 以及第三段的 This is to avoid visible flickering when redirecting to another activity.

上面的程式碼中,mMainThread 在 方法 attach 中被賦值,

final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
    attachBaseContext(context);
    ...
    mUiThread = Thread.currentThread();
    mMainThread = aThread;
}

接著看下 mParent == null 這個條件裡面的語句,主要看 mInstrumentation.execStartActivity ,mInstrumentation 是一個 Instrumentation 物件,接著進入Instrumentation 這個類看下:

/**
 * Execute a startActivity call made by the application.  The default 
 * implementation takes care of updating any active {@link ActivityMonitor}
 * objects and dispatches this call to the system activity manager; you can
 * override this to watch for the application to start an activity, and 
 * modify what happens when it does. 
 *
 * <p>This method returns an {@link ActivityResult} object, which you can 
 * use when intercepting application calls to avoid performing the start 
 * activity action but still return the result the application is 
 * expecting.  To do this, override this method to catch the call to start 
 * activity so that it returns a new ActivityResult containing the results 
 * you would like the application to see, and don't call up to the super 
 * class.  Note that an application is only expecting a result if 
 * <var>requestCode</var> is &gt;= 0.
 *
 * <p>This method throws {@link android.content.ActivityNotFoundException}
 * if there was no Activity found to run the given Intent.
 *
 * @param who The Context from which the activity is being started.
 * @param contextThread The main thread of the Context from which the activity
 *                      is being started.
 * @param token Internal token identifying to the system who is starting 
 *              the activity; may be null.
 * @param target Which activity is performing the start (and thus receiving 
 *               any result); may be null if this call is not being made
 *               from an activity.
 * @param intent The actual Intent to start.
 * @param requestCode Identifier for this request's result; less than zero 
 *                    if the caller is not expecting a result.
 * @param options Addition options.
 *
 * @return To force the return of a particular result, return an 
 *         ActivityResult object containing the desired data; otherwise
 *         return null.  The default implementation always returns null.
 *
 * @throws android.content.ActivityNotFoundException
 *
 * @see Activity#startActivity(Intent)
 * @see Activity#startActivityForResult(Intent, int)
 * @see Activity#startActivityFromChild
 *
 * {@hide}
 */
public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread) contextThread;
    Uri referrer = target != null ? target.onProvideReferrer() : null;
    if (referrer != null) {
        intent.putExtra(Intent.EXTRA_REFERRER, referrer);
    }
    if (mActivityMonitors != null) {
        synchronized (mSync) {
            final int N = mActivityMonitors.size();
            for (int i=0; i<N; i++) {
                final ActivityMonitor am = mActivityMonitors.get(i);
                if (am.match(who, null, intent)) {
                    am.mHits++;
                    if (am.isBlocking()) {
                        return requestCode >= 0 ? am.getResult() : null;
                    }
                    break;
                }
            }
        }
    }
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess();
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

順著程式碼一行行走,可以看到,最後走到的是 ActivityManagerNative.getDefault().startActivity(),並且返回值還會經過一次 checkStartActivityResult,ActivityManagerNative.getDefault() 如下:

static public IActivityManager getDefault() {
    return gDefault.get();
}
// gDefault
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            IActivityManager am = asInterface(b);
            return am;
        }
    };
//get 
public abstract class Singleton<T> {
    private T mInstance;
    protected abstract T create();
    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

這是一個單例模式的運用,返回的是一個 IActivityManager 物件,並且可以看到 gDefault 的 Singleton 泛型引數傳入的是 IActivityManager,看下 ActivityManagerNative 的宣告定義:

public abstract class ActivityManagerNative extends Binder implements IActivityManager
{}

ActivityManagerNative 繼承了 Binder 並且實現了 IActivityManager,這是一個抽象類,android studio 快捷鍵ctrl + H 檢視下 ActivityManagerNative 的具體實現類:

這裡寫圖片描述

可以看到 ActivityManagerNative 的唯一實現類是 ActivityManagerService,ActivityManagerService也是一個Binder,接下來又到了 ActivityManagerService 的 startActivity:

@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options) {
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
        resultWho, requestCode, startFlags, profilerInfo, options,
        UserHandle.getCallingUserId());
}

裡面又呼叫到了 startActivityAsUser

@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
        Intent intent, String resolv