[关闭]
@TryLoveCatch 2018-10-01T23:45:02.000000Z 字数 24272 阅读 1334

一些技术总结

android


关键字

final

理解 Java 关键字 Final

修饰类

当一个类不想被继承时,就可以用final来修饰

修饰方法

当一个方法不想被子类覆写(Override)时,可以用final来修饰。另外一方面,把方法用final来修饰也有一定的性能提升上的帮助,因为虚拟机知道它不会被覆写,所以可以以更简单的方式来处理。

private的方法,默认都会被编译器加上final.

修饰变量

被final修饰的变量只能赋值一次,之后不能再被修改,对于基本类型来说,就是变量值不能再被修改;对于引用来说,就是不能再让其指向其他对象或者null。

修饰成员变量

域变量也是变量,所以用final来修饰的第一个作用就是赋值后,不能再修改变量的值。

但对于成员变量,声明为final的域变量必须在声明时初始化,或者在构造方法中初始化,否则会有编译错误。

遇到过的一个bug

这个有一个线上的bug

  1. class A {
  2. public void test() {}
  3. }
  4. class B {
  5. private A a;
  6. public B() {
  7. // xxxx
  8. a = new A();
  9. }
  10. }
  11. B b = new B();
  12. b.a.test();
  13. 报空指针异常

怎么解决的呢?

  1. private final A a;

jvm禁止编译器把final域的写重排序到构造函数之外。

对象实例化有是那个步骤:
1. 分配内存空间。
2. 初始化对象。
3. 将内存空间的地址赋值给对应的引用。

多线程环境下,如果2和3被重排序了,就有可能是1-3-2,那么B不为null,但是A可能还没有初始化。

volatile

一旦共享变量被volatile修饰之后,那么就具备了两层语义:

synchronized

一个线程执行临界区代码过程如下:

  1. 获得同步锁
  2. 清空工作内存
  3. 从主存拷贝变量副本到工作内存
  4. 对这些变量计算
  5. 将变量从工作内存写回到主存
  6. 释放锁

方法锁和对象锁的区别

深入理解多线程(一)——Synchronized的实现原理

finally

你真的知道finally吗

finally代码块一定会执行吗?

答案当然是否定的。原因显而易见,在如下的情况下,finally代码块不会执行。

返回值问题

示例一

  1. public int finallyCase() {
  2. int i = 1;
  3. try {
  4. i = 3;
  5. System.out.println("try block,i="+i);
  6. return i;
  7. } finally {
  8. i = 4;
  9. System.out.println("finally block,i=" + i);
  10. }
  11. }

结果

  1. try block,i=3
  2. finally block,i=4
  3. 方法返回值:3

JVM会把try或者catch代码块中的返回值保留,再来执行finally代码块中的语句,等到finally代码块执行完毕之后,再把之前保留的返回值给返回出去。

示例二

  1. public int finallyCase() {
  2. int i = 1;
  3. try {
  4. i = 3;
  5. System.out.println("try block,i="+i);
  6. return i;
  7. } finally {
  8. i = 4;
  9. System.out.println("finally block,i=" + i);
  10. return i;
  11. }
  12. }
  1. try block,i=3
  2. finally block,i=4
  3. 方法返回值:4

finally代码块是在try中return之前执行。i变成了4,然后直接return了,所以try中的return语句实际是没有被执行的。因此返回值为4。

finalize

对象不可达之后,如果对象没有覆盖finalize方法,或者finalize已经被执行过一次,则直接回收,反之,对象被加入一个队列,等待finalize线程来执行finalize方法,这个方法里面,如果重新与GCroot建立联系,则被移除队列,反之,就被回收

handle

聊一聊Android的消息机制

handler&Looper&MessageQueue

postxxx和sendxxx什么区别

callback runnable message调用顺序

postDelay如何实现

Message对象池

ThreadLocal

Looper为什么不会ANR

MessageQueue的IdleHandler

你知道android的MessageQueue.IdleHandler吗?
Android常见优化方式-避开高峰期

在消息队列MessageQueue类中,定义了一个IdleHandler接口,它是用来表示当线程处理完所有的消息,准备进入等待状态时的回调接口,可以根据这个来判断应用的核心业务运行状态是否进入了空闲状态。
避免高峰期

