Life is Experience

you always fell regret for the things you should have done, but didn't.

0%

背景

通常我们hook布局解析有几种方式。

  1. 通过asm插桩的方式。代码中一般会写LayoutInflater.from(Context).inflate(@LayoutRes int resource, @Nullable ViewGroup root)。通过asm hook该处,调用我们预定义的代码,然后就可以拿到view做一些想做的事情;
  2. 利用LayoutInflater中两个成员变量mFactory和mFactory2实例。其实AppCompatActivity就是这么做的。AppCompatActivity是为了实现TextView替换为AppCompatTextView实现一些TextView没有的效果,那么我们也可以利用LayoutInflater的factory实现我们想要的一些功能。

本文我们着重分析下LayoutInflater的两个Factory,以及AppCompatActivity的具体实现逻辑。

LayoutInflater inflate方法实现逻辑

在说Factory之前有一个细节我们需要注意下。
LayoutInflater.from(context) 这里获取是根据context获取的,不同的context是不同的实例,因此在setFactory的时候,需要针对一个基类的activity来处理,不可以设置全局。

LayoutInflater inflate核心代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
advanceToRootNode(parser);
final String name = parser.getName();

// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);//@1

ViewGroup.LayoutParams params = null;

if (root != null) {
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs); //@2
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}

// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true); //@3

// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params); //@4
}

// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}

return result;
}

inflate过程大概经历4件事情,
@1 createViewFromTag 创建view
@2 设置LayoutParams
@3 rInflateChildren解析子view
@4 addView添加到布局中

createViewFromTag中创建view大致逻辑如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
View view = tryCreateView(parent, name, context, attrs);

if (view == null) {
if (-1 == name.indexOf('.')) {
view = onCreateView(context, parent, name, attrs);
} else {
view = createView(context, name, null, attrs);
}
}

return view;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public final View tryCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context,
@NonNull AttributeSet attrs) {

View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}

return view;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public final View createView(@NonNull Context viewContext, @NonNull String name,
@Nullable String prefix, @Nullable AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name);
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;

if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
mContext.getClassLoader()).asSubclass(View.class); // @1

constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
} else {

}

Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = viewContext;
Object[] args = mConstructorArgs;
args[1] = attrs;

try {
final View view = constructor.newInstance(args); // @2
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
return view;
} finally {
mConstructorArgs[0] = lastContext;
}
}

createViewFromTag逻辑如下:

  1. 尝试tryCreateView,依次尝试factory2和factory创建view;
  2. 如果tryCreateView为空,则调用onCreateView或者createView,两者区别为onCreateView是没有.,即系统View如TextView,onCreateView最终会对name之前拼接android.view.,最终还会调到createView中;
  3. createView逻辑为调用class.forName获取类,然后获取类的构造方法,创建view实例。

AppCompatActivity和LayoutInflater的两个Factory

两个Factory的区别

mFactory2和mFactory的区别是调用方法的区别,mFactory2多了一个parent参数,即为当前创建的view的父view。

installViewFactory设置Factory

AppCompatActivity中onCreate时会执行installViewFactory方法,内部先判断factory是否为空,如果为空则调用LayoutInflaterCompat.setFactory2方法,否则检查factory2是否是AppCompatDelegateImpl,如果不是则打印日志。这里没有什么约束条件。

