Android显示框架:Activity应用视图的创建流程

Android显示框架:Activity应用视图的创建流程

关于作者

郭孝星,非著名程序员,主要从事Android平台基础架构与中间件方面的工作,欢迎交流技术方面的问题,可以去我的Github提交Issue或者发邮件至[email protected]与我联系。

第一次阅览本系列文章,请参见导读,更多文章请参见文章目录

文章目录

  • 一 创建Context对象
  • 二 创建Window对象
  • 三 创建View对象
  • 四 创建WindowState对象
  • 五 创建Surface对象

Android应用在运行的过程中需要访问一些特定的资源和类,这些特定的资源或者类构成了Android应用运行的上下文环境,即Context。Context是一个抽象类,ContextImpl继承了Context,
并实现它的抽象方法。

因此,每个Activity组件关联的是ContextImpl对象,它们的类图关系如下:

Context家族相关类采用装饰模式设计而成,ContextWrapper与ContextThemeWrapper继承于Context,是它的包装类,用于完成更多的功能。ContextWrapper与ContextThemeWrapper背部都通过
成员变量mBasae引用了一个ContextImpl对象,Activity正是通过这个ContextImpl对象执行一些具体的操作,例如:启动Activity、启动Service等。

Context mBase;

public ContextWrapper(Context base) {
    mBase = base;
}

比较有意思的是,ContextImpl内部也有一个mOuterContext对象,它在自己初始化的时候传入,它引用的正是与它关联的Activity,这样它也可以把一些操作转交给Activity。

private Context mOuterContext;

ContextImpl() {
    mOuterContext = this;
}

一 创建Context对象

我们之前分析过Activity的启动流程,可以得知这个流程的最后一步是调用ActivityThread.perforLaunchActivity()方法在应用进程中创建一个Activity实例,并为它蛇者一个
上下文环境,即创建一个ContexImpl对象。

ContexImpl的创建流程如下所示:

主要角色:

  • Instrumenttation:记录应用与系统的交互过程
  • Contextrapper: ContextImpl的代理类,包装了ContextImpl里的相关操作。
  • ContextThemeWrapper:用来维护一个应用的窗口主题

整个流程还是比较简单清晰的,我们着重分析里面的关键点。

关键点1:ActivityThread.performLaunchActivity(ActivityClientRecord r, Intent customIntent)

这个方法完成了Activity启动以及ContextImpl创建的主要流程,它完成的工作有:

  • 1 从Intent中获取Activity的组件名ComponentName,调用对应的类加载器进行加载,调用Activity的默认构造方法进行实例创建。
  • 2 调用ContextImpl的构造方法创建ContextImpl,并调用ContextImpl.setOuterContext()方法将已经创建完成的Activity关了给ContextImpl。
  • 3 调用Activity.attach()关联上下文信息、Activity信息、Intent信息等Activity运行所需要的信息。
  • 4 调用InstrumentationcallActivityOnCreate(),通知Activity你已经被创建,相关环境与信息也已经准备好,可以执行你的onCreate()方法辣,接着Activity就去执行它的onCreate()方法了。
public final class ActivityThread {
   

    private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

            ActivityInfo aInfo = r.activityInfo;
            if (r.packageInfo == null) {
                r.packageInfo = getPackageInfo(aInfo.applicationInfo,
                        Context.CONTEXT_INCLUDE_CODE);
            }

            //1 从Intent中获取Activity的组件名ComponentName,调用对应的类加载器进行加载,调用
            //Activity的默认构造方法进行实例创建。
            ComponentName component = r.intent.getComponent();
            if (component == null) {
                component = r.intent.resolveActivity(
                    mInitialApplication.getPackageManager());
                r.intent.setComponent(component);
            }

            if (r.activityInfo.targetActivity != null) {
                component = new ComponentName(r.activityInfo.packageName,
                        r.activityInfo.targetActivity);
            }

            Activity activity = null;
            try {
                java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
                activity = mInstrumentation.newActivity(
                        cl, component.getClassName(), r.intent);
                r.intent.setExtrasClassLoader(cl);
                if (r.state != null) {
                    r.state.setClassLoader(cl);
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(activity, e)) {
                    throw new RuntimeException(
                        "Unable to instantiate activity " + component
                        + ": " + e.toString(), e);
                }
            }

