您的当前位置:首页正文

基于Android studio学习安卓之页面间数据传递

2024-11-13 来源:个人技术集锦

基于Android studio学习安卓之页面间数据传递

前言

在Android应用开发过程中,实现页面间的有效数据传递是构建连贯用户界面和确保应用逻辑顺畅的关键环节之一。不同的页面(通常表现为Activity或Fragment)可能需要共享、传递或响应彼此的数据变化,以提供一致的用户体验和维持数据一致性。Android Studio作为官方推荐的开发环境,提供了多种灵活且强大的机制来满足这一需求。下面具体介绍一下Android Studio中常见的页面间数据传递方式有哪些。

一、使用Intent extras与Bundle

contains extras
starts ActivityB with extras
delivers extras
Intent
Bundle
ActivityA
ActivityB

1 使用Intent extras

这是Android中最基础广泛使用的数据传递手段,利用Intent对象携带额外数据(键值对形式)从一个Activity跳转到另一个Activity。

1.1 传递数据

通过Intent对象的putExtra()方法将数据以键值对的形式放入Intent中。

Intent intent = new Intent(CurrentActivity.this, NextActivity.class);
intent.putExtra("username", "JohnDoe");
intent.putExtra("age", 30);
startActivity(intent);
1.2 接收数据

在目标Activity的onCreate()或其他适当生命周期方法中,使用getIntent()获取Intent对象,再调用对应的getXXXExtra()方法(如getStringExtra()、getIntExtra()、getParcelableExtra()等)取出数据。

示例代码如下:

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

    Intent intent = getIntent();
    String username = intent.getStringExtra("username");
    int age = intent.getIntExtra("age", -1); // 第二个参数为默认值,防止数据不存在时崩溃
}

2 使用Bundle

Bundle作为数据容器,可以方便地封装各种类型的数据,便于在Intent中传输。

2.1 传递数据

创建一个Bundle对象,将数据放入其中,然后调用Intent的putExtras(Bundle)方法将其与Intent关联起来。

示例代码如下:

Bundle bundle = new Bundle();
bundle.putString("username", "JohnDoe");
bundle.putInt("age", 30);

Intent intent = new Intent(CurrentActivity.this, NextActivity.class);
intent.putExtras(bundle);
startActivity(intent);
2.2 接收数据

同样通过getIntent()获取Intent,然后调用getExtras()方法获取Bundle,接着使用Bundle的getString()、getInt()等方法提取数据。

示例代码如下:

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

    Bundle bundle = getIntent().getExtras();
    String username = bundle.getString("username");
    int age = bundle.getInt("age");
}

二、通过静态变量或类成员

这种方式适用于简单的数据传递,或者在同一个应用程序内的不同组件之间共享数据。定义一个静态变量或类成员,在一个Activity中设置其值,然后在另一个Activity中读取。

accesses shared data
accesses shared data
SharedDataClass
- static DataItem sharedData
ActivityA
ActivityB

注意:这种方式并不推荐用于跨Activity的数据传递,因为它容易导致内存泄漏、数据同步问题以及违反了Activity的独立性原则。仅在简单场景且明确知晓其潜在风险时使用。

此种方式不建议使用,以下代码仅作示例:

1 定义一个静态变量

示例代码如下:

public class DataHolder {
    public static User currentUser;
}

// 在源Activity中设置数据
DataHolder.currentUser = new User("JohnDoe", 30);

2 在目标Activity中读取数据

示例代码如下:

User user = DataHolder.currentUser;

虽然不推荐作为主要的数据传递方式,但在某些简单场景下,静态变量或类成员可用于跨Activity共享数据。然而,这种做法可能导致内存泄漏、数据同步问题及违背Activity独立性原则,因此在使用时需谨慎权衡。

三、利用SharedPreferences

对于需要持久化存储并跨多个Activity访问的轻量级数据(如用户偏好设置),可以使用SharedPreferences。在源Activity中保存数据,在目标Activity中读取。

