二周目Android开发艺术探索这本书,真的是一本很不错的干货,不管是新手或者老猿,都能从中有所收获。这里就把一些阅后总结提炼出来,希望能对你工作或者面试有帮助。
这是新手很容易忽略的地方,很是需要系统的了解下。
1、第一次启动Activity:onCreate - onStart - onResume。
2、新Activity或回到桌面:onPause - onStop,但是在新页面透明时,不走onStop。
3、回到原Activity:onRestart - onStart - onResume。
4、back返回时:onPause - onStop -onDestroy。
5、onStart和onStop是在是否可见时回调;onResume和onPause是在否前台回调。
6、A打开B页面时,A的onPause先被调用,之后B的启动流程,最后A的onStop,所以不要再onPause做耗时操作。
7、onSaveInstanceState在onStop之前,onRestoreInstanceState 在onStart之后(和onResume顺便前后不一定)。
8、singleTask:
9、sinagleInstance:独立的一个栈,全局唯一,直到任务栈销毁。
多进程,跨进程,相信你总会有用到的时候。最好多去研究IBinder
1、’:remote’属于私有子进程,而’xx.xxx.xx:remote’属于全局进程。
2、多进程下:
3、Serializable接口序列化,在指定serialVersionUID,可以在修改了model后还能交易有效,实现数据的一部分恢复。
强烈推荐章节,日常开发中可以帮你少走很多弯路。
1、TouchSlop是系统所能识别的最小滑动距离,可以通过ViewConfiguration.get(context).getScaledTouchSlop()获取。
2、scrollTo和ScrollBy只能改变View的内容,不能改变View的布局中的位置。
3、Scroller 的startScroll其实是调用了computeScroll。
4、dispatchTouchEvent判断事件分发 -> onInterceptTouchEvent拦截事件 -> onTouchEvent处理事件。
5、事件传递是Activity -> Window -> View -> Activity(没有View处理的时候)
6、事件从根ViewGroup向下分发,最先dispatchTouchEvent触发,只有onInterceptTouchEvent拦截了,那么onTouchEvent才会被调用,不然就child的dispatchTouchEvent被调用。
7、如果一个View的onTouchEvent返回false,那么他的父容易onTouchEvent将会被调用。
8、如果设置了onTouchListener
,那么只有当onTouch
返回false,onTouchEvent
才会被调用。所以onTouchListener
高级于onTouchEvent
,而onClick
出于最低。
9、一个事件序列,只能被一个View拦截且消耗。除非特殊处理强行传递。
10、View在dispatchTouchEvent
一旦拦截了事件,那么onInterceptTouchEvent
将不会被调用。因此onInterceptTouchEvent
不是每次都会被调用的。
11、如果一个View不消耗ACTION_DOWN
(onTouchEvent)事件,那么同一事件序列中的其他事件不会给它处理,交给父级的onTouchEvent
。如果View只消耗了DOWN,不消耗其他事件,那么点击事件会消失,当前View还可以收到后续事件,但是父容器的onTouchEvent
不会被调用。
12、View是没有onInterceptTouchEvent
,一旦获取到了事件,那么触发的是onTouchEvent
,并且和enable无关。
13、FLAG_DISALLOW_INTERCEPT
是通过requestDisallowInterceptTouchEvent
设置的。一旦设置了,ViewGroup无法拦截出了ACTION_DOWN
之外的事件,ViewGroup会在ACTION_DOWN
事件中重置FLAG_DISALLOW_INTERCEPT
。所以如果ViewGroup拦截了ACTION_DOWN
,那么requestDisallowInterceptTouchEvent
无效。
14、滑动冲突可以通过外部拦截法通过onInterceptTouchEvent
处理,或者内部拦截法requestDisallowInterceptTouchEvent
来处理,而内部拦截法,记得父ViewGroup不能拦截ACTION_DOWN
,但是可以拦截其他事件,这样在child设置requestDisallowInterceptTouchEvent
为false时,父容器才可以收到。
强烈推荐章节,日常开发中可以帮你更加优雅。
1、ViewRoot对应ViewRootImpl,ActivityThread中,Activity被创建后,会将DecorView添加到Window,同时创建ViewRootImpl,关联DecorView。
2、MeasureSpec 系统根据它来测量View的大小,是32位的int,高两位代表SpecMode(测量模式),低30位代表SpecSize(测量大小)。
3、SpecMode中UNSPECIFIED(测量中),EXACTLY(match_parent和具体数值),AT_MOST(wrap_content)
4、LayoutParams在父容器的约束下转换对应的MeasureSpec,LayoutParams和父容器(的MeasureSpec)一起决定View的MeasureSpec。
5、子元素MeasureSpec的创建与父容器的MeasureSpec和自己的LayoutParams有关,还和margin,padding有关。
6、MeasureSpec测量结果,但是最终大小还是在layout阶段确定的,它们最终会变为相同。
7、直接继承View需要重写onMeasure并设置wrap_content是的大小,否则wrap_content相当于match_parent。
8、在onWindowFocusChanged,View.post(),ViewTreeObserver来获得页面大小,而不是0或者测量大小。
9、自定义View,注意支持wrap_content,注意支持padding(draw时候),尽量不用handler,本身就有post等接口,在onDetachedFromWindow销毁View中的动画和线程避免泄漏。
10、PendingIntent,待定意图,设置RemoteView使用,RemoteView可以理解为通知中心View,桌面小控件等其他进程View,RemoteView中支持的类型是有限的。
11、View的动画其实就是矩阵变换过程。
1、Window是抽象类,具体实现是PhoneWindow。
2、Window的访问接口是WindowManager,WindowManager的实现类是WindowManagerImpl,Window实际位于WindowManagerService,通过IPC和WindowManager交互。
3、Window本质上是管理View。
4、WindowManager的LayoutParams的Flag和Type,其中flag有:
FLAG_NOT_FOCUSABLE:window不接收输入事件,同时启用FLAG_NOT_TOUCH_MODAL。
FLAG_NOT_TOUCH_MODAL:会将window区域外的点击事件传递给底层Window。
FLAG_NOT_SHOW_WHEN_LOCKED:锁屏界面上显示。
Type表示类型有:
Window的层级数值由上至下,越来越大,而一般可选用系统层的TYPE_SYSTEM_OVELATY和TYPE_SYSTEM_ERROR类型来提高Window层级。
5、WindowManager继承于ViewManager,提供功能为addView(增加view)、updateViewLayout(更新View)、removeView(删除View),由此可见WindowManager操作Window其实是在管理View。
6、每一个Window对应一个View和ViewRootImpl,WindowManager的实现类是WindowManagerImpl,WindowManagerImpl通过WindowManagerGlobal工厂分发,WindowManagerGlobal中WindowSession通过Binder,让WindowManagerService完成Window添加。
7、WindowManagerGlobal中,mRoots存window对应的ViewRootImpl,mView存对应的View,mDyingViews存了真正需要移除的View,所以View的移除是异步的。
8、Activity的attach中,PolicyManager创建了Activity所属的Window。
9、Activity的onResume之后,DecorView才被添加到Window并显示。
10、普通Dialog不能用ApplicationContext,因为没有应用token,token一般只有Activity才有,但是系统Window可以不需要token,所以如果指定了Dialog的Window.LayoutParams.type也可以使用ApplicationContext。
11、Toast是基于Window的,定时消失采用了Handler,Toast和NotificationManagerService的交互属于IPC过程。NotificationManagerService也通过IPC回调Toast的TN类,TN是一个Binder类。因为TN运行在Binder线程池,所以是通过Handler切换到当前线程,因为使用了Handler,就意味着Toast无法在没有Looper的线程中弹出。另外,mToastQueue最多存在50个ToastRecord。
强烈推荐章节,了解它、爱上它、破解它。
startActivity有好几个重载方法,最终都会调用startActivityForResult。
1、startActivityForResult :
2、Instrumentation.execStartActivity :
3、ActivityManagerService :
4、ActivityStackSupervisor
5、ApplicationThread
send message。——-> 6
6、ActivityThread
1、从ActivityClientRecord获取代启动的Activity。
2、通过Instrumentation的newActivity使用类加载器创建Activiy对象。
3、LoadedApk的makeApplication创建Application,Instrumentation的callApplicationOnCreate触发Application的onCreate。
4、创建ContextImpl并通过Activity的attach完成初始化,如果Window关联,context关联。
5、InstrumentationcallActivitynOnCreate。
1、contextImpl :
2、ActivityManagerNative.getDefault() -> ActivityManagerService -> 3
3、ActivityManagerService -> 4
4、 ActiveServices :
5、app.thread(IApplicationThread) -> 6
6、ApplicationThread:
7、ActivityThread.handleCreateService :
1、contextImpl:
2、AndroidManagerService.bindService -> 3
3、ActiveServices
4、ApplicationThread scheduleBindService -> 5
5、ActivityThread handleBindService 调用ServiceConnection的onServiceConnected。
-
你需要了解,因为你经常需要它们。
1、ViewRootImpl在checkThread判断UI是否在主线程。
2、Handler创建时,会采用当前线程的Looper。内部通过ThreadLocal获取当前线程的Looper。
3、线程默认没有Looper,如果在没有Looper的线程中创建Handler会报错。UI线程时ActivityThread,它在创建时就初始化了Looper。
4、因为Looper 运行在创建Handler的线程中,Handler中的业务在处理时,就切换到创建Handler的线程中。
5、ThreadLocal时线程内部数据存储类,根据不同线程,返回现场作用域级别的数据。ThreadLocal操作的,时当前线程的localValues对象的table数组。
6、线程中。可以Looper.prepare()创建一个Looper,然后Looper.loop()启动一个Looper。通过quit()或者quitSafely()退出。创建Handler时,可以通过构造函数指定Looper。
7、Android的主线程时ActivityThread,系统通过Looper.prepareMainLooper(),创建主线程Looper()和MessageQueue并loop启动。ActivityThread的Handler时ActivityThread.H。
8、ActivityThread通过ApplicationThread和AMS通信,AMS完成后通知ApplicationThread,ApplicationThread发送消息至H,H将逻辑切换在ActivityThread中执行。
-
你需要了解,因为你同样经常需要它们。
1、Android中扮演线程的角色,有Thread、HandlerThead、IntentService,AsyncTask。AsyncTask底层使用线程池,HandlerThead、IntentService底层直接使用了线程。
2、AsycnTask必须在主线程中创建,4.1之后系统内部自动完成了,而excute方法必须在主线程中调用。1.6之前,AsycnTask是串行执行任务的;1.6之后并行;3.0之后串行,但是仍然有excecuteOnExecutor方法可以并行执行。
3、AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个InteralHandler。SerialExecutor用于任务的排队,THREAD_POOL_EXECUTOR真正的执行任务,InteralHandler用于切换到主线程,而InteralHandler的创建对象sHandler是一个静态变量,所以是在类创建时构建的,所以属于主线程创建的Handler。
4、HandlerThread继承于Thread,在run方法中通过Looper创建了Looper,所以运行在HandlerThread中创建Handler。HandlerThread的一个使用例子,便是IntentService。HandlerThread的run是一个无限循环,不使用时记得quit。
5、IntentService是一种特殊Service,是一个抽象类,适合执行一些优先级高的特殊后台任务,任务执行后它会自动停止,其内部封装了HandlerThread和Handler,其Handler是通过HandlerThread的Looper创建的。
6、IntentServie通过mServiceHandler发送了一个消息,消息在HandlerThread中处理,之后在mServiceHandler的onHandleIntent中执行处理后,在执行完队列中所有任务后,会执行stopSelf。
7、Android中线程池来源于Executor接口,实现的有ThreadPoolExecutor,ThreadPoolExecutor的执行任务逻辑为:
8、封装版本的ThreadPoolExecutor,有:
拓展你的知识面吧
1、动态加载技术:不同的插件化工具,都集中在主要解决三个基础问题:资源访问、四大组件的启动和管理、ClassLoader的管理。
2、Activity的主要工作是通过ContextImpl完成,对应mBase的内部成员变量,而context有两个抽象方法,其中getAssets()和getResources()都是和资源相关。所以我们可以创建一个AssetManager,然后通过反射加插件apk的路径设置addAssetsPath中,在通过AssetsManager创建出Resources,这样创建出来的Resources就可以访问apk中的资源文件。
-
拓展你的知识面吧
1、布局优化:
2、内存泄漏:如静态变量持有Activity,单例模式下持有的对象未释放,动画未在页面结束时取消等。