前言
我们是程序员,每天都在了解最新的技术,每天都在学习编程语言、框架或者库。很多人都有这样的感觉,不要再更新了,我已经学不动了。
要知道新的东西每年都在变化,可能之前很火的框架现在已经不再用了。对于程序员来说,打好基础才是主要的。
今天把Android的四大组件总结一下,这块也是面试中的高频问题区域。
Activity
生命周期
对于Activity
,来说最主要的就是它的生命周期了。
onCreate()
-> onStart()
-> onResume()
-> onPause()
-> onStop()
-> onDestroy()
方法 | 调用时机 |
---|---|
onCreate() | 首次创建Activity时调用,可以进行创建视图,绑定数据等。 |
onStart() | 在Activity即将对用户可见之前调用 |
onResume() | 开始和用户进行交互之前调用 |
onPause() | 开启另一个Activity或者锁屏时调用 |
onStop() | 当前Activity对用户不可见时调用 |
onDestory() | 在Activity被销毁时调用 |
onRestart() | 在Activity已经停止即将再次启动时调用 |
四种状态
- 活动状态(running): Activity处于可见状态,在屏幕前台,即在栈顶。 期间的生命周期为onCreate->onStart->onResume
- 暂停状态(paused): 已经失去焦点但是仍可见的状态(包括部分可见)。触发的生命周期:onResume-> onPause
- 停止状态(stoped): 该Activity被另一个Activity完全覆盖的状态,onPause -> onStop
- 死亡状态(destroyed): 该Activity完全被销毁,onStop -> onDestroy
切换屏幕时生命周期变化
分为三种情况:
-
没有设置
AndroidManifest
中的configeChanges
。 不论是横屏切竖屏还是竖屏切横屏都是这样的:onPause()
->onSaveInstanceState()
->onStop()
->onDestroy()
->onCreate()
->onStart()
->onRestoreInstanceState()
->onResume()
->onPause()
->onStop()
->onDestroy()
-
设置
configeChangs
为orientation
:6.0
同1一样,7.0
先走onConfigurationChanged
方法,然后和1一样,8.0
只会走onConfigurationChanged
方法 -
设置
configeChangs
为orientation|keyboardHidden|screenSize
或者onfigurationChanged`方法
启动模式
启动模式 | 说明 |
---|---|
standrad | 默认模式,每次创建都会新建新的实例 |
singleTop | 如果该Activity在栈顶,将不会新建,会回调onNewIntent 方法,没有在栈顶,将会新建。适合于通知启动的页面 |
singleTask | 栈内复用,如果栈中存在该Activity的实例,则会将该Activity置顶,将上面的Activity全部出栈,回调onNewIntent ,否则创建新的实例,压入栈。像一些程序的入口,只能有一个实例,这个任务栈是通过taskAffinity属性指定的。 |
singleInstance | activity只能单独在一个任务栈中,单例模式。像系统Launcher、锁屏键、来电显示等系统应用都是使用这种模式 |
可以通过adb shell dumpsys activity activities来查看现有的Activity。
注:启动模式,是每次面试必问的内容,划重点。
Service
什么是Service
Service可以说是一个在后台运行的Activity,它不是一个单独的进程,也不是一个单独的线程,它只需要应用告诉它应该做什么就可以了。
它用于处理一些不干扰用户使用的后台操作。比如播放多媒体、检测SD卡上文件的变化等。
注:默认情况下,Service所有的代码都是运行在主线程上,不能进行比较耗时的操作,可以在Service中创建Thread来执行耗时的操作。
为什么不适用后台线程而使用Service?
- Service可以放在独立的进程中,所有更安全。
- 使用Service可以依赖现有的binder机制,不需要在应用层上处理线程同步的繁杂工作。
- 系统可以重新启动异常死去的Service
分类
本地服务
本地服务,也叫做进程内服务,是应用于程序内部的。执行一些耗时的操作。 本地服务根据启动方式的不同分为:被启动服务Started Service和被绑定服务Bound Service
服务 | 说明 |
---|---|
Started Service | 由其他组件调用startService 方法调用,该方法会导致被启动的服务的onStartCommand 被回调。当服务被启动之后,就与启动者的生命周期没有关联,即使启动者被销毁,改服务也会继续执行,除非调用stopSelf() 或者stopService() 来停止 |
Bound Service | Activity通过bindService 方法来绑定服务,同时该Activity要提供ServiceConnection 接口的实现类,用来监视Activity与服务类之间的连接,Service必要实现onBind()方法,返回Ibinder对象(定义服务类与Activity交互的程序接口),重写onServiceConnected() 方法,将服务赋值到Activity中 |
onStartCommand()
方法有三种返回值:
- START_STICKY: (粘性)使用这个返回值时,我们启动的服务根应用程序"粘"在一起,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,当再次启动服务时,传入的第一个参数将为null。
- START_NOT_STICKY:(非粘性)相对应的,被kill掉之后不会重新启动该服务。
- START_REDELIVER_INTENT:使用这个返回值之后,被系统kill掉之后,不但会重新启动该服务,还会将intent的值传入。
远程服务
绑定远程服务的步骤:
- 在服务的内部创建一个内部类,提供一个方法,用于间接调用服务里的方法。
- 把暴露的接口文件的扩展名改为
.aidl
文件,去掉访问修饰符。 - 实现服务的
onBind
方法,实现aidl定义的接口,提供给外界可调用的方法。 - 在Activity中绑定服务。
- 提供
ServiceConnection
方法的实现类,重写onServiceConneced
方法,传递IBinder对象。 - aidl定义的接口
Stub.asInterface(binder)
调用接口里的方法。
生命周期
- Start Service生命周期:
- 在程序中调用
startService()
,如果该Service还没有运行,就会执行onCreat()
、onStartCommand()
回调方法,如果已经运行了就会执行onStartCommand()
方法。 - 调用
context.stopService()
会执行该服务的onDestroy
方法,如果没有调用该方法,此服务将会一直在后台运行。
- Bound Service 生命周期:
- 在程序中调用
context.bindService
会触发执行该服务的onCreate()
、onBind()
方法。onBind方法会返回一个iBinder的接口实例,用来回调服务里的方法。 - 如果调用者Context退出了,那么会依次执行
onUnbind()
、onDestroy()
方法。
IntentService
IntentService是Service的子类,主要用于处理异步请求,防止线程的阻塞。所有的请求将在一个工作线程(HanderThread)中处理,工作完成了,线程也就结束了。
IntentService中提供了一个onBind()方法的默认实现,返回null。提供一个onStartCommand()方法的默认实现,它将intent想传送到工作队列,然后从工作队列中每次取出一个传送至onHandelIntent()方法,在该方法中对intent做相应处理。
BroadcastReceiver
广播是一个全局的监听器,分为广播发送者和广播接受者。
原理浅析
广播采用的是观察者模式,和eventbus一样基于消息的发布/订阅事件模型,通过AMS(Activity Manager Service)来处理消息中心来协调处理广播发送者和广播接受者。
- 广播接受者通过Binder机制在消息中心注册。
- 广播发送者通过Binder机制发送广播。
- AMS消息中心通过发送者的要求,在消息列表中找到符合条件的接受者。
- AMS将广播发送到符合条件的接受者的消息队列中。
- 接受者通过消息循环拿到消息,回调onRevice,进行相应处理。
使用
- 继承
BroadcastReceivre
类。 - 重写
onReceive
方法,在接受广播时做相应处理。
注:如果有耗时的操作,不要直接在onReceive中执行,默认情况下,接受器是运行在 UI
中的,建议在Service中开启线程执行。
根据注册方式的不同,注册分为静态注册和动态注册。
静态注册
在AndroidManifest
声明:
复制代码
动态注册
registerReceiver(new MyBroadcastReceiver(),new IntentFilter("chang"));复制代码
要加上权限
复制代码
两者的区别
注册方式 | 区别 |
---|---|
静态注册 | 常驻广播,需要时刻关注广播 |
动态注册 | 非常驻,跟随组件生命周期变化,注意要在组件销毁前取消注册 |
类型
广播的类型可以分为五类
- 普通广播
- 系统广播
- 有序广播
- 应用内广播
- 粘性广播
普通广播也就是我们开发者自定义的intent广播。
sendBroadcast(intent)复制代码
当接受者的action与发送的intent中的action一致时,就会回调onReceive
方法。
系统广播就是一些手机上基本操作的变更发出的广播,比如网络状态变化、电池电量等。
有序广播就是发送出去的广播接受者按照顺序接受,现接受到的可以拦截或者修改该广播。
sendOrderedBroadcast(intent);复制代码
应用内广播是一种局部广播,发送者和接受者都在app内,并不对外暴露。 一种实现方式是将exported设置成false,指定包名发送广播。 另一种是使用LocalBroadcastManager
动态注册广播
ContentProvider
ContentProvider:内容提供者,有时候我们需要吧我们自己的数据暴露给其他程序使用,这时候就需要ContentProvider来实现,其他程序通过 ContentResolver来操作暴露的数据。
当应用通过ContentProvider来暴露自己的数据之后,不管该应用是否启动,都可以通过该接口来操作其内部数据,像一些增删改查操作。
开发一个ContentProvider的步骤如下:
- 自定义个一个ContentProvider类,继承ContentProvider基类。
- 在
AndroidManifest.xml
文件中注册这个ContentProvider,绑定一个Uri。 - 实现其基类的几个方法,做相应处理。
- onCreate():第一次访问时,会回调该方法。
- insert
- delete
- update
- query
- getType: 返回当前Uri所代表的数据的MIME类。
insert、delete、update、query,对当前Uri进行增删改查操作。
其他程序通过创建ContentResolver
来操作ContentProvider
暴露的数据。一般来说ContentProvider
是单例模式的,ContentResolver
操作数据,其实是委托给Uri对应的ContentProvider
来进行处理。
总结
看完之后,是否对Android的四大组件:Activity、Service、BroadcastReceiver、ContentProvider有了深刻一点的印象。这里我没有列出一些具体的实现各个组件的例子,我相信看完之后,自己就能很好地写出来。
多积累、多总结、多思考,每天比昨天多努力一点点。