1
2
3
4
5
6
7
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);
} else {
if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LayoutInflaterCompat.java
public static void setFactory2(
@NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
inflater.setFactory2(factory);

if (Build.VERSION.SDK_INT < 21) {
final LayoutInflater.Factory f = inflater.getFactory();
if (f instanceof LayoutInflater.Factory2) {
// The merged factory is now set to getFactory(), but not getFactory2() (pre-v21).
// We will now try and force set the merged factory to mFactory2
forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
} else {
// Else, we will force set the original wrapped Factory2
forceSetFactory2(inflater, factory);
}
}
}

LayoutInflaterCompat提供了对SDK小于21的支持,因此一般通过该方法进行设置factory,我们再看下inflater.setFactory2的具体逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void setFactory2(Factory2 factory) {
if (mFactorySet) {
throw new IllegalStateException("A factory has already been set on this LayoutInflater");
}
if (factory == null) {
throw new NullPointerException("Given factory can not be null");
}
mFactorySet = true;
if (mFactory == null) {
mFactory = mFactory2 = factory;
} else {
mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
}
}

这里会有强制限制的逻辑,也就是setFactory只能设置一次,那么继承AppCompatActivity的activity中只能通过反射的方式去设置factory了。
如果mFactory为空,则直接将两者都设置为需要替换的factory,否则会创建FactoryMerger,包裹之前的mFactory和mFactory2。使用优先级是先调用需要替换的factory,如果创建view为空,则调用origin的factory。具体逻辑参照下面代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private static class FactoryMerger implements Factory2 {
private final Factory mF1, mF2;
private final Factory2 mF12, mF22;

FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
mF1 = f1;
mF2 = f2;
mF12 = f12;
mF22 = f22;
}

@Nullable
public View onCreateView(@NonNull String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
View v = mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF2.onCreateView(name, context, attrs);
}

@Nullable
public View onCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull AttributeSet attrs) {
View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
: mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
: mF2.onCreateView(name, context, attrs);
}
}

AppCompatActivity替换的Factory执行逻辑

设置完factory后,我们看下factory的具体做了什么事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
AppCompatDelegateImpl.java
@Override
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
return createView(parent, name, context, attrs);
}

/**
* From {@link LayoutInflater.Factory2}.
*/
@SuppressWarnings("NullableProblems")
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return onCreateView(null, name, context, attrs);
}

@Override
public View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
if (mAppCompatViewInflater == null) {
mAppCompatViewInflater = new AppCompatViewInflater();
}

return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
true, /* Read read app:theme as a fallback at all times for legacy reasons */
VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
);
}

AppCompatDelegateImpl最终交给了AppCompatViewInflater来处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
AppCompatViewInflater.java
final View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
final Context originalContext = context;

// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
// by using the parent's context
if (inheritContext && parent != null) {
context = parent.getContext();
}
if (readAndroidTheme || readAppTheme) {
// We then apply the theme on the context, if specified
context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
}
if (wrapContext) {
context = TintContextWrapper.wrap(context);
}

View view = null;

// We need to 'inject' our tint aware Views in place of the standard framework versions
switch (name) {
case "TextView":
view = createTextView(context, attrs);
verifyNotNull(view, name);
break;
case "ImageView":
view = createImageView(context, attrs);
verifyNotNull(view, name);
break;
case "Button":
view = createButton(context, attrs);
verifyNotNull(view, name);
break;
case "EditText":
view = createEditText(context, attrs);
verifyNotNull(view, name);
break;
case "Spinner":
view = createSpinner(context, attrs);
verifyNotNull(view, name);
break;
case "ImageButton":
view = createImageButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckBox":
view = createCheckBox(context, attrs);
verifyNotNull(view, name);
break;
case "RadioButton":
view = createRadioButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckedTextView":
view = createCheckedTextView(context, attrs);
verifyNotNull(view, name);
break;
case "AutoCompleteTextView":
view = createAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "MultiAutoCompleteTextView":
view = createMultiAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "RatingBar":
view = createRatingBar(context, attrs);
verifyNotNull(view, name);
break;
case "SeekBar":
view = createSeekBar(context, attrs);
verifyNotNull(view, name);
break;
case "ToggleButton":
view = createToggleButton(context, attrs);
verifyNotNull(view, name);
break;
default:
// The fallback that allows extending class to take over view inflation
// for other tags. Note that we don't check that the result is not-null.
// That allows the custom inflater path to fall back on the default one
// later in this method.
view = createView(context, name, attrs);
}

if (view == null && originalContext != context) {
// If the original context does not equal our themed context, then we need to manually
// inflate it using the name so that android:theme takes effect.
view = createViewFromTag(context, name, attrs);
}

if (view != null) {
// If we have created a view, check its android:onClick
checkOnClickListener(view, attrs);
}

return view;
}

上面代码是AppCompatViewInflater的核心逻辑了,即根据name来创建对应的CompatView。

demo