应用场景

Android里面有一个场景:

当从一个Activity启动另一个Activity时,Ams要先调用当前Activity的onPause(),然后再启动目标Activity,目标Activity启动后,要把原Activity完全覆盖掉,按照Activity的生命周期管理,原Activity要调用onStop(),但它是在什么时候调用的呢,就是在IdleHandler中调用的。为什么这样做呢,因为新Activity的显示非常重要,要保证它的优先处理,旧Activity要销毁了,已经不显示UI界面了,那就等到UI线程处于空闲期再处理。

自己App的场景

Android应用运行启动之后,一般要进行全局初始化、资源加载、UI显示等一系列的操作,显然此时会出现CPU使用一个高峰期,如果这些任务不加区分,一股脑的全部运行起来,势必影响到UI的显示。因此,可以对此进行流程优化,全力把保障UI线程的运行,把初始化、资源加载分为两部分,一部分是UI界面显示必需的,另一部分和初始化界面不相关的,在应用启动阶段只运行和UI相关的部分,其余部分等到高峰期过去之后再运行。

  1. Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
  2. @Override public boolean queueIdle() {
  3. initInBackground(); //后台运行的初始化任务在UI处于空闲时运行
  4. return false;
  5. }
  6. });

同步屏障

setAsynchronous

启动过程

Activity启动过程

一篇文章看明白 Android 从点击应用图标到界面显示的过程

Service启动过程

Broadcast启动过程

窗口机制

Activity&Window&DecorView的关系

Activity&Window的关系

Activity启动流程 可以参考上面的,我们直接说ApplicationThread.scheduleLaunchActivity(),发送小小给H,然后H调用handleLaunchActivity

  1. private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  2. ...
  3. // Initialize before creating the activity
  4. WindowManagerGlobal.initialize();
  5. Activity a = performLaunchActivity(r, customIntent);
  6. if (a != null) {
  7. r.createdConfig = new Configuration(mConfiguration);
  8. Bundle oldState = r.state;
  9. handleResumeActivity(r.token, false, r.isForward,
  10. ...
  11. }

再来看下performLaunchActivity

ActivityThread.performLaunchActivity

  1. ActivityThread.performLaunchActivity {
  2. //类似Application的创建过程,通过classLoader加载到activity.
  3. activity = mInstrumentation.newActivity(classLoader,
  4. component.getClassName(), r.intent);
  5. //因为Activity有界面,所以其Context是ContextThemeWrapper类型,但实现类仍是ContextImpl.
  6. Context appContext = createBaseContextForActivity(r, activity);
  7. activity.attach(context,mInstrumentation,application,...);
  8. //调用activity.oncreate
  9. mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
  10. ...
  11. //调用Activity的onstart方法
  12. activity.performStart();
  13. //调用activitu的OnRestoreInstanceState方法进行Window数据恢复
  14. mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,r.persistentState);
  15. }

由上面这些源码我们可以知道

所以,handleLaunchActivity里面会调用onCreate,onStart,onRestoreInstanceState,onResume;onCreate里面又回调用setContextView。还有就是,一个 Activity 界面的绘制,其实是在 onResume() 之后才开始的

在ActivityThread.performLaunchActivity中,创建Activity的实例,接着会调用Activity.attach()来初始化一些内容,而Window对象就是在attach里进行创建初始化赋值的。

Activity.attach

  1. final void attach(...) {
  2. ...
  3. mWindow = new PhoneWindow(this);
  4. mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
  5. if (mParent != null) {
  6. mWindow.setContainer(mParent.getWindow());
  7. }
  8. mWindowManager = mWindow.getWindowManager();
  9. ...
  10. }

从上面两个代码可以看出来,Activity在onCreate之前,先创建了一个Windown,即PhoneWindow,并设置了WindowManager,因此一个Activity对应着一个Window也就是应用窗口。
由上面的代码可知Window类的成员变量mWindowManager和Activity的成员变量mWindowManager都是指向同一个WindowManager对象,而WindowManager对象是调用如下代码获取:

  1. (WindowManager)context.getSystemService(Context.WINDOW_SERVICE)

