Android(安卓)Jetpack之Navigation源码分析
Android Jetpack之Navigation源码分析
Android Navigation简介
关于Fragment的基础篇:Fragment基础篇
官方指导地址:官方指地址
Github demo 地址:demo
使用Navigation可以管理APP页面跳转。Navigation不部分情况下作用于Fragment中,使用Navigation切换Fragment可以使代码简洁,直观。Navigation导航组件还支持:Fragment、Activity、导航图和子图、自定义目标等。
Navigation的使用
基础使用
- 添加项目组件依赖
def nav_dep = "2.0.0" implementation "androidx.navigation:navigation-fragment:$nav_dep" implementation "androidx.navigation:navigation-ui:$nav_dep"
- 导航文件XML
在module下的res目录下,新建navigation文件夹,然后在navigation文件夹下新建一个navigation的xml文件:navigation_jetpack.xml
<?xml version="1.0" encoding="utf-8"?>
字段解析:
(1)navigation根节点 startDestination 表示第一个显示的fragment。即FirstNavigationFragment
(2)fragment 节点中name属性表示所属的fragment类
(3)fragment 节点中action节点destination属性用于指定下一个目标fragment
(4)fragment 节点中argument 用于传递数据。表示的是传递到当前Fragment的数据,Key为name属性,默认数据是android:defaultValue,数据类型是argType。
3. 创建Fragment
以ThirdNavigationFragment为例。onCreateView返回布局View。onViewCreated设置点击时间,执行相应的action,来完成Fragment的跳转。在页面跳欢时,会执行onDestroyView方法,从新回到该Fragment,会执行方法onCreateView方法。
class ThirdNavigationFragment : Fragment(){ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return LayoutInflater.from(this.activity).inflate(R.layout.fragment_third_navigation,container,false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) btn_third_fragment.setOnClickListener{ Navigation.findNavController(it).navigate(R.id.action_popup_to_first_fragment_from_third) } }}
- 创建Activity
4.1 activity布局文件。
<?xml version="1.0" encoding="utf-8"?>
布局解析:
(1)xml实现和代码实现在使用时,请务必注解其中一个。
(2)如果使用xml实现,fragment务必设置id。navGraph 用来表示上面的导航意图文件 navigation_jetpack.xml
(3)name 必须指定为以下值,这是切换fragment的容器
android:name=“androidx.navigation.fragment.NavHostFragment”
(4)defaultNavHost 表示是否拦截返回键,默认为false。
4.2 Activity中使用
如果使用的是代码实现的布局文件,在Activity中使用如下代码:
(1)初始化NavHostFragment。
(2)将NavHostFragment绑定到布局文件的FrameLayout中。
class NavigationActivity : AppCompatActivity(){ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_navigation) val finalHost = NavHostFragment.create(R.navigation.navigation_jetpack) supportFragmentManager.beginTransaction() .replace(R.id.ll_fragment_navigation, finalHost) .setPrimaryNavigationFragment(finalHost) .commit() }}
- 切换fragment
切换Fragment主要有一下两种方式:
(1)方式1:
Navigation.findNavController(it).navigate(R.id.action_to_second_fragment)
(2)方式2:
NavHostFragment.findNavController(this).navigate(R.id.action_to_second_fragment,null)
这两种实现方式其实都返回了一个 NavController 类,然后再通过调用navigate方法控制页面导航,也就是说通过NavController 我们可以控制所有的Fragment导航行为。
7. 数据传递
在navigation导航文件中可以通过设置argument标签,来设置fragment所接收的参数类型和默认值。
或者在代码中使用传统方式:
val bundle = Bundle() bundle.putString("name","Blank") bundle.putInt("number",10) NavHostFragment.findNavController(this).navigate(R.id.action_to_second_fragment,bundle)
在代码中,使用navigate() 方法并将Bundle并将其传递到目标。接受方Fragment中,使用getArguments()方法检索包并使用其内容。
8. 嵌套导航图
可以将目的地分组为导航图中的子图,子图也被称为“ 嵌套图 ”,包含图称为“ 根图“。如下我们建立子图:third_navigation。
<?xml version="1.0" encoding="utf-8"?>
- include引用其他图形
使用include引用其他图形
比如我们建立视图:navigation_nested.xml 布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
在navigation_jetpack中include navigation_nested。
<?xml version="1.0" encoding="utf-8"?>
- 其他用法
Navigation的其他用法可以参考下面Blog,包含Deep Link等。
https://cloud.tencent.com/developer/article/1446342
https://blog.csdn.net/lyhhj/article/details/93757755
https://cloud.tencent.com/developer/article/1452921
源码分析
3.1 构建NavController
通过上面的Navigation使用,我们知道NavHostFragment作为一个容器,所有的导航操作都是NavHostFragment中进行,在NavHostFragment中又委托给了NavController类。所以我们下面主要看看NavController类是如何被创建出来的,以及在创建过程中NavHostFragment类都做那些初始化工作。NavHostFragment的初始化主要有两种实现方式,1:配置XML文件。2:代码实现。下面我们以代码实现NavHostFragment.create为入口来分析,NavHostFragment类。
val finalHost = NavHostFragment.create(R.navigation.navigation_jetpack) supportFragmentManager.beginTransaction() .replace(R.id.ll_fragment_navigation, finalHost) .setPrimaryNavigationFragment(finalHost) .commit()
- NavHostFragment.create方法
(1)初始化Bundle,并且将graphResId,startDestinationArgs存储在Bundle中。
(2)返回NavHostFragment实例。
public static NavHostFragment create(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) { Bundle b = null; if (graphResId != 0) { b = new Bundle(); b.putInt(KEY_GRAPH_ID, graphResId); } if (startDestinationArgs != null) { if (b == null) { b = new Bundle(); } b.putBundle(KEY_START_DESTINATION_ARGS, startDestinationArgs); } final NavHostFragment result = new NavHostFragment(); if (b != null) { result.setArguments(b); } return result; }
- NavHostFragment.onInflate方法
当Fragment以XML的方式静态加载时,最先会调用onInflate的方法(调用时机:Fragment所关联的Activity在执行setContentView时)。
(1)主要是解析布局文件的两个属性。defaultNavHost和navGraph,并且初始化全局变量
(1)defaltNavHost为true时,NavHostFragment将会通过FragmentManager 切换到回退栈顶部,并且可以拦截返回键事件(back事件)。
public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs, @Nullable Bundle savedInstanceState) { super.onInflate(context, attrs, savedInstanceState); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment); final int graphId = a.getResourceId(R.styleable.NavHostFragment_navGraph, 0); final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false); if (graphId != 0) { mGraphId = graphId; } if (defaultHost) { mDefaultNavHost = true; } a.recycle(); }
- NavHostFragment.onCreate方法。无论是XML实现还是代码实现,都会执行Fragment的onCreate方法,可谓是殊途同归。NavController在这里被创建,并且NavHostFragment中有一个NavController对象。
(1)初始化NavController,NavController为导航的控制类,核心类。
(2)在SimpleNavigatorProvider中以键值对保存FragmentNavigator类。该类之后会做介绍。
(3)savedInstanceState不为空时候,恢复controller的状态
(4)将graph设置给navController,构建NavGraph。下面会单独分析该模块。
(5)当defaltNavHost为true,将会被设置为主导航fragment。可以拦截返回键事件(back事件)。
(6)通过addNavigator添加FragmentNavigator,下面会分析到。
public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Context context = requireContext(); mNavController = new NavController(context); mNavController.getNavigatorProvider().addNavigator(createFragmentNavigator()); Bundle navState = null; if (savedInstanceState != null) { navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE); if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) { mDefaultNavHost = true; requireFragmentManager().beginTransaction() .setPrimaryNavigationFragment(this) .commit(); } } if (navState != null) { // Navigation controller state overrides arguments mNavController.restoreState(navState); } if (mGraphId != 0) { // Set from onInflate() mNavController.setGraph(mGraphId); } else { // See if it was set by NavHostFragment.create() final Bundle args = getArguments(); final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0; final Bundle startDestinationArgs = args != null ? args.getBundle(KEY_START_DESTINATION_ARGS) : null; if (graphId != 0) { mNavController.setGraph(graphId, startDestinationArgs); } } }
- NavController.onCreateView方法
该NavHostFragment的视图就只有一个FrameLayout布局, 在NavHostFragment的创建时,为它创建一个FrameLayout作为导航界面的载体。
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { FrameLayout frameLayout = new FrameLayout(inflater.getContext()); // When added via XML, this has no effect (since this FrameLayout is given the ID // automatically), but this ensures that the View exists as part of this Fragment's View // hierarchy in cases where the NavHostFragment is added programmatically as is required // for child fragment transactions frameLayout.setId(getId()); return frameLayout; }
- NavController.onViewCreated
(1)当通过XML添加时,父View是null,我们的view就是NavHostFragment的根。
(2)但是当以代码方式添加时,需要在父级上设置NavController。
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (!(view instanceof ViewGroup)) { throw new IllegalStateException("created host view " + view + " is not a ViewGroup"); } // When added via XML, the parent is null and our view is the root of the NavHostFragment // but when added programmatically, we need to set the NavController on the parent - i.e., // the View that has the ID matching this NavHostFragment. View rootView = view.getParent() != null ? (View) view.getParent() : view; Navigation.setViewNavController(rootView, mNavController); }
- Navigation.setViewNavController方法。
主要是将NavController对象设置为rootView的tag。方便以后递归遍历到NavController对象,确保NavController对象的唯一。
public static void setViewNavController(@NonNull View view, @Nullable NavController controller) { view.setTag(R.id.nav_controller_view_tag, controller); }
至此整个NavController对象和NavHostFragment的关系我们已经梳理明白了。下面来看看
NavController是如何参与到导航事件的。
3.2 获取NavController
要想NavController参与到导航事件,必须获取到该对象才可以。在Fragment 中控制导航的时候,上面介绍了两种实现方式。
(1)Navigation.findNavController(it).navigate(R.id.action_page)
(2)NavHostFragment.findNavController(this).navigate(R.id.action)
其实无论是通过findNavController或者是findNavController返回的都是NavController对象。
在构建NavController对象的时候,我们使用到了Navigation类,下面就从该类分析。findNavController方法形参是个View对象,所以是通过view就去查找就NavController,还记得上面用到的viewRoot吗?。
- findNavController方法
该方法没什么实质性的代码,只要是调用了findViewNavController方法。
public static NavController findNavController(@NonNull View view) { NavController navController = findViewNavController(view); if (navController == null) { throw new IllegalStateException("View " + view + " does not have a NavController set"); } return navController; }
- findViewNavController方法
通过view递归循环查找NavController。内部调用了getViewNavController方法。
private static NavController findViewNavController(@NonNull View view) { while (view != null) { NavController controller = getViewNavController(view); if (controller != null) { return controller; } ViewParent parent = view.getParent(); view = parent instanceof View ? (View) parent : null; } return null; }
- getViewNavController方法
通过获取view的Tag,获取NavController对象,这里的tag ID和setViewNavController都是nav_controller_view_tag。
private static NavController getViewNavController(@NonNull View view) { Object tag = view.getTag(R.id.nav_controller_view_tag); NavController controller = null; if (tag instanceof WeakReference) { controller = ((WeakReference) tag).get(); } else if (tag instanceof NavController) { controller = (NavController) tag; } return controller; }
至此NavController的获取过程已经分析完毕。
4 真正的导航实现
在实现导航的时候,我们需要根据navigation配置文件生成NavGraph类,然后在根据每个不同的action id,找到对应的NavDestination就可以实现页面导航跳转了。
4.1 构建NavGraph
- SimpleNavigatorProvider类
在构建NavController的时候,在onCreate方法中调用了如下代码。
mNavController = new NavController(context); mNavController.getNavigatorProvider().addNavigator(createFragmentNavigator());
(1)其中mNavigatorProvider是NavController中的全局变量,内部通过HashMap键值对的形式保存Navigator类。
private final NavigatorProvider mNavigatorProvider = new NavigatorProvider() { @Nullable @Override public Navigator<? extends NavDestination> addNavigator(@NonNull String name, @NonNull Navigator<? extends NavDestination> navigator) { Navigator<? extends NavDestination> previousNavigator = super.addNavigator(name, navigator); if (previousNavigator != navigator) { if (previousNavigator != null) { previousNavigator.removeOnNavigatorBackPressListener(mOnBackPressListener); } navigator.addOnNavigatorBackPressListener(mOnBackPressListener); } return previousNavigator; } };
(2)createFragmentNavigator方法,构建了FragmentNavigator对象,其中抽象类Navigator还有个重要的实现类ActivityNavigator和NavGraphNavigator。这个两个类的对象在NavController的构造方法中被添加。。
public class FragmentNavigator extends Navigator
(3)其中Navigator类的作用是:能够实例化对应的NavDestination,并且能够实现导航功能,拥有自己的回退栈。
2. 构建NavGraph
在构建NavController的时候,我们还调用了NavController.setGraph(graphId)方法,该方法主要是构建NavGraph。
(1)调用getNavInflater方法创建NavInflater对象,用于解析navigation xml文件
public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) { setGraph(getNavInflater().inflate(graphResId), startDestinationArgs); }
(2) NavInflater.inflate方法
根据传入的XML资源id构建NavGraph,NavGraph组成Fragment路由的导航地图,而NavDestination代表了导航的每一个目的地。在解析完NavDestination后,需要要求NavDestination为NavGraph,即NavGraph是NavDestination的子类。而且在NavGraph内部存储了NavDestination信息。
public NavGraph inflate(@NavigationRes int graphResId) { Resources res = mContext.getResources(); //拿到XML的解析器 XmlResourceParser parser = res.getXml(graphResId); final AttributeSet attrs = Xml.asAttributeSet(parser); try { String rootElement = parser.getName(); //构建出NavDestination NavDestination destination = inflate(res, parser, attrs, graphResId); //合法性检测 if (!(destination instanceof NavGraph)) { throw new IllegalArgumentException("Root element <" + rootElement + ">" + " did not inflate into a NavGraph"); } return (NavGraph) destination; } catch (Exception e) { throw new RuntimeException("Exception inflating " + res.getResourceName(graphResId) + " line " + parser.getLineNumber(), e); } finally { parser.close(); } }
上面的inflate方法内部会继续调用inflate方法。
(1)getNavigator方法获取都Navigator实例,该实例在构建NavController是被添加进去,这里获取的是FragmentNavigator对象。
(2)createDestination方法,会调用FragmentNavigator的createDestination构建Destination对象。
(3)onInflate方法,调用FragmentNavigator.Destination的方法获取设置的Fragment的类名。
(4)while循环内部通过递归构建导航图。
private NavDestination inflate(Resources res, XmlResourceParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { Navigator navigator = mNavigatorProvider.getNavigator(parser.getName()); final NavDestination dest = navigator.createDestination(); dest.onInflate(mContext, attrs); final int innerDepth = parser.getDepth() + 1; int type; int depth; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { if (type != XmlPullParser.START_TAG) { continue; } if (depth > innerDepth) { continue; } final String name = parser.getName(); if (TAG_ARGUMENT.equals(name)) { //解析参数,存储在dest中 inflateArgument(res, dest, attrs); } else if (TAG_DEEP_LINK.equals(name)) { //解析深度链接 inflateDeepLink(res, dest, attrs); } else if (TAG_ACTION.equals(name)) { //解析Action inflateAction(res, dest, attrs); } else if (TAG_INCLUDE.equals(name) && dest instanceof NavGraph) { //如果子节点为graph,加载子节点的destination。即通过include方法。 final TypedArray a = res.obtainAttributes(attrs, R.styleable.NavInclude); final int id = a.getResourceId(R.styleable.NavInclude_graph, 0); ((NavGraph) dest).addDestination(inflate(id)); a.recycle(); } else if (dest instanceof NavGraph) { //如果子节点为graph加载子节点的destination //向每个NavGraph中加入Destination ((NavGraph) dest).addDestination(inflate(res, parser, attrs)); } } return dest;}
- onGraphCreated方法。
通过NavInflater类之后,解析了XML文件构建整个Graph之后。,下面回到setGraph方法,在解析玩XML后会调用setGraph方法。
(1)popBackStackInternal方法将旧的导航图全部出栈。
(2)调用onGraphCreated主要是显示一个导航Fragment视图。
public void setGraph(@NonNull NavGraph graph, @Nullable Bundle startDestinationArgs) { if (mGraph != null) { // Pop everything from the old graph off the back stack popBackStackInternal(mGraph.getId(), true); } mGraph = graph; onGraphCreated(startDestinationArgs); }
- onGraphCreated方法
(1)恢复之前的导航状态
(2)调用navigate方法,显示第一个Fragment。即在Navigation文件里,属性app:startDestination的Fragment。所以最终都会走到navigate导航方法。
private void onGraphCreated(@Nullable Bundle startDestinationArgs) { if (mNavigatorStateToRestore != null) { ArrayList navigatorNames = mNavigatorStateToRestore.getStringArrayList( KEY_NAVIGATOR_STATE_NAMES); if (navigatorNames != null) { for (String name : navigatorNames) { Navigator navigator = mNavigatorProvider.getNavigator(name); Bundle bundle = mNavigatorStateToRestore.getBundle(name); if (bundle != null) { navigator.onRestoreState(bundle); } } } } if (mBackStackIdsToRestore != null) { for (int index = 0; index < mBackStackIdsToRestore.length; index++) { int destinationId = mBackStackIdsToRestore[index]; Bundle args = (Bundle) mBackStackArgsToRestore[index]; NavDestination node = findDestination(destinationId); if (node == null) { throw new IllegalStateException("unknown destination during restore: " + mContext.getResources().getResourceName(destinationId)); } if (args != null) { args.setClassLoader(mContext.getClassLoader()); } mBackStack.add(new NavBackStackEntry(node, args)); } mBackStackIdsToRestore = null; mBackStackArgsToRestore = null; } if (mGraph != null && mBackStack.isEmpty()) { boolean deepLinked = mActivity != null && handleDeepLink(mActivity.getIntent()); if (!deepLinked) { // Navigate to the first destination in the graph // if we haven't deep linked to a destination navigate(mGraph, startDestinationArgs, null, null); } } }
导航
在构建和获取到NavController对象以及NavGraph之后。,下面是使用它来实现真正的导航了。下面从navigate开始分析。在navigate方法内部会查询到NavDestination,然后根据不同的Navigator实现页面导航。
- navigate 方法
(1)如果回退栈为null返回NavGraph,不为null返回回退栈中的最后一项。
(2)根据id,获取对应的NavAction。然后在通过NavAction获取目的地id。
(4)利用目的地ID属性,通过findDestination方法,找到准备导航的目的地。
(5)根据导航目的地的名字,调用getNavigator方法,获取Navigator对象。这里对应的是FragmentNavigator。
public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) { NavDestination currentNode = mBackStack.isEmpty() ? mGraph : mBackStack.getLast().getDestination(); if (currentNode == null) { throw new IllegalStateException("no current navigation node"); } @IdRes int destId = resId; final NavAction navAction = currentNode.getAction(resId); Bundle combinedArgs = null; if (navAction != null) { if (navOptions == null) { navOptions = navAction.getNavOptions(); } destId = navAction.getDestinationId(); Bundle navActionArgs = navAction.getDefaultArguments(); if (navActionArgs != null) { combinedArgs = new Bundle(); combinedArgs.putAll(navActionArgs); } } if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != -1) { popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive()); return; } NavDestination node = findDestination(destId); navigate(node, combinedArgs, navOptions, navigatorExtras); }private void navigate(@NonNull NavDestination node, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) { boolean popped = false; if (navOptions != null) { if (navOptions.getPopUpTo() != -1) { popped = popBackStackInternal(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive()); } } Navigator navigator = mNavigatorProvider.getNavigator( node.getNavigatorName()); Bundle finalArgs = node.addInDefaultArgs(args); NavDestination newDest = navigator.navigate(node, finalArgs, navOptions, navigatorExtras);
- FragmentNavigator的实现
通过以上的分析,又来到了Navigator 的子类FragmentNavigator类。下面来看看FragmentNavigator.navigate的方法。
(1)调用instantiateFragment,通过反射机制构建Fragment实例
(2)处理进出场等动画逻辑
(3)最终调用FragmentManager来处理导航逻辑。
猜测ActivityNavigator最终也是调用了startActivity方法,这里就不展示代码了。
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) { if (mFragmentManager.isStateSaved()) { Log.i(TAG, "Ignoring navigate() call: FragmentManager has already" + " saved its state"); return null; } String className = destination.getClassName(); if (className.charAt(0) == '.') { className = mContext.getPackageName() + className; } final Fragment frag = instantiateFragment(mContext, mFragmentManager, className, args); frag.setArguments(args); final FragmentTransaction ft = mFragmentManager.beginTransaction(); int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1; int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1; int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1; int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1; if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) { enterAnim = enterAnim != -1 ? enterAnim : 0; exitAnim = exitAnim != -1 ? exitAnim : 0; popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0; popExitAnim = popExitAnim != -1 ? popExitAnim : 0; ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim); } ft.replace(mContainerId, frag); ft.setPrimaryNavigationFragment(frag); ft.setReorderingAllowed(true); ft.commit(); }
小结
(1)NavHostFragment 作为导航载体,在Activity的layout文件里被引用(或者在代码中动态),并且持有导航控制类NavController引用。
(2)NavController 将导航任务委托给Navigator类,Navigator类有两个重要的子类FragmentNavigator和ActivityNavigator子类。NavController类持有NavInflater类引用。
(3)NavInflater 负责解析Navgation文件,负责构建NavGraph导航图。
(4)NavDestination 存有各个目的地信息,在FragmentNavigator和ActivityNavigator内部分别对应一个Destination类,该类继承NavDestination。
(5)在页面导航时,fragment的操作还是交由FragmentManager在操作,activity交由startActivity执行。
下面贴一下,网上总结的比较全的类图信息:
来自图片来源
更多相关文章
- Android(安卓)-- Messenger与Service
- 【原创】【Android(安卓)Camera】—— 关于FaceDetection
- Android(安卓)Launcher3安装应用后,控制应用图标显示位置
- Android(安卓)Framework初步认识
- android自定义相机添加自定义水印
- Android(安卓)事件分发机制(最新源码6.0分析)--ViewGrop
- android 自定义view中onMeasure()理解
- Android欢迎页面闪屏解决方法
- Android进程优先级部分整理与理解