View是所有控件的一个基类,无论是布局(Layout),还是控件(Widget)都是继承自View类。只不过layout是一个特殊的view,它里面创建一个view的数组可以包含其他的view而已。 
这一篇文章把所有的layout和widget都统称为view,那么android是如何创建一个view的呢?

一。在代码中直接new出来。 
比如说你要创建一个TextView的实例,那么你可以这样写:

  1. TextView text = new TextView(c);  //c为context对象,表明textview是在此对象中运行的。


二。把控件写在xml文件中然后通过LayoutInflater初始化一个view。
 
注意:下面的内容不是顺序的看的而是交替的看的。否则可能弄迷糊。 
可以通过

  1. //通过系统提供的实例获得一个LayoutInflater对象
  2. LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  3. //第一个参数为xml文件中view的id,第二个参数为此view的父组件,可以为null,android会自动寻找它是否拥有父组件
  4. View view = inflater.inflate(R.layout.resourceid, null);

这样也得到了一个view的实例,让我们一步一步来看,这个view是怎么new出来的。 
看类android.view.LayoutInflater

  1. public View inflate(int resource, ViewGroup root) {
  2. return inflate(resource, root, root != null);
  3. }
  4. public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
  5. /*可以看到通过resource id返回了一个XmlResourceParser,通过类名就可以猜测
  6. 这是一个xml的解析类。但点进去一看,发现它只是一个接口,它继承自 XmlPullParser用于pull方式解析xml的接口。和AttributeSet用于获取此view的所有属性。
  7. 那么需要能找到它的实现类。先看下面resource类。
  8. */
  9. XmlResourceParser parser = getContext().getResources().getLayout(resource);
  10. try {
  11. return inflate(parser, root, attachToRoot);
  12. } finally {
  13. parser.close();
  14. }
  15. }
  16. /**
  17. * 终于到了重点,获取一个这个View的实例
  18. */
  19. public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
  20. synchronized (mConstructorArgs) {
  21. /**
  22. * 获取一个实现此AttributeSet的实例。因为此XmlPullParser是继承自AttributeSet
  23. * 的,所以parser对象可以直接作为一个AttributeSet对象。也可以用组合的方式
  24. * 把parser传递给另外一个实现自AttributeSet的对象,来获取一个AttributeSet实例。
  25. **/
  26. final AttributeSet attrs = Xml.asAttributeSet(parser);
  27. mConstructorArgs[0] = mContext;      //构造函数的参数,第一个值是此view运行所在的对象context
  28. View result = root;
  29. try {
  30. // parser同时也继承了xmlPullParser,所以可以用pull解析来获取此view的根节点
  31. int type;
  32. while ((type = parser.next()) != XmlPullParser.START_TAG &&
  33. type != XmlPullParser.END_DOCUMENT) {
  34. // Empty
  35. }
  36. if (type != XmlPullParser.START_TAG) {
  37. throw new InflateException(parser.getPositionDescription()
  38. + ": No start tag found!");
  39. }
  40. //获得根节点标签的名字
  41. final String name = parser.getName();
  42. //如果它的根节点是一个merge对象,则必须手动设置此view的父节点,否则抛出异常
  43. //因为由merge创建的xml文件,常常被其他layout所包含
  44. if (TAG_MERGE.equals(name)) {
  45. if (root == null || !attachToRoot) {
  46. throw new InflateException("<merge /> can be used only with a valid "
  47. + "ViewGroup root and attachToRoot=true");
  48. }
  49. rInflate(parser, root, attrs);
  50. } else {
  51. // 此inflate的xml文件中的root view。即我们通过inflate返回得到的view
  52. View temp = createViewFromTag(name, attrs);
  53. ViewGroup.LayoutParams params = null;
  54. if (root != null) {
  55. // Create layout params that match root, if supplied
  56. params = root.generateLayoutParams(attrs);
  57. if (!attachToRoot) {
  58. // Set the layout params for temp if we are not
  59. // attaching. (If we are, we use addView, below)
  60. temp.setLayoutParams(params);
  61. }
  62. }
  63. // 加载temp下所有的子view
  64. rInflate(parser, temp, attrs);
  65. //如果给出了root,则把此view添加到root中去
  66. if (root != null && attachToRoot) {
  67. root.addView(temp, params);
  68. }
  69. // Decide whether to return the root that was passed in or the
  70. // top view found in xml.
  71. if (root == null || !attachToRoot) {
  72. result = temp;
  73. }
  74. }
  75. } catch (XmlPullParserException e) {
  76. InflateException ex = new InflateException(e.getMessage());
  77. ex.initCause(e);
  78. throw ex;
  79. } catch (IOException e) {
  80. InflateException ex = new InflateException(
  81. parser.getPositionDescription()
  82. + ": " + e.getMessage());
  83. ex.initCause(e);
  84. throw ex;
  85. }
  86. return result;
  87. }
  88. }
  89. /**
  90. *
  91. * 有上至下递归的初始化所有子view和子view的子view。在此方法被调用完成后
  92. * 会调用此view的parent view的onFinishInflate方法。表明其子view全部加载完毕
  93. */
  94. private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
  95. throws XmlPullParserException, IOException {
  96. final int depth = parser.getDepth();
  97. int type;
  98. while (((type = parser.next()) != XmlPullParser.END_TAG ||
  99. parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
  100. if (type != XmlPullParser.START_TAG) {
  101. continue;
  102. }
  103. final String name = parser.getName();
  104. if (TAG_REQUEST_FOCUS.equals(name)) {
  105. parseRequestFocus(parser, parent);
  106. } else if (TAG_INCLUDE.equals(name)) {
  107. if (parser.getDepth() == 0) {
  108. throw new InflateException("<include /> cannot be the root element");
  109. }
  110. parseInclude(parser, parent, attrs);
  111. } else if (TAG_MERGE.equals(name)) {
  112. throw new InflateException("<merge /> must be the root element");
  113. } else {         //看这里,创建view的方法。而且这里已经重新获得了它的
  114. final View view = createViewFromTag(name, attrs);
  115. final ViewGroup viewGroup = (ViewGroup) parent;
  116. final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
  117. rInflate(parser, view, attrs);
  118. viewGroup.addView(view, params);
  119. }
  120. }
  121. parent.onFinishInflate();
  122. }
  123. View createViewFromTag(String name, AttributeSet attrs) {
  124. if (name.equals("view")) {
  125. name = attrs.getAttributeValue(null, "class");
  126. }
  127. if (DEBUG) System.out.println("******** Creating view: " + name);
  128. try {
  129. View view = (mFactory == null) ? null : mFactory.onCreateView(name,
  130. mContext, attrs);
  131. if (view == null) {
  132. if (-1 == name.indexOf('.')) {       //这里只是为了判断xml文件中tag的属性是否加了包名
  133. view = onCreateView(name, attrs);
  134. } else {
  135. view = createView(name, null, attrs);
  136. }
  137. }
  138. if (DEBUG) System.out.println("Created view is: " + view);
  139. return view;
  140. } catch (InflateException e) {
  141. throw e;
  142. } catch (ClassNotFoundException e) {
  143. InflateException ie = new InflateException(attrs.getPositionDescription()
  144. + ": Error inflating class " + name);
  145. ie.initCause(e);
  146. throw ie;
  147. } catch (Exception e) {
  148. InflateException ie = new InflateException(attrs.getPositionDescription()
  149. + ": Error inflating class " + name);
  150. ie.initCause(e);
  151. throw ie;
  152. }
  153. }
  154. /**
  155. * 真正创建一个view的方法,
  156. * 此方法是用反射获取构造器来实例对象而不是直接new出来这是为了处于性能优化考虑,
  157. * 同一个类名的不同对象,可以直接得到缓存的构造器直接获取一个构造器对象实例。而不需要
  158. * 重复进行new操作。
  159. *
  160. * @param name 此View的全名
  161. * @param prefix 前缀,值为 "android.view."其实就是是否包含包名
  162. * @param attrs 此view的属性值,传递给此view的构造函数
  163. */
  164. public final View createView(String name, String prefix, AttributeSet attrs)
  165. throws ClassNotFoundException, InflateException {
  166. Constructor constructor = sConstructorMap.get(name); //缓存中是否已经有了一个构造函数
  167. Class clazz = null;
  168. try {
  169. if (constructor == null) {
  170. //通过类名获得一个class对象
  171. clazz = mContext.getClassLoader().loadClass(
  172. prefix != null ? (prefix + name) : name);
  173. if (mFilter != null && clazz != null) {
  174. boolean allowed = mFilter.onLoadClass(clazz);
  175. if (!allowed) {
  176. failNotAllowed(name, prefix, attrs);
  177. }
  178. }
  179. //通过参数类型获得一个构造器,参数列表为context,attrs
  180. constructor = clazz.getConstructor(mConstructorSignature);
  181. sConstructorMap.put(name, constructor);      //把此构造器缓存起来
  182. } else {
  183. // If we have a filter, apply it to cached constructor
  184. if (mFilter != null) {
  185. // Have we seen this name before?
  186. Boolean allowedState = mFilterMap.get(name);
  187. if (allowedState == null) {
  188. // New class -- remember whether it is allowed
  189. clazz = mContext.getClassLoader().loadClass(
  190. prefix != null ? (prefix + name) : name);
  191. boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
  192. mFilterMap.put(name, allowed);
  193. if (!allowed) {
  194. failNotAllowed(name, prefix, attrs);
  195. }
  196. } else if (allowedState.equals(Boolean.FALSE)) {
  197. failNotAllowed(name, prefix, attrs);
  198. }
  199. }
  200. }
  201. Object[] args = mConstructorArgs;
  202. args[1] = attrs;     //args[0]已经在前面初始好了。这里只要初始化args[1]
  203. return (View) constructor.newInstance(args);     //通过反射new出一个对象。。大功告成
  204. } catch (NoSuchMethodException e) {
  205. InflateException ie = new InflateException(attrs.getPositionDescription()
  206. + ": Error inflating class "
  207. + (prefix != null ? (prefix + name) : name));
  208. ie.initCause(e);
  209. throw ie;
  210. } catch (ClassNotFoundException e) {
  211. // If loadClass fails, we should propagate the exception.
  212. throw e;
  213. } catch (Exception e) {
  214. InflateException ie = new InflateException(attrs.getPositionDescription()
  215. + ": Error inflating class "
  216. + (clazz == null ? "<unknown>" : clazz.getName()));
  217. ie.initCause(e);
  218. throw ie;
  219. }
  220. }

在类android.content.res.Resources类中获取XmlResourceParser对象;

  1. public XmlResourceParser getLayout(int id) throws NotFoundException {
  2. return loadXmlResourceParser(id, "layout");
  3. }
  4. ackage*/ XmlResourceParser loadXmlResourceParser(int id, String type)
  5. throws NotFoundException {
  6. synchronized (mTmpValue) {
  7. /*TypedValue对象保存了一些有关resource 的数据值,比如说,对于一个view来说,在xml
  8. 文件中可以定义许多属性,TypedValue保存了其中一个属性的相关信息,包括此属性的值的类型
  9. type,是boolean还是color还是reference还是String,这些在attr.xml文件下都有定义。
  10. 它的值的字符串名称;一个属性有多个值时,它从xml文件中获取的值它的顺序data;如果此属性的值
  11. 的类型是一个reference则保存它的resource id的等等。
  12. */
  13. TypedValue value = mTmpValue;
  14. getValue(id, value, true);
  15. if (value.type == TypedValue.TYPE_STRING) {
  16. return loadXmlResourceParser(value.string.toString(), id,
  17. value.assetCookie, type);
  18. }
  19. throw new NotFoundException(
  20. "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
  21. + Integer.toHexString(value.type) + " is not valid");
  22. }
  23. }
  24. /**
  25. *  getValue方法,id表示要查找的控件的 id,outValue是一个对象,用于保存一些属性相关信息
  26. *  resolveRefs为true表明,当通过属性id找到xml文件中的标签时,比如是一个<Button  android:id="@+id/button"/>
  27. * 它的值是一个引用reference,则继续解析获得这个id的值。这里看AssetManager类的实现*/
  28. public void getValue(int id, TypedValue outValue, boolean resolveRefs)
  29. throws NotFoundException {
  30. boolean found = mAssets.getResourceValue(id, outValue, resolveRefs);
  31. if (found) {
  32. return;
  33. }
  34. throw new NotFoundException("Resource ID #0x"
  35. + Integer.toHexString(id));
  36. }
  37. /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
  38. int assetCookie, String type) throws NotFoundException {
  39. if (id != 0) {
  40. try {
  41. //取缓存
  42. synchronized (mCachedXmlBlockIds) {
  43. // First see if this block is in our cache.
  44. final int num = mCachedXmlBlockIds.length;
  45. for (int i=0; i<num; i++) {
  46. if (mCachedXmlBlockIds[i] == id) {
  47. //System.out.println("**** REUSING XML BLOCK!  id="
  48. //                   + id + ", index=" + i);
  49. return mCachedXmlBlocks[i].newParser();
  50. }
  51. }
  52. //第一次加载时,会打开这个文件获取一个xml数据块对象。
  53. // 这里先看AssetManager类的实现
  54. XmlBlock block = mAssets.openXmlBlockAsset(
  55. assetCookie, file);
  56. //下面会把此xmlBlock对象缓存起来,保存id和block,
  57. //以后如果是同样的id,直接在缓存中取XmlBlock。
  58. //这样就不用再在本地方法中打开文件创建解析树了。
  59. if (block != null) {
  60. int pos = mLastCachedXmlBlockIndex+1;
  61. if (pos >= num) pos = 0;
  62. mLastCachedXmlBlockIndex = pos;
  63. XmlBlock oldBlock = mCachedXmlBlocks[pos];
  64. if (oldBlock != null) {
  65. oldBlock.close();
  66. }
  67. mCachedXmlBlockIds[pos] = id;
  68. mCachedXmlBlocks[pos] = block;
  69. //返回的内部类继承了XmlResourceParser,在APi中此类是隐藏的
  70. return block.newParser();
  71. }
  72. }
  73. } catch (Exception e) {
  74. NotFoundException rnf = new NotFoundException(
  75. "File " + file + " from xml type " + type + " resource ID #0x"
  76. + Integer.toHexString(id));
  77. rnf.initCause(e);
  78. throw rnf;
  79. }
  80. }
  81. throw new NotFoundException(
  82. "File " + file + " from xml type " + type + " resource ID #0x"
  83. + Integer.toHexString(id));
  84. }

