Android四大组件相关知识点

707 阅读19分钟

Activity

一 什么是Activity?

activity是android四大组件之一,它提供屏幕进行交互。每个activity都会获得一个用于绘制其他界面的窗口。

一个应用通常由一个或多个activity组成,一般会指定一个activity作为主界面,activity有自己的什么周期,不同的状态会调用不同的生命周期方法。

二 Activity的生命周期?

  1. onCreate(): 表示Activity 正在被创建,每个activity中我们都重写了这个方法,它会在活动第一次被创建的时候调用。在这个方法中我们可以完成活动的初始化操作,比如说加载布局、绑定事件等
  2. onRestart():表示Activity 正在重新启动。一般情况下,当当前Activity 从不可见变为可见时,onRestart 会被调用。这种情形一般是用户行为所导致的,比如用户按 Home 键切换到桌面或者用户打开了一个新的 Activity,此时当前的Activity 就会暂停,即onPause 和 onStop 被调用,接着用户重新回到了这个Activity,此时onRestart 就会被调用
  3. onStart():表示Activity 正在被启动,即将开始,此时Activity 已经可见了,但还没有出现在前台,即还无法与用户进行交互。可以理解为Activity 已经显现出来了,但是我们还看不到
  4. onResume():表示Activity 已经可见了,并且出现在前台并开始活动,此时可以与用户进行交互。此时的活动一定位于返回栈的栈顶,并且处于运行状态
  5. onPause():表示Activity 正在停止,正常情况下onStop 会紧接着被调用。这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗CPU的资源释放掉(比如动画),以及保存一些关键数据,但不能太耗时,不然会影响到新的栈顶活动的使用,onPause 必须先执行完,新 Activity 的onResume 才会执行
  6. onStop():表示Activity即将停止,在活动完全不可见的时候调用。它和onPause()方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会执行,可以做一些稍微重量级的回收工作,同样不能太耗时。
  7. onDestory():表示Activity 即将被销毁,这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态,这是Activity 生命周期中的最后一个回调,在这里我们可以做一些回收工作和最终的资源释放

2.1 onStart 和 onResume、onPause 和onStop 从描述上来看差不多,对我们来说有什么实质上的不同?

这两个配对的回调分别表示不同的意义,onStart 和onStop 是从Activity 是否可见的角度来回调的,而onResume和 onPause 是从Activity 是否位于前台这个角度来回调的,除了这种区别,在实际使用中没有其他明显的区别

2.2 onSaveInstanceState 和 onRestoreInstanceState 的作用

Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState() 会被调用。

通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存.

2.3 onSaveInstanceState() 什么时候调用?

(1)、当用户按下HOME键时。 这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity 是否会被销毁,因此系统会调用onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则

(2)、长按HOME键,选择运行其他的程序时。

(3)、按下电源按键(关闭屏幕显示)时。

(4)、从activity A中启动一个新的activity时。

(5)、屏幕方向切换时,例如从竖屏切换到横屏时。

(6)、引发activity销毁和重建的其它情况,除了系统处于内存不足的原因会摧毁activity之外, 某些系统设置的改变也会导致activity的摧毁和重建. 例如改变屏幕方向, 改变设备语言设定, 键盘弹出等

三 一个ActivityA跳转到ActivityB,然后ActivityB在返回到ActivityA,这个过程两个Activity的生命周期是什么样的?

默认启动模式下:

在A跳转B会执行:A onPause -> B onCreate -> B onStart -> B onResume->A onStop

在B按下返回键会执行:B onPause -> A onRestart -> A onStart -> A onResume-> B onStop -> B onDestroy

当 B Activity 的 launchMode 为 singleInstance,singleTask 且对应的 B Activity 有可复用的实例时:

A.onPause -> B.onNewIntent -> B.onRestart -> B.onStart -> B.onResume -> A.onStop -> ( 如果 A 被移出栈的话还有一个 A.onDestory)

当 B Activity 的 launchMode 为 singleTop且 B Activity 已经在栈顶时(一些特殊情况如通知栏点击、连点),此时只有 B 页面自己有生命周期变化:B.onPause -> B.onNewIntent -> B.onResume

四 Activity在横竖屏切换时候的生命周期是怎样的?

1、不设置Activity的configChanges属性,或设置configChanges="orientation",或设置configChanges="orientation|keyboardHidden",切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行一次。

2、配置 android:configChanges="orientation|keyboardHidden|screenSize",才不会销毁 activity,且只调用 onConfigurationChanged方法。

竖屏:启动:onCreat->onStart->onResume.

