Android(1)--四大组件

245 阅读10分钟

Android 基础

Activity

1.Activity生命周期

Activity在创建、使用、销毁的时候会回调一下生命周方法,根据调用时机依次为onCreate()、onStart()、onResume()、onPause()、onStop()、onDestory()。

  • onCreate()

主要是初始化UI,创建PhoneWindow,此时 DecorView 并没有被绘制,Window 对象也没有被显示到屏幕,Activity 也是不可见的

  • onStart()

Activity 对用户可见(其实并看不见),可以在这个回调中申请资源,比如相机

  • onResume()

onResume() 是真正进行 UI 绘制以及显示的地方

  1. 创建 ViewRootImpl对象
  2. 调用 ViewRootImpl.setView() 方法(其中调用> requestLayout())发起绘制
  3. 将Window添加屏幕,此时Activity真正可见
  • onPause()

非前台,不可交互,但不一定不可见,时间比较短暂

  • onStop()

完全的不可见状态,可以做一些资源回收和数据保存工作,但此时的 Activity 仍在内存中,只是没有关联到任何 Window,返回调用onRestart -> onStart

  • onDestory()

Activity被销毁,注意在 onDestroy 中释放所有不需要的资源,否则可能导致内存泄露

其他回调方法

  • onRestart()

Activity在执行onStop之后onDestory之前,返回该Activity时调用

  • onSaveInstanceState()

在onStop之后,Activity有可能被系统回收时调用,比如跳转到其他Activity

  • SDK 11 之前,在 onPause() 之前调用
  • SDK 28 之前,会在 onStop() 之前调用
  • SDK 28 之后,会在 onStop 之后调用
  • onRestoreInstanceState()

Activity被系统回收的情况下,再次启动的时候调用

  • onNewIntent()

launchMode是singleTop 、 singleTask 、 singleInstance的,符合复用条件的Activity再次startActivity的时候调用

  • onPostCreate()

onCreate彻底执行完之后调用

  • onPostResume()

onResume彻底执行完之后调用,这个时候可以获取View的宽高

  • onUserInteraction()

activity无论分发按键事件、触摸事件或者轨迹球事件都会调用。onUserLeaveHint之前回调onUserInteraction

  • onUserLeaveHint

用户手动离开当前activity,会调用该方法。

  • onWindowFocusChanged()

onResume/onPause 关注的是 Activity 是否可以交互,onStart/onStop 关注的是 Activity 是否可见。

详情参考:juejin.cn/post/686174…

2.Activity A 启动 Activity B会调用哪些方法?

A:Running -> A:onPause -> B:onCreate -> B:onStart -> B:onResume -> A:onStop

步骤Activity AActivity B
1onPause
2onCreate
3onStart
4onResume
5onStop

如果B是透明主题或者Dialog A:Running -> A:onPause -> B:onCreate -> B:onStart -> B:onResume

步骤Activity AActivity B
1onPause
2onCreate
3onStart
4onResume

3.Activity任务栈是什么

launchMode 属性有四种取值 :standard 、 singleTop 、 singleTask 、 singleInstance 。

  • standard: 标准启动模式

每次启动 Activity 都会新建一个新的实例。待启动 Activity 会进入源 Activity 所属任务栈。

  • singleTop: 栈顶复用模式

待启动 Activity 已经位于源 Activity 所属的任务栈的栈顶时,不会创建新的 Activity,而是直接使用栈顶的 Activity,并回调它的 onNewIntent 方法,onCreate 和 onStart 不会被调用,直接回调 onResume 。

  • singleTask:栈内复用模式

全局单实例,首先会寻找要启动的 Activity 想要的任务栈(默认或者 taskAffinity 属性指定),如果没有找到,则创建新的任务栈并将 Activity 实例放入。如果找到了想要的任务栈,这时候要判断栈中是否已经存在该 Activity 的实例,如果已经存在,会将该 Activity 以上的其他 Activity 实例弹出,把自己放到栈顶,同样也是回调 onNewIntent 和 onResume。如果实例不存在,创建新的实例并压入栈中

  • singleInstance:单实例模式

全局单实例,首次启动时会创建新的 Activity 实例,并放入一个新的任务栈中,且 这个任务栈中只会有这一个实例。 后续启动不会再新建实例。

taskAffinity

  1. taskAffinity 的作用 是指定想要的任务栈。但它并不会在任何场景下都会起作用。
  2. 未显式声明 taskAffinity 的 Activity 都具有默认的任务栈,该任务栈的名称是应用包名
  3. 当启动模式设置为 standard 或 singleTop 时,它是不起作用的
  4. 当启动模式设置了 singleTask 或者 singleInstance 时,它就会新建任务栈来存储待启动的 Activity 实例
  5. 除了 singleTask 和 singleInstance 以外,FLAG_ACTIVITY_NEW_TASK 也会使 taskAffinity 生效

4. flag

  • FLAG_ACTIVITY_NEW_TASK

