源碼探索系列3---四大金剛之Activity的啓動過程完全解析

在不同版本API,底層實現有些不一樣,所以這裏貼出現在在看的API版本號
API: 23


關於Activity的四個啓動flag,這裏下次再說。
先說下我們熟悉的一句吧

startActivity(new Intent(this,Test2Activity.class));

相信我們對這句太熟悉不過了,不過說到這裏,我想說下關於寫Intent的一個感覺好的實踐
相信你已寫膩了一堆這樣的intent,用起來確實很不好的,就像下面這樣

Intent newIntent= new Intent(this,Test2Activity.class);
        newIntent.putExtra("data1", "trialData1");
        Bundle bundle=new Bundle();
        bundle.putSerializable("key","trialData"); 
        startActivity(newIntent);

像這種方式我在很多開源項目看到了很多這樣的寫法,這種不好的方式在於,很多我們要跳到同一個Activity裏面去時候,重複寫了,這很顯然不適合DRY原則,而且我們在另外一個Activity獲取數據時候,也需要記住這些Key,這不太好!看到的另外一種方式就是直接寫一個IntentUtil類,裏面寫滿各種Intent,感覺這種也不是很好!雖然沒重複了,不過在獲取數據方面還不是很好,還需要定義一些Constant數據,這裏提供我個人覺得好的方式,在我們的Activity裏面寫一個靜態的makeIntent()函數。
如下:

public class Test2Activity extends AppCompatActivity {


    public static final String EXTRA_DATA = "extra_data";

    public static Intent makeIntent(Context mContext, String data) {
        return new Intent(mContext, Test2Activity.class).putExtra(EXTRA_DATA, data);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test2);
         //獲取傳過來的數據
        String result = getIntent().getStringExtra(EXTRA_DATA);
    }
}

之後我們需要調整到這個界面,只需要這麼寫

startActivity(Test2Activity.makeIntent(this,"data"));

通過這個靜態方法,我們在數據裏面讀取方面都很簡單,而且啓動的時候也知道是要跳到那個界面去,重要是看起來很簡單!而且不需要去定義一個常量類來記錄這些數據的Key

最後的一個大殺器就是直接寫成模板。哈哈,之後就可以快捷的自己生成了!!
這裏寫圖片描述

以後我們就這樣,就生成了,不再需要重複寫了!

這裏寫圖片描述

是不是很好!!!



StartActivity

上面分享個人覺得好的最佳實戰,下面就從這個開始剖析底層是如何啓動這個Activity的。

@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
   if (options != null) {
       startActivityForResult(intent, -1, options);
   } else {
        startActivityForResult(intent, -1);
    }
}

public void startActivityForResult(Intent intent, int requestCode) {
        startActivityForResult(intent, requestCode, null);
 }

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()函數,而且RequestCode是-1。

if (requestCode >= 0) {
    mStartedActivity = true;
}

結合上面這個判斷條件,這樣我們就理解,爲何在startActivity的RequestCode 有這麼句解釋

requestCode - If >= 0, this code will be returned in
onActivityResult() when the activity exits.

我們需要注意到這麼一句,這句實際的執行了intent程序

 Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);

不知道對MainThread這個單詞還記得嘛,在前篇文章說Looper的時候裏面有介紹到這個單詞,創說中的主線程,他get回ApplicationThread,我們的app線程。另外這個Instrumentation居然出現在這裏,我們在介紹測試教程的時候,很多類都是基礎於它的,就讓我們繼續看這個execStartActivity到底做了什麼

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

(題外話:當年看《Android系統源代碼情景分析》的時候吐槽裏面都是貼代碼的,現在自己分析時候也貼了好多,哈哈)
我們看到裏面結尾處的最重要的一句,好長的一句ActivityManagerNative實際執行了這個startActivity的工作。

int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);