切换横屏时:onPause-> onSaveInstanceState ->onStop->onDestoryonCreat->onStart->onSaveInstanceState->onResume.

如果配置这个属性:androidconfigChanges="orientation|keyboardHidden|screenSize"就不会在调用Activity的生命周期,只会调用onConfigurationChanged方法

五 Activity的启动模式和使用场景

  • Standard:默认模式,每次启动Activity都会创建一个新的Activity实例
  • SingleTop:通知消息打开的页面,如果要启动的Activity已经在栈顶,则不会重新创建Activity,只会调用该该Activity的onNewIntent()方法,如果要启动的Activity不在栈顶,则会重新创建该Activity的实例,可以有效减少activity重复创建对资源的消耗
  • SingleTask:如果要启动的Activity已经存在于它想要归属的栈中,那么不会创建该Activity实例,将栈中位于该Activity上的所有的Activity出栈,同时该Activity的onNewIntent()方法会被调用
  • SingleInstance:创建在一个新栈,然后创建该Activity实例并压入新栈中,新栈中只会存在这一个Activity实例,整个手机操作系统里面只有一个实例存在就是内存单例,一般用于其他应用启动当前应用中的activity,可以保证其他应用和自己本身应用启动此activity都是同一实例

注意:

singleTask的任务栈指定属性android:taskAffinity属性

六 谈谈你对Activity中onNewIntent()方法的认识?

在singleTop、singleTask、singleInstance 中如果在应用内存在Activity实例,并且再次发生startActivity(Intent intent)回到Activity后,由于并不是重新创建Activity而是复用栈中的实例,因此Activity再获取焦点后并没调用onCreate、onStart,而是直接调用了onNewIntent(Intent intent)函数

onNewIntent的作用:当调用到onNewIntent(intent)的时候,需要在onNewIntent() 中使用setIntent(intent)赋值给Activity的Intent.否则,后续的getIntent()都是得到老的Intent

七 什么情况下Activity会单独执行onPause?

当 Activity 被另一个透明或者 Dialog 样式的 Activity 覆盖时的状态。此时它依然与窗口管理器保持连接,系统继续维护其内部状态,所以它仍然可见,但它已经失去了焦点故不可与用户交互,所以被覆盖的Activity并不会执行onStop()方法

八 Activity之间如何通信 & Activity和Fragment之间通信 & Activity和Service之间通信?

1、Activity向Fragment传递数据?

  • 在Activity中创建Bundle,将数据以putString(key,value)形式放入bundle中,最后通过setArguments(bundle)方法,将其关联,在Fragment与Activity依附的Fragment中直接通过getArguments().getString()来获取。
  • 直接通过强转将其强转为Activity,即调用Fragment所依附的Activity中的方法

2、Fragment将数据传递给Activity?

接口回调:

(1)、在fragment定义一个内部回调接口,再让包含这个Fragment的Activity实现这个回调接口,fragment就可以调用这个接口中的方法,将数据传递给Activity。

(2)Activity实现完接口后,又怎样把数据传递给Fragment,利用Fragment刚添加到Activity时的时候调用的生命周期方法的onAttch()方法,我们就在该方法中检查相应的Activity是否实现了Fragment当中定义的内部接口。就是对它进行了类型转换,然后赋值给我们Fragment中定义的接口

(3)、当一个Fragment从Activity当中剥离时,就会走到生命周期的onDetach()方法,在此要将传递进来的Activity对象释放掉,否则会影响Acitvity销毁,产生内存泄漏。

3、 Activity与Service数据通信?

1、绑定服务,利用ServiceConnection类,同时让Activity实现该类,重写该类中的两个方法,即绑定成功()和绑定失败()。

  • 让我们的ServiceActivity去实现ServiceConnection接口,实现接口之后,重写onServiceConnected()和onServiceDisconnected()两个方法。
  • 首先我们要创建好一个Binder对象,接下来在OnServiceConnected当中获取到该Binder对象。最后利用Binder当中setData()方法来向Service传递数据。

2、简单通信,利用Intent进行传值(Intent组件在activity之间、activity与fragment之间数据通信都会用到),该方法简单,但是也只能传递较简单的数据。

3、利用callback接口,将Handler来监听服务中的进程的变化(会有子线程到主线程的切换,因此用到了Handler)。

