基本组件
1.activity
running(栈顶 ---paused(可见,失去交互能力,没有焦点-----stopped(不可见,完全覆盖----killed(已回收
oncreate预加载---onstart可见,不可交互-----onresume可交互
android进程优先级
前台--可见--服务--后台--空
注意:
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3、设置Activity的android:configChanges="orientation|keyboardHidden|screensize"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
横竖屏
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
比方说我们点击跳转一个新Activity,这个时候Activity会入栈,同时它的生命周期也会从onCreate()到onResume()开始变换,这个过程是在ActivityStack里完成的,ActivityStack是运行在Server进程里的,这个时候Server进程就通过ApplicationThread的代理对象ApplicationThreadProxy向运行在app进程ApplicationThread发起操作请求。
ApplicationThread接收到操作请求后,因为它是运行在app进程里的其他线程里,所以ApplicationThread需要通过Handler向主线程ActivityThread发送操作消息。
主线程接收到ApplicationThread发出的消息后,调用主线程ActivityThread执行响应的操作,并回调Activity相应的周期方法。
Activity:通过LoadedApk的makeApplication()方法创建。
Service:通过LoadedApk的makeApplication()方法创建。
2.service
不可做耗时操作,运行在主线程中
因为activity被销毁后无法对thread进行控制,不能获取之前子线程实例,所以需要用到service
启动方式:
1.startservice(intent) stopservice
2.bindservice unbindservice
使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服 务仍然运行。
使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。
如果打算采用startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStartCommand()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStartCommand()方法。采用startService()方法启动的服务,只能调用stopService()方法结束服务,服务结束时会调用onDestroy()方法。(由于会多次调用onStartCommand方法,所以可以通过intent来传值)
如果打算采用bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用,但是多次执行bindService)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。(由于onBind只会调用一次,而onBind又会传入intent参数,所以intent的值不会改变,个人认为本实例不适合bindService)
让service和activity交互,oncreate - > onbind ->onserviceconnected
a.服务端创建Ibinder接口实现实例,给客户端调用
b.用onBind()回调方法返回Binder实例
c.客户端用new serviceconnction,重写onserviceConnectd回调方法接收IBinder,并用bindservice绑定服务
onServiceDisconnected 在服务意外终止时会调用
onUnbind会destroy服务
在Activity中创建一个thread跟在service中创建一个thread之间的区别?
在Activity中被创建:该Thread的就是为这个Activity服务的,完成这个特定的Activity交代的任务,主动通知该Activity一些消息和事件,Activity销毁后,该Thread也没有存活的意义了。
在Service中被创建:这是保证最长生命周期的Thread的唯一方式,只要整个Service不退出,Thread就可以一直在后台执行,一般在Service的onCreate()中创建,在onDestroy()中销毁。所以,在Service中创建的Thread,适合长期执行一些独立于APP的后台任务,比较常见的就是:在Service中保持与服务器端的长连接。
3.broadcast
类似java观察者模式
定义:发送携带数据的intent来广播
1.normal broadcast sendBroadcast
2.有序广播 包括系统广播 sendOrderedBroadcast
3.local broadcast app内
静态注册 manifest 一直运行,app杀死也能收到
动态注册,更灵活,跟随activity生命周期,需要unregist
实现机制:
1.自定义broadcastReceiver,复写onreceive
2.通过binder机制向AcitivityManagerService注册
3.发送者通过binder机制向AcitivityManagerService发送广播
4.AcitivityManagerService查找符合条件(intentfilter/permission)的receiver,将其发到相应receiver的消息循环队列中
5.消息循环拿到此广播,回调broadcastreceiver的onreceive方法
local broadcast
1.app内传播,不担心数据泄漏
2.不可接受其他应用广播
3.比系统广播高效
高效的原因是因为内部通过关联主线程getlooper handler实现,sendbroadcast其实通过handler的sendMessage实现
2.内部协作主要靠两个hashmap, mReceivers和mActions,还有mPendBroadCasts 接受的广播对象,存储广播接收器
PackageManager是包管理服务,在安装的时候,就会将静态注册的广播保存下来。发送对应广播的时候,AMS就会根据intent去查找PackagManager保存的静态注册receiver,若进程还没起来,会先启动进程,然后调用receiver的onReceive函数。
4.view
绘制原理https://www.jianshu.com/p/6bdb3b50d788
由于onCreate会先于handleResumeActivity执行,我们在onCreate中调用了setContentView,也只是生成DecorView并给这个DecorView的内容设置了布局而已,此时还并没有把这个DecorView添加到Window中,同样,WMS中也还没有这个Window,所以此时并不能做任何事情(绘制、接收点击事件等),虽然调用了requestLayout和invalidate,并不会真正触发布局和重绘(因为还没有与ViewRootImpl进行绑定)
Activity与Window产生联系,是在调用activity#attach方法中,生成了一个PhoneWindow,并把这个activity对象自身,设置给了Window的Callback回调(Activity实现了Window的Callback接口)
Window与WindowManagerService产生联系,是在handleResumeActivity中,先执行了onResume方法,通过调用WindowManagerImpl#addView方法将这个Activity对应的DecorView添加到了这个Window中,addView方法是一个IPC操作,将这个Window也添加到了WindowManagerService中;
ViewRootImpl与Window产生联系,是在WindowManagerImpl#addView方法中,这个过程中会new一个ViewRootImpl与DecorView相对应,保存在WindowManagerGloable中;
总的来说,就是setContentView生成了DecorView及其视图,在onResume之后才把这个视图添加进了Window和WMS中,具备了交互能力;
布局查看工具view inspector 取代了hierachy viewer,但是没有性能查看功能,hv仍然可以在sdk tools里找到
measure--layout--draw
measure
自上而下遍历,每个ViewGroup会向它内部的每个子View发送measure命令,然后由具体子View的onMeasure()来测量自己的尺寸。最后测量的结果调用setMeasureDimension()保存在View的mMeasuredWidth和mMeasuredHeight中
1.viewGroup.LayoutParams 视图高宽
2.MeasureSpec 测量规格,传递父布局对子View尺寸测量的约束信息 32位int 3种模式1.不确定 2.exactly 3.atMost mode+大小
子view的layoutPrams结合父容器的mode生成子view的measureSpec
就是ScrollView内部嵌套ListView,而该ListView数据条数是不确定的,所以需要设置为包裹内容,然后就会发现ListView就会显示第一行出来。需要继承ListView,覆写onMeasure方法。
scrollview的super.onInterceptTouchEvent就不会拦截listview的滑动
measure的过程
framelayout 和relativelayout会measure两次
layout
子视图的具体位置是相对父视图而言,view.onlayout方法是抽象实现,必须重新实现,自上而下拜访
draw
invalidate()
请求重绘 View 树,即 draw 过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些调用了invalidate()方法的 View。
requestLayout()
当布局变化的时候,比如方向变化,尺寸的变化,会调用该方法,在自定义的视图中,如果某些情况下希望重新测量尺寸大小,应该手动去调用该方法,它会触发measure()和layout()过程,但不会进行 draw。
5.fragment
Fragment实现滑动可以借助ViewPager。ViewPager为了让滑动的时候防止出现卡顿现象,它的内部有一个缓存机制,默认情况下,ViewPager会提前创建好当前Fragment旁的两个Fragment。但是如果加载的数据比较耗时或者占用内存较大,就需要考虑是否实现懒加载来加载fragment。
加载方式:
1.xml
2.动态加载 fragmentManager管理,fragmentTrancaction.add ----commit() //add和replace的区别,replace替换最上方的F
页面较少使用 FragmentpagerAdapter,只会detach fragment, 较多使用FragmentStatePagerAdapter,会释放不用的fragment,节约内存
生命周期
onattach--onCreate--onCreateView---onViewCreated--(Activity onCreate)---onActivityCreated--(Acitivity onstart)--onStart--(Activity onResume)---onResume
onpause--(Activity onpause)---onstop---(Activity onstop)--onDestroyView--onDestroy--onDettach--(Activity onedestroy)
Fragment和acitivity互相调用
1.fragment调用getActivity
2.Activity调用F,接口回调,F中定义接口,activity实现
使用三方库,Eventbus实现,具体怎么实现百度Eventbus用法
广播
使用观察者模式
3.F调用F,找到另一个activity,然后调用另一个F的实例
8.事件分发机制
dispatchevent 决定是触摸事件是由自己的ontouchevent处理还是分发给子view,并递归调用自身dispatchevent使之向下传递
oninterceptTouchEvent是ViewGroup的方法, 父控件下发事件给子控件处理,若true子控件需要拦截,那dispatchevent 来调用onTouchEvent,返回false继续下发,返回true表示已拦截
在Action_Down的情况下,事件会先传递到最顶层的ViewGroup,调用ViewGroup的dispatchTouchEvent(),
①如果ViewGroup的onInterceptTouchEvent()返回false不拦截该事件,则会分发给子View,调用子View的dispatchTouchEvent(),如果子View的dispatchTouchEvent()返回true,则调用View的onTouchEvent()消费事件。
②如果ViewGroup的onInterceptTouchEvent()返回true拦截该事件,则调用ViewGroup的onTouchEvent()消费事件,接下来的Move和Up事件将由该ViewGroup直接进行处理
当某个子View的dispatchTouchEvent()返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下来的Move和Up事件将由该子View直接进行处理。
Activity---phoneWindow---decorview--viewGroup---子view
分发到最后也没有消费哦,事件依次反转,最后回到activity,若activity也不处理,则放弃
10.webview
1.APi16前有安全漏洞,没有限制addJavascriptInterface ,远程攻击者可以执行任何对象的方法
2.布局文件中使用,webview写在其他容器中时,当destroy时,需要从容器中remove webview,然后webview.destroy,不然内存泄漏
3.jsbridge 本地native和远程js互相调用
4.页面加载完成时调用webView.onPageFinished用webChrome.onProgressChanged
5.后台耗电,webview没正常销毁,webview有时会开线程
6.webView硬件加速导致页面渲染问题:webView设置硬件加速有可能会出现闪烁,加载白块等现象,解决问题的方法就是关闭webView硬件加速。
解决方法
1、独立进程,简单暴力,不过可能涉及到进程间通信(和handler持有外部引用一样
2、动态添加WebView,对传入WebView中使用的Context使用弱引用,动态添加WebView意思是在布局创建一个ViewGroup用来放置WebView,activity创建时add进来,在Activity停止时remove掉。
开发中,webview使用完毕直接干掉进程
11.Binder
https://www.jianshu.com/p/afa794939379
什么是binder
1.binder是一种跨进程的通信机制
2.对server来说,binder指binder本地对象,对client来说,binder是proxy对象
3.对传输过程而言,binder是可以跨进程的对象,用内核转换
为什么用binder,比socket性能好,更安全
在android系统当中,它是运行在内核当中,它负责各个用户进程,通过binder通讯的内核来进行交互的一个模块。
binder通信模型:
1.server端在serviceManager中注册方法attachinterface
2.client端去sm中查询此方法
3.sm给client一个proxy对象的方法(空方法
4.内核在client调用此方法时会调用server端的方法
如果是自己写的service是同一进程是Binder,也就是直接调用,这个在编译后生成的IXXXX.java的asInterface中能看到
如果调用系统的service,显然是跨进程的,是BinderProxy,这里也是在asInterface中转换的。
AIDL 是对Binder的一种封装
全自动生成stub的静态内部抽象类,建立binder本地对象,需要自己实现方法
1.创建一个aidl文件,在android studio中直接右键->new->aidl->aidl file
2.创建一个Service,使用一个内部类继承Stub,实现其中的方法;重写OnBind()方法,返回这个内部类的实例。
3.将该aidl文件复制到要调用接口的项目中,其中包名也要和原项目相同。接着编译该项目。
4.在新项目中连接远程服务,重写onServiceConnected方法,通过Stub的asInterface方法将IBinder对象转换成相应的aidl类,最后就能通过这个aidl类做爱做的事了。
原理:
进程A与进程B进行通信,二者都含有一个相同的aidl文件。假设A要将消息发送给B,则A中的Proxy将消息发送到系统IBinder中,IBinder再将该消息发送到B中的Stub。即Proxy是发送方,Stub是接受方。
在Stub的构造方法中有一个attachInterface方法,这是Binder类中的方法,它将当前的AIDL对象进行注册到系统中,用于之后判断是本地调用还是远程调用
asInterface方法,
asInterface方法用于在客户端获取aidl对象。该方法获取IBinder对象后,将IBinder中的DESCRIPTOR与本地的DESCRIPTOR比较,若相同,则直接返回本地的AIDL对象;若不同,再通过Proxy创建一个AIDL对象。
onTransact方法,根据AIDL函数返回编号进行相应方法调用
12Listview
recyclebin机制,内部类,只显示 暴露的几条item
优化
getview用convertview参数,缓存view
viewholder封装view,避免多次findviewbyid,控件过多应i选哪个性能
getview少做耗时操作,不然滑动卡顿,可以加监听,滑动停止时再加载,减少半透明缓存
添加头布局必须在setAdapter之前
13.launcher流程
Launcher也是个应用程序,不过是个特殊的应用。俗称“桌面”。通过PackageManagerService查询所有已安装的应用程序,并保存相应的图标、应用名称、包名和第一个要启动的类名等。
总体流程
点击桌面应用图标,Launcher进程将启动Activity(MainActivity)的请求以Binder的方式发送给了AMS。
AMS接收到启动请求后,交付ActivityStarter处理Intent和Flag等信息,然后再交给ActivityStackSupervisior/ActivityStack
处理Activity进栈相关流程。同时以Socket方式请求Zygote进程fork新进程。
Zygote接收到新进程创建请求后fork出新进程。
在新进程里创建ActivityThread对象,新创建的进程就是应用的主线程,在主线程里开启Looper消息循环,开始处理创建Activity。
ActivityThread利用ClassLoader去加载Activity、创建Activity实例,并回调Activity的onCreate()方法。这样便完成了Activity的启动。
14 activity启动模式
singleTop的使用
使用singleTop模式的Activity在栈顶时只会在Task中存在一个实例
防止用户多次触发startActivity,可以将目标Activity设置为singleTop
singleTask的使用
当Task中存在Activity实例,不会创建Activity,而是销毁Activity上面所有其他的Activity,以此来使将要跳转的Activity至于栈顶显示,如果不存在,则在栈顶创建一个Activity实例(所以假如有人问如何快速关闭100个Activity,只要给跳转的Activity设置singleTask即可)
一般都是主页和登陆页。
singleTask的Activity如果设置了独立的taskAffinity属性值,启动时就会在新的Task中,否则会在已有Task中
15contentporvider
public boolean onCreate()
在创建ContentProvider时使用
public Cursor query()
用于查询指定uri的数据返回一个Cursor
public Uri insert()
用于向指定uri的ContentProvider中添加数据
public int delete()
用于删除指定uri的数据
public int update()
用户更新指定uri的数据
public String getType()
用于返回指定的Uri中的数据MIME类型
注册ContentObserver来监听对应uri的数据变化,这步不是必须的,如果不需要监听数据变化也可以不注册
在构造函数中我们需要传递一个Handler,到此我们可以明白,ContentObserver在收到数据变化的通知后通过Handler机制来通知主线程更新UI
通过ContentProvider来操作数据