再近一步的看,我們看到這個ActivityManagerNative 居然是個抽象類,而且基礎於Binder,實現了IActivityManager這個接口。另外要提一點就是這個@hide,他的作用很神奇的,可以使類在編譯時不對外開放,但是運行的時候這些類和API都是可以訪問的。所以要直接使用是會報錯的,需要修改下設置的,具體有興趣的,關於他的經一步內容,找下資料吧

/** {@hide} */
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{

    /**
     * Retrieve the system's default/global activity manager.
     */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }

     private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
            protected IActivityManager create() {
                IBinder b = ServiceManager.getService("activity");
                if (false) {
                    Log.v("ActivityManager", "default service binder = " + b);
                }
                IActivityManager am = asInterface(b);
                if (false) {
                    Log.v("ActivityManager", "default service = " + am);
                }
                return am;
            }
        };
}

好吧,一個神奇的Binder,讓我們繼續看下去
很遺憾的是,現在都凌晨一點多了,我居然還沒找到傳說中的ActivityManagerService這個類在哪裏!!
好消息是,這玩意經過一番折騰,終於發現一個事實,這各類在Android.jar裏面沒有,得去源碼裏面找。呵呵。初學,這類坑難免。
位置是你的SDK位置\sources\android-23\com\android\server\am
打開一看,簡直瘋了,這個類居然

20640
20640
20640
20640

簡直不敢相信,怪不得一個文件有871KB大小,服了,這代碼量,簡直夠一個簡單的app的整個代碼量了!
先上一張偷來的大圖,整個啓動的流程。

這裏寫圖片描述

還沒看完一半的代碼。呵呵
一折騰都一點半了,明天繼續寫吧。。。


讓我們繼續看下了這神奇的ActivityManagerService

@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());
}

@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
    enforceNotIsolatedCaller("startActivity");
    userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
            false, ALLOW_FULL_ONLY, "startActivity", null);
    // TODO: Switch to user app stacks here.
    return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
            profilerInfo, null, null, options, false, userId, null, null);
}

這些參數也太多了把,系統的源碼這樣如此的複雜。

  final int startActivityMayWait(IApplicationThread caller, int callingUid,
        String callingPackage, Intent intent, String resolvedType,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        IBinder resultTo, String resultWho, int requestCode, int startFlags,
        ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
        Bundle options, boolean ignoreTargetSecurity, int userId,
        IActivityContainer iContainer, TaskRecord inTask) {

     ...

        int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                voiceSession, voiceInteractor, resultTo, resultWho,
                requestCode, callingPid, callingUid, callingPackage,
                realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
                componentSpecified, null, container, inTask);


      ...
}

這個方法也好長,這裏貼下最重要的一句startActivityLocked()

 final int startActivityLocked(IApplicationThread caller,
        Intent intent, String resolvedType, ActivityInfo aInfo,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        IBinder resultTo, String resultWho, int requestCode,
        int callingPid, int callingUid, String callingPackage,
        int realCallingPid, int realCallingUid, int startFlags, Bundle options,
        boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
        ActivityContainer container, TaskRecord inTask) {
    int err = ActivityManager.START_SUCCESS;

    ...

    err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
            startFlags, true, options, inTask);

    if (err < 0) {
        // If someone asked to have the keyguard dismissed on the next
        // activity start, but we are not actually doing an activity
        // switch...  just dismiss the keyguard now, because we
        // probably want to see whatever is behind it.
        notifyActivityDrawnForKeyguard();
    }
    return err;
}

越到後面的類都好變態,參數看起來可怕,整個類的行數都用千來做單位,看起來真的好辛苦,那些設計這個系統的一開始得花了多少功夫啊!根據查看,他底部是這個方法

    err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
            startFlags, true, options, inTask);

好想跳過。。。這個方法裏面內容也是超級龐大的。看到好難過。。
裏面調整到resumeTopActivitiesLocked()接着到了

 boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
        Bundle targetOptions) {
    if (targetStack == null) {
        targetStack = mFocusedStack;
    }
    // Do targetStack first.
    boolean result = false;
    if (isFrontStack(targetStack)) {
        result = targetStack.resumeTopActivityLocked(target, targetOptions);
    }

    ...
    return result;
}

