继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android源码解析之LayoutInflater

慕妹3146593
关注TA
已关注
手记 107
粉丝 14
获赞 39
  • 知其然,更需知其所以然,让我们开启源码解析之旅.

  • 文章目录

    • LayoutInflater的使用场景

    • LayoutInflater的由来

    • LayoutInflater.inflater()方法源码解析

    • 总结


  • 通常在日常开发中下面几个地方会使用到LayoutInflater

    • Fragment的onCreateView()方法中需要通过LayoutInflate.inflate方法填充布局,该LayoutInflater对象是Fragment方法中传递进来的


  1.    @Override

  2.    public View onCreateView(LayoutInflater inflater, ViewGroup container,

  3.                             Bundle savedInstanceState) {

  4.        return inflater.inflate(layoutId, container, false);

  5.    }

在适配器Adapter的getView()方法中使用到LayoutInflater填充布局


  1.    View view = LayoutInflater.from(mContext).inflate(layoutId, parent, false);

还有一种情况是在Activity中需要获取到一个布局


  1.    View view = LayoutInflater.from(this).inflate(layoutId, null);

接下来我们看看LayoutInflater.form()这个静态方法的实现内幕


  1.    public static LayoutInflater from(Context context) {

  2.        LayoutInflater LayoutInflater =

  3.                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

  4.        if (LayoutInflater == null) {

  5.            throw new AssertionError("LayoutInflater not found.");

  6.        }

  7.        return LayoutInflater;

  8.    }

发现主要方法是通过上下文context调用获取系统服务getSystemService通过传入固定参数获取


