在上一篇中,我们已经探索了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_OK
或 RESULT_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();
}
});
}
}
在这个例子中:
MainActivity
有一个按钮,用户点击这个按钮时,会通过startActivityForResult
启动SecondActivity
。1
是我们定义的请求码(request code),用于区分onActivityResult
中的不同请求。SecondActivity
提供了一个编辑框供用户输入分数,并有一个提交按钮。当用户点击提交按钮时,获取输入的分数,将其放入一个Intent
中,并通过setResult
方法将结果发送回启动它的Activity
,然后结束自己。- 当
SecondActivity
结束时,MainActivity
的onActivityResult
方法会被调用。通过检查请求码(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的魅力远不止于此,它还蕴含着更多精深的理念和机遇有待我们去挖掘。