越探越深-揭秘Activity的高级玩法


在上一篇中,我们已经探索了Activity的基础知识,包括它的本质构成、生命周期管理、启动模式等核心内容。但Activity的真正力量远不止于此,它还拥有许多高级特性,让我们能够打造出更加灵活、强大的Android应用体验。今天,就让我带大家深入挖掘Activity背后的那些独门绝学!


一、恢复重建:数据状态的长生不老药

当Activity发生重建时,例如设备旋转导致的配置变更,之前持有的临时数据将会遗失。Android系统为了解决这个问题,提供了两种主要的状态保存方案。


方案一:借助Bundle暂存关键数据

Activity中的onSaveInstanceState回调,就是为此而生。当系统准备销毁一个Activity实例时,会先调用该方法,并传入一个Bundle供我们暂存数据:

public class MyActivity extends AppCompatActivity {

    private static final String KEY_DATA = "key_data";
    private String data;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState != null) {
            data = savedInstanceState.getString(KEY_DATA);
        }
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(KEY_DATA, data);
    }
}

当Activity重建时,系统会自动将之前的Bundle数据传回给onCreate方法,于是我们就可以从中恢复原有的状态信息了。通过这种方式,临时数据得以跨越重建的鸿沟得到保留。


方案二:ViewModel解决终极数据托管

使用Bundle的方式虽然直接有效,但有时会显得代码过于冗长。更好的选择是借助AndroidX库中内置的ViewModel组件,将状态数据的托管工作完全交给这个专门的管理者:

public class MyViewModel extends ViewModel {
    
    private Data currentData;
    
    public Data getCurrentData() {
        return currentData;
    }
    
    public void setData(Data data) {
        currentData = data;
    }
}
public class MyActivity extends AppCompatActivity {

    private MyViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
        if (viewModel.getCurrentData() != null) {
            renderData(viewModel.getCurrentData());
        }
    }

    private void renderData(Data data) {
        viewModel.setData(data);
        // TODO: 更新UI
    }
}

ViewModel的生命周期是与当前Activity的归属关系绑定的,而不依赖于Activity的具体实例。这就意味着,无论Activity经历多少次重建,ViewModel都能够保持数据的连续性和一致性。

除了解决数据丢失的难题,ViewModel还拥有诸多其他优点,例如可感知页面导航状态、支持工作程协程等。所以在实际开发中,ViewModel无疑是状态恢复最佳实践的不二之选。


二、无所不通:Intent的多路精准控制

当我们想要启动一个新的Activity时,不可或缺的就是Intent这个Android世界中的"护航使者"了。它不仅功能强大,使用方式也相当灵活多变。

1、显式Intent

如果明确知道想要启动的目标Activity,我们就可以通过显式Intent直接指定它:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String data = "Hello";
        Intent intent = new Intent(this, TargetActivity.class);
        intent.putExtra("KEY_DATA", data); 
        startActivity(intent);
    }
}
public class TargetActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_target);

        String data = getIntent().getStringExtra("KEY_DATA");
        // TODO: 处理data
    }
}

2、隐式Intent

隐式Intent通过在AndroidManifest.xml中声明Intent Filter,使自己对外暴露服务能力:

<!-- AndroidManifest.xml -->
<activity android:name=".ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>
public class SenderActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sender);

        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.putExtra(Intent.EXTRA_TEXT, "Hello");
        intent.setType("text/plain");

        startActivity(Intent.createChooser(intent, "分享文本"));
    }
}

这样,所有符合条件的Activity就都可以被系统视为"分享文本"行为的响应目标。用户还可以在多个备选项中自由选择。通过这种方式,隐式Intent赋予了Android生态更大的灵活性与可扩展性。


三、startActivityForResult:启动并得到返回

除了常规的启动方式,Intent还支持"启动-回调"模式。

startActivityForResult 是 Android 中的一个方法,它允许一个 Activity 启动另一个 Activity 并等待结果。

启动的 Activity 可以完成某项工作(如用户填写表单、选择图片等),然后通过 RESULT_OKRESULT_CANCELED 结果代码将结果传递回原始的 Activity

以下是 startActivityForResult 的一个典型使用案例,假设我们要从 MainActivity 中启动一个 SecondActivity 来获取用户输入的分数,并将结果返回:


1、第一步:在 MainActivity 中启动 SecondActivity 并接收结果