            try {
                Application app = r.packageInfo.makeApplication(false, mInstrumentation);

                if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
                if (localLOGV) Slog.v(
                        TAG, r + ": app=" + app
                        + ", appName=" + app.getPackageName()
                        + ", pkg=" + r.packageInfo.getPackageName()
                        + ", comp=" + r.intent.getComponent().toShortString()
                        + ", dir=" + r.packageInfo.getAppDir());

                if (activity != null) {
                    //2 调用ContextImpl的构造方法创建ContextImpl,并调用ContextImpl.setOuterContext()方法将已经创建完成的Activity关了给ContextImpl。
                    ContextImpl appContext = new ContextImpl();
                    appContext.init(r.packageInfo, r.token, this);
                    appContext.setOuterContext(activity);
                    CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                    Configuration config = new Configuration(mConfiguration);
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                            + r.activityInfo.name + " with config " + config);
                    //3 调用Activity.attach()关联上下文信息、Activity信息、Intent信息等Activity运行所需要的信息。
                    activity.attach(appContext, this, getInstrumentation(), r.token,
                            r.ident, app, r.intent, r.activityInfo, title, r.parent,
                            r.embeddedID, r.lastNonConfigurationInstance,
                            r.lastNonConfigurationChildInstances, config);

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

                    activity.mCalled = false;
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onCreate()");
                    }
                    r.activity = activity;
                    r.stopped = true;
                    if (!r.activity.mFinished) {
                        activity.performStart();
                        r.stopped = false;
                    }
                    if (!r.activity.mFinished) {
                        if (r.state != null) {
                            //4 调用InstrumentationcallActivityOnCreate(),通知Activity你已经被创建,相关环境与信息也已经准备好,可以执行
                            // 你的onCreate()方法辣,接着Activity就去执行它的onCreate()方法了。
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                        }
                    }
                    if (!r.activity.mFinished) {
                        activity.mCalled = false;
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                        if (!activity.mCalled) {
                            throw new SuperNotCalledException(
                                "Activity " + r.intent.getComponent().toShortString() +
                                " did not call through to super.onPostCreate()");
                        }
                    }
                }
                r.paused = true;

                mActivities.put(r.token, r);

            } catch (SuperNotCalledException e) {
                throw e;

            } catch (Exception e) {
                if (!mInstrumentation.onException(activity, e)) {
                    throw new RuntimeException(
                        "Unable to start activity " + component
                        + ": " + e.toString(), e);
                }
            }

            return activity;
        }
}

关键点2:Activity.attach()

Activity在被类加载器加载时调用的是默认的构造方法,这个方法什么都没有做,只是创建了个实例,真正的初始化流程在attach()方法里完成。

你可以看到attach()方法会调用ContextWrapper.attachBaseContext(context)进一步设置Context信息,这个方法就是将创建的ContextImpl赋值
给它的成员变量mBase。

除此之外,它还做了两件事:

  • 1 调用PolicyManager.makeNewWindow(this)创建了应用窗口Window,它实际是个PhoneWindow对象,它会接收一些事件,例如:键盘、触摸事件,它会
    转发这些事件给它关联的Activity,转发操作通过Window.Callback接口实现。
  • 2 将Activity运行的一些关键信息带入Activity。

后续的UI绘制就砸Window上完成,并被Window设置了WindowManager。

public class Activity extends ContextThemeWrapper{
   

    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,
            Object lastNonConfigurationInstance,
            HashMap<String,Object> lastNonConfigurationChildInstances,
            Configuration config) {
        attachBaseContext(context);

        //1 调用PolicyManager.makeNewWindow(this)创建了应用窗口Window,它实际是个PhoneWindow对象,它会接收一些事
        // 件,例如:键盘、触摸事件,它会转发这些事件给它关联的Activity,转发操作通过Window.Callback接口实现。
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        mUiThread = Thread.currentThread();
        //2 将Activity运行的一些关键信息带入Activity。
        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstance = lastNonConfigurationInstance;
        mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;

        mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }
}

以上便是ContextImpl对象创建过程的一些关键点,还是比较简单的,我们再来总结一下。

1 一个Android应用窗口的运行上下文环境是使用一个ContextImpl对象来描述的,这个ContextImpl对象会分别保存在Activity类的
父类ContextThemeWrapper和ContextWrapper的成员变量mBase中,即ContextThemeWrapper类和ContextWrapper类的成员变量
mBase指向的是一个ContextImpl对象。
2 Activity组件在创建过程中,即在它的成员函数attach被调用的时候,会创建一个PhoneWindow对象,并且保存在成员变量mWindow
中,用来描述一个具体的Android应用程序窗口。
3 Activity组件在创建的最后,即在它的子类所重写的成员函数onCreate中,会调用父类Activity的成员函数setContentView来创建
一个Android应用程序窗口的视图。

二 创建Window对象

从上面的Activity.attach()方法的分析我们得知了ContextImpl的创建流程,我们发现它不仅创建了上下文环境Context,它还创建了Window对象,用来描述一个具体的应用窗口,可以看出
Activity只不过是一个高度抽象的UI组件,它的具体UI实现是由它的一系列对象来完成的,它们的类图关系如下所示:

从上文的描述我们可以知道,Windows是在Activity的attach()方法中开始创建的,我们来看下它的创建流程。

主要角色:

  • PhoneWindow:Window的子类,应用视图窗口。
  • WindowManagerImpl:实现了WIndowManager接口,用来管理窗口。

关键点1:PhoneWindow(Context context)

PolicyManager.makeNewWindow(this)用来创建Window对象,该函数通过反射最终调用Policy.makeNewWindow(Context context),在这个
方法里调用了PhoneWindow的构造函数,返回了一个PhoneWindow对象。

在PhoneWindow的构造函数里,我们很惊奇的发现它返回了一个LayoutInflater对象。这货就是我们用来绘制xml里面UI的东西。

