Android 开发入门(3)| Activity

发布时间 2023-03-25 16:35:21作者: SRIGT

0x05 活动 Activity

(1)启停活动页面

a. 启动和结束

  • 从当前页面跳转至新页面

    startActivity(new Intent(this, [targetPage].class))
    
  • 从当前页面返回至上个页面(相当于关闭当前页面)

    finish();
    
  • 举例:Activity1 跳转至 Activity2:

    • XML:

      <Button
              android:id="@+id/btn_1"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="开始跳转至二页面" />
      
    • Java:

      // Activity1.java
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_1);
          findViewById(R.id.btn_1).setOnClickListener(this);
      }
      @Override
      public void onClick(View view) {
          // 跳转
          startActivity(new Intent(this, Activity2.class));
      }
      
      // Activity2.java
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_2);
          findViewById(R.id.btn_2).setOnClickListener(this);
      }
      @Override
      public void onClick(View view) {
          // 结束并返回
          finish();
      }
      

b. 生命周期

  • 生命周期:每个对象从创建到销毁的历程

  • 分类:

    1. 前景模式:Foreground Process
    2. 可见模式:Visible Process
    3. 背景模式:Background Process
    4. 空白模式:Empty Process
  • 执行顺序:

    // Activity1.java
    private static final String TAG = "SRIGT";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "Activity1 onCreate");
        setContentView(R.layout.activity_1);
        findViewById(R.id.btn_1).setOnClickListener(this);
    }
    
    @Override
    public void onClick(View view) {
        startActivity(new Intent(this, Activity2.class));
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "Activity1 onStart");
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "Activity1 onResume");
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "Activity1 onPause");
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "Activity1 onStop");
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Activity1 onDestroy");
    }
    
    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG, "Activity1 onRestart");
    }
    
    graph LR 1.onCreate-->2.onStart -->3.onResume --切换活动-->4.onPause -->5.onStop --返回源活动-->6.onRestart -->2.onStart 4.onPause--切换活动等待时间较短-->3.onResume --按退出键-->7.onPause -->8.onStop -->9.onDestroy
    • onCreate:创建活动,将页面布局加载进内存,初始状态
    • onStart:开始活动,将活动页面显示在屏幕上,就绪状态
    • onResume:恢复活动,活动页面进入活跃状态,允许用户交互
    • onPause:暂停活动,活动页面进入暂停状态,不允许用户交互
    • onStop:停止活动,不显示活动页面
    • onRestart:重启活动,重新加载内存中的页面数据
    • onDestroy:销毁活动,回收活动占用的系统资源,把页面从内存中去除
    • onNewIntent:重用已有活动实例
    graph LR A(不存在)-- onCreate-->B(初始状态) -- onStart-->C(就绪状态) -- onResume-->活跃状态 -- onPause-->C -- onStop-->B -- onDestroy-->A

c. 启动模式

  • Activity 的启动模式类似于数据结构中的 “ 先进后出 ” 特点

  • 静态设置启动模式

    在 manifest 中通过 launchMode 属性设置的

    1. 默认启动模式 Standard

      启动的 Activity 会依照启动顺序依次压入 Task 栈中

    2. 栈顶复用模式 SingleTop

      当栈顶 Activity 为目标 Activity 时就不会重复创建新的 Activity。一般用于渠道多、多应用开启调用的 Activity

    3. 栈内复用模式 SingleTask

      如果 task 栈内存在目标 Activity 实例,则将 task 内的对应 Activity 实例之上的所有 Activity 弹出栈,并将对应 Activity 置于栈顶。一般用于程序主界面、耗费系统资源的 Activity

    4. 全局唯一模式 SingleInstance

      该模式为目标 Activity 创建一个新的 task 栈,将目标 Activity 放入新的 task,并让目标 Activity 获得焦点

  • 动态设置启动模式

    通过 Java 代码实现,通过 Intent 动态设置 Activity 启动模式

    1. FLAG_ACTIVITY_NEW_TASK

      开辟新的任务栈

    2. FLAG_ACTIVITY_SINGLE_TOP

      当栈顶为待跳转的 Activity 实例时重用栈顶实例

    3. FLAG_ACTIVITY_CLEAR_TOP

      当栈中存在待跳转的 Activity 实例时重新创建一个新实例,并清除原实例上方的所有实例

    4. FLAG_ACTIVITY_NO_HISTORY

      栈中不保存新启动的 Activity 实例

    5. FLAG_ACTIVITY_CLEAR_TASK

      跳转到新页面时栈中原有实例都被清空

(2)消息传递