九 Activity常用的标记位Flags?

  • FLAG_ACTIVITY_NEW_TASK:这个标记位的作用是为Activity指定”singleTask”启动模式,其效果和在XML中指定该启动模式相同
  • FLAG_ACTIVITY_SINGLE_TOP:这个标记位的作用是为Activity指定”singleTop”启动模式,其效果和在XML中指定该启动模式相同
  • FLAG_ACTIVITY_CLEAR_TOP:具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈,这个标记位一般会和singleTask启动模式一起出现。在这种情况下,被启动Activity的实例如果已经存在,那么系统就会调用它的onNewIntent。如果被启动的Activity采用standrad启动模式,那么它连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈顶,singleTask启动模式默认就具有此标记位的效果
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有这个标记的Activity不会出现在历史Activity的列表中

十 Activity之间传递数据的方式Intent是否有大小限制,如果传递的数据量偏大,有哪些方案?

当使用Intent来传递数据时,用到了Binder机制,数据就存放在了Binder的事务缓冲区里面,而事务缓冲区是有大小限制的。普通的由Zygote孵化而来的用户进程,映射的Binder内存大小是不到1M的Binder 本身就是为了进程间频繁-灵活的通信所设计的, 并不是为了拷贝大量数据

如果非 ipc:单例,eventBus,Application,sqlite、shared preference、file 都可以

如果是 ipc:共享内存性能还不错,Socket或者管道性能不太好,涉及到至少两次拷贝

十一 显示启动和隐式启动

显示启动:直接告诉Android的kernel,要启动哪个activity!在启动同一应用程序时占优!效率会高很多!

隐式启动:不直接告诉Android的kernel启动哪个activity,要让Android的kernel自己选择最适合的activity!

隐式Intent是通过在AndroidManifest文件中设置action、data、category,让系统来筛选出合适的Activity

action的匹配规则:

Intent-filter action可以设置多条intent中的action只要与intent-filter其中的一条匹配成功即可,且intent中action最多只有一条Intent-filter内必须至少包含一个action

category的匹配规则:

Intent-filter内必须至少包含一个category,android:name为android.intent.category.DEFAULT。intent-filter中,category可以有多条intent中,category也可以有多条intent中所有的category都可以在intent-filter中找到一样的(包括大小写)才算匹配成功

data的匹配规则:

intent-filter中可以设置多个dataintent中只能设置一个dataintent-filter中指定了data,intent中就要指定其中的一个data

十二 用Intent传递数据和Bundle传递数据的区别?为什么不用HashMap呢?

1,Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保证更快的速度和更少的内存占用。

2,另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输

十三 了解scheme跳转协议吗?谈一谈

Scheme 是一种页面内跳转协议

使用场景:

1 APP根据URL跳转到另外一个APP指定页面

2 可以通过h5页面跳转app原生页面

3 服务器可以定制化跳转app页面

Scheme链接格式样式:

Uri.parse("bs://test:8080/abc/cbd?name=111&value=222");

可以类似的理解为一个http的地址。

bs: 就是Scheme的协议名称,可以随便定义,类似于http

test:代表Scheme协议的作用域

8080:代表Scheme协议的端口号

/abc/cbd :代表制定的页面(路径)

name =111&value=222 :表示传递的参数

Service

一 什么是service?

Service是Android系统提供的四大组件之一,它的地位和Activity的并列的,只不过没有Activity的使用频率高。顾名思义Service就是运行在后台的一种服务程序,一般情况下,很少和用户交互,最大的特点就是没有可视化界面

二 说说Service的生命周期,两种启动方式的区别

startService : onCreate() -> onStartCommand() -> onDestroy()

bindService : onCreate() -> onbind() -> onUnbind()-> onDestroy()

区别:

启动

如果服务已经开启,多次执行startService不会重复的执行onCreate(), 而是会调用onStart()和onStartCommand()

如果服务已经开启,多次执行bindService时,onCreate和onBind方法并不会被多次调用

销毁

当执行stopService时,直接调用onDestroy方法

调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind->onDestroy

使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服 务仍然运行

使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止

1、单独使用startService & stopService

(1)第一次调用startService会执行onCreate、onStartCommand

(2)之后再多次调用startService只执行onStartCommand,不再执行onCreate

(3)调用stopService会执行onDestroy

2、单独使用bindService & unbindService

(1)第一次调用bindService会执行onCreate、onBind

(2)之后再多次调用bindService不会再执行onCreate和onBind

(3)调用unbindService会执行onUnbind、onDestroy

三 service启动流程

1.Process A进程采用Binder IPC向system_server进程发起startService请求;

2.system_server进程接收到请求后,向zygote进程发送创建进程的请求;

3.zygote进程fork出新的子进程Remote Service进程;

4.Remote Service进程,通过Binder IPC向sytem_server进程发起attachApplication请求;5.system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向remote Service进程发送scheduleCreateService请求;