了解了LayoutInflater的两个Factory以及AppCompatActivity的实现逻辑,我们可以简单写个demo。如果activity继承了AppCompatActivity,因为AppCompatActivity已经设置过了,我们只能通过反射的方式来实现了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class LayoutInflaterActivity extends AppCompatActivity {
private static final String TAG = "LayoutInflaterActivity";

private static void setLayoutInflaterFac(Context context) {
final LayoutInflater.Factory f1 = LayoutInflater.from(context).getFactory();
final LayoutInflater.Factory2 f2 = LayoutInflater.from(context).getFactory2();

LayoutInflater.Factory2 fNew = new LayoutInflater.Factory2() {
@Nullable
@Override
public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
View view = f2.onCreateView(parent, name, context, attrs);
handleView(view);
return view;
}

@Nullable
@Override
public View onCreateView(@NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
View view = f1.onCreateView(name, context, attrs);
handleView(view);
return view;
}

private void handleView(View view) {
Log.i(TAG, "handleView: " + view);
}
};

try {
Field sLayoutInflaterFactory2Field = LayoutInflater.class.getDeclaredField("mFactory2");
sLayoutInflaterFactory2Field.setAccessible(true);

sLayoutInflaterFactory2Field.set(LayoutInflater.from(context), fNew);
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setLayoutInflaterFac(this);

setContentView(R.layout.activity_layout_inflater);

inflateViewStub();
}

private void inflateViewStub() {
ViewStub viewStub = findViewById(R.id.layout_inflater_view_stub);
viewStub.inflate();
}
}

参考文章

  1. http://dandanlove.com/2017/11/15/layoutinflater-factory/
  2. https://www.jianshu.com/p/9a0cc0a5649c

【原文】

天有不测风云,人有旦夕祸福。蜈蚣百足,行不及蛇;雄鸡两翼,飞不过鸦。马有千里之程,无骑不能自往;人有冲天之志,非运不能自通。

盖闻:人生在世,富贵不能淫,贫贱不能移。文章盖世,孔子厄于陈邦;武略超群,太公钓于渭水。颜渊命短,殊非凶恶之徒;盗跖年长,岂是善良之辈。尧帝明圣,却生不肖之儿;瞽叟愚顽,反生大孝之子。张良原是布衣,萧何称谓县吏。晏子身无五尺,封作齐国宰相;孔明卧居草庐,能作蜀汉军师。楚霸虽雄,败于乌江自刎;汉王虽弱,竟有万里江山。李广有射虎之威,到老无封;冯唐有乘龙之才,一生不遇。韩信未遇之时,无一日三餐,及至遇行,腰悬三齐玉印,一旦时衰,死于阴人之手。

有先贫而后富,有老壮而少衰。满腹文章,白发竟然不中;才疏学浅,少年及第登科。深院宫娥,运退反为妓妾;风流妓女,时来配作夫人。

青春美女,却招愚蠢之夫;俊秀郎君,反配粗丑之妇。蛟龙未遇,潜水于鱼鳖之间;君子失时,拱手于小人之下。衣服虽破,常存仪礼之容;面带忧愁,每抱怀安之量。时遭不遇,只宜安贫守份;心若不欺,必然扬眉吐气。初贫君子,天然骨骼生成;乍富小人,不脱贫寒肌体。

天不得时,日月无光;地不得时,草木不生;水不得时,风浪不平;人不得时,利运不通。注福注禄,命里已安排定,富贵谁不欲?人若不依根基八字,岂能为卿为相?

余者,居洛阳之时,朝投僧寺,夜宿破窑。布衣不能遮其体,饘粥不能充其饥。上人嫌,下人憎,皆言余之贱也,余曰:非贱也,乃时也,运也,命也。余后登高及第,入中书,官至极品,位列三公,思衣则有绮罗千箱,思食则有百味珍馐,有挞百僚之杖,有斩佞臣之剑,出则壮士执鞭,入则佳人扶袂,廪有余粟,库有余财,人皆言余之贵也,余曰:非贵也,乃时也,运也,命也。

嗟呼!人生在世,富贵不可尽用,贫贱不可自欺,听由天地循环,周而复始焉。

【译文】