android.content.res.AssetManager类

  1. /*package*/ final boolean getResourceValue(int ident,
  2. TypedValue outValue,
  3. boolean resolveRefs)
  4. {
  5. int block = loadResourceValue(ident, outValue, resolveRefs);
  6. if (block >= 0) {
  7. if (outValue.type != TypedValue.TYPE_STRING) {
  8. return true;
  9. }
  10. //mStringBlocks通过本地方法保存所有布局文件的文件名
  11. outValue.string = mStringBlocks[block].get(outValue.data);
  12. return true;
  13. }
  14. return false;
  15. }
  16. //这是一个本地方法,是在本地方法中获取这个控件信息,返回通过此控件的id找到的文件名
  17. //的位置,由于个人对c++不是很了解,只初略的解释本地方法的一些功能。
  18. //对于的JNI文件位于:\frameworks\base\core\jni\android_util_AssetManager.cpp
  19. private native final int loadResourceValue(int ident, TypedValue outValue,
  20. boolean resolve);
  21. /**
  22. * 通过文件名,在本地方法中找到这个xml文件,并且在本地方法中生成一个xml解析对象。
  23. * 返回一个id,这个id对应java中的xmlBlock对象。这样xml文件就被load进了内存。
  24. * 也就是android所说的预编译,以后再访问只要直接去取数据即可
  25. */
  26. /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
  27. throws IOException {
  28. synchronized (this) {
  29. if (!mOpen) {
  30. throw new RuntimeException("Assetmanager has been closed");
  31. }
  32. int xmlBlock = openXmlAssetNative(cookie, fileName);
  33. if (xmlBlock != 0) {
  34. /*
  35. * 在XmlBlock对象中,终于到找了实现XmlResourceParser接口的类
  36. * Parser,它是XmlBlock的一个内部类。这里面可以获取所有xml文件中的内容。
  37. * 不管是属性还是Tag标签。这里xmlBlock是用来与本地类中的解析树对象对应的。
  38. * 所有的解析方法,其实都是调用的本地xml解析树中的方法。所以此类中有大量的
  39. * 本地方法。
  40. */
  41. XmlBlock res = new XmlBlock(this, xmlBlock);
  42. incRefsLocked(res.hashCode());
  43. return res;
  44. }
  45. }
  46. throw new FileNotFoundException("Asset XML file: " + fileName);
  47. }

