Android面经_111道安卓基础问题(四大组件Activity、Service篇)

111道Android面试基础题目,巩固基础作用


1、Activity

1.1、请介绍activity的生命周期

为了在activity生命周期的各个阶段之间导航转换,activity提供了6个核心回调:onCreate()onStart()onResume()onPause()onPause()onDestroy()。当activity进入到了新的状态时,系统会调用每个回调。

在这里插入图片描述
这个图是对生命周期的直观展现。
当用户离开Activity时候,系统会调用方法来销毁这个Activity。在某个情况下,此销毁只是部分销毁;Activity仍然驻留在内存中(用户切换到了另一个应用),并且可以返回到前台。若用户返回到该Activity,Activity会继续从用户离开位置运行。除了极少数例子,app在后台运行时会受到很多限制,无法启动Activity
系统终止给定进程及其中 Activity 的可能性取决于当时 Activity 的状态

1.1.1、生命周期回调之onCreate()

开发必须实现该回调,会在系统首次创建Activity时候触发。Activity会在创建之后进入已经创建 的状态。在onCreate方法中,需要执行基本应用的启动逻辑,这个逻辑在Activity的整个生命周期中只发生一次。例如,onCreate的实现可能会把某些数据绑定到列表中,让Activity和ViewModel关联,并且实例化某些类作用域的变量。该方法将会接收savedInstanceState参数,后者是包含Activity之前保存状态的Bundle对象。若Activity之前不存在,Bundle对象数值为null。

若有一个生命周期感知型组件和Activity生命周期关联,则该组件将会收到ON_CREATE事件。系统调用带有@OnLifecycleEvent注释的方法让生命周期感知型组件可以执行已经创建状态所需的任何设置代码。

onCreate方法在示例中显示执行Activity某些基本设置的一些代码,例如声明界面(XML布局文件中定义)、定义成员变量;本示例中,系统通过文件资源R.layout.main_activity传递给setContentView()指定XML布局文件。

lateinit var textView: TextView

// 活动实例的某个短暂状态
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // 调用超类onCreate来完成活动的创建,比如视图层次结构
    super.onCreate(savedInstanceState)

    // 恢复实例状态
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // 设置该活动的用户界面布局,布局文件在项目res/layout/main_activity.xml文件中定义
    setContentView(R.layout.main_activity)

    // 初始化成员TextView,以便稍后操作它
    textView = findViewById(R.id.text_view)
}

// 这个回调只在有一个之前使用onSaveInstanceState()保存的实例时被调用。
// 我们在onCreate()中恢复一些状态,而我们可以选择恢复
这里的其他状态,可能在onStart()完成后可用。savedInstanceState Bundle与onCreate()中使用的Bundle相同。
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// 当activity可能被临时销毁时调用,在这里保存实例状态
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // 调用父类保存任何视图层次结构
    super.onSaveInstanceState(outState)
}

不仅定义了XML文件,将其传递给setContentView,也可以在Activity中新建View对象,并且把新建的View插入到ViewGroup中,构建试图层次结构。把根ViewGroup传递给setContentView使用这个布局。

Activity 并未处于“已创建”状态。onCreate() 方法完成执行后,Activity 进入“已开始”状态,系统会相继调用 onStart() onResume() 方法。


1.1.2、生命周期回调之onStart()

当 Activity 进入“已开始”状态时,系统会调用此回调。·onStart() ·调用使 Activity 对用户可见,因为应用会为 Activity 进入前台并支持互动做准备。例如,app通过此方法来初始化维护界面的代码。

当 Activity 进入已开始状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_START事件。

onStart() 方法会非常快速地完成,并且与“已创建”状态一样,Activity 不会一直处于“已开始”状态。一旦此回调结束,Activity 便会进入“已恢复”状态,系统将调用 onResume() 方法。


1.1.3、生命周期回调之onResume()

Activity 会在进入“已恢复”状态时来到前台,然后系统调用 onResume() 回调。这是应用与用户互动的状态。应用会一直保持这种状态,直到某些事件发生,让焦点远离应用。此类事件包括接到来电、用户导航到另一个 Activity,或设备屏幕关闭。