import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView scoreTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        scoreTextView = findViewById(R.id.score_text_view);
        Button getScoreButton = findViewById(R.id.get_score_button);

        getScoreButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 使用 startActivityForResult 启动 SecondActivity
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivityForResult(intent, 1); // 1 是请求码(request code)
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // 检查请求码是否匹配
        if (requestCode == 1) {
            // 检查结果码
            if (resultCode == RESULT_OK) {
                // 获取返回的数据
                int score = data.getIntExtra("score", 0);
                scoreTextView.setText("Your score is: " + score);
            } else {
                // 结果码不是 RESULT_OK,表示用户取消了操作
                scoreTextView.setText("No score was entered.");
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

第二步:在 SecondActivity 中获取用户输入并返回结果

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.content.Intent;

public class SecondActivity extends AppCompatActivity {

    private EditText scoreEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        scoreEditText = findViewById(R.id.score_edit_text);
        Button submitButton = findViewById(R.id.submit_button);

        submitButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 获取用户输入的分数
                int score = Integer.parseInt(scoreEditText.getText().toString());
                // 创建一个新的 Intent 来传递结果
                Intent resultIntent = new Intent();
                resultIntent.putExtra("score", score);
                // 结束 SecondActivity 并传递结果
                setResult(RESULT_OK, resultIntent);
                finish();
            }
        });
    }
}

在这个例子中:

  1. MainActivity 有一个按钮,用户点击这个按钮时,会通过 startActivityForResult 启动 SecondActivity1 是我们定义的请求码(request code),用于区分 onActivityResult 中的不同请求。
  2. SecondActivity 提供了一个编辑框供用户输入分数,并有一个提交按钮。当用户点击提交按钮时,获取输入的分数,将其放入一个 Intent 中,并通过 setResult 方法将结果发送回启动它的 Activity,然后结束自己。
  3. SecondActivity 结束时,MainActivityonActivityResult 方法会被调用。通过检查请求码(requestCode)和结果码(resultCode),MainActivity 可以判断结果是否有效,并更新 UI 来显示用户输入的分数。

使用 startActivityForResult 可以很好地管理 Activity 之间的交互和结果传递,使得逻辑更加清晰和易于维护。


四、无界无垠:URI路由与Scheme协议接入


Intent所承载的不仅是Android内部的组件互联,它更将随着技术的发展而创造出新的无限可能性。通过支持URI路径,Intent实现了与手机浏览器的无缝连接,为我们的应用打开了一扇全新的大门。


1、精心构建:注册URI路径

首先,我们需要在AndroidManifest.xml中为想要对外暴露的Activity注册一个URI路径:

<activity
    android:name=".uri.UriActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="www.myapp.com"
            android:pathPrefix="/uri"
            android:scheme="https" />
    </intent-filter>
</activity>

在上面的示例中,我们声明了一个scheme为"https"、host为"www.myapp.com"、路径前缀为"/uri"的URI路由配置。这意味着,只要外部应用发出形如https://www.myapp.com/uri/*的Intent请求,就能够打开该Activity。


2、模式识别:解析传入的URI

接下来,Activity就需要提供解析URI的能力,从而获取隐含在其中的数据:

public class UriActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_uri);

        Intent intent = getIntent();
        Uri uri = intent.getData();
        if (uri != null) {
            String path = uri.getPath(); // 获取路径
            String param = uri.getQueryParameter("key"); // 获取参数
            // TODO: 处理获取到的数据
        }
    }
}

通过解析获取的Uri对象,我们就能轻松获取请求路径、参数等关键信息。这无疑为构建复杂的路由体系奠定了基础。


3、Scheme无边界:与外部通路互联接轨


除了与浏览器互联,我们的应用还可以借助Scheme与其他应用打通数据链路,构建出一张立体而广阔的通信网络。只要约定一个包名范围内唯一的Scheme协议,再搭配合适的Intent Filter,就能为外部应用提供丰富的服务接口:

<!-- 其他应用的 AndroidManifest.xml -->
<activity
    android:name=".SchemeActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="open"
            android:scheme="myapp" />
    </intent-filter>
</activity>
// 我们应用发出的请求
String uri = "myapp://open?data=hello";
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
startActivity(intent);

上述代码将唤起目标应用的SchemeActivity,并将"data=hello"参数传递过去。通过这种方式,我们就能与各种应用进行互联互通、资源共享,打造出一个生动活泼、融合无限的智能化体验。


序幕未终:更多探索仍在继续

本文探讨了Activity作为Android四大组件核心的一些高级特性,包括保存和恢复状态数据、Intent的精细控制、回调机制的应用,以及面向未来的Uri路由与Scheme通信等内容。但Activity的魅力远不止于此,它还蕴含着更多精深的理念和机遇有待我们去挖掘。


  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w风雨无阻w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值