三 。通过view.findViewById(resourceid)获得一个view的实例 
android.View.View类中

  1. //调用了通过id检索view的方法
  2. public final View findViewById(int id) {
  3. if (id < 0) {
  4. return null;
  5. }
  6. return findViewTraversal(id);
  7. }
  8. //不是吧,这不是坑爹吗?猜想肯定是被viewgroup重写了
  9. protected View findViewTraversal(int id) {
  10. if (id == mID) {
  11. return this;
  12. }
  13. return null;
  14. }

android.View.ViewGroup类中

  1. //哈哈,果然重写了此方法。其实就是在viewgroup包含的
  2. //子view数组中进行遍历。那么view是什么时候被加入进
  3. //viewgroup中的呢?如果是在代码中写,肯定是直接使用
  4. //addView方法把view加入viewGroup。如果写在xml布局文件
  5. //中,其实是在第二种方法中被加入view的。inflate加载父view
  6. //时会同时把其所有的子view加载完,同时addView到父view中
  7. protected View findViewTraversal(int id) {
  8. if (id == mID) {
  9. return this;
  10. }
  11. final View[] where = mChildren;
  12. final int len = mChildrenCount;
  13. for (int i = 0; i < len; i++) {
  14. View v = where[i];
  15. if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
  16. v = v.findViewById(id);
  17. if (v != null) {
  18. return v;
  19. }
  20. }
  21. }
  22. return null;
  23. }

