基础知识(二)android
android四种启动模式:
任务栈的形式存储。
standard: 每次产生一个新的实例,放于栈顶。
singleTop:实例位于栈顶,不再产生新的活动实例,可用于接收后台消息和弹出消息等。
singleTask:实例位于任务栈内,上层的实例统统出栈,可用于app入口点或新闻界面等。
singleInstance:一个活动放进一个新的栈中,适用于与app分列的界面或跨进城通信等。
activity生命周期:要点提炼|开发艺术之Activity - 简书
a.onCreate():
状态:Activity正在创建
任务:做初始化工作,如setViewContent界面资源、初始化数据
注意:此方法的传参Bundle为该Activity上次被异常情况销毁时保存的状态信息。
b.onStart():
状态:Activity正在启动,这时Activity可见但不在前台,无法和用户交互。
c.onResume():
状态:Activity获得焦点,此时Activity可见且在前台并开始活动。
d.onPause():
状态: Activity正在停止
任务:可做数据存储、停止动画等操作。
注意:Activity切换时,旧Activity的onPause会先执行,然后才会启动新的Activity。
e.onStop():
状态:Activity即将停止
任务:可做稍微重量级回收工作,如取消网络连接、注销广播接收器等。
注意:新Activity是透明主题时,旧Activity都不会走onStop。
f.onDestroy():
状态:Activity即将销毁
任务:做回收工作、资源释放。
g.onRestart():
状态:Activity重新启动,Activity由后台切换到前台,由不可见到可见。
onSaveInstanceState和onRestoreInstanceState如下三种情况调用:
a.出现时机:异常 情况下Activity 重建,非用户主动去销毁
b.系统异常终止时,调用onSavaInstanceState来保存状态。该方法调用在onStop之前,但和onPause没有时序关系。
c.Activity被重新创建时,调用onRestoreInstanceState(该方法在onStart之后),并将onSavaInstanceState保存的Bundle对象作为参数传到onRestoreInstanceState与onCreate方法。
Android下拉通知栏不会影响Activity的生命周期方法
Android的进程优先级:
android的系统进程分为五个等级:
Foreground Process(前台进程)
Visible Process(可见进程)
Service Process(服务进程)
Background Process(后台进程)
Empty Process(空进程)
window与activity、view:
a.Window&PhoneWindow:
Window是一个抽象类,它定义了顶级窗体样式和行为。其唯一的实现类是PhoneWindow。
b.Window&View:
每个Window都对应一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系。Window并不可见,它实际以View的形式存在,它是View的直接管理者。
c.Window&WindowManager:
实际使用中无法访问Window,对Window的访问必须通过WindowManager,对Window的操作通过它完成。
在自定义控件时候,可以使用windowmanger来指定控件在整个窗口的位置和大小。
View和SurfaceView的区别:
SurfaceView中采用了双缓存技术,在单独的线程中更新界面View在UI线程中更新界面。
当程序需要更新View上的图像时,程序必须重绘View上显示的整张图片新线程无法直接更新View组件。
View事件及工作流程:要点提炼|开发艺术之View - 简书
1.View的位置参数
a.Android坐标系:以屏幕的左上角为坐标原点,向右为x轴增大方向,向下为y轴增大方向。
b.View的位置由它的四个顶点决定,分别对应View的四个属性:top、left、right、bottom。其中left是左上角的横坐标,right是右下角的横坐标,top是左上角的纵坐标,bottom是右下角的纵坐标。注意这些坐标是相对于view父容器而言,是一种相对的坐标。
c.从android3.0开始,View增加了额外几个参数:x,y,translationX、translationY。其中x和y是View左上角的坐标,translationX和translationY是View左上角相对于父容器的偏移量,它们默认值是0。这些参数也是相对于View父容器。具体关系见下图:
2.触控系列
a.MotionEvent:是手指触摸屏幕锁产生的一系列事件。典型事件有:
ACTION_DOWN:手指刚接触屏幕
ACTION_MOVE:手指在屏幕上滑动
ACTION_UP:手指在屏幕上松开的一瞬间
b.TouchSlop:系统所能识别的被认为是滑动的最小距离。即当手指在屏幕上滑动时,如果两次滑动之间的距离小于这个常量,那么系统就不认为你是在进行滑动操作。
c.VelocityTracker:速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度。
d.GestureDetector:手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。Android手势检测——GestureDetector全面分析 - 简书
3.滑动系列
a.实现View滑动三种办法:
①通过View本身提供的scrollTo/scrollBy方法:scrollTo为绝对滑动,scrollBy为相对当前位置的滑动,两者都是view内容的滑动,不是view本身的移动,这点非常特殊。两个都不是平滑的滑动,是一瞬间过去的,若要平滑滑动可以采用Scroller,属性动画以及postdelay延时方法。
②通过动画给View施加平移效果:主要通过改变View的translationX和translationY参数来实现。可用view动画,也可以采用属性动画,如果使用属性动画的话,为了能够兼容3.0以下版本,需要采用开源动画库nineoldandroids。注意View动画的View移动只是位置移动,并不能真正的改变view的位置,而属性动画可以。
③通过改变View的LayoutParams使得View重新布局:比如将一个View向右移动100像素,向右,只需要把它的marginLeft参数增大即可。
4.事件分发机制
点击事件的传递顺序:Activity(Window) -> ViewGroup -> View
dispatchTouchEvent:进行事件的分发(传递)。返回值是 boolean 类型,受当前onTouchEvent和下级view的dispatchTouchEvent影响
onInterceptTouchEvent:对事件进行拦截。该方法只在ViewGroup中有,View(不包含 ViewGroup)是没有的。一旦拦截,则执行ViewGroup的onTouchEvent,在ViewGroup中处理事件,而不接着分发给View。且只调用一次,所以后面的事件都会交给ViewGroup处理。
onTouchEvent:进行事件处理。
view滑动冲突及解决:一文解决Android View滑动冲突 - 简书
onTouch()、onTouchEvent()、onClick()简单说明:
优先级:onTouch()>onTouchEvent()>onClick()
一旦被消费掉,则不会触发下一层的事件,如下面三句描述:
onTouchListener的onTouch方法优先级比onTouchEvent高,会先触发。
假如onTouch方法返回false会接着触发onTouchEvent,反之onTouchEvent方法不会被调用。
内置诸如click事件的实现等等都基于onTouchEvent,假如onTouch返回true,这些事件将不会被触发。
5.view工作原理:
1.View工作流程:measure测量->layout布局->draw绘制
measure确定View的测量宽高
layout确定View的最终宽高和四个顶点的位置
draw将View绘制到屏幕上
对应onMeasure()、onLayout()、onDraw()三个方法。具体过程如下图所示:
1.measure自定义View Measure过程 - 最易懂的自定义View原理系列(2) - 简书
三种模式:
UNSPECIFIED:父容器不对View有任何限制,要多大有多大。常用于系统内部。
EXACTLY(精确模式):父视图为子视图指定一个确切的尺寸SpecSize。对应LyaoutParams中的match_parent或具体数值。
AT_MOST(最大模式):父容器为子视图指定一个最大尺寸SpecSize,View的大小不能大于这个值。对应LayoutParams中的wrap_content。
MeasureSpec:
作用:通过宽测量值widthMeasureSpec和高测量值heightMeasureSpec决定View的大小
组成:一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpecSize( 某种测量模式下的规格大小)。
MeasureSpec和LayoutParams关系:系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec。MeasureSpec由LayoutParams和父容器共同决定。
2.layout过程:确定View的最终宽高和四个顶点的位置(3)自定义View Layout过程 - 最易懂的自定义View原理系列 - 简书
从顶级View开始依次调用layout(),其中子View的layout()会调用setFrame()来设定自己的四个顶点(mLeft、mRight、mTop、mBottom),接着调用onLayout()来确定其坐标,注意该方法是空方法,因为不同的ViewGroup对其子View的布局是不相同的。
3.draw过程:绘制到屏幕(4)自定义View Draw过程- 最易懂的自定义View原理系列 - 简书
绘制顺序:
绘制背景:background.draw(canvas)
绘制自己:onDraw(canvas)
绘制children:dispatchDraw(canvas)
绘制装饰:onDrawScrollBars(canvas)
获取自定义view大小的方法:
onWindowFocusChanged方法中获取:
onWindowFocusChanged方法执行时代表View已经初始化完毕了,宽度和高度已经测量完毕并且最终确认好了,这时我们可以放心的在这里去获取View 的宽度和高度。
View.post(new Runnable())方法中获取:
通过View.post方法把Runnable对象放到消息队列的末尾,当执行到这个runable方法的时候,View所有的初始化测量方法说明都已经执行完毕了。因此在这里获取的时候就是测量后的真实值。
Fragment:学习笔记| AS入门(六) 碎片Fragment - 简书
它的出现是为了解决Android碎片化 ,它可作为Activity界面的组成部分,可在Activity运行中实现动态地加入、移除和交换。一个Activity中可同时出现多个Fragment,一个Fragment也可在多个Activity中使用。
生命周期如图所示:
onAttach():当Fragment和Activity建立关联时调用
onCreateView():当Fragment创建视图时调用
onActivityCreated():当与Fragment相关联的Activity完成onCreate()之后调用
onDestroyView():在Fragment中的布局被移除时调用
onDetach():当Fragment和Activity解除关联时调用
activity加载fragment分为动态和静态两种方式:
静态加载为直接在布局文件中指定相应的fragment,并不常用。
动态加载在布局文件中使用framelayout等或者viewstub占据位置(可用于根据不同的操作加载不同的布局,例如新闻界面,体育赛事这类的应用)
接下来按照如下三步:第一步,先用getFragmentManager()方法获取一个FragmentManager对象,再通过它的beginTransaction()获取一个FragmentTransaction的实例。
第二步,用beginTransaction.add()方法将MyFragemnt实例添加到main布局里LinearLayout里,终于知道之前铺垫的Id是怎么回事了。一定要注意,add()方法里的第一个参数是容器视图资源Id,而不是layout。容器视图资源Id有两个作用:告知FragmentManager,碎片视图应该出现在活动视图的什么地方;它也是FragmentManager队列中碎片的唯一标识符。而静态加载时碎片的唯一标识符正是在活动布局里< fragment>下的id。
第三步:调用beginTransaction.commit()提交。另外,如果允许用户通过按下返回按键返回到前一个Fragment状态,在调用commit()之前先调用addToBackStack(true)方法。
fragment和activity通信:
在activity中可以通过FragmentManager得到fragment的实例,在fragment中也可以getactivity得到相应依附的活动。
三种方式交互:
1.接口方式:在fragment类中定义一个接口并在Activity中实现它。Fragment在onAttach()回调函数中获取接口的具体实现的对象。后面,fragment就可以调用接口中的方法实现与Activity的通信。
2.广播:在Activity中注册广播接收器,在Fragment中发送广播。
3.Fragment 从Activity获取数据:在Fragment 的getInstance()方法中setArguments()设置参数,
再通过getArguments()方法获取。
service服务:学习笔记| AS入门(十) 组件篇之Service - 简书
Service的运行不依赖于任何用户界面,因此即便程序被切换到后台或者用户打开了另一个应用程序,Service仍能够保持正常运行。但当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。实际上Service默认执行在主线程中(UI线程)。换句话说,不要在Service里执行耗时操作,除非你手动打开一个子线程,否则有可能出现主线程被阻塞(ANR)的情况。
ANR大致的时间:
前台服务20s,后台服务10s,广播10s,按键无响应5s
service分类:
普通Service
前台Service
系统Service
两种启动服务的生命周期:
onCreate():服务第一次被创建时调用
onStartComand():服务启动时调用
onBind():服务被绑定时调用
onUnBind():服务被解绑时调用
onDestroy():服务停止时调用
第一种:Context的startService()方法可以启动一个Service,并回调服务中的onStartCommand()。如果该服务之前还没创建,那么回调的顺序是onCreate()->onStartCommand()。服务启动了之后会一直保持运行状态,直到stopService()或stopSelf()方法被调用,服务停止并回调onDestroy()。另外,无论调用多少次startService()方法,只需调用一次stopService()或stopSelf()方法,服务就会停止了。(连续点击启动服务oncreate也不会重复执行)
第二种:Context的bindService()可以绑定一个Service,并回调服务中的onBind()方法。类似地,如果该服务之前还没创建,那么回调的顺序是onCreate()->onBind()。之后,调用方可以获取到onBind()方法里返回的IBinder对象的实例,从而实现和服务进行通信。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态,直到调用了unbindService()方法服务会停止,回调顺序onUnBind()->onDestroy()。
两种形式并不冲突,start后仍然可以bind。
前台Service
前台服务和普通服务最大的区别是,前者会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。使用前台服务或者为了防止服务被回收掉,比如听歌,或者由于特殊的需求,比如实时天气状况。想要实现一个前台服务非常简单,它和之前学过的发送一个通知非常类似,只不过在构建好一个Notification之后,不需要NotificationManager将通知显示出来,而是调用了startForeground()方法。
普通Service
第一步:新建类并继承Service且必须重写onBind()方法,有选择的重写onCreate()、onStartCommand()及onDestroy()方法。
第二步:在配置文件中进行注册。学到现在会发现,四大组件除了广播接收器可用动态注册,定义好组件之后都要完成在配置文件注册的这一步。
第三步:在活动中利用Intent可实现Service的启动。
系统Service
通过getSyetemService()方法并传入一个Name就可以得到相应的服务对象了,常用的系统服务有:电源管理、闹钟服务,状态栏服务等。
无指定活动,服务监控网络状态,弹出系统级dialog方法:声明系统级弹窗的权限,使用WindowManager 的addView()方法弹窗,服务监测网络状态,触发弹窗,此方法不依赖具体活动。
IntentService
为了可以简单地创建一个异步的、会自动停止的服务,Android 专门提供了一个IntentService类。
第一步:新建类并继承IntentService,在这里需要提供一个无参的构造函数且必须在其内部调用父类的有参构造函数,然后具体实现onHandleIntent()方法,在里可以去处理一些耗时操作而不用担心 ANR的问题,因为这个方法已经是在子线程中运行的了。
第二步:在配置文件中进行注册。
第三步:在活动中利用Intent实现IntentService的启动,和Service用的方法是完全一样的。
HandlerThread在线程种负责发送消息,通过onStartCommand传递给服务intent,创建工作队列发送intent给onHandlerIntent。
Service与Activity的通信:
第一步:在MyService里自定义一个类MyBinder并继承Binder。
第二步:在MyService的onBind()方法里返回刚刚定义好的MyBinder类。
第三步:在活动中实例化一个ServiceConnection类,并重写它的onServiceConnection()和onServiceDisconnection()方法,这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用。在 onServiceConnected()方法中,又通过向下转型得到了MyBinder 的实例。
第四步:在活动布局里再准备两个按钮用于绑定和解绑服务,在它们的点击事件里利用Intent对象实现活动和服务的绑定和解绑。
Broadcast(广播):学习笔记| AS入门(九) 组件篇之Broadcast Receiver - 简书
是一种广泛应用在应用程序之间传输信息的机制,而BroadcastReceiver(广播接收器)则是用于接收来自系统和应用的广播对并对其进行响应的组件。
分类:普通广播,有序广播,本地广播,粘性广播
普通广播
普通广播是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们接收的先后是随机的。另外,接收器不能截断普通广播。
第一步:自定义接收器类并继承BroadcastReceiver,然后具体实现onReceive()方法。几点注意:BroadcastReceiver生命周期只有十秒左右,因此在onReceive()不要做一些耗时的操作,应该发送给service,由service来完成;还有onReceive()不要开启子线程。
第二步:对广播接收器进行注册。有两种注册方法:一种在活动里通过代码动态注册,另一种在配置文件里静态注册。其实仔细观察,两种方式都是完成了对接收器以及它能接收的广播值这两个值的定义。这两种注册方法一个区别是:动态注册的接收器必须要在程序启动之后才能接收到广播,而静态注册的接收器即便程序未启动也能接收到广播,比如想接收到手机开机完成后系统发出的广播就只能用静态注册了。
有序广播
有序广播是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,所以此时的广播接收器是有先后顺序的,且优先级(priority)高的广播接收器会先收到广播消息。有序广播可以被接收器截断使得后面的接收器无法收到它。
只需要将sendBroadcast()方法改成sendOrderedBroadcast()方法,两个参数其中一个为priority,数值越大优先级越大。
本地广播
Android有一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收本应用程序发出的广播。实现本地广播的发送和接收也很简单,主要使用了一个LocalBroadcastManager来对广播进行管理,并提供了相应的发送广播和注册广播接收器的方法。
粘性广播
通过Context.sendStickyBroadcast()方法可发送粘性(sticky)广播,这种广播会一直滞留,当有匹配该广播的接收器被注册后,该接收器就会收到此条广播。注意,发送粘性广播还需要BROADCAST_STICKY权限,endStickyBroadcast()只保留最后一条广播,并且一直保留下去,这样即使已经有广播接收器处理了该广播,一旦又有匹配的广播接收器被注册,该粘性广播仍会被接收。
未完待续..............