系列笔记1、单例模式-LayoutInflater

最近在读《Android源码设计模式解析与实战》,会尝试自己阅读、整理android源码。


单例模式


1.饿汉模式--类声明时初始化

public class MyObj{

   private static final MyObj sObj=new MyObj(); 

   private  MyObj(){}

   public static MyObj getInstance(){

       return sObj;

    }

}


2.懒汉模式 ---使用时才被初始化,劣势:同步锁会导致访问速度慢

public class MyObj{

   private static MyObj sObj; 

   private  MyObj(){}

   public static synchronized MyObj getInstance(){

       if(sObj==null){

           sObj=new MyObj();

       }

       return sObj;

    }

}


3.双重锁定检查DCL模式--- 

缺点 “sObj=new MyObj(); ”这句包含三步:1给MyObj的实例分配内存;2.初始化MyObj;3将sObj指向分配的内存空间。无法保证2、3的顺序,1-3-2会出现DCL失效问题。sObj非空,却未完成初始化,使用会出错。

JDK是1.5或之后的版本,只需要将sObj的定义改成“private volatile static MyObj sObj = null;”就可以保证每次都去instance都从主内存读取

public class MyObj{

   private static MyObj sObj; 

   private  MyObj(){}

   public static MyObj getInstance(){

       if(sObj==null){

         synchronized(MyObj.class){

               if(sObj==null){

                  sObj=new MyObj();

                }

           }

       }

       return sObj;

    }

}


4.静态内部类单例模式

public class MyObj{

   private  MyObj(){}

   public static synchronized MyObj getInstance(){

       return MyObjHolder.sObj;

    }

    private static class MyObjHolder{ // Note:(static)inner classes cannot have static declarations

             static final MyObj sObj=new MyObj()

     }

}




LayoutInflater的加载过程



1.LayoutInflater是通过LayoutInflater.from(context)来获取到一个LayoutInflater实例的。


在from方法中会调用 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);也就是说最终的初始化是context的getSystemService方法来做的。

LayoutInflater.java
public static LayoutInflater from(Context context) {
    LayoutInflater LayoutInflater =
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    if (LayoutInflater == null) {
        throw new AssertionError("LayoutInflater not found.");
    }
    return LayoutInflater;
}


那么context是哪个呢?以Activity为例,继承的是父类ContextThemeWrapper的getSystemService方法。



Activity.java
@Override
    public Object getSystemService(String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }


        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }


可以看到在ContextThemeWrapper里有mInflater = LayoutInflater.from(mBase).cloneInContext(this);这行代码。最终传入的是一个mBase变量(其实是个Context),那么这个context是哪里来的呢?


ContextThemeWrapper.java
 private Context mBase;


   @Override protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        mBase = newBase;
    }


    @Override public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(mBase).cloneInContext(this);
            }
            return mInflater;
        }
        return mBase.getSystemService(name);
    }




2.来看ActivityThread这个类,main方法作为入口。 里面有行代码thread.attach(false);

(ActivityThread类无法直接搜索到,是因为是个隐藏类)

package android.app;
 * {@hide}
 */

public final class ActivityThread {

public static void main(String[] args) {

        SamplingProfilerIntegration.start();


        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);


        Process.setArgV0("");


        Looper.prepareMainLooper();//NOTE:这里还调用了我们熟悉的Looper的prepare及loop()方法,以后分析
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }


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


        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }


        Looper.loop();


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



 private void attach(boolean system) {
        sThreadLocal.set(this);
        mSystemThread = system;
        if (!system) {
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
                public void run() {
                    ensureJitEnabled();
                }
            });
            android.ddm.DdmHandleAppName.setAppName("");
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
        } else {
           .....
        }
        
        ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
            public void onConfigurationChanged(Configuration newConfig) {
                synchronized (mPackages) {
                    // We need to apply this change to the resources
                    // immediately, because upon returning the view
                    // hierarchy will be informed about it.
                    if (applyConfigurationToResourcesLocked(newConfig, null)) {
                        // This actually changed the resources!  Tell
                        // everyone about it.
                        if (mPendingConfiguration == null ||
                                mPendingConfiguration.isOtherSeqNewer(newConfig)) {
                            mPendingConfiguration = newConfig;
                            
                            queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig);
                        }
                    }
                }
            }
            public void onLowMemory() {
            }
            public void onTrimMemory(int level) {
            }
        });
    }




   通过 mgr.attachApplication(mAppThread);建立通信???TODO:此处待详细了解。