四。通过activity的setContentView方法和findViewById获取一个view的实例。 
它是通过 
getWindow().setContentView(layoutResID);设置window对象的view 
再来看看window对象是在哪里获得到的,在类Activity中找到 
mWindow = PolicyManager.makeNewWindow(this); 
它是由PolicyManager生成的。 
找到com.android.internal.policy.PolicyManager,找到方法

  1. //window是由sPolicy对象创建的
  2. public static Window makeNewWindow(Context context) {
  3. return sPolicy.makeNewWindow(context);
  4. }
  5. //sPolicy对象是通过反射,获取的一个实例
  6. //此类的实现在com.android.internal.policy.impl.Policy中
  7. private static final String POLICY_IMPL_CLASS_NAME =
  8. "com.android.internal.policy.impl.Policy";
  9. private static final IPolicy sPolicy;
  10. static {
  11. // Pull in the actual implementation of the policy at run-time
  12. try {
  13. Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
  14. sPolicy = (IPolicy)policyClass.newInstance();
  15. } catch (ClassNotFoundException ex) {
  16. throw new RuntimeException(
  17. POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
  18. } catch (InstantiationException ex) {
  19. throw new RuntimeException(
  20. POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
  21. } catch (IllegalAccessException ex) {
  22. throw new RuntimeException(
  23. POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
  24. }
  25. }

找到com.android.internal.policy.impl.Policy类

  1. public PhoneWindow makeNewWindow(Context context) {
  2. return new PhoneWindow(context);
  3. }

它其实是一个phoneWindow对象,继承自window对象 
找到com.android.internal.policy.impl.phoneWindow 看它内部是如何把resourceid加载成一个view的

    1. private ViewGroup mContentParent;
    2. //这是window的顶层视图,它包含一些窗口的装饰,比图title bar,状态栏等等
    3. private DecorView mDecor;
    4. //这里的layoutResID也是由mLayoutInflater进行加载的,加载的方式与第二种方法一样。
    5. //只不过这里把的到的view变成了mContentParent的子view
    6. @Override
    7. public void setContentView(int layoutResID) {
    8. if (mContentParent == null) {
    9. installDecor();
    10. } else {
    11. mContentParent.removeAllViews();
    12. }
    13. mLayoutInflater.inflate(layoutResID, mContentParent);
    14. final Callback cb = getCallback();
    15. if (cb != null) {        //这是回调方法,表明mContentParent的子view已经发生改变
    16. cb.onContentChanged();
    17. }
    18. }
    19. //再来看看mContentParent究竟是何物,它肯定是一个viewGroup
    20. private void installDecor() {
    21. if (mDecor == null) {
    22. mDecor = generateDecor();
    23. mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    24. mDecor.setIsRootNamespace(true);
    25. }
    26. if (mContentParent == null) {
    27. mContentParent = generateLayout(mDecor);
    28. mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
    29. if (mTitleView != null) {        //这里设置的是是否隐藏titleContainer,即头部titlebar
    30. if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
    31. View titleContainer = findViewById(com.android.internal.R.id.title_container);
    32. if (titleContainer != null) {
    33. titleContainer.setVisibility(View.GONE);
    34. } else {
    35. mTitleView.setVisibility(View.GONE);
    36. }
    37. if (mContentParent instanceof FrameLayout) {
    38. ((FrameLayout)mContentParent).setForeground(null);
    39. }
    40. } else {
    41. mTitleView.setText(mTitle);
    42. }
    43. }
    44. }
    45. }
    46. //当顶层view为null是,new了一个DecorView
    47. protected DecorView generateDecor() {
    48. return new DecorView(getContext(), -1);
    49. }
    50. //这里生成了mContentParent。
    51. protected ViewGroup generateLayout(DecorView decor) {
    52. mDecor.startChanging();
    53. //根据window的不同参数选择layoutResource
    54. View in = mLayoutInflater.inflate(layoutResource, null);
    55. decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    56. ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    57. if (contentParent == null) {
    58. throw new RuntimeException("Window couldn't find content container view");
    59. }
    60. return contentParent;
    61. }
    62. //顶层view是一个framelayout
    63. private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    64. public DecorView(Context context, int featureId) {
    65. super(context);
    66. mFeatureId = featureId;
    67. }
    68. }
    69. //下面说明findVIewById
    70. //首先是获取顶层view,即继承自FrameLayout的viewgorup
    71. @Override
    72. public final View getDecorView() {
    73. if (mDecor == null) {
    74. installDecor();
    75. }
    76. return mDecor;
    77. }
    78. //然后mDecor.findViewById根据id获取它的子view
    79. //这里就是通过第三种方法获取它的子view

android2.3 View视图框架源码分析之一:android是如何创建一个view的?的更多相关文章

  1. Weex框架源码分析(Android)(一)

    一.weexSDK初始化流程 WXSDKEngine.initialize(Application application,InitConfig config); //WXSDKEngine的init ...

  2. YII框架源码分析(百度PHP大牛创作-原版-无广告无水印)

           YII 框架源码分析    百度联盟事业部——黄银锋 目 录 1. 引言 3 1.1.Yii 简介 3 1.2.本文内容与结构 3 2.组件化与模块化 4 2.1.框架加载和运行流程 4 ...

  3. iOS常用框架源码分析

    SDWebImage NSCache 类似可变字典,线程安全,使用可变字典自定义实现缓存时需要考虑加锁和释放锁 在内存不足时NSCache会自动释放存储的对象,不需要手动干预 NSCache的key不 ...

  4. 介绍开源的.net通信框架NetworkComms框架 源码分析

    原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架  作者是英国人  以前是收费的 售价249英镑 我曾经花了 ...

  5. Android Small插件化框架源码分析

    Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github ...

  6. Spark RPC框架源码分析(一)简述

    Spark RPC系列: Spark RPC框架源码分析(一)运行时序 Spark RPC框架源码分析(二)运行时序 Spark RPC框架源码分析(三)运行时序 一. Spark rpc框架概述 S ...

  7. Spark RPC框架源码分析(二)RPC运行时序

    前情提要: Spark RPC框架源码分析(一)简述 一. Spark RPC概述 上一篇我们已经说明了Spark RPC框架的一个简单例子,Spark RPC相关的两个编程模型,Actor模型和Re ...

  8. Spark RPC框架源码分析(三)Spark心跳机制分析

    一.Spark心跳概述 前面两节中介绍了Spark RPC的基本知识,以及深入剖析了Spark RPC中一些源码的实现流程. 具体可以看这里: Spark RPC框架源码分析(二)运行时序 Spark ...

  9. nodejs的Express框架源码分析、工作流程分析

    nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...

随机推荐

  1. redis入门指南学习笔记

    redis的常见命令 set key hello get key incr num get num set foo lorem incr foo hset car price 500 hset car ...

  2. static类

    静态类,用于类内部. 静态类,不需要创建父类对象,即可使用. 非静态类,需要先创建父类对象,才可使用. class A{ static class  B1{} class B2{} } = new A ...

  3. “The SQL Server license agreenment cannot be located for the selected edition.”MSSQL安装问题

    今天老邹又来吐槽了.今天不和IE7较劲了.说点别的吧. 我呢什么软件都喜欢装最新版的,这部刚出来windows 8.1就赶紧装上了,随后就用上了vs2013.前天看到新闻说微软已经发布了sql ser ...

  4. ORA-19502: write error on file "/u01/app/oracle/oradata/standby/system01.dbf", blockno 40321 (blocksize=8192)【error收集】

    在RMAN备份中,出现了一个问题,就是出现坏块了. ORA-: write error on file (blocksize=) ORA-: File I/O error Linux Error: : ...

  5. xcode 删除 Provisioning Profile

    provisioning profile path: ~/Library/MobileDevice/Provisioning Profiles 打开并日期排序,删除老的 provisioning pr ...

  6. [转载] 与WIN不同,linux替换文件夹会删除原文件夹下的全部内容!

    今天差点把源码给覆盖掉了><...555... 虚惊一场!!看了一篇博客分析这种情况.我的环境是CentOS5.5,不会出现文件夹直接被覆盖的情况,但是在Linux下不要用Win下的一些直 ...

  7. JAVA程序优化之字符串优化处理

    字符串是软件开发中最为重要的对象之一.通常,字符串对象或其等价对象(如char数组),在内存中总是占据了最大的空间块.因此如何高效地处理字符串,必将是提高系统整体性能的关键所在. 1.String对象 ...

  8. Python入门100例题

    原文链接:http://www.cnblogs.com/CheeseZH/archive/2012/11/05/2755107.html 无论学习哪门计算机语言,只要把100例中绝大部分题目都做一遍, ...

  9. Swift —构造函数与存储属性初始化-备

    构造函数的主要作用是初始化实例,其中包括:初始化存储属性和其它的初始化.在Rectangle类或结构体中,如果在构造函数中初始化存储属性width和height后,那么在定义他们时就不需要初始化了. ...

  10. C# 文件创建时间,修改时间

    System.IO.FileInfo fi = new System.IO.FileInfo(@"D:\site\EKECMS\skin\Grey\default#.html"); ...