二周目Android开发艺术探索这本书,真的是一本很不错的干货,不管是新手或者老猿,都能从中有所收获。这里就把一些阅后总结提炼出来,希望能对你工作或者面试有帮助。

一、Activity生命周期与启动方式

这是新手很容易忽略的地方,很是需要系统的了解下。

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:

  • 如果存在任务栈,就使用当前任务栈。
  • 如果任务栈中存在Activity,就使用已创建的Activity。
  • 默认带有clearTop效果,因为先进后出,推到栈顶需要干掉其他的。
  • 如栈1中AB启动了栈2中CD的C,因为singleTask,D会被出栈。
  • 如果栈1中AB启动了栈2中的CD的D,那么会back会是D-C-B-A。
  • TaskAffinity 标志任务栈的名称。
  • allowTaskReparenting 表示Activity的应用栈可以转移。

9、sinagleInstance:独立的一个栈,全局唯一,直到任务栈销毁。

二、IPC机制

多进程,跨进程,相信你总会有用到的时候。最好多去研究IBinder

1、’:remote’属于私有子进程,而’xx.xxx.xx:remote’属于全局进程。

2、多进程下:

  • 静态成员和单例模式完全失效。
  • 线程同步(锁)完全失效。
  • SharePreferences不可靠,因为多进程读取xml。
  • Application多次创建。

3、Serializable接口序列化,在指定serialVersionUID,可以在修改了model后还能交易有效,实现数据的一部分恢复。

三、View的事件体系

强烈推荐章节,日常开发中可以帮你少走很多弯路。

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时,父容器才可以收到。

四、View的工作原理

强烈推荐章节,日常开发中可以帮你更加优雅。

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的动画其实就是矩阵变换过程。

八、理解Window和WindowManager

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:对应Activity。
  • 子Window:不能单独,需要衣服有父Window,如Dialog。
  • 系统Window:比如Toast、系统状态栏。

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。

九、四大组件的工作过程:

强烈推荐章节,了解它、爱上它它。

Activity的启动过程

startActivity有好几个重载方法,最终都会调用startActivityForResult。

1、startActivityForResult :

  • Instrumentation.execStartActivity ——-> 2
  • ApplicationThread.sendActivityResult

2、Instrumentation.execStartActivity :

  • ActivityManagerNative.getDefult().startActivity启动。(ActivityManagerNative是一个Binder,实现是ActivityManagerService)——-> 3
  • checkStartActivityResult 校验启动结果,比如是否注册AndroidManifest。

3、ActivityManagerService :

  • ActivityManagerService.startActivity ——-> 4

4、ActivityStackSupervisor

  • startActivityMayWait
  • startActivityLocked
  • startActivityUncheckedLocked
  • ActivityStack.resumeTopActivitiesLocked
  • ActivityStack.resumeTopActivitiesInnerLocked
  • startSpecificActivityLocked
  • realStaratActivityLocked:ApplicationThread.scheduleLaunchActivity ——-> 5

5、ApplicationThread

send message。——-> 6

6、ActivityThread

  • handleLaunchActivity
  • performLaunchActivity完成了创建和启动:
1、从ActivityClientRecord获取代启动的Activity。
2、通过Instrumentation的newActivity使用类加载器创建Activiy对象。
3、LoadedApk的makeApplication创建Application,Instrumentation的callApplicationOnCreate触发Application的onCreate。
4、创建ContextImpl并通过Activity的attach完成初始化,如果Window关联,context关联。
5、InstrumentationcallActivitynOnCreate。
  • handleResumeActivity触发onResume

Service的启动过程

startService

1、contextImpl :

  • startService
  • startServiceCommon -> 2

2、ActivityManagerNative.getDefault() -> ActivityManagerService -> 3

3、ActivityManagerService -> 4

4、 ActiveServices :

  • startServiceLocked
  • startServiceInnerLocked
  • bringUpServiceLocked
  • realStartServiceLocked -> 5

5、app.thread(IApplicationThread) -> 6

6、ApplicationThread:

  • scheduleCreateService
  • send Message H.CREAT_SERVICE) -> mH的Handler -> 7

7、ActivityThread.handleCreateService :

  • 类加载器是实例化Service 。
  • Service 的attach建立context联系。
  • 调用Service的onCreate并保存到ActivityThread。
  • 调用onStartCommand。
bindService

1、contextImpl:

  • bindService
  • bindServiceCommon -> ServiceDispatcher.InnerConnection对象 (ServiceDispatcher链接ServiceConnection和InnerConnection作用) -> 2

2、AndroidManagerService.bindService -> 3

3、ActiveServices

  • bindServiceLocked
  • bringUpServiceLocked
  • realStartServiceLocked -> 4

4、ApplicationThread scheduleBindService -> 5

5、ActivityThread handleBindService 调用ServiceConnection的onServiceConnected。

  -
  
  

十、Android的消息机制

你需要了解,因为你经常需要它们。

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中执行。

  -
  
  

十一 Android的线程和线程池

你需要了解,因为你同样经常需要它们。

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的执行任务逻辑为:

  • 如果线程池未达到核心线程数,那么启动一个核心线程。
  • 如果线程池已满,那么插入队列等待。
  • 如果无法插入队列,但是线程数未达到规定最大,那么启动非核心线程。
  • 如果无法插入队列,线程数达到规定最大,那么拒绝执行,回调rejectedExecution。

8、封装版本的ThreadPoolExecutor,有:

  • FixedThreadPool :固定线程池,空闲不回收。
  • CachedThreadPool:线程数不固定,空闲线程60秒后回收,适合执行大量耗时较少的任务。
  • ScheduledThreadPool:核心线程固定,非核心线程不限制,非核心空闲立即回收,主要在执行定时任务和固定周期任务。
  • SingleThreadExecutor:只有一个核心线程,顺序执行,所有任务都在一个线程中。
      
      -
      

十三、综合技术

拓展你的知识面吧

1、动态加载技术:不同的插件化工具,都集中在主要解决三个基础问题:资源访问、四大组件的启动和管理、ClassLoader的管理。

2、Activity的主要工作是通过ContextImpl完成,对应mBase的内部成员变量,而context有两个抽象方法,其中getAssets()和getResources()都是和资源相关。所以我们可以创建一个AssetManager,然后通过反射加插件apk的路径设置addAssetsPath中,在通过AssetsManager创建出Resources,这样创建出来的Resources就可以访问apk中的资源文件。
  
  -
  

十五、Android性能优化

拓展你的知识面吧

1、布局优化:

  • < include >标签实现布局重复利用,其中android:id冲突时,以include的android:id为准。
  • < merge >标签用于减少布局层级,通过配合include标签,merge可以减少一层布局,为include内部的布局合并到外布局中。
  • ViewStub 轻量级且高和宽都是0,它本身不参与任何布局,主要用于实现按需加载。

2、内存泄漏:如静态变量持有Activity,单例模式下持有的对象未释放,动画未在页面结束时取消等。