而我们知道Activity是继承Context类的,ContextImpl类中实现了WindowManager服务的注册,代码如下:

  1. class ContextImpl extends Context {
  2. static {
  3. registerService(WINDOW_SERVICE, new ServiceFetcher() {
  4. Display mDefaultDisplay;
  5. public Object getService(ContextImpl ctx) {
  6. Display display = ctx.mDisplay;
  7. if (display == null) {
  8. if (mDefaultDisplay == null) {
  9. DisplayManager dm = (DisplayManager)ctx.getOuterContext().
  10. getSystemService(Context.DISPLAY_SERVICE);
  11. mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
  12. }
  13. display = mDefaultDisplay;
  14. }
  15. return new WindowManagerImpl(display);
  16. }});
  17. .......
  18. }
  19. }

有上面代码可知,在ContextImpl类中注册服务是一个静态代码块,也就是说值执行main方法之前就已经完成了WindowManager服务的注册。在整个应用第一次创建Context对象的时候就已经创建WindowManagerImpl对象了,所以,不管一个应用程序中有多少个Activity,但只有一个WindowManagerImpl对象。

Activity添加View

每个Activity都会在onCreate里面调用setContextView方法来加载布局视图,而这其实就是视图View添加到Activity窗口上的一个过程。加载布局视图代码如下Activity#setContentView:

  1. public void setContentView(int layoutResID) {
  2. getWindow().setContentView(layoutResID);
  3. initWindowDecorActionBar();
  4. }

PhoneWindow#setContentView

  1. public void setContentView(int layoutResID) {
  2. if (mContentParent == null) {
  3. //给窗口安装装饰视图DecorView
  4. installDecor();
  5. } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
  6. mContentParent.removeAllViews();
  7. }
  8. if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
  9. final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
  10. getContext());
  11. transitionTo(newScene);
  12. } else {
  13. //加载xml布局视图内容到视图容器mContentParent中
  14. mLayoutInflater.inflate(layoutResID, mContentParent);
  15. }
  16. }

调用installDecor方法给窗口安装视图装饰,所谓视图装饰指的就是界面上看到的标题栏,导航栏Actionbar,也就是窗口的标题栏。

installDecor

  1. private void installDecor() {
  2. if (mDecor == null) {
  3. //
  4. mDecor = generateDecor();
  5. mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
  6. mDecor.setIsRootNamespace(true);
  7. if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
  8. mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
  9. }
  10. }
  11. if (mContentParent == null) {
  12. mContentParent = generateLayout(mDecor);
  13. }
  14. ........
  15. }

该方法中主要做了两件事:
- 调用generateDecor方法获得DecorView对象,该对象是ViewGroup类型,用于描述窗口Window的顶层视图。
- 调用generateLayout方法给窗口添加标题栏,同时获得装载窗口内容的容器mContentParent,该成员变量也是ViewGroup类型。

generateDecor()

  1. protected DecorView generateDecor() {
  2. return new DecorView(getContext(), -1);
  3. }

generateLayout()

  1. protected ViewGroup generateLayout(DecorView decor) {
  2. int layoutResource; //窗口修饰布局文件
  3. if() {
  4. layoutResource = com.android.internal.R.layout.dialog_title_icons;
  5. } else if() {
  6. layoutResource = com.android.internal.R.layout.screen_title_icons;
  7. } else if() {
  8. layoutResource = com.android.internal.R.layout.screen_progress;
  9. } else if() {
  10. }
  11. ....
  12. View in = mLayoutInflater.inflate(layoutResource, null);
  13. decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
  14. ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  15. return contentParent;
  16. }
  1. 根据窗口的风格修饰类型为该窗口选择不同的窗口布局文件(根视图),例如FullScreen(全屏)、NoTitleBar(不含标题栏)等
  2. android:id="@android:id/content",根据这个id得到mContentParent,这个就是咱们setContentView的根布局,一般为FrameLayout