最终在activity.attach方法里传入一个ContextImpl对象。而activity.attach方法最终调用了父类ContextThemeWrapper的attachBaseContext方法,将ContextImpl对象赋值给mBase。


    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
           ...
        }


        try {
           ...

            if (activity != null) {
                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(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config);


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

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
         ...
        }
        return activity;

    }


那么ContextImpl里是如何处理getSystemService方法的呢?原来是初始化了一个static的hashMap,当ContextImpl类被加载时,static定义的一段静态代码块会被执行,然后各种service调用registerService方法,以名称作为key,ServiceFetcher作为value,保存在hashMap里;在外部调用getSystemService方法的时候,就以名称来取出对应的ServiceFetcher,然后返回fetcher.getService(this),fetcher.getService(this)第一次会调用createService创建一个service对象,缓存到列表中,下次再取时直接从缓存列表中拿。这样就保证了服务的单例。


ContextImpl.java

package android.app;


     private static final HashMap SYSTEM_SERVICE_MAP =
            new HashMap();


     private static void registerService(String serviceName, ServiceFetcher fetcher) {
        if (!(fetcher instanceof StaticServiceFetcher)) {
            fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
        }
        SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
    }




    @Override
    public Object getSystemService(String name) {
        ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
        return fetcher == null ? null : fetcher.getService(this);
    }

      static { 

       //NOTE:这里会注册各种service

        ...

       registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
        public Object createService(ContextImpl ctx) {
            return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
        }});
      }

    }


可以看到LAYOUT_INFLATER_SERVICE的createService是调用了PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());来实例化的。那PolicyManager.makeNewLayoutInflater(ctx.getOuterContext())里又做了什么呢?



3.PolicyManager通过反射实例化了一个com.android.internal.policy.impl.Policy类,调用它的sPolicy.makeNewLayoutInflater(context);方法。



PolicyManager.java

  private static final String POLICY_IMPL_CLASS_NAME =
        "com.android.internal.policy.impl.Policy";


    private static final IPolicy sPolicy;


    static {
        // Pull in the actual implementation of the policy at run-time
        try {
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            sPolicy = (IPolicy)policyClass.newInstance();
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
        } 
        ...
    }


    // Cannot instantiate this class
    private PolicyManager() {}


    public static LayoutInflater makeNewLayoutInflater(Context context) {
        return sPolicy.makeNewLayoutInflater(context);
    }



来看看com.android.internal.policy.impl.Policy类的makeNewLayoutInflater方法。创建并返回了一个PhoneLayoutInflater对象。


    Policy.java
    public LayoutInflater makeNewLayoutInflater(Context context) {
        return new PhoneLayoutInflater(context);
    }
    public Window makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }




也就是说PhoneLayoutInflater才是真正的LayoutInflater实现类。看看它做了什么。


PhoneLayoutInflater.java
  private static final String[] sClassPrefixList = {
        "android.widget.",
        "android.webkit."
    };

      /** Override onCreateView to instantiate names that correspond to the
        widgets known to the Widget factory. If we don't find a match,
        call through to our super class.
    */
    @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
        for (String prefix : sClassPrefixList) {
            try {
                View view = createView(name, prefix, attrs);
                if (view != null) {
                    return view;
                }
            } catch (ClassNotFoundException e) {
                // In this case we want to let the base class take a crack
                // at it.
            }
        }


        return super.onCreateView(name, attrs);
    }
    
主要就是重写了LayoutInflater的onCreateView方法,主要目的是在传递进来的View名字前面加上"android.widget."或者"android.webkit."前缀,以此构造对应的View对象。


