Android 创建应用类窗口

参考书籍: <Android 内核剖析>

  • 每个应用窗口都对应一个Activity对象,创建应用类窗口首先需要创建一个Activity对象.
// ActivityThread.java
public final class ActivityThread {
    //创建Activity对象
    private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity activity = null;
        try {
            //获取类加载器
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            // TODO 第一
            activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
            r.intent.setExtrasClassLoader(cl);
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            ...
        }
        ...
        try {
            if (activity != null) {
                //TODO 第三 去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);
            }
        }
        //TODO 第五 callActivityOnCreate()方法最终会调用Activity.onCreate()
        mInstrumentation.callActivityOnCreate(activity, r.state);
        ...
        return activity;
    }
    // TODO 第十三, 当执行完第十二步之后,也就是Activity已经准备好了,最后会通知AMS,然后AMS经过调用,最终会执行到ActivityThread中的handleResumeActivity()
    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
        // ActivityClientRecord对象的来历这里简单的说一下, 是在ApplicationThread中的scheduleLaunchActivity()被调用时候创建的.
        // 之后ActivityThread.performLaunchActivity()被调用存入变量mActivities集合中.
        // performLaunchActivity()被调用时会创建一个Activity,这个Activity对象也会被保存在ActivityClientRecord对象的activity变量中.
        ActivityClientRecord r = performResumeActivity(token, clearHide);
        //ActivityClientRecord 中的变量activity对应的是将要在手机中展示的Activity.
        final Activity a = r.activity;
        if (r.window == null && !a.mFinished && willBeVisible) {
            // 创建该activity时候的PhoneWindow.
            r.window = r.activity.getWindow();
            // 通过PhoneWindow获取decor
            View decor = r.window.getDecorView();
            ...
            // 将PhoneWinodw的decor赋值给Activity的mDecor变量,这个地方很容易忽略.当Activity要显示内容的时候,
            a.mDecor = decor;
            ...
        }
        if (r.activity.mVisibleFromClient) {
            // TODO 第十四 这里将要开始将Activity中的内容展示出来了.
            r.activity.makeVisible();
        }
    }
}
// Activity.java
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks {
    // TODO 第三
    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);
        // TODO 第四 创建PhoneWindow
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        ...
        // 创建 为PhoneWindow对象创建WindowManagerImpl对象变量和 Window.LocalWindowManager对象变量   
        mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
        ...
        mWindowManager = mWindow.getWindowManager();
    }
    // TODO 第六
    // 这个方法再熟悉不过了 经常用到得onCreate方法
    protected void onCreate(Bundle savedInstanceState) {
        mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
                com.android.internal.R.styleable.Window_windowNoDisplay, false);
        mCalled = true;
        //在Activity的子类中会重载onCreate方法,里面模板代码setContentView(R.layout.XXXX),我们现在来看看setContentView()方法
    }
    //TODO 第七
    public void setContentView(int layoutResID){
        // TODO 第八 看看PhoneWindow中的setContentView()方法
        getWindow().setContentView(layoutResID);
    }
    // TODO 第十四 再往下就是WMS那块的知识了,另外起一片笔记再来记录.
    void makeVisible() {
        if (!mWindowAdded) {
            // 获取Window.LocalWindowManager对象
            ViewManager wm = getWindowManager();
            // 最终会调到WindowManagerImpl.addView()
            // 接着会调到ViewRoot.setView()
            // 最后得跨进程了 sWindowSession.add(mWindow, mWindowAttributes,getHostVisibility(), mAttachInfo.mContentInsets,mInputChannel);
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }
}
// PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
    // TODO 第八
    @Override
    public void setContentView(int layoutResID) {
        //这是创建Activity 所以mContentParent肯定为null   
        if (mContentParent == null) {
            // TODO 第九 安装decorView  
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        // TODO 第十二 将传入的layoutResID布局文件添加到mContentParent中.
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }
    }
    // TODO 第九  
    private void installDecor() {
        if (mDecor == null) {
            // TODO 第十 创建decorView
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
        }
        if (mContentParent == null) {
            // TODO 第十一 获取用来盛放 setContentView(R.layout.XXX) 中的布局文件的容器.
            mContentParent = generateLayout(mDecor);
            ...
        }
    }
    // TODO 第十 
    protected DecorView generateDecor() {
        // 创建DecorView,它其实是一个FramLayout容器,下面会将一个布局放入这个容器中.
        // 这个布局是Framwork层定义好的布局, 它有标题栏和内容栏,这两个是Framwork层给划分好的.
        // 还记得当setContentView(R.layout.XXX)进来的一个布局么, 
        // 那个布局最终会被添加到内容栏中, 内容栏的id为android.R.id.content.
        return new DecorView(getContext(), -1);
    }
    // TODO 第十一
    protected ViewGroup generateLayout(DecorView decor) {
                
        TypedArray a = getWindowStyle();
        ...
        int layoutResource;
        int features = getLocalFeatures();
        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                layoutResource = com.android.internal.R.layout.dialog_title_icons;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title_icons;
            }
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) {
            layoutResource = com.android.internal.R.layout.screen_progress;
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            if (mIsFloating) {
                layoutResource = com.android.internal.R.layout.dialog_custom_title;
            } else {
                layoutResource = com.android.internal.R.layout.screen_custom_title;
            }
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            if (mIsFloating) {
                layoutResource = com.android.internal.R.layout.dialog_title;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title;
            }
        } else {
            layoutResource = com.android.internal.R.layout.screen_simple;
        }
        ...
        // 从上面的源码可以看出,layoutResource 这个布局文件是通过一系列的配置筛选出来的
        // TODO 第十二 将系统layoutResource布局文件解析为View
        View in = mLayoutInflater.inflate(layoutResource, null);
        // TODO 第十三 将View加入到decorView中
        // 上面讲过decroView其实是FramLayout, 然后系统layoutResource布局文件中有一个容器的id为android.R.id.content
        // id为content的这个容器最终会用来放setContentView(R.layout.XXX)中的布局文件
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        // TODO 第十四 这里就是取出id为content的容器,将这个容器最终再返回去.
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        ...
        return contentParent;
    }
}
// Instrumentation.java
public class Instrumentation {
    // TODO 第一
    public Activity newActivity(ClassLoader cl, String className,Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        // TODO 第二
        // 创建Activity对象
        return (Activity)cl.loadClass(className).newInstance();
    }
    //TODO 第五
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        if (mWaitingActivities != null) {
        ...
         // TODO 第六
        //这里调用onCreate()方法
        activity.onCreate(icicle);
        ...
    }  
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果你想在 Android 应用程序中创建一个副屏窗口,你可以使用 Android 的 Presentation API 。该 API 允许你将不同的内容显示在不同的屏幕上,并且可以在多个屏幕之间切换。 以下是创建副屏窗口的步骤: 1. 创建一个,继承自 Presentation 。 ```java public class MyPresentation extends Presentation { public MyPresentation(Context context, Display display) { super(context, display); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.my_presentation_layout); } } ``` 2. 在你的 Activity 中,获取 DisplayManager 对象并使用它来检索第二个屏幕。 ```java DisplayManager displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE); Display[] displays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); if (displays.length > 0) { Display display = displays[0]; MyPresentation myPresentation = new MyPresentation(this, display); myPresentation.show(); } ``` 3. 创建一个布局文件来定义你的副屏窗口中的内容。 ```xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/my_presentation_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello from the presentation!" android:textSize="32sp" android:layout_centerInParent="true"/> </RelativeLayout> ``` 这样,当你的应用程序在第二个屏幕上启动时,就会显示你定义的布局文件内容。注意,如果你想在主屏幕和副屏幕之间切换,你需要使用系统的 Presentation API 进行管理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值