窗口布局文件,R.layout.screen_title

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:orientation="vertical"
  3. android:fitsSystemWindows="true">
  4. <FrameLayout
  5. android:layout_width="match_parent"
  6. android:layout_height="?android:attr/windowTitleSize"
  7. style="?android:attr/windowTitleBackgroundStyle">
  8. <TextView android:id="@android:id/title"
  9. style="?android:attr/windowTitleStyle"
  10. android:background="@null"
  11. android:fadingEdge="horizontal"
  12. android:gravity="center_vertical"
  13. android:layout_width="match_parent"
  14. android:layout_height="match_parent" />
  15. </FrameLayout>
  16. <FrameLayout android:id="@android:id/content"
  17. android:layout_width="match_parent"
  18. android:layout_height="0dip"
  19. android:layout_weight="1"
  20. android:foregroundGravity="fill_horizontal|top"
  21. android:foreground="?android:attr/windowContentOverlay" />
  22. </LinearLayout>

全屏的窗口布局文件 R.layout.screen_simple:

  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:id="@android:id/content"
  3. android:fitsSystemWindows="true"
  4. android:foregroundInsidePadding="false"
  5. android:foregroundGravity="fill_horizontal|top"
  6. android:foreground="?android:attr/windowContentOverlay" />

然后,回过头来,在installDecor里面调用inflate方法将布局视图内容加载到刚刚获得的内容容器mContentParent上。

现在总结一下他们之间的关系:一个Activity中有一个Window对象用于描述当前应用的窗口,而Window是抽象类,实际上指向PhoneWindow类。PhoneWindow类中有一个内部类DecorView用于描述窗口的顶层视图,该类实际上是ViewGroup类型。它里面有一个@android:id/content指向的mContentParent,也是一个ViewGraoup,我们setContextView里面设置进去的xml,会成为它的子 view。

DecorView添加到Window上

我们知道Activity中的PhoneWindow对象帮我们创建了一个PhoneWindow内部类DecorView(父类为FrameLayout)窗口顶层视图,然后通过LayoutInflater将xml内容布局解析成View树形结构添加到DecorView顶层视图中id为content的FrameLayout父容器上面。到此,我们已经知道Activity的content内容布局最终会添加到DecorView窗口顶层视图上面

那么,窗口顶层视图DecorView是怎么绘制到我们的手机屏幕上的呢?
上面我们说到

handleLaunchActivity里面会调用onCreate,onStart,onRestoreInstanceState,onResume;onCreate里面又回调用setContextView

所以接下来,我们看下onResume的调用。
我们上面说过,在handleLaunchActivity里面,performLaunchActivity之后,会调用handleResumeActivity:

  1. final void handleResumeActivity(IBinder token,
  2. boolean clearHide, boolean isForward, boolean reallyResume){
  3. //调用activity.onResume,把activity数据记录更新到ActivityClientRecord
  4. ActivityClientRecord r = performResumeActivity(token, clearHide);
  5. if (r != null) {
  6. final Activity a = r.activity;
  7. //activity.mStartedActivity是用来标记启动Activity,有没有带返回值,一般我们startActivity(intent)是否默认是startActivityForResult(intent,-1),默认值是-1,所以这里mStartedActivity = false
  8. boolean willBeVisible = !a.mStartedActivity;
  9. ...
  10. //mFinished标记Activity有没有结束,而r.window一开始activity并未赋值给ActivityClientRecord,所以这里为null
  11. if (r.window == null && !a.mFinished && willBeVisible) {
  12. r.window = r.activity.getWindow(); //赋值
  13. View decor = r.window.getDecorView();
  14. decor.setVisibility(View.INVISIBLE);
  15. ViewManager wm = a.getWindowManager();
  16. WindowManager.LayoutParams l = r.window.getAttributes();
  17. a.mDecor = decor;
  18. l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
  19. l.softInputMode |= forwardBit;
  20. if (a.mVisibleFromClient) {
  21. a.mWindowAdded = true;
  22. //把当前的DecorView与WindowManager绑定一起
  23. wm.addView(decor, l);
  24. }
  25. ...

performResumeActivity注释已经很清楚了,里面会回调onResume方法,这里就不详细说明了。一个 Activity 界面的绘制,其实是在 onResume() 之后才开始的。
我们主要看这句:

  1. wm.addView(decor, l);

很明显,这句就是把Activity的顶层视图DecorView添加到窗口视图上,我们来看addView方法
下面是WindowManagerImpl的源码

  1. public final class WindowManagerImpl implements WindowManager {
  2. private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
  3. private final Display mDisplay;
  4. private IBinder mDefaultToken;
  5. public WindowManagerImpl(Display display) {
  6. this(display, null);
  7. }
  8. private WindowManagerImpl(Display display, Window parentWindow) {
  9. mDisplay = display;
  10. mParentWindow = parentWindow;
  11. }
  12. public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
  13. return new WindowManagerImpl(mDisplay, parentWindow);
  14. }
  15. @Override
  16. public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
  17. applyDefaultToken(params);
  18. mGlobal.addView(view, params, mDisplay, mParentWindow);
  19. }
  20. @Override
  21. public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
  22. applyDefaultToken(params);
  23. mGlobal.updateViewLayout(view, params);
  24. }
  25. @Override
  26. public void removeView(View view) {
  27. mGlobal.removeView(view, false);
  28. }
  29. @Override
  30. public void removeViewImmediate(View view) {
  31. mGlobal.removeView(view, true);
  32. }
  33. @Override
  34. public Display getDefaultDisplay() {
  35. return mDisplay;
  36. }
  37. }