天上有预测不到的风和云,人也会有早晚遇到的灾祸与喜事。蜈蚣有上百只足,但却不如蛇行走得好。家鸡翅膀虽然很大,却不能像鸟一样飞行。马虽然能行走千里之遥,但没有人驾驭也不能自己到达目的地。人有远大的理想,但缺乏机遇就不能实现。

人们常说:人生在世,富贵不能淫,贫贱不能移。孔子的文章写得超过世人却被围困于陈国。拥有文韬武略的姜子牙也曾在渭水垂钓等待机会。孔子的学生颜回虽然早亡,但绝非凶恶的人。盗跖虽然活得长,却不是善良人。尧、舜虽然英明,却生下不肖的儿子。舜的父亲瞽叟顽固愚蠢,反而生下舜这样的大孝子。张良原来只是普通百姓,萧何也只是县吏。晏子的身高不到五尺,却被封为齐国宰相。孔明居住在茅草屋里,却能担当蜀国的军师。项羽虽然强大,却兵败而自刎乌江;刘邦虽然弱小,最终取得了国家政权。汉将李广虽有射虎石的威名,却终身都未获得封侯。冯唐虽有治国安邦的才能,却一生怀才不遇。韩信时运不济时,连饭都吃不上,等到运气来了,成为掌印的大将军,而一旦运气衰败,又死于阴毒人的计谋。

有的人先富裕后贫穷,也有人老年富裕少年衰落。满腹锦绣文章,直到老年还没有考上功名;才疏学浅的人,可能很年轻就金榜提名。皇帝的妃娥在动乱中可能沦为妾妓,风流的妓女,时来运转也能做贵夫人。青春美丽的嫂子嫁了愚蠢的丈夫,俊秀的青年倒找了丑陋的嫂子。蛟龙没有机遇,只能藏身于鱼鳖之间,君子没有机会时,只能屈从于小人。衣服虽然破旧,常保持恭敬礼仪,面带忧愁却可能怀有兼济天下的志向。不得志时,只能安于贫穷和本分,心中坦荡一定会有扬眉吐气的一天。君子贫寒也有一身傲骨,暴富的小人脱不了内心的贫寒。

天气不好时,就见不到太阳和月亮的光辉;土地没有合适的气候条件时,草木都不会生长。水得不到恰当的环境时,就会掀起疾风巨浪;人若得不到机遇时,好运就不畅通。富贵荣华命中都有安排,谁不想要呢?人如果没有依从八德而生活,哪里能做高官当宰相?

以前,我在洛阳,白天到寺庙里吃斋饭,晚上住在寒冷的窑洞里。所穿衣服不能避寒,吃的粥饭抵御不了饥饿。上等人憎恨我,下等人讨厌我,都说我卑贱,是我没有机遇啊。现在我入朝为官,官职做到最高层,地位达到三公,地位只在皇帝一人之下,千万人之上,拥有管理百官的权利,有惩罚卑鄙悭吝官员的权力。穿衣服是绫罗锦缎,吃的则是山珍海味,出门有武士保护,回家有仆人侍奉,皇上宠爱我,百官拥戴我,所有的人说我尊贵,不是我真有多大本领,这是我得到天时和命运的眷顾啊。

所以人活在世上,居富贵不能尽情享用,处贫贱也不要自暴自弃,听从天地的循环和周而复始吧!

《寒窑赋》读后感

时间:2020/10/6 22:23:00
盖闻:千古四大奇文居其首者,《寒窑赋》也。其名又二,《破窑赋》、《劝世章》。乃北宋丁丑科状元宰相吕蒙正所作。然《宋史》、《古文观止》未收录其中以遗后世,吾心甚憾之。作此拙见,以勉己身。

当是时,真宗年少,为东宫。狂傲不羁,不喜文业功名。先生值太子师,推己及人,谏太子以谦和谨慎,好学业。是以作此千古奇文,读之朗朗上口。其状物明理之精、之深,真乃千古无出其二者也。