Activity 进入已恢复状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_RESUME事件。这时,生命周期组件可以启用在组件可见且位于前台时需要运行的任何功能,例如启动相机预览。

当发生中断事件时,Activity 进入“已暂停”状态,系统调用 onPause() 回调。

如果 Activity 从“已暂停”状态返回“已恢复”状态,系统将再次调用 onResume() 方法。因此,您应实现 onResume(),以初始化在 onPause() 期间释放的组件,并执行每次 Activity 进入“已恢复”状态时必须完成的任何其他初始化操作。
给出如下生命周期感知型组建的示例,该组件在收到ON_RESUME事件访问相机:

class CameraComponent : LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
}

LifecycleObserver 收到 ON_RESUME 事件后,上述代码便会初始化相机。然而,在多窗口模式下,即使处于“已暂停”状态,Activity 也可能完全可见。例如,当用户处于多窗口模式,并点按另一个不包含 Activity 的窗口时, Activity 将进入“已暂停”状态。

  • 若希望相机仅在应用处于“已恢复”(可见且在前台运行)状态时可用,请在收到上述 ON_RESUME 事件后初始化相机。
  • 若希望在 Activity 处于“已暂停”状态但可见时(例如在多窗口模式下)保持相机可用,应在收到 ON_START 事件后初始化相机。

但请注意,若要让相机在 Activity 处于“已暂停”状态时可用,可能会导致系统在多窗口模式下拒绝其他处于“已恢复”状态的应用访问相机。
有时可能有必要让相机在 Activity 处于“已暂停”状态时保持可用,但这样做实际可能会降低整体用户体验

无论正选择哪个构建事件中执行初始化操作,请务必使用相应的生命周期事件来释放资源。若收到ON_START事件后初始化某些内容,务必在收到ON_STOP事件后释放或者终止对应的内容。

注意:上述代码段把相机初始化放置在生命周期感知型组件之中,也可以直接把代码放入Activity生命周期回调(例如onStartonStop),但通常不建议这么做,一般来说该逻辑应该添加到独立的组件中,可以对多个Activity重复使用,无需复制代码。


1.1.4、生命周期回调之onPause()

系统调用此方法视为用户将要离开Activity的第一个标记(尽管这并不总以为着Activity会被销毁);此方法表示Activity不再位于前台(尽管用户处于多窗口模式时Activity仍然可见)。使用 onPause() 方法暂停或调整当 Activity 处于“已暂停”状态时不应继续(或应有节制地继续)的操作。Activity 进入此状态的原因有很多。例如:

  • 例如上述onResume所述,某个事件会中断app执行。
  • 在 Android 7.0(API 级别 24)或更高版本中,有多个应用在多窗口模式下运行。无论何时,都只有一个应用(窗口)可以拥有焦点,因此系统会暂停所有其他应用。
  • 有新的半透明 Activity(例如对话框)处于开启状态。只要 Activity 仍然部分可见但并未处于焦点之中,它便会一直暂停。

当 Activity 进入已暂停状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_PAUSE 事件。这时,生命周期组件可以停止在组件未位于前台时无需运行的任何功能,例如停止相机预览。

响应 ON_PAUSE 事件的以下 LifecycleObserver示例与上述 ON_RESUME 事件示例相对应,会释放在收到 ON_RESUME 事件后初始化的相机:

class CameraComponent : LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }
}

请注意,上述代码段在 LifecycleObserver 收到 ON_PAUSE 事件后放置相机释放代码。

onPause() 执行非常简单,而且不一定要有足够的时间来执行保存操作。因此,不应使用 onPause()保存应用或用户数据、进行网络调用或执行数据库事务。因为在该方法完成之前,此类工作可能无法完成。相反,应在onStop()期间执行高负载的关闭操作。