我们发现,其实都是调用了WindowManagerGlobal类相对应的方法:

  1. public final class WindowManagerGlobal {
  2. private final ArrayList<View> mViews = new ArrayList<View>();
  3. private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
  4. private final ArrayList<WindowManager.LayoutParams> mParams =
  5. new ArrayList<WindowManager.LayoutParams>();
  6. ........
  7. //单例模式构造方法
  8. public static WindowManagerGlobal getInstance() {
  9. synchronized (WindowManagerGlobal.class) {
  10. if (sDefaultWindowManager == null) {
  11. sDefaultWindowManager = new WindowManagerGlobal();
  12. }
  13. return sDefaultWindowManager;
  14. }
  15. }
  16. ........
  17. //窗口的添加过程
  18. public void addView(View view, ViewGroup.LayoutParams params,
  19. Display display, Window parentWindow) {
  20. final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
  21. if (parentWindow != null) {
  22. parentWindow.adjustLayoutParamsForSubWindow(wparams);
  23. } else {
  24. final Context context = view.getContext();
  25. if (context != null
  26. && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
  27. wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
  28. }
  29. }
  30. ViewRootImpl root;
  31. View panelParentView = null;
  32. synchronized (mLock) {
  33. if (mSystemPropertyUpdater == null) {
  34. mSystemPropertyUpdater = new Runnable() {
  35. @Override public void run() {
  36. synchronized (mLock) {
  37. for (int i = mRoots.size() - 1; i >= 0; --i) {
  38. mRoots.get(i).loadSystemProperties();
  39. }
  40. }
  41. }
  42. };
  43. }
  44. //不能重复添加窗口
  45. int index = findViewLocked(view, false);
  46. if (index >= 0) {
  47. if (mDyingViews.contains(view)) {
  48. mRoots.get(index).doDie();
  49. } else {
  50. throw new IllegalStateException("View " + view
  51. + " has already been added to the window manager.");
  52. }
  53. }
  54. //判断当前窗口是否为子窗口,如果是则获得其父窗口并保存在panelParentView变量中
  55. if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
  56. wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
  57. final int count = mViews.size();
  58. for (int i = 0; i < count; i++) {
  59. if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
  60. panelParentView = mViews.get(i);
  61. }
  62. }
  63. }
  64. //每一个窗口对应着一个ViewRootImpl对象
  65. root = new ViewRootImpl(view.getContext(), display);
  66. //给当前窗口视图设置参数
  67. view.setLayoutParams(wparams);
  68. //保存三个数组
  69. mViews.add(view);
  70. mRoots.add(root);
  71. mParams.add(wparams);
  72. }
  73. try {
  74. //真正执行窗口的视图View绘制工作的方法
  75. root.setView(view, wparams, panelParentView);
  76. }
  77. }
  78. //主要更新当前窗口的参数LayoutParams
  79. public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
  80. final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
  81. view.setLayoutParams(wparams);
  82. synchronized (mLock) {
  83. int index = findViewLocked(view, true);
  84. ViewRootImpl root = mRoots.get(index);
  85. mParams.remove(index);
  86. mParams.add(index, wparams);
  87. root.setLayoutParams(wparams, false);
  88. }
  89. }
  90. //从三个数组里面分别移除DecorView对象,ViewRootIpl对象,WindowManager.LayoutParams对象
  91. public void removeView(View view, boolean immediate) {
  92. synchronized (mLock) {
  93. int index = findViewLocked(view, true);
  94. View curView = mRoots.get(index).getView();
  95. removeViewLocked(index, immediate);
  96. if (curView == view) {
  97. return;
  98. }
  99. }
  100. }