首先,在不设置 taskAffinity 的情况下,单独设置 FLAG_ACTIVITY_NEW_TASK 并没有任何意义,不会创建新的任务栈,每次启动都会创建新的 Activity 实例,不会 栈内复用。

  • FLAG_ACTIVITY_CLEAR_TOP

CLEAR_TOP 在单独使用时,如果想要的任务栈中已经存在待启动的 Activity 的实例,则会将该 Activity 实例之上的其他 Activity 弹出,把自己放到栈顶,并回调 onNewIntent 。但这是有前提的,就是待启动的 Activity 的 launchMode 不能是 standard 。 如果是 standard ,则会把自己及之上的所有 Activity 全部弹出,新建一个实例放入。

  • FLAG_ACTIVITY_SINGLE_TOP

等同于 singleTop ,栈顶复用。即使待启动的 Activity 是 standard ,如果已经处于栈顶的话,也会复用。

5. Intent传递数据大小限制,数据太大怎么办?

Intent 携带信息的大小其实是受 Binder 限制,Binder 传递缓存有一个限定大小,通常是 1Mb。但同一个进程中所有的传输共享缓存空间,所以实际更低。

改用其他方式
  • 静态static
  • 单例
  • Application
  • 数据持久化

Service

1. Service生命周期

  • startService() -> onCreate() -> onStartCommand() -> onDestroy()
graph TD
A[startService]-->B{是否已执行onCreate}
B --> |否| C(onCreate)
B --> |是| D(onStartCommand)
C --> D
--> Stop[onDestroy]
  • bindService() -> onCreate() -> onBind() ->unbindService() -> onUnbind() -> onDestroy()
graph TD
A[bindService]-->B{是否已执行onCreate}
B --> |否| C(onCreate)
B --> |是| D{是否binding}
C -->D
D --> |否| E(onBind)
D --> |是| F(binding)
E --> F
F --> G(unbindService)
G --> H{是否binding}
H --> |否| Stop[onDestroy]
H --> |是| I(onUnbind)
I --> Stop

2. bindService 和 startService混合调用情况

startService 和 bindService 同时调用的情况,必须同时stopService和unbindService才能停止服务。

3. Service保活

android O 以后,后台service只能存活几分钟,并且后台应用不能通过startService启动服务,否则会抛出异常.

回收优先级

前台进程 < 可视进程 < 服务进程 < 后台进程 < 内容供应根节点 < 空进程

解决方案
  1. startForegroundService,显示通知notification
  2. 跳转到设置,添加到手机白名单,不同厂商有不同处理方式

4. onStartCommand相关

参数说明

onStartCommand(Intent intent, int flags, int startId)

  • 第一个参数是启动过来的Intent信息,也就是调用者的Intent信息
  • 第二个参数flags代表着启动请求的附加参数,由系统传入
  1. 通过startService()启动,flags传入0
  2. onStartCommand()返回值为START_STICKY_COMPATIBILITY或者START_STICKY并且服务被强制杀死时重启后,flags传入START_FLAG_REDELIVERY(1)
  3. onStartCommand()返回值为START_REDELIVER_INTENT并且服务被强制杀死重启后,flags传入START_FLAG_REDELIVERY(2)
  • 第三个参数为启动的ID,每次启动都对应不同的ID,与stopSelf()连同停止Service

回值说明

  • START_STICKY

默认返回值,表示Service被杀掉后会重新启动,但是不会携带之前的Intent信息

  • START_NOT_STICKY

Service被kill后,不会进行重启

  • START_REDELIVER_INTENT

Service被杀掉后,会进行重新启动,并且还会携带之前的Intent信息

这里的重启的前提是,有自启权限。

5. IntentService

IntentService是Service的子类, onCreate()方法中,创建了HandlerThread对象,使用HandlerThread的线程处理异步任务。

HandlerThread

HandlerThread是Thread的子类,开启线程后,在run方法中初始化了Looper,我们通过HandlerThread提供的Looper对象新建Handlder对象实现线城。

6. Service和Activity通信

  1. Binder

Activity通过bindService绑定Service,获取Service中实现了Binder的内部类,Service获取Activity实现的Handler 实现双向通信

  1. 广播+startService
  2. 各类事件总线,EventBus、RxBus等

7. 常用系统服务

枚举值对应对象说明
WINDOW_SERVICE          WindowManager        管理Window
LAYOUT_INFLATER_SERVICELayoutInflater        解析Xml
ACTIVITY_SERVICE       ActivityManager    Activity、运行时封装,Process、应用程序/包、Service、Task
POWER_SERVICE           PowerManger        电源的服务
ALARM_SERVICE           AlarmManager       闹钟的服务
NOTIFICATION_SERVICE    NotificationManager   状态栏的服务
KEYGUARD_SERVICE       KeyguardManager       键盘锁的服务
LOCATION_SERVICE        LocationManager      位置的服务,如GPS
SEARCH_SERVICE          SearchManager        搜索的服务
VIBRATOR_SERVICE       Vibrator            手机震动的服务
CONNECTIVITY_SERVICE    Connectivity          网络连接的服务
WIFI_SERVICE           WifiManager      Wi-Fi服务
TELEPHONY_SERVICE       TeleponyManager      电话服务