下面這個是ActivityStack的resumeTopActivityLocked

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
    if (mStackSupervisor.inResumeTopActivity) {
        // Don't even start recursing.
        return false;
    }

    boolean result = false;
    try {
        // Protect against recursion.
        mStackSupervisor.inResumeTopActivity = true;
        if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
            mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
            mService.updateSleepIfNeededLocked();
        }
        result = resumeTopActivityInnerLocked(prev, options);
    } finally {
        mStackSupervisor.inResumeTopActivity = false;
    }
    return result;
}

繼續堅持看下去。。

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
    if (DEBUG_LOCKSCREEN) mService.logLockScreen("");

      ....

        mStackSupervisor.startSpecificActivityLocked(next, true, true);
     ...

}

蒼天,終於這個方法短一點了,都敢直接貼上來了,前面都是幾百行的,而且重點是很多操作是沒深入看,都暈暈的!!

  void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        r.task.stack.setLaunchTime(r);

        if (app != null && app.thread != null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    // Don't add this if it is a platform component that is marked
                    // to run in multiple processes, because this is actually
                    // part of the framework so doesn't make sense to track as a
                    // separate apk in the process.
                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }

        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

好日子到頭了,又繼續看下吧

final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig)
            throws RemoteException {

           ...

            app.forceProcessStateUpTo(mService.mTopProcessState);
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
        ...

        return true;
    }

看這些,突然想說,知道個整個流程真不容易,好複雜的流程,一開始搭架構的人能力也太犀利了!設計這麼多!
看到這裏我們的線索中斷了下,因爲app.thread的這個thread是一個接口,繼承IInterface,成了個Binder,我們需要找到真正幹活的人

public interface IApplicationThread extends IInterface { 

}

我們搜索了這個ProcessRecord類,發現在他的makeActivie裏面對他進行了賦值

 public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {

     ....

    thread = _thread;
}

是時候了,使用傳說中的Alt+Ctrl+Shift+F7組合鍵,我們發現他的來源是在AMS裏面,我的跪了,感覺新新苦苦回到革命前的感覺啊。

這裏寫圖片描述
接着我們繼續追尋,發現下面這個入口

@Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }

既然是Override的方法,我們回來看的聲明

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

}

好吧,一個熟悉的東西,我們在ActivityManagerNative發現了最重要的信息,居然跳回了這裏!

case ATTACH_APPLICATION_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IApplicationThread app = ApplicationThreadNative.asInterface(
                    data.readStrongBinder());
            if (app != null) {
                attachApplication(app);
            }
            reply.writeNoException();
            return true;
        }

嫌疑犯ApplicationThreadNative出現了,這名字感覺很有規律啊,XXXNative和我們的ActivityManagerNative是類似的,萬惡的Binder又出現了。他的具體實現是下面這個ApplicationThread,居然是ActivityThread 裏面的內部類,不要問我怎麼知道的

private class ApplicationThread extends ApplicationThreadNative


 ....

public abstract class ApplicationThreadNative extends Binder
    implements IApplicationThread {

}

既然回到了ApplicationThread,我們就繼續吧,我快看膩了,不知道看這個幹什麼。。
但最少我們找到了真正幹活的人!!!希望你看這麼久,不會忘了我們是要找下面這句

 app.thread.scheduleLaunchActivity()

好了,我們繼續看他的具體內容把

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
            ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
            Bundle state, List<ResultInfo> pendingResults,
            List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
            String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
        ActivityClientRecord r = new ActivityClientRecord();

        r.token = token;
        r.ident = ident;
        r.intent = intent;
        r.activityInfo = info;
        r.compatInfo = compatInfo;
        r.state = state;

        r.pendingResults = pendingResults;
        r.pendingIntents = pendingNewIntents;

        r.startsNotResumed = notResumed;
        r.isForward = isForward;

        r.profileFile = profileName;
        r.profileFd = profileFd;
        r.autoStopProfiler = autoStopProfiler;

        updatePendingConfiguration(curConfig);

        queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
    }