具体是什么流程呢?以Activity的setContentView为例来看。


Activity.java
    public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
        initActionBar();
    }


对于Activity的getWindow()调用的是mWindow = PolicyManager.makeNewWindow(this);实例化是用Policy的makeNewWindow方法,返回一个PhoneWindow对象。最终会调用mLayoutInflater.inflate(layoutResID, mContentParent);方法。


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


   @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }






4.

一个Window的View层级图



--Activity
   --PhoneWindow
       --DecorView
           --DefaultLayout
              --ViewGroup
                 --Xml布局




来看看LayoutInflater的inflate方法


LayoutInflater.java
public View inflate(XmlPullParser parser, ViewGroup root) {
        //root不为空,则会从resource布局解析到view,并添加到root中
        return inflate(parser, root, root != null);
    }


 public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
        if (DEBUG) System.out.println("INFLATING from resource: " + resource);
        //获取xml资源解析器
        XmlResourceParser parser = getContext().getResources().getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }


    //参数1为xml解析器,参数2为父视图,参数3为是否要将解析到视图添到父视图中
    public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context)mConstructorArgs[0];
            mConstructorArgs[0] = mContext;
            //初始化result为父视图
            View result = root;


            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }
              
                final String name = parser.getName();
              
                 ...


                 //解析merge标签
                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException(" can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }
                    // 解析其父视图root下的所有子View
                    rInflate(parser, root, attrs, false);
                } else {
                    // 不是merge标签直接解析布局视图
                    View temp;
                    if (TAG_1995.equals(name)) {
                        temp = new BlinkLayout(mContext, attrs);
                    } else {
                        // 这里通过xml的tag来解析layout的根视图,name就是要解析的视图的类名,如RelativeLayout
                        // TODO:暂时未查看createViewFromTa方法的实现
                        temp = createViewFromTag(root, name, attrs);
                    }


                    ViewGroup.LayoutParams params = null;


                    if (root != null) {
                        // 生成布局参数
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // 如果不添加视图到父布局,将布局参数设给temp
                            temp.setLayoutParams(params);
                        }
                    }
            
                    // 解析temp视图下的所有子View
                    rInflate(parser, temp, attrs, true);
         
                    // 如果父视图不为null并且attachToRoot为true,将temp添加到父视图
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }


                    //如果父视图为null或者attachToRoot为false,返回结果为temp
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }


            } catch (XmlPullParserException e) {
              ...
            }


            return result;
        }
    }




上述inflate方法中,主要有下面几步: 
1. 解析xml中的根标签(第一个元素) 
2. 如果根标签是merge,那么调用rInflate进行解析,rInflate会将merge标签下的所有子View直接添加到根标签中; 
3. 如果标签是普通元素,那么调用createViewFromTag对该元素进行解析; 
4. 调用rInflate解析temp根元素下的所有子View,并且将这些子View添加到temp下; 
5. 返回解析到的根视图;


看一下XmlResourceParser parser = getContext().getResources().getLayout(resource);获取的XmlResourceParser是什么   

调用的是Resources类的getLayout方法,一步步追踪下去,可以发现最终是实例化了XmlBlock.Parser这个类。


Resources.java
    public XmlResourceParser getLayout(int id) throws NotFoundException {
        return loadXmlResourceParser(id, "layout");
    }


    XmlResourceParser loadXmlResourceParser(String file, int id,
            int assetCookie, String type) throws NotFoundException {
        ...
        return block.newParser();
        ...
               
    }


XmlBlock.java
     public XmlResourceParser newParser() {
        synchronized (this) {
            if (mNative != 0) {
                return new Parser(nativeCreateParseState(mNative), this);
            }
            return null;
        }
    }






这里理解下createViewFromTag方法。



LayoutInflater.java
    View createViewFromTag(View parent, String name, AttributeSet attrs) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }


      
        try {
            View view;
            //可以通过设置factory解析View,默认这些Factory都为空,可以忽略这段
            if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
            else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
            else view = null;


            ...


            if (view == null) {
                if (-1 == name.indexOf('.')) {
                    //系统自带的view的解析(如                     view = onCreateView(parent, name, attrs);
                } else {
                    //自定义View的解析(如                     view = createView(name, null, attrs);
                }
            }


            return view;


        } catch (InflateException e) {
           ...
        }
    }