在WindowManagerGlobal类中维系着三个数组分别是:

  • mViews:保存着当前应用所有窗口的顶层视图DecorView对象
  • mRoots:保存着当前应用所有窗口的视图绘制类ViewRootImpl
  • mParams:保存着当前应用所有窗口的参数 WindowManager.LayoutParams

另外,上面我们说过:

一个应用中不管有多少个Activity,都共用一个WindowManagerImpl对象,

因此:

而在WindowManagerImpl类中是单例模式获得WindowManagerGlobal对象,因此:一个应用中也就只有一个WindowManagerGlobal对象了。而在该对象中通过维护以上三个数组来维护一个应用中所有窗口的管理。

我们来看下addView方法:

  1. public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
  2. ...
  3. ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
  4. view.setLayoutParams(wparams);
  5. mViews.add(view);
  6. mRoots.add(root);
  7. mParams.add(wparams);
  8. root.setView(view, wparams, panelParentView);
  9. ...
  10. }

这个过程创建一个ViewRootImpl,并将之前创建的DecoView作为参数传入,以后DecoView的事件都由ViewRootImpl来管理了,比如DecoView上添加View,删除View。
ViewRootImpl实现了ViewParent这个接口,这个接口最常见的一个方法是requestLayout()。
ViewRootImpl是个ViewParent,在DecoView添加的View时,就会将View中的ViewParent设为DecoView所在的ViewRootImpl,View的ViewParent相同时,理解为这些View在一个View链上。
所以每当调用View的requestLayout()时,其实是调用到ViewRootImpl,ViewRootImpl会控制整个事件的流程。可以看出一个ViewRootImpl对添加到DecoView的所有View进行事件管理。

我们接着看ViewRootImpl的setView方法:

  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  2. ...
  3. // Schedule the first layout -before- adding to the window
  4. // manager, to make sure we do the relayout before receiving
  5. // any other events from the system.
  6. requestLayout();
  7. ...
  8. try {
  9. ...
  10. res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mInputChannel);
  11. }
  12. }。

这时候,我们在看下这种图,就会很清晰了

我们在看下requestLayout()这个方法

  1. @Override
  2. public void requestLayout() {
  3. if (!mHandlingLayoutInLayoutRequest) {
  4. checkThread();
  5. mLayoutRequested = true;
  6. scheduleTraversals();
  7. }
  8. }

checkThread判断线程一致行的,注意其实并不是判断是否时UI线程

  1. void checkThread() {
  2. if (mThread != Thread.currentThread()) {
  3. throw new CalledFromWrongThreadException(
  4. "Only the original thread that created a view hierarchy can touch its views.");
  5. }
  6. }

这里就涉及到一个问题,子线程是否可以更新UI,具体情况看这里 子线程是否可以更新UI