BroastReceiver

1. 分类

  • 无序广播
  • 使用Context.sendBroadcast()方法发送
  • 完全异步,无优先级
  • 效率高
  • 有序广播
  • 使用Context.sendOrderedBroadcast()方法发送
  • 允许接收者设定优先级,并且根据优先级依次传递广播
  • 优先级高的接收者,可以对广播数据处理、继续传播、停止传播
  • 本地广播
  • 只能使用LocalBroadcastManager对象提供的方法注册和发送
  • 只在当前App内传播
  • 优先级更高
  • Sticky广播
  • 使用Context.sendStickyBroadcast()方法发送
  • 需要BROADCAST_STICKY权限
  • Sticky广播发送后不会取消,滞留在系统中,等待接收者出现
  • 接收者处理后需要手动调用removeStickyBoradcast取消
  • 6.0已废弃(@Depracated)

2. 注册

  • 静态注册

编写BroastReceiver子类,在AndroidManifest.xml中注册,通过设置广播优先级和过滤器。

  • 动态注册

编写BroastReceiver子类,创建IntentFilter对象,设置广播优先级和过滤器,用Context.registerReceiver(mReceiver,mIntentFilter)等方法注册,使用Context.unregisterReceiver()取消注册。

  • 动态广播需要手动取消,不需要可能导致内存泄漏,例如Activity内部类和Context对象等
  • 出于安全考虑和效率原因,一些系统广播只能通过动态注册,例如SCREEN_ON、SCREEN_OFF、TIME_TICK等

3. 原理

Android的广播本质上是一种事件的订阅和发布机制。通过registerReceiver或者Manifest文件订阅消息,通过sendBroadcast等方法发送消息。

  • 普通广播

  普通广播消息由AMS管理,通过registerReceiver或者Manifest文件订阅消息,最后都会走AMS,由AMS统一派发。涉及进程间通信,效率有损耗。

  • 本地广播

  本地广播消息由LocalBroadcastManager管理,基于观察者模式,注册时记录Action、IntentFilter和Receiver对象,发送时遍历记录,通过Handler异步回调Receiver对象的onReceive方法。

ContentProvider

  • 定义

ContentProvider是Android SDK提供的不同进程间实现数据共享的机制。具体实现方式有SQLite、文件、SP等。

  • 使用
        Context context = .......;
        Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;         //1. 获取要查询的Uri
        ContentResolver contentResolver = context.getContentResolver(); //2. 获取内容解析器ContentResolver
        Cursor cursor = contentResolver.query(uri, null,
                null, null, null);                                      //3. ContentResolver使用Uri查询数据,返回游标Cursor
        if (cursor != null) {
            cursor.moveToFirst();
            while (cursor.moveToNext()){                                //4. 移动Cursor到指定位置或者遍历Cursor,获取数据
                /** ..........**/
            }
            cursor.close();
        }

  • 权限
属性说明
android:grantUriPermssions:临时许可标志。
android:permission:Provider读写权限。
android:readPermission:Provider的读权限。
android:writePermission:Provider的写权限。
android:enabled:标记允许系统启动Provider。
android:exported:标记允许其他应用程序使用这个Provider。
android:multiProcess:标记允许系统启动Provider相同的进程中调用客户端。
  • ContentResolver

ContentResolver通过URI查询ContentProvider中提供的数据。ContentProvider是以类似数据库中表的方式将数据暴露出去,ontentResolver也是采用类似数据库的操作来从ContentProviders中获取数据。

  • ContentObserver

内容观察者,观察特定URI指向的ContentProvider数据变化,做出响应。(类似监听器)

  • 进程间通信原理
  1. ContentProvider是通过IBinder实现通信过程的
  2. getContentResolver获得到的是ApplicationContentResolver(在ContextImpt中实现的)
  3. Client端ApplicationContentResolver使用ContentProviderProxy作为IBinder的Proxy(ContentProviderNative中实现)
  4. Provider端通过Transport作为IBinder的实现端(ContentProvider中实现)
  • 注意事项

ContentProvider的onCreate方法在Application的onCreate方法之前,耗时操作会影响应用的启动。

  • 优点
  1. 安全,开发了增删改查接口,但是没有直接开放数据库权限,避免数据库毁灭性危机
  2. 统一数据共享方式,解耦底层数据存储方式
  3. 更加高效简单的访问系统应用数据

Uri

统一资源标识符,唯一标识ContentProvider的数据。

  • 格式

schema + authority + path + id,例如:content://media/external/images/media/61645616

组成说明举例
schemaAndroid固定为content://content://
authority标识ContentProvider的唯一字符串,注册时指定的android:authoritymedia
path标识 authority 数据的某些子集external/images/media
id标识 path 子集中的某个记录,不指定是标识全部记录61645616