a. 显/隐式 Intent

  • Intent 是各组件之间信息沟通的桥梁

    • 标明本次通信请求的起点、终点、方式
    • 发起方携带所需数据,接收方解析该数据
    • 发起方若想判断接收方是否接收成功,Intent 就要让接收方传回应答的数据内容
  • Intent 组成部分

    元素名称 设置方法 说明
    Component setComponent 组件,指定 Intent 的来源和目标
    Action setAction 行为,指定 Intent 的行为
    Data setData URI,指定行为要操纵的数据路径
    Category addCategory 类别,指定 Intent 的操作类别
    Type setType 数据类型,指定消息的数据类型
    Extras putExtras 扩展信息,指定装载的包裹信息
    Flags setFlags 标志位,指定 Activity 的启动标志
  • 显式 Intent

    直接指定来源活动与目标活动,属于精准匹配

    构建方式:

    1. 在 Intent 的构造函数中指定

      // 创建一个目标确定的 Intent
      Intent intent = new Intent(this, ActNextActivity.class);
      
    2. 调用 Intent 对象的 setClass 方法指定

      // 创建一个新 Intent
      Intent intent = new Intent();
      // 设置 Intent 要跳转的目标活动
      intent.setClass(this, ActNextActivity.class);
      
    3. 调用 Intent 对象的 setComponent 方法指定

      // 创建一个新 Intent
      Intent intent = new Intent();
      // 创建包含目标活动在内的组件名称对象
      ComponentName component = new ComponentName(this, ActNextActivity.class);
      // 设置 Intent 携带的组件信息
      intent.setComponent(component);
      
  • 隐式 Intent

    具有标记过滤作用,从而不向外暴露活动名称,没有明确指定要跳转的目标活动,只给出一个行为字符串让系统自动匹配,属于模糊匹配

    Intent 类的系统行为常量名 系统行为的常量值 说明
    ACTION_MAIN android.intent.action.MAIN App 启动时的入口
    ACTION_VIEW android.intent.action.VIEW 向用户显示数据
    ACTION_SEND android.intent.action.SEND 分享内容
    ACTION_CALL android.intent.action.CALL 直接拨号
    ACTION_DIAL android.intent.action.DIAL 准备拨号
    ACTION_SEMDTO android.intent.action.SENDTO 发送短信
    ACTION_ANSWER android.intent.action.ANSWER 接听电话

    举例一:调用系统拨号程序:

    @Override
    public void onClick(View view) {
        // 设定目标号码
        String phone = "13800000000";
        // 创建新 Intent
        Intent intent = new Intent();
        // 设置 Intent 行为为准备拨号
        intent.setAction(Intent.ACTION_DIAL);
        // 声明一个拨号的 Uri
        Uri uri = Uri.parse("tel: " + phone);
        // 设置 Intent 前往的路径
        intent.setData(uri);
        // 启动 Intent 通往的 Activity 页面
        startActivity(intent);
    }
    

    举例二:调用系统发送短信:

    intent.setAction(Intent.ACTION_SENDTO);
    Uri uri = Uri.parse("smsto: " + phone);
    

b. 向下一个 Activity 发送数据

  • Intent 使用 Bundle 对象存放待传递的数据信息

  • Bundle 对象操作各类型数据的读写方法

    数据类型 读取方法 写入方法
    整型数 getInt putInt
    浮点数 getFloat putFloat
    双精度浮点数 getDouble putDouble
    布尔值 getBoolean putBoolean
    字符串 getString putString
    字符串数组 getStringArray putStringArray
    字符串列表 getStringArrayList putStringArrayList
    可序列化结构 getSerializable putSerializable
  • Bundle

    • 发送方通过调用 Intent 对象的putExtras()方法存入消息包裹
    • 接收方通过调用 Intent 对象的getExtras()方法取出消息包裹
  • 实例:

    • 发送方

      @Override
      public void onClick(View view) {
          Intent intent = new Intent(this, Activity2.class);
          
          // 创建包裹用来发送消息
          Bundle bundle = new Bundle();
          bundle.putString("message", "需要发送的消息");
          
          intent.putExtras(bundle);
          startActivity(intent);
      }
      
    • 接收方

      TextView rec = findViewById(R.id.tv_rec);
      
      // 接收消息
      Bundle bundle = getIntent().getExtras();
      // 将接收到的消息存储在 str 中
      String str = bundle.getString("message");
      
      rec.setText(str);
      

c. 向上一个 Activity 返回数据

  • 上一个页面打包好请求数据,调用startActivityForResult()方法执行跳转动作
  • 下一个页面接收并解析请求数据,进行相应处理
  • 下一个页面在返回上一个页面时,打包应答数据并调用setResult()方法返回数据包裹
  • 上一个页面重写方法onActivityResult(),解析获得下一个页面的返回数据

实例:

  • 发送方

    private String str = "text";
    private ActivityResultLauncher<Intent> register;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_1);
        ((TextView)findViewById(R.id.req)).setText("待发送的消息: " + str);
        findViewById(R.id.btn_1).setOnClickListener(this);
    
        register = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            if (result != null) {
                Intent intent = result.getData();
                if (intent != null && result.getResultCode() == Activity.RESULT_OK) {
                    Bundle bundle = intent.getExtras();
                    String str = bundle.getString("content");
                    ((TextView)findViewById(R.id.res)).setText("应答内容" + str);
                }
            }
        });
    }
    
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(this, Activity2.class);
        Bundle bundle = new Bundle();
        bundle.putString("content", str);
        intent.putExtras(bundle);
        register.launch(intent);
    }
    
  • 接收方

    private String str;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_2);
        TextView req = findViewById(R.id.req);
        Bundle bundle = getIntent().getExtras();
        String content = bundle.getString("content");
        str = String.format("收到的消息: " + content);
        req.setText(str);
    
        findViewById(R.id.btn_2).setOnClickListener(this);
        ((TextView)findViewById(R.id.res)).setText("带返回的消息: " + str);
    }
    
    @Override
    public void onClick(View view) {
        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString("string", str);
        setResult(Activity.RESULT_OK, intent);
        finish();
    }
    

(3)消息附加

a. 利用资源文件配置字符串

  • 在 /values/strings.xml 中配置字符串

    <resources>
        <string name="app_name">Test</string>
    </resources>
    
  • 在 Activity 中调用

    ((TextView)findViewById(R.id.req)).setText(getString(R.string.app_name));
    

b. 利用元数据传递配置信息

元数据时一种描述其他数据的数据,它相当于描述固定活动的参数信息。通过在 AndroidManifest.xml 文件的 Activity 节点中添加<meta-data />,通过android:name=""属性指定元数据的名称,通过android:value=""属性指定元数据的值

  • 调用getPackageManger()方法获取当前应用的包管理器
  • 调用包管理器的getActivityInfo()方法获取当前活动的信息对象
  • 活动信息对象的 metaData 是 Bundle 包裹类型,调用包裹对象的getString()方法获取指定名称的参数值

实例:

  • 在 AndroidManifest.xml 中设置元数据

    <activity
        android:name=".Activity1"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
    
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    
        <meta-data
            android:name="ysjName"
            android:value="text" />
    </activity>
    
  • 在 Activity 中调用元数据

    // 获取应用包管理器
    PackageManager pm = getPackageManager();
    try {
        // 从应用包管理器中获取当前 Activity 信息
        ActivityInfo activityInfo = pm.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
        // 获取 Activity 附加的元数据信息
        Bundle metaData = activityInfo.metaData;
        ((TextView)findViewById(R.id.req)).setText(metaData.getString("ysjName"));
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    

c. 利用元数据配置快捷菜单

可用元数据传送资源数据从而给应用页面注册快捷方式。元数据中<meta-data />android:resource=""属性可指定一个 XML 文件,表示元数据需要的信息保存在 XML 数据中

  • 在 res/values/strings.xml 添加各菜单项的在字符串配置
  • 创建 res/xml/shortcuts.xml,在此设置各组菜单项的快捷方式定义
  • 给 Activity 节点注册元数据的快捷菜单配置

实例:

  • shortcuts.xml

    <?xml version ="1.0" encoding="utf-8"?>
    <shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
        <shortcut
            android:shortcutId="sc1"
            android:enabled="true"
            android:icon="@drawable/ic_launcher_background"
            android:shortcutShortLabel="@string/title1"
            android:shortcutLongLabel="@string/longTitle1">
    
            <intent
                android:action="android.intent.action.VIEW"
                android:targetPackage="com.example.test"
                android:targetClass="com.example.test.Activity1" />
    
            <categories
                android:name="android.shortcut.conversation" />
    
        </shortcut>
        <shortcut
            android:shortcutId="sc2"
            android:enabled="true"
            android:icon="@mipmap/ic_launcher"
            android:shortcutShortLabel="@string/title2"
            android:shortcutLongLabel="@string/longTitle2">
    
            <intent
                android:action="android.intent.action.VIEW"
                android:targetPackage="com.example.test"
                android:targetClass="com.example.test.Activity2" />
    
            <categories
                android:name="android.shortcut.conversation" />
    
        </shortcut>
    </shortcuts>
    

    其中,strings.xml 文件内容如下:

    <resources>
        <string name="app_name">Test</string>
        <string name="title1">短标题1</string>
        <string name="title2">短标题2</string>
        <string name="longTitle1">很长很长很长很长的长标题1</string>
        <string name="longTitle2">很长很长的长标题2</string>
    </resources>
    
  • 在 AndroidManifest.xml 中设置元数据

    <meta-data
        android:name="android.app.shortcuts"
        android:resource="@xml/shortcuts"
        android:value="" />
    

-End-