onPause() 方法的完成并不意味着 Activity 离开“已暂停”状态。相反,Activity 会保持此状态,直到其恢复或变成对用户完全不可见。如果 Activity 恢复,系统将再次调用onResume()回调。如果 Activity 从“已暂停”状态返回“已恢复”状态,系统会让 Activity 实例继续驻留在内存中,并会在系统调用 onResume() 时重新调用该实例。在这种情况下,您无需重新初始化在任何回调方法导致 Activity 进入“已恢复”状态期间创建的组件。如果 Activity 变为完全不可见,系统会调用 onStop()


1.1.5、生命周期回调之onStop()

如果Activity 不再对用户可见,说明其已进入“已停止”状态,因此系统将调用 onStop() 回调。例如,当新启动的 Activity 覆盖整个屏幕时,可能会发生这种情况。如果 Activity 已结束运行并即将终止,系统还可以调用 onStop()

当 Activity 进入已停止状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_STOP 事件。这时,生命周期组件可以停止在组件未显示在屏幕上时无需运行的任何功能。

onStop()方法中,应用应释放或调整在应用对用户不可见时的无用资源。例如,应用可以暂停动画效果,或从精确位置更新切换到粗略位置更新。使用 onStop() 而非onPause()可确保与界面相关的工作继续进行,即使用户在多窗口模式下查看您的 Activity 也能如此。

可以使用 onStop() 执行 CPU 相对密集的关闭操作。例如,若无法找到更合适的时机来将信息保存到数据库,可以在 onStop() 期间执行此操作。以下示例展示了 onStop() 的实现,它将草稿笔记内容保存到持久性存储空间中:

override fun onStop() {
    // 首先调用超类方法
    super.onStop()

    // 保存笔记的当前草稿,因为活动正在停止,我们希望确保当前笔记的进度不会丢失。
    val values = ContentValues().apply {
        put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
        put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
    }

    // 这个更新在后台的AsyncQueryHandler
    asyncQueryHandler.startUpdate(
            token,     // int token to correlate calls
            null,      // cookie, not used here
            uri,       // The URI for the note to update.
            values,    // The map of column names and new values to apply to them.
            null,      // No SELECT criteria are used.
            null       // No WHERE columns are used.
    )
}

注意,上述代码示例直接使用了Sqlite数据库;不过应该修改为ROOM,这是一个通过SQLITE提供抽象层的持久性库。

当 Activity 进入“已停止”状态时,Activity 对象会继续驻留在内存中:该对象将维护所有状态和成员信息,但不会附加到窗口管理器。Activity 恢复后,Activity 会重新调用这些信息。无需重新初始化在任何回调方法导致 Activity 进入“已恢复”状态期间创建的组件。系统还会追踪布局中每个 View 对象的当前状态,如果用户在 EditText 微件中输入文本,系统将保留文本内容,因此无需保存和恢复文本。

注意:Activity 停止后,如果系统需要恢复内存,可能会销毁包含该 Activity 的进程。即使系统在 Activity 停止后销毁相应进程,系统仍会保留 Bundle(键值对的 blob)中 View 对象(例如 EditText 微件中的文本)的状态,并在用户返回 Activity 时恢复这些对象。

进入“已停止”状态后,Activity 要么返回与用户互动,要么结束运行并消失。如果 Activity 返回,系统将调用 onRestart()。如果 Activity 结束运行,系统将调用 onDestroy()


1.1.6、生命周期回调之onDestroy()

销毁Activity之前,系统会调用onDestroy(),系统调用该回调的原因如下:

  • Activity即将结束了(由于用户彻底关闭了Activity或者由于系统Activity调用了finish或者配置变更(例如设备旋转或者多窗口模式),系统暂时销毁Activity)。

当 Activity 进入已销毁状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_DESTROY 事件。这时,生命周期组件可以在 Activity 被销毁之前清理所需的任何数据。

使用 ViewModel 对象来包含 Activity 的相关视图数据,而不是在 Activity 中加入逻辑来确定 Activity 被销毁的原因。如果因配置变更而重新创建 Activity,ViewModel 不必执行任何操作,因为系统将保留 ViewModel 并将其提供给下一个 Activity 实例。如果不重新创建Activity,ViewModel 将调用 onCleared() 方法,以便在 Activity 被销毁前清除所需的任何数据。

也可以使用 isFinishing() 方法区分这两种情况。

如果 Activity 即将结束,onDestroy() 是 Activity 收到的最后一个生命周期回调。如果由于配置变更而调用 onDestroy(),系统会立即新建 Activity 实例,然后在新配置中为新实例调用 onCreate()。
onDestroy() 回调应释放先前的回调(例如 onStop())尚未释放的所有资源。


1.2、请介绍Activity四种启动模式及应用场景

基本描述:

  • standard,标准模式:若在mainfest中不设置默认standard;standard就是创建一个Activity在栈中创建该Activity的实例。
  • singleTop,栈顶复用模式:和standard相比栈顶复用可以有效减少Activity重复创建对资源的消耗,根据具体情况而定。
  • singleTask,栈内单例模式:栈内只有一个activity实例,栈内已存activity实例,在其他activity中start这个activity,Android直接把这个实例上面其他activity实例踢出栈GC掉。
  • singleInstance ,堆内单例:整个手机操作系统里面只有一个实例存在就是内存单例。

1.2.1、四种启动模式的实际应用场景

这四种启动模式中的Standard模式是最普遍的一种,没有什么特别注意。singleInstance模式是整个系统的单例模式,应用中一般不会用到。

  • singleTask模式运用场景:例如保持app开启之后仅仅只有一个activity实例,展示Home主页;假定用户在主页跳转到其他页面,运行多次操作返回主页,若不借助singleTask模式,则点击返回过程中会多次看到主页,设计不合理。
  • singleTop模式的运用场景:设定当前activity中启动同类型的Activity,那么建议把该类型的Activity的启动模式设定为SingleTop,能够降低Activity创建的资源消耗:
@Override
protected void onNewIntent(Intent intent) {
	super.onNewIntent(intent);
	setIntent(intent);
	initData();
	initView();
}

当前singleTop模式的Activity正处于栈顶时,跳转该Activity会调用onNewintent方法且不会重新创建该Activity实例,只会重新调用该实例,生命周期为:onPause->onNewIntent->onResume


1.3、请说下切换横竖屏时 Activity的生命周期变化

新建一个能打印生命周期的Activity并且运行:

onCreate:
onStart:
onResume:

切换横屏:

onConfigurationChanged: // 当Activity可能被销毁时回调,供app暂存数据
onPause: 
onSaveInstanceState: 
onStop: 
onDestroy: 
onCreate: 
onStart: 
onRestoreInstanceState: // Activity被销毁后重新运行时回调
onResume:

切换竖屏:

onConfigurationChanged: 
onPause: 
onSaveInstanceState: 
onStop: 
onDestroy: 
onCreate: 
onStart: 
onRestoreInstanceState: 
onResume: 
// 基于Android 7.0,并没有见到传说中的调用两遍

为Activity添加属性:android:configChanges=“orientation|screenSize”,切换横屏

onConfigurationChanged:

现在只有一行**onConfigurationChanged:**了。
再切换竖屏:

onConfigurationChanged:

总结

  • 不设置 Activity 的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次。
  • 设置 Activity 的 android:configChanges=“orientation|screenSize” 时,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法。

android:configChanges 属性

VALUE                   DESCRIPTION  

"mcc"                   国际移动用户识别码所属国家代号是改变了
"mnc"                   国际移动用户识别码的移动网号码是改变了
"locale"                地址改变了-----用户选择了一个新的语言会显示出来
"touchscreen"           触摸屏是改变了------通常是不会发生的
"keyboard"              键盘发生了改变----例如用户用了外部的键盘
"keyboardHidden"        键盘的可用性发生了改变
"navigation"            导航发生了变化-----通常也不会发生
"screenLayout"          屏幕的显示发生了变化------不同的显示被激活
**加粗样式**"fontScale"             字体比例发生了变化----选择了不同的全局字体 
"uiMode"                用户的模式发生了变化
"orientation"           屏幕方向改变了
"screenSize"            屏幕大小改变了
"smallestScreenSize"    屏幕的物理大小改变了,如:连接到一个外部的屏幕上