6.Remote Service进程的binder线程在收到请求后,通过handler向主线程发送CREATE_SERVICE消息;

7.主线程在收到Message后,通过发射机制创建目标Service,并回调Service.onCreate()方法。到此,服务便正式启动完成。

当创建的是本地服务或者服务所属进程已创建时,则无需经过上述步骤2、3,直接创建服务即可。 

四 Service与Activity怎么实现通信

通过Binder对象

1.Service中添加一个继承Binder的内部类,并添加相应的逻辑方法

2.Service中重写Service的onBind方法,返回我们刚刚定义的那个内部类实例

3.Activity中绑定服务,重写ServiceConnection,onServiceConnected时返回的IBinder(Service中的binder)调用逻辑方法

Service通过BroadCast广播与Activity通信

五 IntentService是什么,IntentService原理,应用场景及其与Service的区别

What:

IntentService 是 Service 的子类,默认开启了一个工作线程HandlerThread,使用这个工作线程逐一处理所有启动请求,在任务执行完毕后会自动停止服务。只要实现一个方法 onHandleIntent,该方法会接收每个启动请求的 Intent,能够执行后台工作和耗时操作

可以启动 IntentService 多次,而每一个耗时操作会以队列的方式在 IntentService 的 onHandlerIntent 回调方法中执行,并且,每一次只会执行一个工作线程,执行完第一个再执行第二个。并且等待所有消息都执行完后才终止服务

How:

1.创建一个名叫 ServiceHandler 的内部 Handler

2.把内部Handler与HandlerThread所对应的子线程进行绑定

3.HandlerThread开启线程 创建自己的looper

4.通过 onStartCommand() intent,依次插入到工作队列中,并发送给 onHandleIntent()逐个处理可以用作后台下载任务 静默上传

Why:

IntentService会创建独立的worker线程来处理所有的Intent请求 ,Service主线程不能处理耗时操作,IntentService不会阻塞UI线程,而普通Serveice会导致ANR异常

为Service的onBind()提供默认实现,返回null;onStartCommand提供默认实现,将请求Intent添加到队列中。所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service

BroadcastReceiver

一 广播是什么?

BroadcastReceiver,“广播接收者”的意思,顾名思义,它就是用来接收来自系统和应用中的广播

二 广播的特性是什么?

1.广播接收者的生命周期是非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁

2.广播接收者中不要做一些耗时的工作,否则会弹出Application No Response错误对话框

3.最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉

4.耗时的较长的工作最好放在服务中完成

三 广播的分类是什么?及使用场景

Android 广播分为两个角色:广播发送者、广播接受者

广播接收器的注册分为两种:静态注册、动态注册。

静态广播接收者:通过AndroidManifest.xml的标签来申明的BroadcastReceiver。

动态广播接收者:通过AMS.registerReceiver()方式注册的BroadcastReceiver,动态注册更为灵活,可在不需要时通过unregisterReceiver()取消注册。

广播类型:根据广播的发送方式

  1. 普通广播 : 通过Context.sendBroadcast()发送,可并行处理
  2. 系统广播 : 当使用系统广播时,只需在注册广播接收者时定义相关的action即可,不需要手动发送广播
  3. 有序广播 : 指的是发送出去的广播被 BroadcastReceiver 按照先后顺序进行接收 发送方式变为:sendOrderedBroadcast(intent);
  4. App应用内广播(本地广播): 可理解为一种局部广播,广播的发送者和接收者都同属于一个App。相比于全局广播(普通广播),App应用内广播优势体现在:安全性高效率高

四 广播的两种注册方式的区别

静态注册:常驻系统,不受组件生命周期影响,即便应用退出,广播还是可以被接收,耗电、占内存。

动态注册:非常驻,跟随组件的生命变化,组件结束,广播结束。在组件结束前,需要先移除广播,否则容易造成内存泄漏。

五 如何实现拦截一条短信?

①.首先添加接收短信的权限:

注意android 6.0及以上需要运行时权限处理。

②.在AndroidManifest中注册广播接收器,优先级尽量设置高些。

③.在广播接收器中接收到广播的处理后然后设置拦截器abortBroadcast()。

关注公众号:Android老皮
解锁  《Android十大板块文档》 ,让学习更贴近未来实战。已形成PDF版

内容如下

1.Android车载应用开发系统学习指南(附项目实战)
2.Android Framework学习指南,助力成为系统级开发高手
3.2023最新Android中高级面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习路线+项目实战(附源码)
5.Android Jetpack从入门到精通,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提升架构基础
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 性能优化实战+360°全方面性能调优
10.Android零基础入门到精通,高手进阶之路

敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