used to edit preferences
reads/writes data
reads/writes data
SharedPreferences
+String getString(String key, String defValue)
+void edit()
Editor
+putString(String key, String value)
+apply()
ActivityA
ActivityB

对于需要持久化存储且跨多个Activity访问的轻量级数据(如用户偏好设置),可以利用SharedPreferences进行存储与读取。尽管不是直接的页面间数据传递方式,但它能确保数据在不同Activity访问时的一致性。

1 保存数据

示例代码如下:

SharedPreferences preferences = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("username", "JohnDoe");
editor.putInt("age", 30);
editor.apply();

2 读取数据

示例代码如下:

SharedPreferences preferences = getPreferences(Context.MODE_PRIVATE);
String username = preferences.getString("username", "");
int age = preferences.getInt("age", -1);

四、借助EventBus、LiveData、ViewModel等架构组件

1 借助EventBus

EventBus作为事件驱动的通信框架,简化了组件间的解耦通信。

1.1 什么是EventBus

EventBus是一个发布/订阅事件总线,允许在不同组件(包括Activity、Fragment、Service等)之间解耦地传递数据。发送者发布事件,接收者订阅感兴趣的事件类型,无需直接依赖。

publishes events
posts events
subscribes to events
EventBus
+post(Event event)
Event
// Event data
ActivityA
ActivityB
1.2 发送数据

示例代码如下:

EventBus.getDefault().post(new UserEvent("JohnDoe", 30));
1.3 接收数据

在目标Activity的onCreate()中注册并订阅事件。

示例代码如下:

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

    EventBus.getDefault().register(this);
}

@Subscribe(threadMode = ThreadMode.MAIN)
public void onUserEvent(UserEvent event) {
    String username = event.getUsername();
    int age = event.getAge();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    EventBus.getDefault().unregister(this);
}

2 使用LiveData

LiveData遵循观察者模式,自动处理生命周期相关问题,确保数据与UI状态同步。

2.1 什么是LiveData

LiveData是Android Jetpack组件的一部分,支持数据观察者模式。在一个Activity或ViewModel中持有LiveData对象,其他Activity或Fragment可以通过观察这个LiveData来获取更新的数据,同时它能自动处理生命周期相关问题。

2.2 定义ViewModel

ViewModel专为跨UI组件共享可观察数据而设计,即使面对配置变更也能保持数据持久。

示例代码如下:

public class UserDataViewModel extends ViewModel {
    private MutableLiveData<User> userData = new MutableLiveData<>();

    public LiveData<User> getUserData() {
        return userData;
    }

    public void setUserData(User user) {
        userData.setValue(user);
    }
}
2.3 在源Activity中设置数据

示例代码如下:

UserDataViewModel viewModel = new ViewModelProvider(this).get(UserDataViewModel.class);
viewModel.setUserData(new User("JohnDoe", 30));
2.4 在目标Activity中观察数据

示例代码如下:

UserDataViewModel viewModel = new ViewModelProvider(this).get(UserDataViewModel.class);
viewModel.getUserData().observe(this, user -> {
    String username = user.getUsername();
    int age = user.getAge();
});

五、使用ContentProvider

对于复杂的数据交换或跨应用的数据共享,ContentProvider提供了一种高效、安全的机制。尽管其主要用于数据存储访问,但因其特性,也可适用于大规模结构化数据的页面间传递。

queries ContentProvider
queries ContentProvider
manages underlying database
ContentResolver
+query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
ActivityA
ActivityB
ContentProvider
+onCreate()
+query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
SQLiteOpenHelper
// Database management methods

以下是一个使用ContentProvider的简单示例,假设我们要创建一个ContentProvider用于管理用户联系人信息,并在两个不同的应用之间共享这些数据

1 定义ContactContract

首先,定义一个ContactContract类,用于声明ContentProvider中使用的表结构、列名、URI路径常量等。这有助于保持代码的整洁和一致性。

public final class ContactContract {

    public static final String AUTHORITY = "com.example.provider.contact";

    public static final Uri CONTENT_URI = new Uri.Builder()
            .scheme(ContentResolver.SCHEME_CONTENT)
            .authority(AUTHORITY)
            .appendPath("contacts")
            .build();

    public static final String TABLE_NAME = "contacts";
    public static final String _ID = "_id";
    public static final String COLUMN_NAME = "name";
    public static final String COLUMN_PHONE = "phone";

    public static final String[] PROJECTION_ALL = { _ID, COLUMN_NAME, COLUMN_PHONE };

    public static final int CODE_GET_ALL = 1;
    public static final int CODE_GET_SINGLE = 2;
    public static final int CODE_INSERT = 3;
    public static final int CODE_UPDATE = 4;
    public static final int CODE_DELETE = 5;

    private ContactContract() {} // Prevent instantiation
}

2. 实现ContactProvider

创建一个继承自ContentProvider的类,即ContactProvider,并实现其基本方法:

public class ContactProvider extends ContentProvider {

    private ContactDatabaseHelper dbHelper;

    @Override
    public boolean onCreate() {
        dbHelper = new ContactDatabaseHelper(getContext());
        return true; // Indicate successful initialization
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        switch (sUriMatcher.match(uri)) {
            case CODE_GET_ALL:
                return db.query(ContactContract.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
            case CODE_GET_SINGLE:
                // Handle single item query
            default:
                throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
    }

    // Implement other required methods: insert(), update(), delete(), getType()

    private static final UriMatcher sUriMatcher = buildUriMatcher();

    private static UriMatcher buildUriMatcher() {
        UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
        matcher.addURI(ContactContract.AUTHORITY, "contacts", CODE_GET_ALL);
        matcher.addURI(ContactContract.AUTHORITY, "contacts/#", CODE_GET_SINGLE); // # for ID placeholder
        // Add other match codes for insert, update, and delete
        return matcher;
    }
}

3. 注册ContactProvider在Manifest.xml中

在应用的AndroidManifest.xml文件中,为ContactProvider添加 标签:

<manifest>
    ...
    <application>
        ...
        <provider
            android:name=".ContactProvider"
            android:authorities="com.example.provider.contact"
            android:exported="true" />
        ...
    </application>
</manifest>

4. 使用ContentResolver访问ContactProvider

在另一个应用中,可以通过ContentResolver接口来查询、修改或删除ContactProvider中的数据:

// 获取ContentResolver实例
ContentResolver resolver = getContentResolver();

// 查询所有联系人
Uri contentUri = ContactContract.CONTENT_URI;
Cursor cursor = resolver.query(contentUri, ContactContract.PROJECTION_ALL, null, null, null);

if (cursor != null) {
    while (cursor.moveToNext()) {
        String id = cursor.getString(cursor.getColumnIndex(ContactContract._ID));
        String name = cursor.getString(cursor.getColumnIndex(ContactContract.COLUMN_NAME));
        String phone = cursor.getString(cursor.getColumnIndex(ContactContract.COLUMN_PHONE));

        Log.d(TAG, "Contact: ID=" + id + ", Name=" + name + ", Phone=" + phone);
    }
    cursor.close();
}

// 插入新联系人
ContentValues values = new ContentValues();
values.put(ContactContract.COLUMN_NAME, "John Doe");
values.put(ContactContract.COLUMN_PHONE, "+1234567890");
Uri insertedUri = resolver.insert(ContactContract.CONTENT_URI, values);

// 更新联系人信息
Uri updateUri = ContentUris.withAppendedId(ContactContract.CONTENT_URI, contactId);
values.clear();
values.put(ContactContract.COLUMN_NAME, "Jane Doe");
int rowsUpdated = resolver.update(updateUri, values, null, null);

// 删除联系人
int rowsDeleted = resolver.delete(ContentUris.withAppendedId(ContactContract.CONTENT_URI, contactId), null, null);

注意:实际应用中,还需要考虑权限控制、事务处理、响应Uri的不同操作类型(如使用UriMatcher)等细节。

总结

Top