先生文中言者:大成至圣先师,教化万民,困于陈邦;昭烈武成王,武定大周,钓于渭水;年长盗跖之贼比于短折颜回之善、圣明尧舜之不孝之子比于愚顽瞽叟之大孝之子,皆叹运之过也。布衣张良、小吏萧何,君子得时,官居极品,位及人臣。五尺晏子、村夫孔明、三杰韩信、泗水刘邦,或帝、或相。冯唐易老,岁终而不得志;李广难封,卒尔为将;霸王扛鼎,败于乌江。

少年及第登科,非少者满腹经纶;白发老而不中,非老者才疏学浅;天子婢女,运退为烟柳之客;千人娼客,运转为名门拙荆,盖人各有命尔。潜龙在渊,混迹于鱼虾河泽;九五隐野,行走于布衣陇亩。日月无光,乃天地不得时;风浪不平,乃舟楫不得时。及人耳,为卿为相,岂可得乎?

先生昔居洛阳,家徒四壁,卑不足道。衣无华裳锦服,住无豪府广厦,食无精羮细脍,无良师益友之属授业解惑,礼尚往来。先生叹曰:“非吾贱也,乃时也运也命也。”

及尔登科及第,纵横朝堂,位及人臣。思衣有绫罗绸缎,思住有良田美宅,思食有珍馐百味。上宠下用,一时风光无两,门庭若市。先生再叹曰:“非吾贵也,乃时也运也命也。” 天时、地利、人和,缺一不可。然谋事在人,行事亦在人尔。

先生以“天地循环。周而复始“为文末,是以托道法自然之名而言常怀鸿鹄之志哉。

乙未年春,余叔父观吾荒于学业,述之以惊梦。振聋发聩,如醍醐灌顶:秋闱将近,小子狂傲,文无安邦定国之大策,武无探囊三军之大勇,黄粱美梦,南柯太守尔。视秋闱于无物。及尔悬崖勒马,浪子回头,南柯梦醒。知耻而后勇,知弱而后学。寒窗三载,苦读不坠。幸而丙申季夏,恬为万千亚元者一。现今忆及,言犹在耳,尚羞愧乎!

人依根基八字,注福注禄。然后天戮力,可改天干、可更地支、可为卿为相。是以学为终生大事者也,黄发垂髫,学之于下庠上庠。寒窗十载,常伴圣贤书,济世策。头悬梁,锥刺股,只待一朝题名金榜,效古贤人之志:文能定国,武能安邦,垂拱而天下治,固所愿也!

嗟乎,凡一千余言,皆勉励直言:但请诸君常怀鸿鹄之志哉。青衣翼张,纵横无相。愿与诸君共勉尔!

很久没有动笔了,自己表达能力也一般,不太愿意围绕一篇文章不停的修改。写文章也是为了监督自己,同时也是对之前学习的一个巩固作用,其效果不言而喻。看到很多人的年终总结,自己也不由地想提起笔,写一篇属于自己的年终总结。

坎坷的一年

2021实在是经历太多,有点超出自己的想象。先是从自己熟悉的主APP业务出来,开始做一块全新的视频业务,对自己来说有着不一样的挑战;期间经历过兴奋、压力、沮丧。而后,来到了架构组,感到了关心、绝望、放弃;人们常说,三十而立,现在的我正处于人生的十字路口,健康、家庭、工作缺一不可,真正感受到肩负家庭重担的责任感、无力感,但却依然要充满希望,不管是为了孩子、还是为了自己没在这个世上白走一回。

年底找工作的一些感受

年底尝试找了几次工作,基本都能挺到二面,然后就挂了。原因的话,猜测对一些小公司可能感觉留不住人吧(有点自恋了);对于一些大公司的话,每次二面下来的一个感受就是,如果我是面试官,感觉你这候选人能力不是特别的有亮点啊。java基础、多线程、android基础弄得再好是应该的,项目经验有点缺了。所以说在自己做一些项目的时候一定不能一股脑先做,就和算法面试一样,得先理清思路,再动手,这是我最大的一个缺点吧。

展望下未来

坎坷的一年,也总算是过来了。明年应该还是比较难熬的一年,但是无论如何也要达成心中的目标,希望明年年终总结的时候,不至于太过遗憾。
工作方面围绕架构这块,希望能一直深耕以下几点吧。

  1. 播放器;
  2. 启动优化;
  3. OOM优化;
  4. 。。。