我们主要来看scheduleTraversals()

  1. void scheduleTraversals() {
  2. if (!mTraversalScheduled) {
  3. mTraversalScheduled = true;
  4. mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
  5. mChoreographer.postCallback(
  6. Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
  7. if (!mUnbufferedInputDispatch) {
  8. scheduleConsumeBatchedInput();
  9. }
  10. notifyRendererOfFramePending();
  11. }
  12. }
  13. ..............
  14. final class TraversalRunnable implements Runnable {
  15. @Override
  16. public void run() {
  17. doTraversal();
  18. }
  19. }
  20. final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
  21. ...............
  22. void doTraversal() {
  23. if (mTraversalScheduled) {
  24. mTraversalScheduled = false;
  25. mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
  26. try {
  27. performTraversals();
  28. } finally {
  29. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  30. }
  31. }
  32. }
  33. ............

跟踪代码,最后DecorView的绘制会进入到ViewRootImpl类中的performTraversals()成员方法:

  1. private void performTraversals() {
  2. // cache mView since it is used so much below...
  3. //我们在Step3知道,mView就是DecorView根布局
  4. final View host = mView;
  5. //在Step3 成员变量mAdded赋值为true,因此条件不成立
  6. if (host == null || !mAdded)
  7. return;
  8. //是否正在遍历
  9. mIsInTraversal = true;
  10. //是否马上绘制View
  11. mWillDrawSoon = true;
  12. .............
  13. //顶层视图DecorView所需要窗口的宽度和高度
  14. int desiredWindowWidth;
  15. int desiredWindowHeight;
  16. .....................
  17. //在构造方法中mFirst已经设置为true,表示是否是第一次绘制DecorView
  18. if (mFirst) {
  19. mFullRedrawNeeded = true;
  20. mLayoutRequested = true;
  21. //如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要窗口的宽度和高度就是除了状态栏
  22. if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
  23. || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
  24. // NOTE -- system code, won't try to do compat mode.
  25. Point size = new Point();
  26. mDisplay.getRealSize(size);
  27. desiredWindowWidth = size.x;
  28. desiredWindowHeight = size.y;
  29. } else {//否则顶层视图DecorView所需要窗口的宽度和高度就是整个屏幕的宽高
  30. DisplayMetrics packageMetrics =
  31. mView.getContext().getResources().getDisplayMetrics();
  32. desiredWindowWidth = packageMetrics.widthPixels;
  33. desiredWindowHeight = packageMetrics.heightPixels;
  34. }
  35. }
  36. ............
  37. //获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
  38. int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
  39. int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
  40. // Ask host how big it wants to be
  41. //执行测量操作
  42. performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  43. ........................
  44. //执行布局操作
  45. performLayout(lp, desiredWindowWidth, desiredWindowHeight);
  46. .......................
  47. //回调OnGlobalLayoutListener,在meausre、layout之后,draw之前
  48. if (triggerGlobalLayoutListener) {
  49. mAttachInfo.mRecomputeGlobalAttributes = false;
  50. mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
  51. }
  52. .......................
  53. //执行绘制操作
  54. performDraw();
  55. }

performTraversals方法会经过measure、layout和draw三个过程才能将一个View绘制出来,所以View的绘制是ViewRootImpl完成的,另外当手动调用invalidate,postInvalidate,requestInvalidate也会最终调用performTraversals,来重新绘制View。

这里面还有一个问题,我们通过上面的分析可以知道,在onCreate和onResume里面调用getMeasureHeight() = 0是没有问题,平时我们获取的时候,可以使用监听:

  1. view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  2. @Override
  3. public void onGlobalLayout() {
  4. // TODO Auto-generated method stub
  5. }
  6. });

上面的代码已经很清楚了:

  1. //执行测量操作
  2. performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  3. ........................
  4. //执行布局操作
  5. performLayout(lp, desiredWindowWidth, desiredWindowHeight);
  6. .......................
  7. //回调OnGlobalLayoutListener,在meausre、layout之后,draw之前
  8. if (triggerGlobalLayoutListener) {
  9. mAttachInfo.mRecomputeGlobalAttributes = false;
  10. mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
  11. }
  12. .......................
  13. //执行绘制操作
  14. performDraw();

OnGlobalLayoutListener,是在meausre、layout之后,draw之前回调的。

  • ActivityThread.performLaunchActivity()
  • Activity.attach()
  • Activity.onCreate()
  • Activity.setContentView()
  • PhoneWindow.setContentView()
  • PhoneWindow.installDecor()
    • PhoneWindow.generateDecor():创建DecorView对象,继承FrameLayout
    • PhoneWindow.generateLayout():根据不通的窗口风格,选择不通的layout文件,并添加到DecorView,返回id为content的FrameLayout,作为
  • ActivityThread.performResumeActivity()

绘制机制

卡顿检测的方法

屏幕刷新机制

Android 屏幕刷新机制

进程间通信

AIDL

Messager

Context

Android Context 上下文 你必须知道的一切

数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。
数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。

View

View.post

View.post() 不靠谱的地方你知道吗?

集合

SparseArray

为什么不用一个数组?

线程

Lock & synchronized 区别

(1). Lock是一个接口,是JDK层面的实现;而synchronized是Java中的关键字,是Java的内置特性,是JVM层面的实现;
(2). synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
(3). Lock 可以让等待锁的线程响应中断,而使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
(4). 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;
(5). Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的。而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

