Activity从创建到显示的整个过程

简介: 写在前面的话今天有点烦,有点烦。项目写的乱成团,改起需求真要完。此后当个加班狗,无钱无名心要宽。昨晚写到十一点,我都差点不相信这是我自己了。今天接着昨天的节奏来,准备写下关于Activity从创建到显示的整个过程。

写在前面的话

今天有点烦,有点烦。项目写的乱成团,改起需求真要完。此后当个加班狗,无钱无名心要宽。
昨晚写到十一点,我都差点不相信这是我自己了。


img_d023a979c36ee1c50ae1f83b28a6e828.png

今天接着昨天的节奏来,准备写下关于Activity从创建到显示的整个过程。


img_632b49059d24a0a5f0b75c5688829a3b.png

1. Activity的attach方法

之前分析过Activity的生命周期具体调用时机,我们知道Activity是通过反射创建出来的,之后会执行attach方法:

ActivityThread.java:
activity.attach(appContext, this, getInstrumentation(), r.token,
    r.ident, app, r.intent, r.activityInfo, title, r.parent,
    r.embeddedID, r.lastNonConfigurationInstances, config,
    r.referrer, r.voiceInteractor, window);
Activity.java:
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,
        Window window) {
    //把context赋值给mBase,可以通过getBaseContext获取到context
    attachBaseContext(context);
    
    mFragments.attachHost(null /*parent*/);
    //创建了一个窗口
    mWindow = new PhoneWindow(this, window);
    //这个好像是画中画的callback
    mWindow.setWindowControllerCallback(this);
    //设置callback,里面有各种事件包括键盘、触摸等事件的回调
    mWindow.setCallback(this);
    //设置窗口消失的回调
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    //与输入法有关的设置
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    //ui线程
    mUiThread = Thread.currentThread();
    //主线程
    mMainThread = aThread;
    //之前说到的小秘书
    mInstrumentation = instr;
    //binder
    mToken = token;
    mIdent = ident;
    mApplication = application;
    mIntent = intent;
    mReferrer = referrer;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    ......
    //设置(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    //WindowManager
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;
}

attach方法大部分都是在做成员变量的赋值操作,比如上下文,主线程和UI线程。里面比较重要的一点是对Window的创建,我们都知道Activity的layout是显示在窗口上面的,这个PhoneWindow就是我们的窗口。可以看下其构造方法:

PhoneWindow.java:
public PhoneWindow(Context context) {
    super(context);
        //初始化mLayoutInflater  layout加载器
    mLayoutInflater = LayoutInflater.from(context);
}

public PhoneWindow(Context context, Window preservedWindow) {
    this(context);
    
    mUseDecorContext = true;
    //传过来的preservedWindow为null
    if (preservedWindow != null) {
        ......
    }
    boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),
            DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
    //是否支持画中画
    mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(
            PackageManager.FEATURE_PICTURE_IN_PICTURE);
}

Window.java:
public Window(Context context) {
    //把上下文赋值,并设置默认的特征
    mContext = context;
    mFeatures = mLocalFeatures = getDefaultFeatures(context);
}

attach到这边基本上就结束了。


2. Activity的onCreate

按照上篇讲的,在attach完成后,会执行Activity的onCreate方法。

ActivityThread.java:
activity.attach(appContext, this, getInstrumentation(), r.token,
        r.ident, app, r.intent, r.activityInfo, title, r.parent,
        r.embeddedID, r.lastNonConfigurationInstances, config,
        r.referrer, r.voiceInteractor, window);

if (customIntent != null) {
    activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
    activity.setTheme(theme);
}

activity.mCalled = false;
if (r.isPersistable()) {
    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
    mInstrumentation.callActivityOnCreate(activity, r.state);
}
Activity.java:
protected void onCreate(@Nullable Bundle savedInstanceState) {
    if (mLastNonConfigurationInstances != null) {
        mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
    }
    if (mActivityInfo.parentActivityName != null) {
        if (mActionBar == null) {
            mEnableDefaultActionBarUp = true;
        } else {
            mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
        }
    }
    if (savedInstanceState != null) {
        Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
        mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.fragments : null);
    }
    //FragmentController分发Create事件
    mFragments.dispatchCreate();
        //application分发ActivityCreated事件
    getApplication().dispatchActivityCreated(this, savedInstanceState);
    if (mVoiceInteractor != null) {
        mVoiceInteractor.attachActivity(this);
    }
    mCalled = true;
}