通过源码注释可以知道,调用该方法可以根据传入的名称,获取到不同的系统级服务.具体包含的系统级服务有:


  1.     *

  2.     * <dl>

  3.     *  <dt> {@link #WINDOW_SERVICE} ("window")

  4.     *  <dd> The top-level window manager in which you can place custom

  5.     *  windows.  The returned object is a {@link android.view.WindowManager}.

  6.     *  <dt> {@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater")

  7.     *  <dd> A {@link android.view.LayoutInflater} for inflating layout resources

  8.     *  in this context.

  9.     *  <dt> {@link #ACTIVITY_SERVICE} ("activity")

  10.     *  <dd> A {@link android.app.ActivityManager} for interacting with the

  11.     *  global activity state of the system.

  12.     *  <dt> {@link #POWER_SERVICE} ("power")

  13.     *  <dd> A {@link android.os.PowerManager} for controlling power

  14.     *  management.

  15.     *  <dt> {@link #ALARM_SERVICE} ("alarm")

  16.     *  <dd> A {@link android.app.AlarmManager} for receiving intents at the

  17.     *  time of your choosing.

  18.     *  <dt> {@link #NOTIFICATION_SERVICE} ("notification")

  19.     *  <dd> A {@link android.app.NotificationManager} for informing the user

  20.     *   of background events.

  21.     *  <dt> {@link #KEYGUARD_SERVICE} ("keyguard")

  22.     *  <dd> A {@link android.app.KeyguardManager} for controlling keyguard.

  23.     *  <dt> {@link #LOCATION_SERVICE} ("location")

  24.     *  <dd> A {@link android.location.LocationManager} for controlling location

  25.     *   (e.g., GPS) updates.

  26.     *  <dt> {@link #SEARCH_SERVICE} ("search")

  27.     *  <dd> A {@link android.app.SearchManager} for handling search.

  28.     *  <dt> {@link #VIBRATOR_SERVICE} ("vibrator")

  29.     *  <dd> A {@link android.os.Vibrator} for interacting with the vibrator

  30.     *  hardware.

  31.     *  <dt> {@link #CONNECTIVITY_SERVICE} ("connection")

  32.     *  <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for

  33.     *  handling management of network connections.

  34.     *  <dt> {@link #WIFI_SERVICE} ("wifi")

  35.     *  <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi

  36.     *  connectivity.  On releases before NYC, it should only be obtained from an application

  37.     *  context, and not from any other derived context to avoid memory leaks within the calling

  38.     *  process.

  39.     *  <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p")

  40.     *  <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of

  41.     * Wi-Fi Direct connectivity.

  42.     * <dt> {@link #INPUT_METHOD_SERVICE} ("input_method")

  43.     * <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager}

  44.     * for management of input methods.

  45.     * <dt> {@link #UI_MODE_SERVICE} ("uimode")

  46.     * <dd> An {@link android.app.UiModeManager} for controlling UI modes.

  47.     * <dt> {@link #DOWNLOAD_SERVICE} ("download")

  48.     * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads

  49.     * <dt> {@link #BATTERY_SERVICE} ("batterymanager")

  50.     * <dd> A {@link android.os.BatteryManager} for managing battery state

  51.     * <dt> {@link #JOB_SCHEDULER_SERVICE} ("taskmanager")

  52.     * <dd>  A {@link android.app.job.JobScheduler} for managing scheduled tasks

  53.     * <dt> {@link #NETWORK_STATS_SERVICE} ("netstats")

  54.     * <dd> A {@link android.app.usage.NetworkStatsManager NetworkStatsManager} for querying network

  55.     * usage statistics.

  56.     * <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties")

  57.     * <dd> A {@link android.os.HardwarePropertiesManager} for accessing hardware properties.

  58.     * </dl>

  59.     *

  1.    /**

  2.     * Return the handle to a system-level service by name. The class of the

  3.     * returned object varies by the requested name. Currently available names

  4.     * are:

  5.     */

  6.    public abstract Object getSystemService(@ServiceName @NonNull String name);

到Context这一步我们发现getSystemService()是抽象方法,那他的具体实现类有哪些呢?

  • ==>我们可以通过Context类继承关系来查看,发现一个应用Context的总个数为Activity个数+Service个数+1


  • 而一个Activity的入口是ActivityThread的main()方法,在该方法中会new一个ActivityThread实例,启动主线程的Looper消息轮询,创建新的Activity和Context对象,并将Context对象传递给Activity.

    • ActivityThread.main()方法


  1.   public static void main(String[] args) {

  2.        //事件跟踪,使用Systrace工具通过打标记tag来收集后写入内部缓存,与后面的调用方法Trance.traceEnd()成对出现

  3.        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

  4.        //启动性能分析

  5.        SamplingProfilerIntegration.start();

  6.        // CloseGuard defaults to true and can be quite spammy.  We

  7.        // disable it here, but selectively enable it later (via

  8.        // StrictMode) on debug builds, but using DropBox, not logs.

  9.        CloseGuard.setEnabled(false);

  10.        Environment.initForCurrentUser();

  11.        // Set the reporter for event logging in libcore

  12.        EventLogger.setReporter(new EventLoggingReporter());

  13.        // Make sure TrustedCertificateStore looks in the right place for CA certificates

  14.        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());

  15.        TrustedCertificateStore.setDefaultUserDirectory(configDir);

  16.        Process.setArgV0("<pre-initialized>");

  17.        //主线程消息轮询

  18.        Looper.prepareMainLooper();

  19.        //创建ActivityThread对象

  20.        ActivityThread thread = new ActivityThread();

  21.        thread.attach(false);

  22.        if (sMainThreadHandler == null) {

  23.            sMainThreadHandler = thread.getHandler();

  24.        }

  25.        if (false) {

  26.            Looper.myLooper().setMessageLogging(new

  27.                    LogPrinter(Log.DEBUG, "ActivityThread"));

  28.        }

  29.        // End of event ActivityThreadMain.

  30.        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

  31.        //开启轮询

  32.        Looper.loop();

  33.        throw new RuntimeException("Main thread loop unexpectedly exited");

  34.    }

再看thread.attach(false)方法


  1.   private void attach(boolean system) {

  2.        sCurrentActivityThread = this;

  3.        mSystemThread = system;

  4.        //不是系统应用的情况

  5.        if (!system) {

  6.            ViewRootImpl.addFirstDrawHandler(new Runnable() {

  7.                @Override

  8.                public void run() {

  9.                    ensureJitEnabled();

  10.                }

  11.            });

  12.            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",

  13.                                                    UserHandle.myUserId());

  14.            RuntimeInit.setApplicationObject(mAppThread.asBinder());

  15.            //通过Binder机制与AMS通信,并与ApplicaitonThread关联

  16.            final IActivityManager mgr = ActivityManagerNative.getDefault();

  17.            try {

  18.                mgr.attachApplication(mAppThread);

  19.            } catch (RemoteException ex) {

  20.                throw ex.rethrowFromSystemServer();

  21.            }

  22.            // ...代码省略

  23.    }

最后通过Handle的消息处理机制调用handleLaunchActivity()方法->接着调用performLaunchActivity()方法


  1.   private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

  2.        // ...

  3.        Activity a = performLaunchActivity(r, customIntent);

  4.        // ...

  5. }

performLauncherActivity()


Activity的主要步骤在代码中已标明,接着我们看第三步创建上下文的方法

  1.    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

  2.        //需要的属性信息可以通过封装类ActivityClientRecord获取

  3.        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

  4.        ActivityInfo aInfo = r.activityInfo;

  5.        if (r.packageInfo == null) {

  6.            //package信息

  7.            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,

  8.                    Context.CONTEXT_INCLUDE_CODE);

  9.        }

  10.        //组件信息

  11.        ComponentName component = r.intent.getComponent();

  12.        if (component == null) {

  13.            component = r.intent.resolveActivity(

  14.                mInitialApplication.getPackageManager());

  15.            r.intent.setComponent(component);

  16.        }

  17.        if (r.activityInfo.targetActivity != null) {

  18.            component = new ComponentName(r.activityInfo.packageName,

  19.                    r.activityInfo.targetActivity);

  20.        }

  21.        Activity activity = null;

  22.        try {

  23.            //拿到类加载器

  24.            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();

  25.            //1.创建activity

  26.            activity = mInstrumentation.newActivity(

  27.                    cl, component.getClassName(), r.intent);

  28.            StrictMode.incrementExpectedActivityCount(activity.getClass());

  29.            r.intent.setExtrasClassLoader(cl);

  30.            r.intent.prepareToEnterProcess();

  31.            if (r.state != null) {

  32.                r.state.setClassLoader(cl);

  33.            }

  34.        } catch (Exception e) {

  35.            if (!mInstrumentation.onException(activity, e)) {

  36.                throw new RuntimeException(

  37.                    "Unable to instantiate activity " + component

  38.                    + ": " + e.toString(), e);

  39.            }

  40.        }

  41.        try {

  42.            //2.创建Application对象

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

  44.            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);

  45.            if (localLOGV) Slog.v(

  46.                    TAG, r + ": app=" + app

  47.                    + ", appName=" + app.getPackageName()

  48.                    + ", pkg=" + r.packageInfo.getPackageName()

  49.                    + ", comp=" + r.intent.getComponent().toShortString()

  50.                    + ", dir=" + r.packageInfo.getAppDir());

  51.            if (activity != null) {

  52.                //3.创建上下文Context对象

  53.                Context appContext = createBaseContextForActivity(r, activity);

  54.                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());

  55.                Configuration config = new Configuration(mCompatConfiguration);

  56.                if (r.overrideConfig != null) {

  57.                    config.updateFrom(r.overrideConfig);

  58.                }

  59.                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "

  60.                        + r.activityInfo.name + " with config " + config);

  61.                Window window = null;

  62.                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {

  63.                    window = r.mPendingRemoveWindow;

  64.                    r.mPendingRemoveWindow = null;

  65.                    r.mPendingRemoveWindowManager = null;

  66.                }

  67.                //4.将上下文appContex等对象attach到activity中

  68.                activity.attach(appContext, this, getInstrumentation(), r.token,

  69.                        r.ident, app, r.intent, r.activityInfo, title, r.parent,

  70.                        r.embeddedID, r.lastNonConfigurationInstances, config,

  71.                        r.referrer, r.voiceInteractor, window);

  72.                if (customIntent != null) {

  73.                    activity.mIntent = customIntent;

  74.                }

  75.                r.lastNonConfigurationInstances = null;

  76.                activity.mStartedActivity = false;

  77.                int theme = r.activityInfo.getThemeResource();

  78.                if (theme != 0) {

  79.                    activity.setTheme(theme);

  80.                }

  81.                activity.mCalled = false;

  82.               //根据Activity是否进行过持久化,调用不同的onCreate()方法

  83.                if (r.isPersistable()) {

  84.                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);

  85.                } else {

  86.                    mInstrumentation.callActivityOnCreate(activity, r.state);

  87.                }

  88.                

  89.          //......

  90.        return activity;

  91.    }

createBaseContextForActivity()


  1.   private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {

  2.        int displayId = Display.DEFAULT_DISPLAY;

  3.        try {

  4.            displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);

  5.        } catch (RemoteException e) {

  6.            throw e.rethrowFromSystemServer();

  7.        }

  8.        //到这步我们确认我们使用的上下文的实现类是ContextImpl

  9.        ContextImpl appContext = ContextImpl.createActivityContext(

  10.                this, r.packageInfo, r.token, displayId, r.overrideConfig);

  11.        appContext.setOuterContext(activity);

  12.        Context baseContext = appContext;

  13.        //...

  14.        return baseContext;

  15.    }

找到了上下文的实现类ContextImpl,继续跟踪getSystemService()方法


  • 发现真正的实现类是SystemServiceRegistry.getSystemService()

  1.    @Override

  2.    public Object getSystemService(String name) {

  3.        return SystemServiceRegistry.getSystemService(this, name);

  4.    }

接着跟踪SystemServiceRegistry的实现


  1.    /**

  2.     * Gets a system service from a given context.

  3.     * 从传入的上下文对象获取系统级服务

  4.     */

  5.    public static Object getSystemService(ContextImpl ctx, String name) {

  6.        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);

  7.        return fetcher != null ? fetcher.getService(ctx) : null;

  8.    }

第六行代码这里出现了一个属性SYSTEM_SERVICE_FETCHERS,我们看下这个属性的实现


  1.   //首先发现他是一个HashMap对象

  2.   private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =  new HashMap<String, ServiceFetcher<?>>();

既然是HasnMap对象那就一定实现了put(),和get()方法


发现在registerService()方法中实现,再接着看该方法

  1.    private static <T> void registerService(String serviceName, Class<T> serviceClass,

  2.            ServiceFetcher<T> serviceFetcher) {

  3.        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);

  4.        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);

  5.    }

registerService()-->发现该方法在静态代码块中调用,其中就包括我们我们需要找的LAYOUT_INFLATE_SERVICE,并且返回的是LayoutInfalter的子类PhoneLayoutInflater,这里面使用到HashMap用来保存各种系统级服务的对象,是为了保证这些服务对象实例的单一性,也是单例模的一种实现.


  1.    static {

  2.        registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,

  3.                new CachedServiceFetcher<AccessibilityManager>() {

  4.            @Override

  5.            public AccessibilityManager createService(ContextImpl ctx) {

  6.                return AccessibilityManager.getInstance(ctx);

  7.            }});

  8.        //..........

  9.        registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,

  10.                new CachedServiceFetcher<LayoutInflater>() {

  11.            @Override

  12.            public LayoutInflater createService(ContextImpl ctx) {

  13.                return new PhoneLayoutInflater(ctx.getOuterContext());

  14.            }});

  15.        //..........

  16.   }

             到这里我们把LayoutInflater的使用场景和LayoutInflater怎么来的问题搞清楚了,下面我们一起来看看inflate方法的实现,是怎么将xml文件控件加载到界面中

  • LayoutInflate.inflate():解析xml布局界面是如何展示出来的?

    • inflate有两个重载函数,分别是:



  1.    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {

  2.        final Resources res = getContext().getResources();

  3.        if (DEBUG) {

  4.            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("

  5.                    + Integer.toHexString(resource) + ")");

  6.        }

  7.        //根据布局id,拿到xml资源解析器

  8.        final XmlResourceParser parser = res.getLayout(resource);

  9.        try {

  10.            //实际调用infalte方法

  11.            return inflate(parser, root, attachToRoot);

  12.        } finally {

  13.            //注意关闭资源

  14.            parser.close();

  15.        }

  16.    }

  1.    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {

  2.        //内部调用重载函数,第三个参数根据是否传入根布局确定

  3.        return inflate(resource, root, root != null);

  4.    }

第一个方法内部调用第二个方法:其中的参数分别代表

  • resource:xml资源文件

  • root:代表传入的根布局

  • attachToRoot:意思是否将该xml布局绑定到根布局root中,如果是的话,会使用根布局的属性信息

接着跟踪核心方法infalte,通过解析xml文件的节点得到最终想要的View控件


该方法处理解析文件的主要业务逻辑:

  1.    /**

  2.      *参数解析: parser为xml文件解析器

  3.                root为根布局

  4.                attachToRoot标示是否attach到根布局root中

  5.      *

  6.      /

  7.     public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {

  8.        synchronized (mConstructorArgs) {

  9.            //日志跟踪

  10.            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

  11.            //拿到构造函数中传入的上下文mContext

  12.            final Context inflaterContext = mContext;

  13.            //拿到属性信息

  14.            final AttributeSet attrs = Xml.asAttributeSet(parser);

  15.            Context lastContext = (Context) mConstructorArgs[0];

  16.            mConstructorArgs[0] = inflaterContext;

  17.            //默认返回的result为根布局

  18.            View result = root;

  19.            try {

  20.                // 查找根节点

  21.                int type;

  22.                while ((type = parser.next()) != XmlPullParser.START_TAG &&

  23.                        type != XmlPullParser.END_DOCUMENT) {

  24.                    // Empty

  25.                }

  26.                if (type != XmlPullParser.START_TAG) {

  27.                    throw new InflateException(parser.getPositionDescription()

  28.                            + ": No start tag found!");

  29.                }

  30.                //拿到根节点名称

  31.                final String name = parser.getName();

  32.                

  33.                if (DEBUG) {

  34.                    System.out.println("**************************");

  35.                    System.out.println("Creating root view: "

  36.                            + name);

  37.                    System.out.println("**************************");

  38.                }

  39.                if (TAG_MERGE.equals(name)) {


  40.                    //1.根结点为merge ==> 则根布局root必须不为空,且必须绑定到跟布局中,(attachToRoot为true)

  41.                    if (root == null || !attachToRoot) {

  42.                        throw new InflateException("<merge /> can be used only with a valid "

  43.                                + "ViewGroup root and attachToRoot=true");

  44.                    }

  45.                    //解析merge内部包含的子控件

  46.                    rInflate(parser, root, inflaterContext, attrs, false);

  47.                } else {

  48.                    // 2.从xml文件中解析到顶层控件temp->后面会单独详细讲解

  49.                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

  50.                    ViewGroup.LayoutParams params = null;

  51.                    if (root != null) {

  52.                        if (DEBUG) {

  53.                            System.out.println("Creating params from root: " +

  54.                                    root);

  55.                        }

  56.                        //3.拿到root属性信息

  57.                        params = root.generateLayoutParams(attrs);

  58.                        if (!attachToRoot) {

  59.                            // attachToRoot为false时将从根布局获取的属性信息设置给temp,如果attachToRoot为true,则直接调用root.addView()

  60.                            temp.setLayoutParams(params);

  61.                        }

  62.                    }

  63.                    if (DEBUG) {

  64.                        System.out.println("-----> start inflating children");

  65.                    }

  66.                    //递归解析xml布局文件中temp包含的子控件

  67.                    rInflateChildren(parser, temp, attrs, true);

  68.                    if (DEBUG) {

  69.                        System.out.println("-----> done inflating children");

  70.                    }

  71.                    // 4.attachToRoot为true直接调用addView()添加,将temp绑定到root下,root作为temp的父控件

  72.                    if (root != null && attachToRoot) {

  73.                        root.addView(temp, params);

  74.                    }

  75.                    // 5.根布局root为null或者attachToRoot为false不绑定到root中,返回的temp(xml文件中解析得到的控件)

  76.                    //其他情况返回的是根布局root

  77.                    if (root == null || !attachToRoot) {

  78.                        result = temp;

  79.                    }

  80.                }

  81.            } catch (XmlPullParserException e) {

  82.                final InflateException ie = new InflateException(e.getMessage(), e);

  83.                ie.setStackTrace(EMPTY_STACK_TRACE);

  84.                throw ie;

  85.            } catch (Exception e) {

  86.                final InflateException ie = new InflateException(parser.getPositionDescription()

  87.                        + ": " + e.getMessage(), e);

  88.                ie.setStackTrace(EMPTY_STACK_TRACE);

  89.                throw ie;

  90.            } finally {

  91.                // Don't retain static reference on context.

  92.                mConstructorArgs[0] = lastContext;

  93.                mConstructorArgs[1] = null;

  94.                Trace.traceEnd(Trace.TRACE_TAG_VIEW);

  95.            }

  96.            return result;

  97.        }

  98.    }

               1.先解析拿到xml布局文件中顶层控件(注意和根布局root区分)名称name

               2.如果name为merge,则判断root一定不能为空,且attachToRoot必须为true,之后调用rInflate方法将merger标签包含的控件全部添加到根布局root中,并且最后返回的是根布局root.

               3.接下来的情况就是name不是merge,通过createViewFromTag()方法获取到xml布局文件的顶层控件temp

                   a.接着拿到根布局root的属性信息params,如果attachToRoot为false,则将根布局的属性信息设置给顶层控件temp

                   b.接着递归调用rInflateChildren()方法解析拿到顶层控件temp下的子控件

                   c.接着判断如果root不为空,且attachToRoot为true,则直接调用根布局root的addView()方法,表示该xml控件的顶层控件temp将以根布局root为父控件,并且最后返回的是根布局root.

                   d.如果root为空,或者attachToRoot为false,则最后返回的是xml文件的顶层控件temp.

  • 接下来查看createViewFromTag()方法:该方法会根据xml文件的节点名称真正来解析得到控件


    createViewFromTag()主要是判断xml文件的控件是使用的是系统控件还是自定义控件,判断依据是控件名称是否包含点,因为系统控件都是在android.view.包名下,系统会为我们自动添加,后面会看到,而自定义控件需要添加全包名.

  1.    /**

  2.      * 参数信息:parent为根布局,name为控件名称(例如TextView),attrs在创建view需要用到

  3.      *

  4.    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,

  5.            boolean ignoreThemeAttr) {

  6.        if (name.equals("view")) {

  7.            name = attrs.getAttributeValue(null, "class");

  8.        }

  9.        // Apply a theme wrapper, if allowed and one is specified.

  10.        if (!ignoreThemeAttr) {

  11.            final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);

  12.            final int themeResId = ta.getResourceId(0, 0);

  13.            if (themeResId != 0) {

  14.                context = new ContextThemeWrapper(context, themeResId);

  15.            }

  16.            ta.recycle();

  17.        }

  18.         //直接返回BlinkLayout

  19.        if (name.equals(TAG_1995)) {

  20.            // Let's party like it's 1995!

  21.            return new BlinkLayout(context, attrs);

  22.        }

  23.        try {

  24.            View view;

  25.    

  26.             //因为我们是以new PhoneLayoutInflate(Context ctx)方法创建LayoutInflate实例的,所以mFactory,mFactory2和mPrivateFactory皆为null

  27.            if (mFactory2 != null) {

  28.                view = mFactory2.onCreateView(parent, name, context, attrs);

  29.            } else if (mFactory != null) {

  30.                view = mFactory.onCreateView(name, context, attrs);

  31.            } else {

  32.                view = null;

  33.            }

  34.            if (view == null && mPrivateFactory != null) {

  35.                view = mPrivateFactory.onCreateView(parent, name, context, attrs);

  36.            }

  37.             //前面的判断逻辑都不会执行,最后会执行该判断逻辑

  38.            if (view == null) {

  39.                final Object lastContext = mConstructorArgs[0];

  40.                mConstructorArgs[0] = context;

  41.                try {

  42.                    if (-1 == name.indexOf('.')) {

  43.                        //1.进入该判断语句,说明控件名称不包含点".",使用的系统控件如TextView,Button

  44.                        view = onCreateView(parent, name, attrs);

  45.                    } else {

  46.    

  47.                        //2.使用的是自定义控件,界面名称是类的全包名,包含点

  48.                        view = createView(name, null, attrs);

  49.                    }

  50.                } finally {

  51.                    mConstructorArgs[0] = lastContext;

  52.                }

  53.            }

  54.            return view;

  55.        } catch (InflateException e) {

  56.            throw e;

  57.        } catch (ClassNotFoundException e) {

  58.            final InflateException ie = new InflateException(attrs.getPositionDescription()

  59.                    + ": Error inflating class " + name, e);

  60.            ie.setStackTrace(EMPTY_STACK_TRACE);

  61.            throw ie;

  62.        } catch (Exception e) {

  63.            final InflateException ie = new InflateException(attrs.getPositionDescription()

  64.                    + ": Error inflating class " + name, e);

  65.            ie.setStackTrace(EMPTY_STACK_TRACE);

  66.            throw ie;

  67.        }

  68.    }

             eg:xml布局文件例子

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="com.timmy.customeView.countdownTime.CountDownTimeActivity">    <include layout="@layout/common_toolbar"/>    //自定义控件书写    <com.timmy.MyView        android:layout_width="120dp"        android:layout_margin="30dp"        android:layout_height="120dp" />        //系统控件    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content" /></LinearLayout>
  • 假如使用的系统控件,进入逻辑1调用 onCreateView(parent,name,attrs)


  1.    protected View onCreateView(View parent, String name, AttributeSet attrs)

  2.            throws ClassNotFoundException {

  3.        return onCreateView(name, attrs);

  4.    }

为系统控件添加包名前缀


  1.    protected View onCreateView(String name, AttributeSet attrs)

  2.            throws ClassNotFoundException {

  3.        return createView(name, "android.view.", attrs);

  4.    }

最后系统控件和自定义控件都会调用createView()方法


最终创建View控件的就是createView()方法,根据控件全包名使用反射方式得到控件对象view,并返回.

  1.    /**

  2.     *  name为控件名称

  3.        prefix为控件报名的前缀

  4.     */

  5.   public final View createView(String name, String prefix, AttributeSet attrs)

  6.            throws ClassNotFoundException, InflateException {


  7.        //使用集合进行缓存控件的构造函数

  8.        Constructor<? extends View> constructor = sConstructorMap.get(name);

  9.        if (constructor != null && !verifyClassLoader(constructor)) {

  10.            constructor = null;

  11.            sConstructorMap.remove(name);

  12.        }

  13.        Class<? extends View> clazz = null;

  14.        try {

  15.            Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);

  16.            if (constructor == null) {


  17.                //1. 缓存中没有,反射拿到控件的Class对象

  18.                clazz = mContext.getClassLoader().loadClass(

  19.                        prefix != null ? (prefix + name) : name).asSubclass(View.class);

  20.                

  21.                if (mFilter != null && clazz != null) {

  22.                    boolean allowed = mFilter.onLoadClass(clazz);

  23.                    if (!allowed) {

  24.                        failNotAllowed(name, prefix, attrs);

  25.                    }

  26.                }

  27.                //2.拿到控件构造函数

  28.                constructor = clazz.getConstructor(mConstructorSignature);

  29.                constructor.setAccessible(true);

  30.                sConstructorMap.put(name, constructor);

  31.            } else {

  32.                //缓存中有该控件的构造函数

  33.                if (mFilter != null) {

  34.                    // Have we seen this name before?

  35.                    Boolean allowedState = mFilterMap.get(name);

  36.                    if (allowedState == null) {

  37.                        // New class -- remember whether it is allowed

  38.                        clazz = mContext.getClassLoader().loadClass(

  39.                                prefix != null ? (prefix + name) : name).asSubclass(View.class);

  40.                        

  41.                        boolean allowed = clazz != null && mFilter.onLoadClass(clazz);

  42.                        mFilterMap.put(name, allowed);

  43.                        if (!allowed) {

  44.                            failNotAllowed(name, prefix, attrs);

  45.                        }

  46.                    } else if (allowedState.equals(Boolean.FALSE)) {

  47.                        failNotAllowed(name, prefix, attrs);

  48.                    }

  49.                }

  50.            }

  51.            Object[] args = mConstructorArgs;

  52.            args[1] = attrs;

  53.            //3.构造函数执行

  54.            final View view = constructor.newInstance(args);

  55.            if (view instanceof ViewStub) {

  56.                // Use the same context when inflating ViewStub later.

  57.                final ViewStub viewStub = (ViewStub) view;

  58.                viewStub.setLayoutInflater(cloneInContext((Context) args[0]));

  59.            }

  60.            return view;

  61.        } catch (NoSuchMethodException e) {

  62.            final InflateException ie = new InflateException(attrs.getPositionDescription()

  63.                    + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);

  64.            ie.setStackTrace(EMPTY_STACK_TRACE);

  65.            throw ie;

  66.        } catch (ClassCastException e) {

  67.            // If loaded class is not a View subclass

  68.            final InflateException ie = new InflateException(attrs.getPositionDescription()

  69.                    + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);

  70.            ie.setStackTrace(EMPTY_STACK_TRACE);

  71.            throw ie;

  72.        } catch (ClassNotFoundException e) {

  73.            // If loadClass fails, we should propagate the exception.

  74.            throw e;

  75.        } catch (Exception e) {

  76.            final InflateException ie = new InflateException(

  77.                    attrs.getPositionDescription() + ": Error inflating class "

  78.                            + (clazz == null ? "<unknown>" : clazz.getName()), e);

  79.            ie.setStackTrace(EMPTY_STACK_TRACE);

  80.            throw ie;

  81.        } finally {

  82.            Trace.traceEnd(Trace.TRACE_TAG_VIEW);

  83.        }

  84.    }

              到这里就了解了我们在xml文件写的布局是如何解析得到我们想要的控件.

    • 最后我们来看下在infalate方法中的rInflateChilder()方法时如何递归拿到所有的控件-->先调用rInflate()->内部再调用rInflateChildren()



  1.    void rInflate(XmlPullParser parser, View parent, Context context,

  2.            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {

  3.        //拿到xml文件的层级

  4.        final int depth = parser.getDepth();

  5.        int type;

  6.        //xml解析器不断进行节点解析

  7.        while (((type = parser.next()) != XmlPullParser.END_TAG ||

  8.                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

  9.            if (type != XmlPullParser.START_TAG) {

  10.                continue;

  11.            }

  12.             //拿到节点名称

  13.            final String name = parser.getName();

  14.            

  15.            if (TAG_REQUEST_FOCUS.equals(name)) {

  16.                parseRequestFocus(parser, parent);

  17.            } else if (TAG_TAG.equals(name)) {

  18.                parseViewTag(parser, parent, attrs);

  19.            } else if (TAG_INCLUDE.equals(name)) {

  20.                if (parser.getDepth() == 0) {

  21.                    throw new InflateException("<include /> cannot be the root element");

  22.                }

  23.                parseInclude(parser, context, parent, attrs);

  24.            } else if (TAG_MERGE.equals(name)) {

  25.                throw new InflateException("<merge /> must be the root element");

  26.            } else {

  27.                 //拿到该节点的控件view

  28.                final View view = createViewFromTag(parent, name, context, attrs);

  29.                final ViewGroup viewGroup = (ViewGroup) parent;

  30.                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);

  31.                //以view为父控件递归rInfalateChildren()

  32.                rInflateChildren(parser, view, attrs, true);

  33.                //最后按照层级添加

  34.                viewGroup.addView(view, params);

  35.            }

  36.        }

  37.        if (finishInflate) {

  38.            //当所有的子控件都添加完毕,就会调用我们熟悉的onFinishInfalte()方法,通过该方法我们可以拿到内部的子控件.

  39.            parent.onFinishInflate();

  40.        }

  41.    }

总结:

  • 奉上infalte方法调用时序图:

            5b9cf5ca0001581a12390796.jpg

原文链接:http://www.apkbus.com/blog-689749-68110.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP