第二天笔记

Android 四大组件课程概要总结

1. Activity

a. 创建

  • 自动创建
    • 在新建项目后,会自动为我们创建一个 MainActivity。Activity 组成部分如下:
      • 继承自 Activity 的类
      • 放在 res/layout 文件夹的布局 XML 文件
      • 在 AndroidManifest.xml 文件中声明

b. Activity 生命周期回调方法

  • Activity 生命周期方法

    • onCreate(): 是 - 它会在系统创建您的 Activity 时被唤起。必须在此类中调用 setContentView() 来定义 Activity 的布局。
    • onStart(): 否 - onCreate 完成后,Activity 将进入已启动状态,并对用户可见。此回调通常会在 Activity 进入前台与用户互动之前的最后准备工作。
    • onResume(): 否 - 系统会在 Activity 开始与用户互动之前调用此回调。此时,Activity 位于 Activity 堆栈的顶部,并获取所有用户输入。
    • onPause(): 否 - 当 Activity 失焦点并进入“暂停”状态时,系统会调用 onPause()。
    • onStop(): 否 - 当 Activity 对用户不可见时,系统会调用 onStop()。出现这种情况的原因可能是新活动启动、现有 Activity 返回前台,或销毁旧的 Activity 正在进入它“停止”状态之前即将终止的 Activity。
    • onRestart(): 否 - 当用户已中断的 Activity 直接重新启动时,系统会调用 onRestart()。
    • onDestroy(): 否 - 系统会在销毁 Activity 之前调用此回调。Activity 被销毁的最后一个回调 onDestroy() 是 Activity 进行清理工作和释放所有资源的最后机会。
  • Activity 理解生命周期

    • 通过生命周期执行顺序简化图了解生命周期的各个阶段。

c. Activity 的四种启动方式

i. 通过如下命令查看详细的 Activity 任务栈信息:adb shell dumpsys activity activities

这份总结涵盖了 Activity 的创建、生命周期方法及其理解、以及启动方式的概述。希望这些内容对您的学习有帮助!

AB页面跳转:

// 普通样式
A.onPause
B.onCreate
B.onStart
B.onResume
A.stop

// B是透明或者dialog样式
A.onPause
B.onCreate
B.onStart
B.onResume

// B页面关闭重新回到A页面
B.onPause
A.onRestart
A.onStart
A.onResume
B.onStop
B.onDestroy

启动模式与避坑指南笔记

定义启动模式

可以通过启动模式定义Activity的新实例如何与当前任务关联:

  1. AndroidManifest.xml中声明

    <activity
        android:name=".YourActivity"
        android:launchMode="singleTask" />
    

启动模式介绍

属性值 对应 flags 描述
standard – 默认模式。系统在启动Activity的任务中创建新实例,并将intent传递给该实例。每个实例可以属于不同的任务。
singleTop FLAG_ACTIVITY_SINGLE_TOP 如果当前任务的顶部已有该Activity实例,系统调用onNewIntent()方法传递intent给该实例,而不是创建新实例。
singleTask FLAG_ACTIVITY_NEW_TASK 系统会创建新任务,并实例化新的Activity。如果任务中已有该Activity实例,则调用onNewIntent()方法传递intent,而不是创建新实例。
singleInstance – 与singleTask相似,唯一不同是该模式下的Activity在启动时会独占一个任务栈,且不会与其他Activity共享。
  • standard : 不管怎样都会新建activity
  • singleTop:如果目标在任务栈的栈顶,那么直接复用实例,触发目标activity的onNewIntent方法, 如果目标任务不在任务栈的栈顶,那么新建一个目标实例,添加到任务栈
  • singleTask:如果任务栈内没有目标实例,则直接创建新的目标实例到任务栈,如果有,则直接把目标activity上面所有的activity都移出栈,把目标activity置于栈顶。
  • singleInstance:

image-20240707111307879

image-20240707085350853

设置singleInstance

在 Android 中,如果你希望一个 Activity 采用 singleInstance 启动模式,需要在 AndroidManifest.xml 文件中为该 Activity 设置相应的 launchMode 属性。此外,你还可以通过 Intent 设置 Flags 来控制 Activity 的启动行为。

在 AndroidManifest.xml 中设置 singleInstance

在 AndroidManifest.xml 文件中为目标 Activity 添加 launchMode 属性,并将其值设置为 singleInstance:

<activity
    android:name=".YourActivity"
    android:launchMode="singleInstance">
</activity>

通过 Intent 设置 Flags

当你启动一个 Activity 时,可以通过设置 Intent Flags 来影响 Activity 的启动行为。常见的 Flags 包括:

  • FLAG_ACTIVITY_NEW_TASK: 如果 Activity 不存在于当前任务中,将启动一个新的任务并将 Activity 放置在该任务的根上。
  • FLAG_ACTIVITY_CLEAR_TOP: 如果 Activity 已存在于任务中,将清除其上的所有 Activity,并将其置于顶部。

下面是一个示例代码,展示了如何使用 Intent 启动 singleInstance 模式的 Activity 并设置 Flags:

Intent intent = new Intent(this, YourActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

结合使用 singleInstance 和 Intent Flags

结合上述方法,确保在 AndroidManifest.xml 中正确配置 singleInstance 模式,并在启动 Activity 时设置合适的 Flags。以下是一个完整的示例:

1. 在 AndroidManifest.xml 中配置:

<activity
    android:name=".YourActivity"
    android:launchMode="singleInstance">
</activity>

2. 在代码中启动 Activity 并设置 Flags:

Intent intent = new Intent(this, YourActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

通过这种方式,你可以确保目标 Activity 以 singleInstance 模式启动,并根据需要设置相应的 Flags 来控制其启动行为。

Intent两种启动方式

  1. 显式 Intent
  2. 隐式 Intent

Activity 避坑指南

  1. 资源回收:在Activity的生命周期结束时,确保释放资源、取消网络请求、注销监听器等操作,以防止内存泄漏。
  2. 内存泄漏:避免在Activity中引用长生命周期对象,使用弱引用或在适当的生命周期方法中释放资源。
  3. Context 使用:谨慎使用Context,避免持有Activity的引用,以免引发内存泄漏。尽量使用Application的Context。
  4. 生命周期管理:理解Activity的生命周期并正确地管理资源。确保在onPause、onStop、onDestroy等生命周期方法中释放资源和取消注册监听器。
  5. 横竖屏切换:当横竖屏切换时,Activity会销毁并重新创建。确保在这个过程中保存和恢复必要的状态信息,以防止用户体验的中断。

Activity-Service两种启动方式及关键方法回调(start bind)

Service创建

Service的创建包括两部分:

  1. 新建类需要继承 Service
  2. 在 AndroidManifest.xml 文件中进行声明

示例代码:

<service
    android:name=".service.FirstService"
    android:enabled="true"
    android:exported="true">
</service>

启动和停止服务

方法 描述
startService() 启动服务
startForegroundService() 启动前台服务
stopService()、stopSelf() 停止服务

启动服务的生命周期

方法 描述
onCreate() 首次创建服务时,系统会(在调用 onStartCommand() 或 onBind() 之前)调用此方法来执行一次性设置程序。如果服务已在运行,则不会调用此方法。
onStartCommand() 通过 startService() 方法启动 Service 时调用此方法,执行此方法时,服务即会启动并可在后台无限期运行。
onDestroy() 当不再使用服务且准备销毁时,系统会调用此方法。

服务生命周期示意图

Call to startService()
    ↓
onCreate()
    ↓
onStartCommand()
    ↓
Service running
    ↓
onDestroy()
    ↓
Service shut down

Service的启动与绑定

一、启动和停止服务

Service创建

  1. 新建类继承 Service

  2. 在 AndroidManifest.xml 文件中声明

    <service
        android:name=".service.FirstService"
        android:enabled="true"
        android:exported="true">
    </service>
    

启动和停止服务方法

image-20240707133413501

方法 描述
startService() 启动服务
startForegroundService() 启动前台服务
stopService()、stopSelf() 停止服务

启动服务的生命周期

方法 描述
onCreate() 首次创建服务时调用,用于一次性设置程序。如果服务已在运行,则不会调用此方法。
onStartCommand() 通过 startService() 方法启动 Service 时调用此方法。
onDestroy() 服务不再使用且准备销毁时调用。

服务生命周期示意图

image-20240707133532389

Call to startService()
    ↓
onCreate()
    ↓
onStartCommand()
    ↓
Service running
    ↓
onDestroy()
    ↓
Service shut down

二、绑定和解绑服务

绑定和解绑方法

方法 描述
bindService() 绑定服务
unbindService() 解绑服务

绑定服务的生命周期

方法 描述
onCreate() 首次创建服务时系统调用(在调用 onStartCommand() 或 onBind() 之前)。
onBind() 通过 bindService() 启动 Service 时调用,返回 IBinder 接口供客户端与服务通信。
onUnbind() 所有客户端通过 unbindService() 解绑时调用。
onDestroy() 服务不再使用且准备销毁时系统调用。
unbindService() 解绑服务。

image-20240707133634620

image-20240707151014360

image-20240707151039874

image-20240707151404742

绑定服务生命周期示意图

Call to bindService()
    ↓
onCreate()
    ↓
onBind()
    ↓
Clients are bound to service
    ↓
All clients unbind by calling unbindService()
    ↓
onUnbind()
    ↓
onDestroy()
    ↓
Service shut down

三、其他概念

广播

  • 实现方式:使用 IntentService 代码实现。
  • 广播回调:两种实现方式。

image-20240707161534947

image-20240707162816043

image-20240707163435910

image-20240707171122755

ContentProvider

  • 理解原理:掌握 ContentProvider 的工作机制和使用方法。

跨进程实现方式

  • Messenger 和 AIDL:使用 Messenger 和 AIDL 实现跨进程通信。

总结

image-20240707173836795

image-20240707174821162

image-20240707175223185

image-20240707175234694

image-20240707175243518

image-20240707175313864

activity:
生命周期:onCreate/onStart/onResume/onPause/onStop/onDestroy
其中 onStart:可见,不可交互
onResume:可见,可交互
onPause:可见、不可交互
onStop:不可见,不可交互

onCreate - onDestroy
onStart - onStop
onResume - onPause

场景1:直接A跳转B
 A.onpause
 B.oncreate
 B.onstart
 B.onresume
 A.stop

场景2: A跳B,但是B是透明的 or  dialog样式的
A.onpause
B.oncreate
B.onstart
B.onresume

场景3: B页面关闭,重新回到A页面
B.onpause
A.onrestart
A.onstart
A.onReusme
B.onstop
B.ondestroy

activity三大要素
1: 创建一个新的activity,继承自系统的AppCompatActivity
2: res/layout文件夹中创建对应的xml布局文件
3: activity 与 layout的结合,在activity.onCreate的setContentView(R.layout.xxx)
4.  清单文件中androidManifast必须要声明组件activity

启动模式
standard : 不管怎么样,都会新建一个目标activity的示例
singleTop: 如果目标activity在任务栈的栈顶,那么直接复用实例,会触发目标activity的onnewintent方法
           如果目标activity不在任务栈的栈顶,新建一个目标实例,添加到任务栈
singleTask:1. 如果任务栈没有目标实例,则直接创建新的实例,添加到任务栈
            2. 如果任务栈中已经存在目标activity,那么直接把目标activity上面的所有的其他activity都移除任务栈,
            把目标activity置于栈顶


activity跳转:
显式跳转:
Intent intent = new Intent(this,XXXActivity.class)
startActivity(intent)
隐式跳转:通过action进行跳转
Intent intent = new Intent()
intent.setAction("xxxx")
startActivity
示例代码:跳转拨号页面
Intent intent = new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:110"));
startActivity(intent);


intent:组件之间消息传递的媒介,同时我们可以用它启动各个组件(activity、service)

Service创建流程
1. 继承系统Service,实现抽象方法 onBind
2. 清单文件中注册组件
示例:
  <service android:name=".service.MyService"
            android:exported="true"
            android:enabled="true"/>

Service启动模式:
1. startService(new Intent(this,xxxService.class))
   触发生命周期:onCreate、onStartCommand,如果service已经创建了,则不会再次触发onCreate
2. stopService、stopSelf
   触发生命周期:onDestroy

1. bindService (intent,serviceConnection,BIND_AUTO_CREATE)
   触发生命周期:onCreate、onbind. 如果服务已经绑定,再次绑定不会触发生命周期
2. unBindService
   触发生命周期:onDestroy


intent传值:
putExtra("key",value)  value是任何数据类型 Object
getExtra("key") 有些类型需要默认值
示例:传递
intent.putExtra("name","唐鸿程");
intent.putExtra("age",32);
intent.putExtra("gender",false);
示例:获取
Intent intent = getIntent();
String name = intent.getStringExtra("name");
int age = intent.getIntExtra("age", 18);
boolean gender = intent.getBooleanExtra("gender", true);
注意重点:获取的时候,是通过getIntent(),不是自己new intent()

注意点:key前后一定要保持一致
如果我们需要传递的属性比较多,可以选择使用序列化的实体类来进行传递
序列化:Serializable、Parcelable
获取对象的时候,需要转型,示例:
Person person = (Person) intent.getSerializableExtra("personInfo");

broadcastreceiver
注册方式:
1. 静态注册
step1:创建一个广播接收器 ,继承自BroadcastReceiver(不能内部类)
step2:在清单文件中进行声明
        <!--        清单文件中 声明静态注册广播-->
                <receiver android:name=".MyBroadcastReceiver"
                    android:exported="true">

                    <intent-filter>
        <!--                action可以开发者自己指定-->
                        <action android:name="myFirstBroadcast"/>

                    </intent-filter>
                </receiver>
step3:可以自己发送广播(可选)
       //发送广播
       Intent intent = new Intent();
       intent.setAction("myFirstBroadcast");
       //发送广播一定要指定包名
       intent.setPackage(getPackageName());
       sendBroadcast(intent);
step4:接收并处理广播, 是在第一步自定义创建的广播接收器的onReceive方法中,处理广播,可以通过intent来获取对应的action
        @Override
        public void onReceive(Context context, Intent intent) {
            //获取action,后续可以根据action的不同,做逻辑区分处理
            String action = intent.getAction();
            switch (action){
                case "myFirstBroadcast":
                        Toast.makeText(context,"第一个广播:" +action,Toast.LENGTH_SHORT).show();
                        break;
                    case "secondBroadcast":
                        Toast.makeText(context,"第二个广播:" +action,Toast.LENGTH_SHORT).show();
                        break;
                }
                Log.d("lollipop","llll:" + action);
        }
注意重点:如果我们想要接受到action,我们必须要保证 清单文件中注册的action和 广播发送者发送的action是一致的