可以看到代码里针对系统自带的view调用的是onCreateView方法,对自定义的view调用的是createView方法,onCreateView与createView的区别是什么呢?
其实之前已经看过PhoneLayoutInflater的onCreateView方法,对于系统自带的view,name不包含完整包名,只有类名。因此在onCreateView方法作出如下处理:如果以"android.widget."或"android.webkit."为前缀参数调用createView(name, prefix, attrs)创建出view,就返回该view,否则调用父类LayoutInflater的onCreateView方法,以"android.view."为前缀参数调用createView(name, prefix, attrs)创建出view。
而自定义的View调用createView(name, null, attrs)传的name参数是类名的完整路径。


5.

现在再来看看createView方法吧。


LayoutInflater.java
//根据完整类名,通过反射构造View对象
public final View createView(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        //缓存获取构造函数
        Constructor constructor = sConstructorMap.get(name);
        Class clazz = null;
        try {
            if (constructor == null) {    
                clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name).asSubclass(View.class);
                
                if (mFilter != null && clazz != null) {
                    boolean allowed = mFilter.onLoadClass(clazz);
                    if (!allowed) {
                        failNotAllowed(name, prefix, attrs);
                    }
                }
                constructor = clazz.getConstructor(mConstructorSignature);
                sConstructorMap.put(name, constructor);
            } else {
                // If we have a filter, apply it to cached constructor
                if (mFilter != null) {
                    // Have we seen this name before?
                    Boolean allowedState = mFilterMap.get(name);
                    if (allowedState == null) {
                        // New class -- remember whether it is allowed
                        clazz = mContext.getClassLoader().loadClass(
                                prefix != null ? (prefix + name) : name).asSubclass(View.class);
                        
                        boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                        mFilterMap.put(name, allowed);
                        if (!allowed) {
                            failNotAllowed(name, prefix, attrs);
                        }
                    } else if (allowedState.equals(Boolean.FALSE)) {
                        failNotAllowed(name, prefix, attrs);
                    }
                }
            }

            Object[] args = mConstructorArgs;
            args[1] = attrs;
            return constructor.newInstance(args);

            ...
    }


发现其实是根据完整的类名,通过反射创建对应的实体类,然后对应的View就被创建出来了。


6.

有了单个View的实例化,那View所包含的子view是如何被实例的呢?

现在来关注下LayoutInflater的rInflate方法

 void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        //获取深度, 在parser.next()方法里,mDepth是递增的(type== XmlPullParser.START_TAG)或者递减的
        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();
            
            if (TAG_REQUEST_FOCUS.equals(name)) {
                parseRequestFocus(parser, parent);
            } else if (TAG_INCLUDE.equals(name)) {
                if (parser.getDepth() == 0) {
                    throw new InflateException(" cannot be the root element");
                }
                parseInclude(parser, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException(" must be the root element");
            } else if (TAG_1995.equals(name)) {
                final View view = new BlinkLayout(mContext, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflate(parser, view, attrs, true);
                viewGroup.addView(view, params);                
            } else {//普通view的处理
                //先实例化自身,
                final View view = createViewFromTag(parent, name, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                //然后递归实例化子View
                rInflate(parser, view, attrs, true);
                //将view添加到父view
                viewGroup.addView(view, params);
            }
        }
        if (finishInflate) parent.onFinishInflate();
    }

   rInflate方法通过深度优先遍历(?这个名词很高大上)来构造视图树,每解析到一个view元素就递归调用rInflate,直到该路径的最后一个元素,然后回溯过来把每个view元素添加到它们的parent中,整个过程结束后,整颗视图树就构建完成,通过setContentView设置的内容就会在界面上显示。


你可能感兴趣的:(系列笔记1、单例模式-LayoutInflater)