以上是 android:configChanges 属性的所有值。当我们希望一种或者多种配置改变时避免重新启动 activity。就可以通过在 AndroidManifest 中设置 android:configChanges 属性来实现。如下所示:

<activity
    android:name=".XXXActivity"
    android:configChanges="XXX|XXX"/>

我们可以在这里声明 activity 可以处理的任何配置改变,当这些配置改变时不会重新启动activity,而会调用 onConfigurationChanged() 方法。如果改变的配置中包含了你所无法处理的配置(在android:configChanges并未声明),activity 仍然要被重新启动。


1.3.1、onSaveInstanceState() 和 onRestoreInstanceState() 方法

基本作用

注意:Activity的 onSaveInstanceState() onRestoreInstanceState() 并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,并不一定会被触发。

若app遇到了意外情况,例如内存不足、按下了Home返回键,由系统去销毁一个Activity时,onSaveInstanceState会被调用,但是当用户主动去销毁一个Activity时,例如app中按下了返回键,则该方法不会被调用。通常onSaveInstanceState() 只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。

具体到应用中,当我们想切换横竖屏时候保存视频的播放进度:可以在Activity中重写onSaveInstanceState(Bundle outState) 方法,调用 outState.putXXX()来保存数据;紧接着在Activity重新被创建的时候onCreate(Bundle savedInstanceState) onRestoreInstanceState(Bundle savedInstanceState) 中 调用 savedInstanceState.getXXX() 来获取数据。

  • onRestoreInstanceState()什么时候调用?
    onRestoreInstanceState() 被调用的前提是,activity A “确实” 被系统销毁了,且 activity A 被重新创建。当 activity A 未被重新创建时,该方法不会被调用。例如,当正在显示 activity A 的时候,用户按下 HOME 键回到主界面,然后用户紧接着又返回到 activity A,这种情况下 activity A 一般不会因为内存的原因被系统销毁,故 onRestoreInstanceState() 方法不会被执行, 这也证明这两个方法不一定会成对被使用。
    onRestoreInstanceState() 在 onStart() 和 onResume() 之间调用。

  • 是否需要重写onSaveInstanceState()方法?
    如果我们没有覆写 onSaveInstanceState() 方法, 此方法的默认实现会自动保存 activity 中的某些状态数据,,比如 activity 中各种 UI 控件的状态.。android 应用框架中定义的几乎所有 UI 控件都恰当的实现了onSaveInstanceState() 方法,因此当 activity 被摧毁和重建时,这些 UI 控件会自动保存和恢复状态数据.:比如 EditText 控件会自动保存和恢复输入的数据。而 CheckBox 控件会自动保存和恢复选中状态。开发者只需要为这些控件指定一个唯一的 ID (通过设置 android:id 属性即可),剩余的事情就可以自动完成了.如果没有为控件指定 ID, 则这个控件就不会进行自动的数据保存和恢复操作。


2、Service

service,中文名称是服务,服务是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互,而且还要长期运行的任务。

2.1、请介绍Service的启动方式,启动方式的区别?

service有两种启动方式,一种是通过 startService() 来启动的,另一种是通过 bindService() 来启动的。

通过startService启动方式的生命周期如下图:

在这里插入图片描述

使用service如下:

  • 定义一个类,继承Service;
  • 在AndroidMainfest中配置该service;
  • 使用Context的startService方法启动Service;
  • 不适用这个服务时,调用了stopService方法停止Service。

注意:若服务已经开启,则不会重复执行onCreate,而是调用onStart或者onStartCommand,而服务停止的时候调用onDestroy()

特点:

  • 一旦服务开始就跟启动者没关系了。
  • 开启者推出之后,服务还是可以在后台保持长期的运行,前提是没有调用stopService(Intent)
  • 开启者不能够调用服务里面的方法。

通过bindService来启动

通过bindService启动方式的生命周期如下:
在这里插入图片描述

使用Service的步骤如下:

  • 定义一个类,并继承 Service;
  • AndroidManifest.xml 中配置此 Service;
  • 使用 Context 的 bindService(Intent, ServiceConnection, int) 方法来启动此 Service;
  • 不使用该服务时,调用 unbindService(ServiceConnection) 方法停止此 Service。