public PhoneWindow(Context context) {
    super(context);
    mLayoutInflater = LayoutInflater.from(context);
}

PhoneWindow其实就是我们最终要用的视图窗口了,除了mLayoutInflater,它里面还有两个重要的成员变量:

  • private DecorView mDecor:顶级View视图,它由mLayoutInflater来创建。
  • private ViewGroup mContentParent:视图容器。

关键点2:Window.setCallback(this)

Activity实现了Window.Callback接口,将Activity关联给Window,Window就可以将一些事件交由Activity处理,具体有哪些事情呢?

 public interface Callback {
   

        //键盘事件分发
        public boolean dispatchKeyEvent(KeyEvent event);

        //触摸事件分发
        public boolean dispatchTouchEvent(MotionEvent event);

        //轨迹球事件分发
        public boolean dispatchTrackballEvent(MotionEvent event);

        //可见性事件分发
        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);

        //创建Panel View
        public View onCreatePanelView(int featureId);

        //创建menu
        public boolean onCreatePanelMenu(int featureId, Menu menu);

        //画板准备好时回调
        public boolean onPreparePanel(int featureId, View view, Menu menu);

        //menu打开时回调
        public boolean onMenuOpened(int featureId, Menu menu);

        //menu item被选择时回调
        public boolean onMenuItemSelected(int featureId, MenuItem item);

        //Window Attributes发生变化时回调
        public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);

        //Content View发生变化时回调
        public void onContentChanged();

        //窗口焦点发生变化时回调
        public void onWindowFocusChanged(boolean hasFocus);

        //Window被添加到WIndowManager时回调
        public void onAttachedToWindow();

        //Window被从WIndowManager中移除时回调
        public void onDetachedFromWindow();

         */
        //画板关闭时回调
        public void onPanelClosed(int featureId, Menu menu);

        //用户开始执行搜索操作时回调
        public boolean onSearchRequested();
    }

关键点3:Window.setSoftInputMode(int mode)

这个我们就比较熟悉了,我们会在AndroidManifest.xml里Activity的标签下设置android:windowSoftInputMode=”adjustNothing”,来控制输入键盘显示行为。

可选的有6个参数,源码里也有6个值与之对应:

  • SOFT_INPUT_STATE_UNSPECIFIED:没有指定软键盘输入区域的显示状态。
  • SOFT_INPUT_STATE_UNCHANGED:不要改变软键盘输入区域的显示状态。
  • SOFT_INPUT_STATE_HIDDEN:在合适的时候隐藏软键盘输入区域,例如,当用户导航到当前窗口时。
  • SOFT_INPUT_STATE_ALWAYS_HIDDEN:当窗口获得焦点时,总是隐藏软键盘输入区域。
  • SOFT_INPUT_STATE_VISIBLE:在合适的时候显示软键盘输入区域,例如,当用户导航到当前窗口时。
  • SOFT_INPUT_STATE_ALWAYS_VISIBLE:当窗口获得焦点时,总是显示软键盘输入区域。

关键点4: Window.setWindowManager(WindowManager wm, IBinder appToken, String appName)

public void setWindowManager(WindowManager wm,
        IBinder appToken, String appName) {
    mAppToken = appToken;
    mAppName = appName;
    if (wm == null) {
        wm = WindowManagerImpl.getDefault();
    }
    mWind
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当搭建Android应用的MVVM架构时,你可以按照以下步骤进行操作: 1. 配置项目依赖:在项目的 `build.gradle` 文件中,添加以下依赖项: ```groovy dependencies { implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' } ``` 2. 创建数据模型(Model):创建用于存储应用数据的类。这些类应该是独立于UI的,通常包含与数据源进行交互的方法。 ```kotlin data class User(val id: String, val name: String) ``` 3. 创建视图模型(ViewModel):创建负责管理UI相关逻辑和数据的ViewModel类。ViewModel从数据源(如Repository)获取数据,并通过LiveData或其他观察者模式通知View层更新。 ```kotlin class UserViewModel : ViewModel() { private val userRepository = UserRepository() private val _user = MutableLiveData<User>() val user: LiveData<User> get() = _user fun getUser(userId: String) { viewModelScope.launch { val user = userRepository.getUser(userId) _user.postValue(user) } } } ``` 4. 创建视图层(View):创建Activity、Fragment或自定义View等作为View层。在View层中,使用ViewModel的实例,并观察LiveData以更新UI。 ```kotlin class MainActivity : AppCompatActivity() { private val viewModel: UserViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel.user.observe(this, { user -> // 更新UI textView.text = user.name }) viewModel.getUser("123") } } ``` 5. 创建数据源(Repository):创建负责从数据源(如网络或数据库)获取数据的Repository类。在Repository中,可以使用Retrofit、Room等库来进行数据访问。 ```kotlin class UserRepository { suspend fun getUser(userId: String): User { // 从数据源获取用户数据的逻辑 // 可以使用Retrofit、Room等库 return User(userId, "John Doe") } } ``` 这是一个简单的MVVM框架搭建代码示例,你可以根据实际需求进行调整和扩展。希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值