从上面的onCreate中可以看到,这个过程并没有做太多的操作,只有当前create的事件分发。


3. Activity的onStart和onResume

在执行完Activity的onCreate方法就会执行Activity的onStart方法,onStart方法更简单。。。

protected void onStart() {
    mCalled = true;
    mFragments.doLoaderStart();
    getApplication().dispatchActivityStarted(this);
}

看完onStart,就可以猜出onResume做了什么。没错,就是:

protected void onResume() {
    getApplication().dispatchActivityResumed(this);
    mActivityTransitionState.onResume(this, isTopOfTask());
    mCalled = true;
}

看到这里,其实会有一点点懵逼的。前人总是说,onStart代表着Activity可见了,onResume代表着可以交互了。但是到这里了,我才发现如何把View添加到窗口上的?什么时候添加的?绘制的时间呢?古人诚但欺我。


img_7a6503c6de4a64fd8ee8d43581680612.png

到了这里,希望得到的答案并没有出现,所以继续分析。


4. 真正的添加过程

真正添加的过程其实在执行完performResumeActivity这个方法后,系统根据该Activity是否要显示来设置页面是否需要添加到窗口上,其中最主要的是wm.addView(decor, l)这句代码,通过WindowManager将view和LayoutParams添加到窗口上,并且显示出来(这部分后面会有分析)。

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ......
    r = performResumeActivity(token, clearHide, reason);

    if (r != null) {
        final Activity a = r.activity;
        final int forwardBit = isForward ?
                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
        //将要显示
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            try {
                willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                        a.getActivityToken());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        //这个条件a.mFinished false  r.window == null willBeVisible = true
        if (r.window == null && !a.mFinished && willBeVisible) {
            //这个r.activity.getWindow()就是我们attach中创建的PhoneWindow
            r.window = r.activity.getWindow();
            //获得DecorView,这里暂时理解为一个View吧,后面还会有分析的
            View decor = r.window.getDecorView();
            //DecorView设为INVISIBLE
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            //false(在创建这个对象的时候没有赋值)
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            //mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(com.android.internal.R.styleable.Window_windowNoDisplay, false);
            //如果Activity没有设置NoDisplay,则这个mVisibleFromClient变量是true
            //mWindowAdded这个值是默认的,只有被add后才会变为true
            if (a.mVisibleFromClient && !a.mWindowAdded) {
                a.mWindowAdded = true;
                wm.addView(decor, l);
            }

        } else if (!willBeVisible) {
            r.hideForNow = true;
        }
        
        cleanUpPendingRemoveWindows(r, false /* force */);

        if (!r.activity.mFinished && willBeVisible
                && r.activity.mDecor != null && !r.hideForNow) {
            if (r.newConfig != null) {
                performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
                r.newConfig = null;
            }
            
            WindowManager.LayoutParams l = r.window.getAttributes();
            ......
            r.activity.mVisibleFromServer = true;
            mNumVisibleActivities++;
            //这里又执行了makeVisible,因为我们在上面已经add了,mWindowAdded这个变量为true,所以不会进行多次add。
            //最后decorView设置为显示
            //void makeVisible() {
            //  if (!mWindowAdded) {
            //      ViewManager wm = getWindowManager();
            //      wm.addView(mDecor, getWindow().getAttributes());
            //      mWindowAdded = true;
            //  }
            //  mDecor.setVisibility(View.VISIBLE);
            //}
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }

        if (!r.onlyLocalRequest) {
            r.nextIdle = mNewActivities;
            mNewActivities = r;
            //这两天才了解到这个addIdleHandler是指空闲时处理的消息,MessageQueue有专门的接口MessageQueue.IdleHandler
            //这里之前说道会执行前个页面的onStop
            Looper.myQueue().addIdleHandler(new Idler());
        }
        r.onlyLocalRequest = false;

        // Tell the activity manager we have resumed.
        if (reallyResume) {
            try {
                ActivityManagerNative.getDefault().activityResumed(token);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }

    } else {
        //出现问题就finish
        try {
            ActivityManagerNative.getDefault()
                .finishActivity(token, Activity.RESULT_CANCELED, null,
                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

5. 大致要总结下

从上面的分析过程中,我们可以知道Activity在创建的过程中,onCreate、onStart以及onResume其实都没有关于对页面显示的操作,真正显示页面是在onResmue之后,WindowManager将decorView添加后进行绘制(后面应该会将)。
今天也恰巧看到了一篇讲解关于MessageQueue.IdleHandler的文章,这个IdleHandler会在线程空闲的时候,指定一个操作。使用这个IdleHandler对于某些延时操作,但又不清楚页面是否真正绘制完成有奇效。


6. 写在后面的话

这些天讲了从Android启动到HomeActivity的启动,又从HomeActivity启动过程开始分析Activity的生命周期,接着这篇讲解了Activity从创建到显示的过程,但是并没有完全完成。这里只讲到了执行这些方法会将页面显示,但是具体如何显示没有说明。接下来应该将的就是这个页面如何绘制并展示出来。就这样。


img_5473fab044a555227fe3bc76f4958c0b.png
目录
相关文章
|
缓存 数据处理 Android开发
Android经典实战之Kotlin常用的 Flow 操作符
本文介绍 Kotlin 中 `Flow` 的多种实用操作符,包括转换、过滤、聚合等,通过简洁易懂的例子展示了每个操作符的功能,如 `map`、`filter` 和 `fold` 等,帮助开发者更好地理解和运用 `Flow` 来处理异步数据流。
402 4
|
6月前
|
人工智能 云栖大会 云计算
因AI相聚,新年启航
因AI相聚,新年启航
|
11月前
|
Java
Java关键字 —— static 与 final 详细解释!一看就懂 有代码实例运行!
这篇文章详细解释了Java中static和final关键字的用法,包括它们修饰类、方法、变量和代码块时的行为,并通过代码示例展示了它们的具体应用。
731 0
Java关键字 —— static 与 final 详细解释!一看就懂 有代码实例运行!
|
Android开发 开发者 Kotlin
Android 多进程情况下判断应用是否处于前台或者后台
本文介绍在多进程环境下判断Android应用前后台状态的方法。通过`ActivityManager`和服务信息`RunningAppProcessInfo`可有效检测应用状态,优化资源使用。提供Kotlin代码示例,帮助开发者轻松集成。
776 8
|
设计模式 Android开发
44. 【Android教程】广播接收器:Broadcast Receiver
44. 【Android教程】广播接收器:Broadcast Receiver
491 2
|
XML 存储 Android开发
Android实战经验之Kotlin中快速实现MVI架构
本文介绍MVI(Model-View-Intent)架构模式,强调单向数据流与不可变状态管理,提升Android应用的可维护性和可测试性。MVI分为Model(存储数据)、View(展示UI)、Intent(用户动作)、State(UI状态)与ViewModel(处理逻辑)。通过Kotlin示例展示了MVI的实现过程,包括定义Model、State、Intent及创建ViewModel,并在View中观察状态更新UI。
480 12
|
存储 数据处理 Kotlin
Kotlin Flow背后的神秘力量:背压、缓冲与合并策略的终极揭秘!
【9月更文挑战第13天】Kotlin Flow 是 Kotlin 协程库中处理异步数据流的强大工具,本文通过对比传统方法,深入探讨 Flow 的背压、缓冲及合并策略。背压通过 `buffer` 函数控制生产者和消费者的速率,避免过载;缓冲则允许数据暂存,使消费者按需消费;合并策略如 `merge`、`combine` 和 `zip` 则帮助处理多数据源的整合。通过这些功能,Flow 能更高效地应对复杂数据处理场景。
432 2
|
存储 虚拟化 索引
虚拟机数据恢复-虚拟机还原快照原理和误还原快照的数据恢复方案
由一台物理服务器迁移到ESXI上的虚拟机,虚拟机迁移完成后做了一个快照,该ESXI上面一共运行了数十台虚拟机。某天工作人员不小心将快照进行了还原,虚拟机内的数据还原到了数年前刚迁移过来时的状态,迁移过来后的这几年更新的数据全部被删除。
虚拟机数据恢复-虚拟机还原快照原理和误还原快照的数据恢复方案
|
算法 UED
探索编程思维:不仅是代码,更是解决问题的艺术
【5月更文挑战第24天】 在数字世界的舞台上,编程不单是一系列指令的排列组合,它更是一种独特的思维方式。本文将深入探讨编程思维的本质及其在问题解决过程中的应用。我们将剖析编程思维如何影响逻辑构建、创新思考和系统分析,并通过实例说明如何将编程原则应用于日常生活和非技术领域。
|
XML API Android开发
Android 自定义View 之 Dialog弹窗
Android 自定义View 之 Dialog弹窗
427 1