绑定服务 不会调用onStart或者onStartCommand方法。

特点:

  • bind方式开启服务,绑定服务,若调用者挂了,服务service也会挂掉。
  • 绑定者可以调用服务里面的方法。

下面通过具体的代码来展示Activity如何管理service,其生命周期具体的变化:

启动服务:startService

23:42:02.003 1173-1173/? I/MyService: onCreate
23:42:02.003 1173-1173/? I/MyService: onStartCommand

注意:多次调用startService方法,onCreate就只会调用一次,但是onStartCommand会调用多次。

停止服务:stopService

23:45:17.443 1292-1292/com.yirong.androidpractice I/MyService: onDestroy

注意:在调用bindService之后,就算再次调用stopService也无法停止服务了,必须首先解绑才可以。

绑定服务:bindService

bindService(intent,this, Context.BIND_AUTO_CREATE)
stopService(intent)
00:09:38.653 1361-1361/com.yirong.androidpractice I/MyService: onCreate
00:09:38.653 1361-1361/com.yirong.androidpractice I/MyService: onBind

注意:bindService不会调用·onStart·方法。

解绑服务:unbindService

00:10:40.882 1411-1411/com.yirong.androidpractice I/MyService: onUnbind
00:10:40.882 1411-1411/com.yirong.androidpractice I/MyService: onDestroy

2.2、Activity、Service、intent之间的联系?

首先从通俗易懂的角度来解释:

  • 他们都是 Android 开发中使用频率最高的类。其中 Activity 和 Service 都是 Android 四大组件之一。他俩都是Context 类的子类 ContextWrapper 的子类,因此他俩可以算是兄弟关系吧。不过兄弟俩各有各自的本领,Activity负责用户界面的显示和交互,Service 负责后台任务的处理。Activity 和 Service 之间可以通过 Intent 传递数据,因此可以把 Intent 看作是通信使者。

2.2.1、概述Android Service和Activity之间通信的几种方式

在Android整体系统中,Activity主要负责前台页面的展示,Service主要负责需要长期运行的任务;一般在Activity中启动后台Service,并且通过Intent来启动,intent可以传递数据给Service;

  • Intent意图:主要是解决Android中各个组件之间的通讯问题,负责对app中一次操作的动作、动作涉及到数据、附加数据的描述,Android根据Intent的描述,负责找到对应的组件,把Intent传递给调用的组件,完成组件的调用。

Activity之间常见的跳转代码:

Intent intent = new Intent(MainActivity.this,DemoActivity.class);
startActivity(intent);
//startActivity(new Intent(MainActivity.this,DemoActivity.class));

上面的代码实现了MainActivity和DemoActivity之间的跳转,其中一个就是startActivity(Intent intent);使用方式就是传入一个Intent对象,封装好意图之后,通过如图所示流程:
在这里插入图片描述