停止线程

Java多线程初探——正确停止线程

性能优化

卡顿优化

图片优化

包大小减少

架构

设计模式

反向控制IOC

IOC模式是一种依赖倒置,也即反向调用机制。如果把应用层调用框架层代码的过程,称为正向调用的话,那么反过来,由框架层调用应用层代码的过程,就称为反向调用。也就是说框架层根据自己的流程,会在需要的时候回调应用层的代码,来实现具体业务逻辑,从应用层的角度看,相当于流程控制从自己一方转移到了框架层,相当于控制反转了。

优缺点

  • 优点是完美地兼顾了框架的通用性和应用的具体性,由框架负责不变的流程,应用负责易变的业务;
  • 缺点就是流程不容易理解、难以调试,因为是反向调用,和人的正向调用思维方式不一样,尤其是在调试代码时,不能像正向调用那样一步步的跟踪,来查找问题。

实现方式

模板方法模式
框架层定义基类,在基类中实现框架的基本逻辑流程,并定义了抽象方法以供基类使用,由应用层继承该基类并override抽象方法

观察者模式
框架层定义回调接口类,由应用层实现该接口类,并注册到框架层,框架层负责调用该回调接口。

Android中的应用

  • Application、Activity、Fragment类中的onXxx()等生命周期方法、View的onMeasure()、onLayout()、onDraw()、onXxxEvent()等方法,都是模板方法模式中的“钩子方法”
  • View类中常用的OnXxxListener接口,由应用层实现具体的接口添加View实例中,OnXxxListener为Observer角色,框架层为Subject角色。

依赖注入DI

DI(依赖注入)模式是通过对象注入的方式让框架得到所需要的对象。这样,应用层不再关注何时以及如何把对象实例传给框架,只要提供相关的类就行了,由框架自己在需要的时候负责创建依赖对象的实例,从而由应用层创建对象实例,变成了由框架层自己创建。总之,概括起来就是:DI模式就是由框架层负责创建应用层所定义的类的对象实例,从应用层视角看,就好像是自己定义的类被自动注入到了框架层。

优缺点

  • 好处就是解耦,能够保持软件模块之间的松耦合,为设计开发带来灵活性。我们开发时只需要定义相关类就行了,框架在需要的时候自己会主动创建。如果业务需求变化了,比如,需要替换一个Activity,修改非常简单,只需要重新编写一个新的Activity类,在Manifest中替换掉旧的Activty即可,除了编写一个类外,没有修改其它地方的任何代码,和其它代码没有任何耦合,方便多了。当然,严格地说只是在代码层面上解耦了,把耦合放到了Manifest中去了。定义一个类,修改一下配置文件,就满足了需求变化,无需修改原来的代码,够灵活够方便吧
  • 缺点,首先是效率低,因为需要解析配置文件、动态加载类、使用反射来创建对象,不过毕竟DI模式能够为编程带来方便,牺牲点性能应该还能接受的。其次,在创建对象时不够灵活,不如应用层那样可以按照不同的具体场景创建对象。

实现方式

实现DI模式的方法一般就是配置文件+ClassLoader+反射,通常应用层完成了接口类的定义之后,把它按照一定的格式要求,在配置文件中进行设置,比如:类的名称、包名、相关参数、是否单例等。框架在运行的时候,解析该配置文件,得到创建这些类的相关信息,然后在合适的时机使用ClassLoader来加载这些类,接着使用反射机制创建出相应的对象实例。

  • 提供反向控制IOC:框架层定义了基类,并抽象依赖于这个基类定义的接口方法进行业务处理,应用层定义了这个基类的派生类,并实现或者override基类的接口方法。框架层使用这个派生类创建了对象实例,并供自己使用它来实现具体的业务逻辑。
  • 提供工厂方法来管理、控制对象实例:应用层定义类,自己并不创建它的对象实例,当要使用的时候,就调用框架层提供的类似getInstance(String id)方法来得到一个对象。框架层预先或者在应用层调用getInstance()的时候来创建相应的对象实例,实现了工厂方法的功能,框架层可以延迟创建或者缓冲这些对象实例。

Android中的应用

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注