發送消息,又事坑

private void queueOrSendMessage(int what, Object obj) {
       queueOrSendMessage(what, obj, 0, 0);
}

private void queueOrSendMessage(int what, Object obj, int arg1) {
    queueOrSendMessage(what, obj, arg1, 0);
}

private void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
    synchronized (this) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        mH.sendMessage(msg);
    } 
}

final H mH = new H();

我們看到,他最後居然用一個Handler發送消息,呵呵,而且這個handler居然那麼簡單的名字

private class H extends Handler {

    public static final int LAUNCH_ACTIVITY         = 100;
    public static final int PAUSE_ACTIVITY          = 101; 

    ...

    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                ActivityClientRecord r = (ActivityClientRecord)msg.obj;

                r.packageInfo = getPackageInfoNoCheck(
                        r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null);
            } break;
            case RELAUNCH_ACTIVITY: {
                ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                handleRelaunchActivity(r);
            } break;

            ...
        }
        if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
    }
}

繼續看下去吧,handleLaunchActivity(),寫到這裏,很想說,沒什麼動力,估計沒什麼人沒事會把整個系統的源碼看一遍。實在好累的感覺

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {


    if (r.profileFd != null) {
        mProfiler.setProfiler(r.profileFile, r.profileFd);
        mProfiler.startProfiling();
        mProfiler.autoStopProfiler = r.autoStopProfiler;
    }

    // Make sure we are running with the most recent config.
    handleConfigurationChanged(null, null);

    if (localLOGV) Slog.v(
        TAG, "Handling launch of " + r);
    Activity a = performLaunchActivity(r, customIntent);

    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        Bundle oldState = r.state;
        handleResumeActivity(r.token, false, r.isForward);


            r.paused = true;
        }
    }  

    ...
}

恭喜你,看到這裏基本到尾聲了,我們的Activity被創建出來了,這個函數一百多行,實在太長了,就不貼了
,一個函數長度都夠我平時寫的一個類的長度了。
在performLaunchActivity()函數裏面,對我們的Activity粘貼到window,設置主題,設置Context ,調用OnCreate函數等等操作。

然後就是在handleResumeActivity裏面,會調用我們最熟悉不過的onResume()函數!
好了,到這裏結束!!!!!

關於別的Activity的生命週期函數是怎麼調用到的,下次有空再繼續寫!
就這點內容,看了我幾個小時,得洗澡先了!!


後記

第一次自己看完這麼長的一個流程,真的是不容易,寫這篇花了我兩個晚上
雖然覺得挺好,不過也不知道有什麼意義呢,看完整個啓動的生命週期!
寫系統代碼的人也是牛逼,繞來繞去的,這麼長流程,想做到沒bug的話真的不容易!
我看着就累了,特別是看到那個AMS裏面居然兩萬多行!!!
這一個類,真的完全都夠寫一箇中小型的項目的代碼量了
發現自己貼了好多代碼,整篇文章都16,000多了,好長!!
估計一般人都不會看,就當記錄下整個流程,
下次遇到問題,方便自己查閱

——————-再次更新——————–

補充下一個笑話

深夜,交警查車,突然插到一部車的後尾箱放了幾百萬美元,在這樣一個夜黑風高之晚,顯然很有嫌疑,所以警察質問司機:“爲何這麼晚還帶這麼多錢跑?”,實際雖然有點緊張,但就回答了句,“嗯,我能”。
這個是以前在知乎看到的。具體大意入手,最終想說的就是那麼兩個字,因爲我能。>.<


參考:
整個流程圖,來自下面這篇文章
Activity啓動創建 (AcitivtyManageService,ActivityThread,Activity)

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