3、在Activity和Service中创建Thread的区别?

  • 首先默认情况下,Service运行在主线程中,若要执行复杂的耗时操作,则必须在Service中创建一个thread来执行任务。

  • Service 优先级高于后台挂起的Acitivity,并且高于Acitivity创建的thread,所以系统在内存不足的时候会优先杀死后台Activity或者Thread,而不会轻易杀死Service组件,即使被迫杀死Service,也会在资源可用时重启被杀死的Service 其实,Service和Thread根本就不是一个级别的东西,Service是系统的四大组件之一,Thread只是一个用来执行后台任 务的工具类,它可以在Activity中被创建,也可以在Service中被创建。因此,我们其实不应该讨论该使用Service还是 Thread,而是应该讨论在什么地方创建Thread;

    • 在Activitiy中创建,这种情况下,一般在onCreate时创建,在onDestroy()中销毁,否则,Activity销毁后,Thread是会依然在后台运行着。这种情况下,Thread的生命周期即为整个Activity的生命周期。所以,在Activity中创建的Thread只适合完成一些依赖 Activity本身有关的任务,比如定时更新一下Activity的控件状态等。
    • 核心特点:该Thread的就是为这个Activity服务的,完成这个特定的Activity交代的任务,主动通知该Activity一些消息和 事件,Activity销毁后,该Thread也没有存活的意义了。
  • 在Application中被创建 这种情况下,一般自定义Application类,重载onCreat方法,并在其中创建Thread,当然,也会在onTerminate()方法 中销毁Thread,否则,如果Thread没有退出的话,即使整个Application退出了,线程依然会在后台运行着。这种情况下,Thread的生命周期即为整个Application的生命周期。所以,在Application中创建的Thread,可以执行一 些整个应用级别的任务,比如定时检查一下网络连接状态等等。

    • 核心特点:该Thread的终极目标是为这个APP的各个Activity服务的,包括完成某个Activity交代的任务,主动通知某个 Activity一些消息和事件等,APP退出之后该Thread也没有存活的意义了。以上这两种情况下,Thread的生命周期都不应该超出整个应用程序的生命周期,也就是,整个APP退出之后,Thread都应该完全退出,这样才不会出现内存泄漏或者僵尸线程。那么,如果你希望整个APP都退出之后依然能运行该Thread, 那么就应该把Thread放到Service中去创建和启动了。

4、android进程的优先级?以及如何保证Service不被杀死?

service服务是Android系统中非常重要的组件。Service可以脱离app运行,也就是说,app只是负责启动Service。

一旦Service启动,就算app关闭,其依旧可以在后台运行,Android的Service主要有两个作用:后台运行跨进程通讯
若想要app可以跨进程通讯,就需要使用AIDL服务,全称就是Android Interface Definition Language,也就是说,AIDL实际上是一种Android接口定义语言;通过这种语言定义的接口,Android studio会在编译后生成相应的Java代码。

4.1、在onStartCommand方法中,返回START_STICKY

StartCommand中几个常量:

  • START_STICKY 系统重新创建服务并且调用onStartCommand()方法,但并不会传递最后一次传递的intent,只是传递一个空的intent。除非存在将要传递来的intent,那么就会传递这些intent。这个适合播放器一类的服务,不需要执行命令,只需要独自运行,等待任务。
  • START_REDELIVER_INTENT 系统重新创建服务并且调用onStartCommand()方法,传递最后一次传递的intent。其余存在的需要传递的intent会按顺序传递进来。这适合像下载一样的服务,立即恢复,积极执行。

4.2、通过前台服务来提升Service的优先级

前台服务是被认为用于已知的正在运行的服务,当系统需要释放内存时不会优先杀掉该进程。前台进程必须发一个notification在状态栏中显示,知道进程被杀死。因为前台服务一直消耗一部分资源,但不像一般服务那样会在需要的时候被杀掉,所以为了节约资源,保护电池寿命,一定要在建前台服务的时候发送notification,提示用户。当然系统提供的方法就必须有notification参数的,所以不要想着怎么把notification隐藏掉。

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onCreate() {
    super.onCreate();
    Log.d(TAG, "onCreate: 已执行");
    Intent intent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
    Notification notification = new Notification.Builder(this)
            .setContentTitle("这是内容标题")
            .setContentText("这是内容正文")
            .setWhen(System.currentTimeMillis())
            .setSmallIcon(R.mipmap.ic_launcher)
            .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
            .setContentIntent(pendingIntent)
            .build();
    startForeground(1, notification);   //前台服务
}

4.3、在onDestroy中发送广播,让广播来开启自己

服务+广播方式, 就是当service调用到onDestroy的时候,发送一个自定义的广播,当收到广播的时候,重新启动service;

    <receiver
        android:name=".MyReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="com.peterli.localservicetest.destroy"/>
        </intent-filter>
    </receiver>

在service中onDestroy的时候:

@Override
public void onDestroy() {
    Log.d(TAG, "onDestroy: ");
    stopForeground(true);  //有前台服务的时候把这句话加上
    Intent intent = new Intent("com.peterli.localservicetest.destroy");
    sendBroadcast(intent);
    super.onDestroy();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值