欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android开发艺术探索阅后提炼

程序员文章站 2022-07-10 10:50:17
二周目Android开发艺术探索这本书,真的是一本很不错的干货,不管是新手或者老猿,都能从中有所收获。这里就把一些阅后总结提炼出来,希望能对你工作或者面试有帮助。 一、Act...

二周目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,单例模式下持有的对象未释放,动画未在页面结束时取消等。