从0开始写android

文章目录

  • 一、 从0开始实现 onCreate 的setContentView
  • 二、 从0 开始实现 onMeasure
  •  从0 开始实现 onLayout
  •  从0 开始实现 onDraw
  • 总结


前言

接上一章节,我们实现了 创建一个应用进程的完整流程,接下来从ActivityThread 的attach开始,我们尝试在linux环境下实现 onCreate的 setContentView 函数 和onResume 函数,我们不会用到binder等跨进程通信机制,尽量用简洁的代码复现 setContentView 的将xml文件装成DecorView为根节点的树形结构的过程。


从0开始实现 onCreate 的setContentView

一、ActivityThread 的入口main()

ActivityThread 的入口函数 创建了looper 消息循环。只调用了attach函数。

    public static void main(String[] args) {

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        Looper.loop();
    }

二、反射的方式创建Acitivty 实例

在android源码中 是通过binder调用 发送  BIND_APPLICATION 消息 走到

handleBindApplication  ,我们简化这种方式。直接看是怎么反射Activity类.
    public Activity newActivity(String className) {
        Log.d(TAG, "newActivity className " + className);
        ContextImpl context = new ContextImpl();
        try {
            Class<?> clazz = Class.forName(className);
            Activity activity = (Activity) clazz.newInstance();

            activity.attach(context );
            return activity;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
源码用 ClassLoader.loadClass(className).newInstance()的方式来创建Activity, 效果是一样的。

三、在PhoneWindow实现 setContentView

setContentView 在onCreate函数中。 在我们自己的实现过程中,将输入参数替换成File。

    public void setContentView(File file) {
        Log.d(TAG, "setContentView");
        if (mContentParent == null) {
            installDecor();
        }
        mLayoutInflater.inflate(file, mContentParent);
    }

1. installDecor

installDecor  的关键作用是 new DecorView。generateLayout 的作用和 inflate 一样。

    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor(-1);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
        }
    }
    protected DecorView generateDecor(int featureId) {
        Context context;
        context = getContext();
        return new DecorView(context, featureId, this);

    }



2. LayoutInflater.inflate

在实现 inflate 前,看下这个算法的基本原理。

例如 如果我们的 xml

 如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
        <TextView
            android:id="@+id/notice"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="horizontal!"/>
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/app_name"
            android:layout_below="@id/notice"
            />
    </RelativeLayout>

    <Button
        android:id="@+id/portrait"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/app_name"
        />
</LinearLayout>

转换下 图形

按照先序遍历的规则 , 遍历出的节点 是    LinearLayout - RelativeLayout -TextView-Button - Button。  LayoutInflater  inflate 实现的也是这个经典的先序遍历 模型。

public class LayoutInflater {



    final Object[] mConstructorArgs = new Object[2];
    static final Class<?>[] mConstructorSignature = new Class[] {
            Context.class, AttributeSet.class};

    private static final String TAG ="LayoutInflater." ;
    protected final Context mContext;
    private static final String[] sClassPrefixList = {
            "com.widget.",
            "android.widget.",
            "android.webkit.",
            "android.app."
    };

    public static LayoutInflater from(Context context) {
        LayoutInflater layoutInflater = new LayoutInflater(context);
        return layoutInflater;
    }
    LayoutInflater(Context context) {
        mContext = context;
    }

    public View inflate(File file, ViewGroup root) {
        return inflate(file, root, root != null);
    }

    public View inflate(File file, ViewGroup root, boolean attachToRoot) {

        try {
            XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
            XmlPullParser parser = parserFactory.newPullParser();
            parser.setInput(new FileInputStream(file), "UTF-8");
            return inflate(parser, root, attachToRoot);

        } catch (Exception e) {

        }
        return null;
    }

    public View inflate(XmlPullParser parser,  ViewGroup root, boolean attachToRoot) {
        Log.d(TAG, "inflate");
        View result = root;
        try{
//            快速跳到根节点
            advanceToRootNode(parser);
            final String name = parser.getName();

            final View temp = createViewFromTag(root, name, mContext, null);
            rInflateChildren(parser, temp, null, true);
            if( root == null ){
                result = temp;
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {

        }
        return  result;
    }

    private void advanceToRootNode(XmlPullParser parser) throws IOException, XmlPullParserException {
        int type;
        while ((type = parser.next()) != XmlPullParser.START_TAG &&
                type != XmlPullParser.END_DOCUMENT) {
            // Empty
        }
    }



    final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
                                boolean finishInflate) throws XmlPullParserException, IOException {
        try {
            rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    void rInflate(XmlPullParser parser, View parent, Context context,
                  AttributeSet attrs, boolean finishInflate) throws IOException, XmlPullParserException, ClassNotFoundException {
        final int depth = parser.getDepth();
        int type;
        while (((type = parser.next()) != XmlPullParser.END_TAG
                || parser.getDepth() > depth)
                && type != XmlPullParser.END_DOCUMENT) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
            final String name = parser.getName();
            Log.d(TAG, " rInflate  name "+name);
            final View view = createViewFromTag(parent, name, context, attrs);
            final ViewGroup viewGroup = (ViewGroup) parent;
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(null);
            rInflateChildren( parser, view , attrs, true);
            viewGroup.addView( view, params);

        }
    }
	
    private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) throws ClassNotFoundException {
        return createViewFromTag(parent, name, context, attrs, false);
    }

    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
                           boolean ignoreThemeAttr) throws ClassNotFoundException {
        View view;
        view = onCreateView(context, parent, name, attrs);

        return  view;
    }
	
    public View onCreateView( Context viewContext,  View parent,
                              String name,  AttributeSet attrs)
            throws ClassNotFoundException {
        return onCreateView(parent, name, attrs);
    }
    protected View onCreateView(View parent, String name, AttributeSet attrs)
            throws ClassNotFoundException {
        return onCreateView(name, attrs);
    }
    protected View onCreateView(String name, AttributeSet attrs){
        for (String prefix : sClassPrefixList) {
            try {
                View view = createView(name, prefix, attrs);
                if (view != null) {
                    return view;
                }
            } catch (Exception e) {
                // In this case we want to let the base class take a crack
                // at it.
                return  null;
            }
        }
        return  null;
    }
    public final View createView(String name, String prefix, AttributeSet attrs){
        return createView(mContext, name, prefix, attrs);
    }

    public final View createView( Context viewContext,  String name,
                                  String prefix,  AttributeSet attrs){
        Class<? extends View> clazz = null;
        Constructor<? extends View> constructor;
        try{

            clazz = Class.forName(prefix != null ? (prefix + name) : name).asSubclass(View.class);
            constructor = clazz.getConstructor(mConstructorSignature);
            mConstructorArgs[0] = viewContext;
            Object[] args = mConstructorArgs;
            args[1] = attrs;
            View view = constructor.newInstance( args );
            return  view;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return  null;
    }

}

和实际结果相符。

2023-12-20 19:28:49 LayoutInflater.inflate
2023-12-20 19:58:58 LayoutInflater. name LinearLayout
2023-12-20 19:28:49 LayoutInflater. rInflate  name RelativeLayout
2023-12-20 19:28:49 LayoutInflater. rInflate  name TextView
2023-12-20 19:28:49 LayoutInflater. rInflate  name Button
2023-12-20 19:28:49 LayoutInflater. rInflate  name Button

总结

本文对 setContentView 函数 将xml 转成DecorView 的过程进行了简化。了解到解析xml文件实际上是先序遍历的使用。

代码实现上传到: 

https://github.com/MrXiangVip/framework.git

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值