大厂Android面试秘籍:Activity 组件间通信

深入剖析 Android Activity 组件间通信模块

本人掘金号,欢迎点击关注:掘金号地址

本人公众号,欢迎点击关注:公众号地址

一、引言

在 Android 开发中,Activity 作为四大组件之一,是用户与应用交互的重要界面载体。在实际的应用开发过程中,经常会遇到多个 Activity 之间需要进行数据传递和通信的场景。例如,从一个列表页的 Activity 跳转到详情页的 Activity 时,需要将列表项的相关数据传递过去;又或者在一个 Activity 中进行了某些操作后,需要通知另一个 Activity 更新界面等。因此,深入理解和掌握 Android Activity 组件间的通信机制对于开发高质量的 Android 应用至关重要。

本文将从源码级别深入分析 Android Activity 组件间的各种通信方式,包括使用 Intent 传递数据、使用 Bundle 传递复杂数据、使用静态变量和单例模式共享数据、使用广播机制进行通信、使用 EventBus 进行事件传递以及使用回调接口进行通信等。通过对这些通信方式的源码分析,我们可以更好地理解它们的工作原理和适用场景,从而在实际开发中灵活运用。

二、使用 Intent 传递数据

2.1 Intent 简介

Intent 是 Android 中用于在不同组件(如 Activity、Service、BroadcastReceiver 等)之间进行通信的一种机制。它可以携带数据,并指定要启动的组件。Intent 主要有显式 Intent 和隐式 Intent 两种类型,这里我们主要关注在 Activity 之间传递数据时使用的显式 Intent。

2.2 使用 Intent 传递基本数据类型

下面是一个简单的示例,展示了如何使用 Intent 在两个 Activity 之间传递基本数据类型(如字符串、整数等)。

发送数据的 Activity(MainActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

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

        // 找到按钮控件
        Button sendButton = findViewById(R.id.send_button);
        // 为按钮设置点击事件监听器
        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建一个新的 Intent 对象,指定要启动的目标 Activity 为 SecondActivity
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                // 向 Intent 中添加一个字符串数据,键为 "message",值为 "Hello from MainActivity"
                intent.putExtra("message", "Hello from MainActivity");
                // 向 Intent 中添加一个整数数据,键为 "number",值为 123
                intent.putExtra("number", 123);
                // 启动目标 Activity
                startActivity(intent);
            }
        });
    }
}
接收数据的 Activity(SecondActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class SecondActivity extends AppCompatActivity {

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

        // 找到用于显示接收到的数据的 TextView 控件
        TextView receivedDataTextView = findViewById(R.id.received_data_textview);

        // 获取启动该 Activity 的 Intent 对象
        Intent intent = getIntent();
        // 从 Intent 中获取键为 "message" 的字符串数据,如果没有找到则返回默认值 "No message"
        String message = intent.getStringExtra("message");
        // 从 Intent 中获取键为 "number" 的整数数据,如果没有找到则返回默认值 0
        int number = intent.getIntExtra("number", 0);

        // 拼接接收到的数据字符串
        String receivedData = "Message: " + message + ", Number: " + number;
        // 将接收到的数据显示在 TextView 中
        receivedDataTextView.setText(receivedData);
    }
}

2.3 Intent 源码分析

2.3.1 Intent 的构造函数

Intent 类有多个构造函数,其中最常用的是用于创建显式 Intent 的构造函数:

java

java 复制代码
/**
 * 创建一个显式 Intent,指定要启动的组件。
 * @param packageContext 调用者的上下文,通常是当前 Activity 的实例
 * @param cls 要启动的组件的类对象
 */
public Intent(Context packageContext, Class<?> cls) {
    mComponent = new ComponentName(packageContext, cls);
}

在上述代码中,构造函数接收一个 Context 对象和一个 Class 对象,通过这两个参数创建了一个 ComponentName 对象,并将其赋值给 Intent 的 mComponent 成员变量。ComponentName 用于唯一标识一个组件,在启动 Activity 时,系统会根据这个 ComponentName 找到对应的 Activity 并启动它。

2.3.2 putExtra 方法

putExtra 方法用于向 Intent 中添加额外的数据。以 putExtra (String name, String value) 方法为例:

java

java 复制代码
/**
 * 向 Intent 中添加一个字符串类型的额外数据。
 * @param name 数据的键
 * @param value 数据的值
 * @return 返回当前 Intent 对象,以便进行链式调用
 */
public Intent putExtra(String name, String value) {
    if (mExtras == null) {
        mExtras = new Bundle();
    }
    mExtras.putString(name, value);
    return this;
}

在这个方法中,首先检查 mExtras 成员变量是否为 null,如果为 null 则创建一个新的 Bundle 对象。然后调用 Bundle 的 putString 方法将字符串数据添加到 Bundle 中。最后返回当前 Intent 对象,这样可以进行链式调用,例如:

java

java 复制代码
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("key1", "value1").putExtra("key2", "value2");
2.3.3 getExtra 系列方法

getExtra 系列方法用于从 Intent 中获取额外的数据。以 getStringExtra (String name) 方法为例:

java

java 复制代码
/**
 * 从 Intent 中获取指定键的字符串类型的额外数据。
 * @param name 数据的键
 * @return 如果找到对应的数据则返回该字符串,否则返回 null
 */
public String getStringExtra(String name) {
    return mExtras == null ? null : mExtras.getString(name);
}

这个方法首先检查 mExtras 是否为 null,如果不为 null 则调用 Bundle 的 getString 方法获取指定键的字符串数据,否则返回 null。

2.4 使用 Intent 传递数据的优缺点

2.4.1 优点
  • 简单易用:只需要创建 Intent 对象,调用 putExtra 方法添加数据,然后启动目标 Activity 即可,代码实现简单。
  • 系统支持:Intent 是 Android 系统提供的标准机制,无需额外引入第三方库。
2.4.2 缺点
  • 数据类型受限:只能传递基本数据类型(如 int、String、boolean 等)和实现了 Serializable 或 Parcelable 接口的对象。
  • 数据量有限:由于 Intent 传递的数据会存储在 Bundle 中,而 Bundle 有一定的大小限制,因此不适合传递大量的数据。

三、使用 Bundle 传递复杂数据

3.1 Bundle 简介

Bundle 是 Android 中用于存储键值对数据的容器,类似于 Java 中的 Map。它可以存储各种基本数据类型和实现了 Serializable 或 Parcelable 接口的对象。在 Activity 之间传递复杂数据时,通常会使用 Bundle 来封装数据,然后将 Bundle 放入 Intent 中传递。

3.2 使用 Bundle 传递实现了 Serializable 接口的对象

定义一个实现了 Serializable 接口的类(User.java)

java

java 复制代码
import java.io.Serializable;

// 实现 Serializable 接口,以便该类的对象可以在 Activity 之间传递
public class User implements Serializable {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
发送数据的 Activity(MainActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

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

        // 找到按钮控件
        Button sendButton = findViewById(R.id.send_button);
        // 为按钮设置点击事件监听器
        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建一个 User 对象
                User user = new User("John", 25);
                // 创建一个 Bundle 对象
                Bundle bundle = new Bundle();
                // 将 User 对象放入 Bundle 中,键为 "user"
                bundle.putSerializable("user", user);
                // 创建一个新的 Intent 对象,指定要启动的目标 Activity 为 SecondActivity
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                // 将 Bundle 放入 Intent 中
                intent.putExtras(bundle);
                // 启动目标 Activity
                startActivity(intent);
            }
        });
    }
}
接收数据的 Activity(SecondActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class SecondActivity extends AppCompatActivity {

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

        // 找到用于显示接收到的数据的 TextView 控件
        TextView receivedDataTextView = findViewById(R.id.received_data_textview);

        // 获取启动该 Activity 的 Intent 对象
        Intent intent = getIntent();
        // 从 Intent 中获取 Bundle 对象
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            // 从 Bundle 中获取键为 "user" 的 User 对象
            User user = (User) bundle.getSerializable("user");
            if (user != null) {
                // 拼接接收到的 User 对象的信息字符串
                String receivedData = "Name: " + user.getName() + ", Age: " + user.getAge();
                // 将接收到的信息显示在 TextView 中
                receivedDataTextView.setText(receivedData);
            }
        }
    }
}

3.3 使用 Bundle 传递实现了 Parcelable 接口的对象

定义一个实现了 Parcelable 接口的类(Book.java)

java

java 复制代码
import android.os.Parcel;
import android.os.Parcelable;

// 实现 Parcelable 接口,以便该类的对象可以在 Activity 之间高效传递
public class Book implements Parcelable {
    private String title;
    private String author;

    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }

    protected Book(Parcel in) {
        // 从 Parcel 中读取数据
        title = in.readString();
        author = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            // 根据 Parcel 中的数据创建 Book 对象
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            // 创建指定大小的 Book 数组
            return new Book[size];
        }
    };

    public String getTitle() {
        return title;
    }

    public String getAuthor() {
        return author;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // 将数据写入 Parcel
        dest.writeString(title);
        dest.writeString(author);
    }
}
发送数据的 Activity(MainActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

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

        // 找到按钮控件
        Button sendButton = findViewById(R.id.send_button);
        // 为按钮设置点击事件监听器
        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建一个 Book 对象
                Book book = new Book("Android Programming", "Jane");
                // 创建一个 Bundle 对象
                Bundle bundle = new Bundle();
                // 将 Book 对象放入 Bundle 中,键为 "book"
                bundle.putParcelable("book", book);
                // 创建一个新的 Intent 对象,指定要启动的目标 Activity 为 SecondActivity
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                // 将 Bundle 放入 Intent 中
                intent.putExtras(bundle);
                // 启动目标 Activity
                startActivity(intent);
            }
        });
    }
}
接收数据的 Activity(SecondActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class SecondActivity extends AppCompatActivity {

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

        // 找到用于显示接收到的数据的 TextView 控件
        TextView receivedDataTextView = findViewById(R.id.received_data_textview);

        // 获取启动该 Activity 的 Intent 对象
        Intent intent = getIntent();
        // 从 Intent 中获取 Bundle 对象
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            // 从 Bundle 中获取键为 "book" 的 Book 对象
            Book book = bundle.getParcelable("book");
            if (book != null) {
                // 拼接接收到的 Book 对象的信息字符串
                String receivedData = "Title: " + book.getTitle() + ", Author: " + book.getAuthor();
                // 将接收到的信息显示在 TextView 中
                receivedDataTextView.setText(receivedData);
            }
        }
    }
}

3.4 Bundle 源码分析

3.4.1 Bundle 的构造函数

java

java 复制代码
/**
 * 创建一个空的 Bundle 对象。
 */
public Bundle() {
    mMap = new ArrayMap<String, Object>();
}

在这个构造函数中,创建了一个 ArrayMap 对象并赋值给 mMap 成员变量,ArrayMap 是 Android 中用于存储键值对的一种数据结构,类似于 Java 中的 HashMap,但在内存使用上更优化。

3.4.2 putSerializable 方法

java

java 复制代码
/**
 * 向 Bundle 中添加一个实现了 Serializable 接口的对象。
 * @param key 数据的键
 * @param value 实现了 Serializable 接口的对象
 */
public void putSerializable(String key, Serializable value) {
    unparcel();
    mMap.put(key, value);
}

这个方法首先调用 unparcel 方法,确保 Bundle 处于可操作状态。然后将实现了 Serializable 接口的对象放入 ArrayMap 中。

3.4.3 putParcelable 方法

java

java 复制代码
/**
 * 向 Bundle 中添加一个实现了 Parcelable 接口的对象。
 * @param key 数据的键
 * @param value 实现了 Parcelable 接口的对象
 */
public void putParcelable(String key, Parcelable value) {
    unparcel();
    mMap.put(key, value);
}

与 putSerializable 方法类似,putParcelable 方法也是先调用 unparcel 方法,然后将实现了 Parcelable 接口的对象放入 ArrayMap 中。

3.4.4 getSerializable 方法

java

java 复制代码
/**
 * 从 Bundle 中获取指定键的实现了 Serializable 接口的对象。
 * @param key 数据的键
 * @return 如果找到对应的数据则返回该对象,否则返回 null
 */
public Serializable getSerializable(String key) {
    unparcel();
    return (Serializable) mMap.get(key);
}

这个方法先调用 unparcel 方法,然后从 ArrayMap 中获取指定键的对象,并将其强制转换为 Serializable 类型返回。

3.4.5 getParcelable 方法

java

java 复制代码
/**
 * 从 Bundle 中获取指定键的实现了 Parcelable 接口的对象。
 * @param key 数据的键
 * @return 如果找到对应的数据则返回该对象,否则返回 null
 */
@SuppressWarnings("unchecked")
public <T extends Parcelable> T getParcelable(String key) {
    unparcel();
    return (T) mMap.get(key);
}

与 getSerializable 方法类似,getParcelable 方法先调用 unparcel 方法,然后从 ArrayMap 中获取指定键的对象,并将其强制转换为指定的 Parcelable 类型返回。

3.5 Serializable 和 Parcelable 的比较

3.5.1 Serializable
  • 优点:实现简单,只需要让类实现 Serializable 接口即可,无需编写额外的代码。
  • 缺点:序列化和反序列化过程使用 Java 的反射机制,性能较低,并且会产生大量的临时对象,占用较多的内存。
3.5.2 Parcelable
  • 优点:性能较高,因为它是 Android 专门为在组件间传递数据而设计的,序列化和反序列化过程直接操作内存,避免了反射机制的开销。
  • 缺点:实现相对复杂,需要实现 Parcelable 接口的几个方法,并且需要手动处理数据的读写。

四、使用静态变量和单例模式共享数据

4.1 使用静态变量共享数据

定义一个包含静态变量的类(DataHolder.java)

java

java 复制代码
// 定义一个包含静态变量的类,用于在 Activity 之间共享数据
public class DataHolder {
    // 定义一个静态字符串变量,用于存储共享的数据
    public static String sharedData;
}
发送数据的 Activity(MainActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

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

        // 找到输入框控件
        EditText inputEditText = findViewById(R.id.input_edittext);
        // 找到按钮控件
        Button sendButton = findViewById(R.id.send_button);
        // 为按钮设置点击事件监听器
        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 获取输入框中的文本
                String inputText = inputEditText.getText().toString();
                // 将输入的文本赋值给 DataHolder 类的静态变量 sharedData
                DataHolder.sharedData = inputText;
                // 创建一个新的 Intent 对象,指定要启动的目标 Activity 为 SecondActivity
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                // 启动目标 Activity
                startActivity(intent);
            }
        });
    }
}
接收数据的 Activity(SecondActivity.java)

java

java 复制代码
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class SecondActivity extends AppCompatActivity {

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

        // 找到用于显示接收到的数据的 TextView 控件
        TextView receivedDataTextView = findViewById(R.id.received_data_textview);
        // 从 DataHolder 类的静态变量 sharedData 中获取共享的数据
        String sharedData = DataHolder.sharedData;
        if (sharedData != null) {
            // 将接收到的数据显示在 TextView 中
            receivedDataTextView.setText("Shared Data: " + sharedData);
        }
    }
}

4.2 使用单例模式共享数据

定义一个单例类(SingletonDataHolder.java)

java

java 复制代码
// 定义一个单例类,用于在 Activity 之间共享数据
public class SingletonDataHolder {
    // 定义一个静态的单例实例
    private static SingletonDataHolder instance;
    // 定义一个字符串变量,用于存储共享的数据
    private String data;

    // 私有构造函数,确保只能通过 getInstance 方法获取实例
    private SingletonDataHolder() {}

    // 静态方法,用于获取单例实例
    public static synchronized SingletonDataHolder getInstance() {
        if (instance == null) {
            instance = new SingletonDataHolder();
        }
        return instance;
    }

    // 获取共享数据的方法
    public String getData() {
        return data;
    }

    // 设置共享数据的方法
    public void setData(String data) {
        this.data = data;
    }
}
发送数据的 Activity(MainActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

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

        // 找到输入框控件
        EditText inputEditText = findViewById(R.id.input_edittext);
        // 找到按钮控件
        Button sendButton = findViewById(R.id.send_button);
        // 为按钮设置点击事件监听器
        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 获取输入框中的文本
                String inputText = inputEditText.getText().toString();
                // 获取单例实例
                SingletonDataHolder singletonDataHolder = SingletonDataHolder.getInstance();
                // 将输入的文本设置到单例实例中
                singletonDataHolder.setData(inputText);
                // 创建一个新的 Intent 对象,指定要启动的目标 Activity 为 SecondActivity
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                // 启动目标 Activity
                startActivity(intent);
            }
        });
    }
}
接收数据的 Activity(SecondActivity.java)

java

java 复制代码
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class SecondActivity extends AppCompatActivity {

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

        // 找到用于显示接收到的数据的 TextView 控件
        TextView receivedDataTextView = findViewById(R.id.received_data_textview);
        // 获取单例实例
        SingletonDataHolder singletonDataHolder = SingletonDataHolder.getInstance();
        // 从单例实例中获取共享的数据
        String sharedData = singletonDataHolder.getData();
        if (sharedData != null) {
            // 将接收到的数据显示在 TextView 中
            receivedDataTextView.setText("Shared Data: " + sharedData);
        }
    }
}

4.3 静态变量和单例模式的优缺点

4.3.1 优点
  • 简单方便:使用静态变量和单例模式可以很方便地在不同的 Activity 之间共享数据,无需进行复杂的序列化和反序列化操作。
  • 数据共享范围广:静态变量和单例实例在整个应用的生命周期内都存在,可以在任何 Activity 中访问。
4.3.2 缺点
  • 内存泄漏风险:由于静态变量和单例实例的生命周期与应用的生命周期相同,如果在其中持有 Activity 的引用,可能会导致 Activity 无法被垃圾回收,从而造成内存泄漏。
  • 数据一致性问题:多个 Activity 可能同时修改静态变量或单例实例中的数据,容易导致数据不一致的问题。

五、使用广播机制进行通信

5.1 广播机制简介

广播机制是 Android 中一种全局的消息传递机制,它允许应用内的组件(如 Activity、Service、BroadcastReceiver 等)之间进行通信。广播分为系统广播和自定义广播,这里我们主要关注自定义广播在 Activity 之间的通信应用。

5.2 发送广播

发送广播的 Activity(MainActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

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

        // 找到按钮控件
        Button sendBroadcastButton = findViewById(R.id.send_broadcast_button);
        // 为按钮设置点击事件监听器
        sendBroadcastButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建一个新的 Intent 对象,指定广播的动作
                Intent broadcastIntent = new Intent("com.example.MY_CUSTOM_BROADCAST");
                // 向 Intent 中添加一个字符串数据,键为 "message",值为 "Hello from MainActivity"
                broadcastIntent.putExtra("message", "Hello from MainActivity");
                // 发送广播
                sendBroadcast(broadcastIntent);
            }
        });
    }
}

5.3 接收广播

定义一个 BroadcastReceiver 类(MyBroadcastReceiver.java)

java

java 复制代码
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

// 定义一个 BroadcastReceiver 类,用于接收自定义广播
public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // 检查接收到的广播的动作是否为我们定义的自定义广播动作
        if ("com.example.MY_CUSTOM_BROADCAST".equals(intent.getAction())) {
            // 从 Intent 中获取键为 "message" 的字符串数据
            String message = intent.getStringExtra("message");
            if (message != null) {
                // 显示一个 Toast 消息,提示接收到的广播内容
                Toast.makeText(context, "Received broadcast: " + message, Toast.LENGTH_SHORT).show();
            }
        }
    }
}
注册广播接收器的 Activity(SecondActivity.java)

java

java 复制代码
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class SecondActivity extends AppCompatActivity {
    // 定义一个 BroadcastReceiver 对象
    private BroadcastReceiver myBroadcastReceiver;

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

        // 创建一个 MyBroadcastReceiver 实例
        myBroadcastReceiver = new MyBroadcastReceiver();
        // 创建一个 IntentFilter 对象,指定要接收的广播动作
        IntentFilter intentFilter = new IntentFilter("com.example.MY_CUSTOM_BROADCAST");
        // 注册广播接收器
        registerReceiver(myBroadcastReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销广播接收器,避免内存泄漏
        unregisterReceiver(myBroadcastReceiver);
    }
}

5.4 广播机制源码分析

5.4.1 sendBroadcast 方法

java

java 复制代码
/**
 * 发送一个无序广播。
 * @param intent 包含广播信息的 Intent 对象
 */
public void sendBroadcast(Intent intent) {
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        intent.prepareToLeaveProcess(this);
        ActivityManager.getService().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                getUserId());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

在这个方法中,首先调用 intent.resolveTypeIfNeeded 方法解析 Intent 的数据类型。然后调用 intent.prepareToLeaveProcess 方法准备将 Intent 发送到系统进程。最后通过 ActivityManager.getService ().broadcastIntent 方法将广播发送到系统的 ActivityManagerService 中进行处理。

5.4.2 registerReceiver 方法

java

java 复制代码
/**
 * 注册一个广播接收器。
 * @param receiver 要注册的 BroadcastReceiver 对象
 * @param filter 用于过滤广播的 IntentFilter 对象
 * @return 返回一个 PendingResult 对象,用于接收广播的结果
 */
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    return registerReceiver(receiver, filter, null, null);
}

public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
        String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, getUserId(),
            filter, broadcastPermission, scheduler, getOuterContext(), 0);
}

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
        IntentFilter filter, String broadcastPermission, Handler scheduler,
        Context context, int flags) {
    IIntentReceiver rd = null;
    if (receiver != null) {
        if (mPackageInfo != null && context != null) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
        } else {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            rd = new LoadedApk.ReceiverDispatcher(
                    receiver, context, scheduler, null, true).getIIntentReceiver();
        }
    }
    try {
        final Intent intent = ActivityManager.getService().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                broadcastPermission, userId, flags);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess();
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

在 registerReceiver 方法中,最终会调用 registerReceiverInternal 方法。在这个方法中,首先创建一个 IIntentReceiver 对象,它是一个跨进程通信的接口,用于接收广播。然后通过 ActivityManager.getService ().registerReceiver 方法将广播接收器注册到系统的 ActivityManagerService 中。

5.4.3 onReceive 方法

java

java 复制代码
@Override
public void onReceive(Context context, Intent intent) {
    // 检查接收到的广播的动作是否为我们定义的自定义广播动作
    if ("com.example.MY_CUSTOM_BROADCAST".equals(intent.getAction())) {
        // 从 Intent 中获取键为 "message" 的字符串数据
        String message = intent.getStringExtra("message");
        if (message != null) {
            // 显示一个 Toast 消息,提示接收到的广播内容
            Toast.makeText(context, "Received broadcast: " + message, Toast.LENGTH_SHORT).show();
        }
    }
}

当广播接收器接收到广播时,系统会调用 onReceive 方法。在这个方法中,我们可以根据 Intent 的动作和携带的数据进行相应的处理。

5.5 广播机制的优缺点

5.5.1 优点
  • 解耦性强:广播机制允许组件之间进行松耦合的通信,发送者和接收者不需要直接引用对方,只需要通过广播的动作和数据进行交互。
  • 全局通信:广播可以在整个应用内甚至不同的应用之间进行通信,具有很强的灵活性。
5.5.2 缺点
  • 性能开销大:广播的发送和接收涉及到系统的 ActivityManagerService 进行处理,会带来一定的性能开销。
  • 安全性问题:由于广播是全局的,如果广播的动作和数据没有进行适当的保护,可能会被其他应用截获和利用,存在一定的安全风险。

六、使用 EventBus 进行事件传递

6.1 EventBus 简介

EventBus 是一个开源的 Android 事件总线库,它可以简化 Activity、Fragment、Service 等组件之间的通信。EventBus 采用发布 - 订阅模式,组件可以发布事件,其他组件可以订阅这些事件并进行相应的处理。

6.2 添加 EventBus 依赖

在项目的 build.gradle 文件中添加 EventBus 的依赖:

groovy

java 复制代码
implementation 'org.greenrobot:eventbus:3.2.0'

6.3 定义事件类

java

java 复制代码
// 定义一个事件类,用于在 Activity 之间传递事件
public class MessageEvent {
    private String message;

    public MessageEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

6.4 发送事件

发送事件的 Activity(MainActivity.java)

java

java 复制代码
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import org.greenrobot.eventbus.EventBus;

public class MainActivity extends AppCompatActivity {

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

        // 找到按钮控件
        Button sendEventButton = findViewById(R.id.send_event_button);
        // 为按钮设置点击事件监听器
        sendEventButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建一个 MessageEvent 对象,携带要传递的消息
                MessageEvent messageEvent = new MessageEvent("Hello from MainActivity");
                // 使用 EventBus 发布事件
                EventBus.getDefault().post(messageEvent);
            }
        });
    }
}

6.5 接收事件

接收事件的 Activity(SecondActivity.java)

java

java 复制代码
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class SecondActivity extends AppCompatActivity {
    private TextView receivedEventTextView;

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

        // 找到用于显示接收到的事件信息的 TextView 控件
        receivedEventTextView = findViewById(R.id.received_event_textview);
        // 注册 EventBus 订阅者
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销 EventBus 订阅者,避免内存泄漏
        EventBus.getDefault().unregister(this);
    }

    // 定义一个订阅方法,用于接收 MessageEvent 事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {

java

java 复制代码
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class SecondActivity extends AppCompatActivity {
    private TextView receivedEventTextView;

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

        // 找到用于显示接收到的事件信息的 TextView 控件
        receivedEventTextView = findViewById(R.id.received_event_textview);
        // 注册 EventBus 订阅者
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销 EventBus 订阅者,避免内存泄漏
        EventBus.getDefault().unregister(this);
    }

    // 定义一个订阅方法,用于接收 MessageEvent 事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
        // 获取事件中携带的消息
        String message = event.getMessage();
        // 将消息显示在 TextView 中
        receivedEventTextView.setText("Received event: " + message);
    }
}

在上述代码中,SecondActivity 作为事件的接收方。在 onCreate 方法里,调用 EventBus.getDefault().register(this) 来注册当前 ActivityEventBus 的订阅者,这样 EventBus 就会记录该订阅者可以处理相应的事件。在 onDestroy 方法中,调用 EventBus.getDefault().unregister(this) 进行注销,防止因 Activity 销毁后仍保留订阅关系而造成内存泄漏。

@Subscribe(threadMode = ThreadMode.MAIN) 注解标记的 onMessageEvent 方法是事件处理方法,threadMode = ThreadMode.MAIN 表示该方法会在主线程中执行,这适合更新 UI 操作。当 MainActivity 发布 MessageEvent 事件时,EventBus 会找到所有订阅了 MessageEvent 事件的方法并执行,这里就会执行 onMessageEvent 方法,将事件中携带的消息显示在 TextView 上。

6.6 EventBus 源码分析

6.6.1 EventBus.getDefault()

java

java 复制代码
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

这是一个典型的单例模式实现。EventBus.getDefault() 方法用于获取 EventBus 的单例实例。首先检查 defaultInstance 是否为 null,如果为 null,则进入同步块,再次检查以避免多线程环境下的重复创建,最后创建一个新的 EventBus 实例并赋值给 defaultInstance,确保整个应用中只有一个 EventBus 实例。

6.6.2 register 方法

java

java 复制代码
/**
 * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
 * are no longer interested in receiving events.
 * <p/>
 * Subscribers have event handling methods that are identified by their name, typically called "onEvent". Event
 * handling methods must have exactly one parameter, the event. If the event handling method is to be called in a
 * specific thread, a modifier is appended to the method name. Valid modifiers match one of the {@link ThreadMode}
 * enums. For example, if a method is to be called in the UI thread by EventBus, it would be called "onEventMainThread".
 */
public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    // 查找订阅者类中的所有订阅方法
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            // 订阅事件
            subscribe(subscriber, subscriberMethod);
        }
    }
}

register 方法用于注册订阅者。首先获取订阅者的类对象,然后调用 subscriberMethodFinder.findSubscriberMethods(subscriberClass) 方法查找该类中所有带有 @Subscribe 注解的订阅方法。接着在同步块中遍历这些订阅方法,调用 subscribe 方法进行具体的订阅操作。

6.6.3 subscribe 方法

java

java 复制代码
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }
    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    if (subscriberMethod.sticky) {
        if (eventInheritance) {
            // Existing sticky events of all subclasses of eventType have to be considered.
            // Note: Iterating over all events may be inefficient with lots of sticky events,
            // thus data structure should be changed to allow a more efficient lookup
            // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> candidateEventType = entry.getKey();
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

subscribe 方法用于完成具体的订阅逻辑。它会根据事件类型从 subscriptionsByEventType 中获取对应的订阅列表,如果列表不存在则创建一个新的列表并添加到 subscriptionsByEventType 中。接着将新的订阅信息 newSubscription 按照优先级插入到订阅列表中。同时,会将订阅者订阅的事件类型记录到 typesBySubscriber 中。如果订阅方法标记为 sticky(粘性事件),则会检查是否有对应的粘性事件,如果有则将其发送给新的订阅者。

6.6.4 post 方法

java

java 复制代码
/** Posts the given event to the event bus. */
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

post 方法用于发布事件。首先从 currentPostingThreadState 中获取当前线程的发布状态 postingState,将事件添加到事件队列 eventQueue 中。如果当前没有正在发布事件,则标记为正在发布,设置当前线程是否为主线程,然后循环处理事件队列中的事件,调用 postSingleEvent 方法处理每个事件。处理完成后,重置发布状态。

6.6.5 postSingleEvent 方法

java

java 复制代码
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    if (eventInheritance) {
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

postSingleEvent 方法用于处理单个事件。如果 eventInheritancetrue,则会查找事件类及其所有父类的类型,依次调用 postSingleEventForEventType 方法处理每个类型的事件。如果没有找到订阅者,会根据配置记录日志或发布 NoSubscriberEvent 事件。

6.6.6 postSingleEventForEventType 方法

java

java 复制代码
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

postSingleEventForEventType 方法根据事件类型从 subscriptionsByEventType 中获取对应的订阅列表。如果列表不为空,则遍历订阅列表,调用 postToSubscription 方法将事件发送给每个订阅者。如果在处理过程中事件被取消,则停止后续处理。

6.6.7 postToSubscription 方法

java

java 复制代码
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                // temporary: technically not correct as poster not decoupled from subscriber
                invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            if (isMainThread) {
                backgroundPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

postToSubscription 方法根据订阅方法的 ThreadMode 来决定如何处理事件。如果 ThreadModePOSTING,则直接在当前线程调用订阅方法;如果为 MAIN,且当前是主线程则直接调用,否则通过 mainThreadPoster 将事件放入主线程队列;如果为 BACKGROUND,且当前是主线程则通过 backgroundPoster 将事件放入后台线程队列,否则直接调用;如果为 ASYNC,则通过 asyncPoster 将事件放入异步线程队列。

6.7 EventBus 的优缺点

6.7.1 优点
  • 简化通信:通过发布 - 订阅模式,极大地简化了组件之间的通信逻辑,避免了复杂的接口回调和广播机制的使用。
  • 解耦性强:发送者和接收者不需要直接引用对方,只需要关注事件的发布和订阅,降低了组件之间的耦合度。
  • 支持多线程 :提供了多种 ThreadMode,可以方便地控制事件处理方法在不同线程中执行,适用于各种场景。
6.7.2 缺点
  • 代码可读性降低 :过多使用 EventBus 会使代码的调用关系变得不直观,增加了代码的理解难度。
  • 调试困难:由于事件的发布和订阅是隐式的,当出现问题时,调试和定位问题会比较困难。

七、使用回调接口进行通信

7.1 回调接口简介

回调接口是一种常见的设计模式,在 Android 中常用于实现 Activity 之间的通信。通过定义一个接口,一个 Activity 可以实现该接口,另一个 Activity 持有该接口的引用,当某个事件发生时,调用接口的方法通知实现该接口的 Activity。

7.2 定义回调接口

java

java 复制代码
// 定义一个回调接口,用于在 Activity 之间传递数据
public interface DataCallback {
    // 定义一个回调方法,用于接收数据
    void onDataReceived(String data);
}

7.3 发送数据的 Activity(MainActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    // 定义一个 DataCallback 接口的引用
    private DataCallback dataCallback;

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

        // 找到按钮控件
        Button sendDataButton = findViewById(R.id.send_data_button);
        // 为按钮设置点击事件监听器
        sendDataButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建一个新的 Intent 对象,指定要启动的目标 Activity 为 SecondActivity
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                // 启动目标 Activity 并等待返回结果
                startActivityForResult(intent, 1);
            }
        });
    }

    // 设置 DataCallback 接口的实现
    public void setDataCallback(DataCallback callback) {
        this.dataCallback = callback;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1 && resultCode == RESULT_OK) {
            // 从返回的 Intent 中获取数据
            String receivedData = data.getStringExtra("data");
            if (dataCallback != null) {
                // 调用回调方法,将数据传递给实现了 DataCallback 接口的 Activity
                dataCallback.onDataReceived(receivedData);
            }
        }
    }
}

7.4 接收数据并发送回的 Activity(SecondActivity.java)

java

java 复制代码
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;

public class SecondActivity extends AppCompatActivity {

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

        // 找到输入框控件
        EditText inputEditText = findViewById(R.id.input_edittext);
        // 找到按钮控件
        Button sendBackButton = findViewById(R.id.send_back_button);
        // 为按钮设置点击事件监听器
        sendBackButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 获取输入框中的文本
                String inputText = inputEditText.getText().toString();
                // 创建一个新的 Intent 对象,用于返回数据
                Intent resultIntent = new Intent();
                // 向 Intent 中添加一个字符串数据,键为 "data",值为输入的文本
                resultIntent.putExtra("data", inputText);
                // 设置返回结果码为 RESULT_OK
                setResult(RESULT_OK, resultIntent);
                // 关闭当前 Activity
                finish();
            }
        });
    }
}

7.5 使用回调接口的 Activity(ThirdActivity.java)

java

java 复制代码
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class ThirdActivity extends AppCompatActivity implements DataCallback {
    private TextView receivedDataTextView;

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

        // 找到用于显示接收到的数据的 TextView 控件
        receivedDataTextView = findViewById(R.id.received_data_textview);

        // 创建 MainActivity 的实例
        MainActivity mainActivity = new MainActivity();
        // 设置当前 Activity 为 DataCallback 接口的实现
        mainActivity.setDataCallback(this);

        // 启动 MainActivity
        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
    }

    @Override
    public void onDataReceived(String data) {
        // 将接收到的数据显示在 TextView 中
        receivedDataTextView.setText("Received data: " + data);
    }
}

7.6 回调接口源码分析

7.6.1 接口的定义

java

java 复制代码
// 定义一个回调接口,用于在 Activity 之间传递数据
public interface DataCallback {
    // 定义一个回调方法,用于接收数据
    void onDataReceived(String data);
}

接口是一种抽象类型,它定义了一组方法的签名,但不包含方法的实现。DataCallback 接口定义了一个 onDataReceived 方法,用于接收数据。任何实现了该接口的类都必须实现这个方法。

7.6.2 接口的实现

java

java 复制代码
public class ThirdActivity extends AppCompatActivity implements DataCallback {
    private TextView receivedDataTextView;

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

        // 找到用于显示接收到的数据的 TextView 控件
        receivedDataTextView = findViewById(R.id.received_data_textview);

        // 创建 MainActivity 的实例
        MainActivity mainActivity = new MainActivity();
        // 设置当前 Activity 为 DataCallback 接口的实现
        mainActivity.setDataCallback(this);

        // 启动 MainActivity
        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
    }

    @Override
    public void onDataReceived(String data) {
        // 将接收到的数据显示在 TextView 中
        receivedDataTextView.setText("Received data: " + data);
    }
}

ThirdActivity 实现了 DataCallback 接口,并重写了 onDataReceived 方法。在 onCreate 方法中,创建了 MainActivity 的实例,并将当前 Activity 设置为 DataCallback 接口的实现。当 MainActivity 调用 DataCallback 接口的 onDataReceived 方法时,ThirdActivity 中的 onDataReceived 方法会被执行。

7.6.3 接口的调用

java

java 复制代码
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1 && resultCode == RESULT_OK) {
        // 从返回的 Intent 中获取数据
        String receivedData = data.getStringExtra("data");
        if (dataCallback != null) {
            // 调用回调方法,将数据传递给实现了 DataCallback 接口的 Activity
            dataCallback.onDataReceived(receivedData);
        }
    }
}

MainActivityonActivityResult 方法中,当接收到 SecondActivity 返回的结果时,会从 Intent 中获取数据。如果 dataCallback 不为 null,则调用 dataCallbackonDataReceived 方法,将数据传递给实现了该接口的 Activity

7.7 回调接口的优缺点

7.7.1 优点
  • 灵活可控:可以根据具体的需求定义不同的回调接口和方法,实现灵活的通信逻辑。
  • 代码可读性高:回调接口的使用使得代码的调用关系清晰,易于理解和维护。
7.7.2 缺点
  • 耦合度较高:发送者和接收者之间需要明确知道对方的接口实现,增加了组件之间的耦合度。
  • 代码复杂度增加:当通信逻辑复杂时,需要定义多个回调接口和方法,会增加代码的复杂度。

八、总结与展望

8.1 总结

通过对 Android Activity 组件间通信模块的深入分析,我们详细探讨了多种通信方式,包括使用 Intent 传递数据、使用 Bundle 传递复杂数据、使用静态变量和单例模式共享数据、使用广播机制进行通信、使用 EventBus 进行事件传递以及使用回调接口进行通信。每种通信方式都有其独特的特点和适用场景:

  • Intent 和 Bundle:是 Android 系统提供的基本通信方式,适用于在 Activity 之间传递简单或复杂的数据,使用方便,但数据量和类型有一定限制。

  • 静态变量和单例模式:简单直接,可方便地在不同 Activity 之间共享数据,但存在内存泄漏和数据一致性问题。

  • 广播机制:具有很强的解耦性和全局通信能力,但性能开销较大,且存在安全风险。

  • EventBus:通过发布 - 订阅模式简化了组件间的通信,支持多线程处理,但可能降低代码的可读性和调试难度。

  • 回调接口:灵活可控,代码可读性高,但会增加组件之间的耦合度和代码复杂度。

在实际开发中,开发者应根据具体的需求和场景选择合适的通信方式,以提高代码的可维护性和性能。

8.2 展望

随着 Android 技术的不断发展,Activity 组件间的通信机制也可能会有新的改进和发展:

  • 性能优化:现有的一些通信方式在性能上还有提升的空间,未来可能会出现更高效的通信机制,减少性能开销,提高应用的响应速度。

  • 安全性增强:在保证通信灵活性的同时,加强通信过程的安全性,防止数据泄露和恶意攻击。

  • 简化开发:进一步简化组件间通信的代码实现,提供更简洁、易用的 API,降低开发难度。

  • 跨进程通信改进:对于需要进行跨进程通信的场景,可能会有更稳定、高效的解决方案,提升应用的整体性能和稳定性。

总之,Android Activity 组件间通信模块是 Android 开发中非常重要的一部分,未来的发展将围绕着性能、安全、易用性等方面不断优化,为开发者提供更好的开发体验。

相关推荐
着迷不白28 分钟前
Linux单用户模式密码修改与硬盘注释指南
面试
奔跑中的蜗牛66633 分钟前
一次播放器架构升级:Android 直播间 ANR 下降 60%
android
有意义1 小时前
深度拆解分割等和子集:一维DP数组与倒序遍历的本质
前端·算法·面试
测试工坊3 小时前
Android 视频播放卡顿检测——帧率之外的第二战场
android
我叫黑大帅3 小时前
Go 语言中处理「未知类型数据」的两大核心手段
后端·面试·go
Kapaseker4 小时前
一杯美式深入理解 data class
android·kotlin
鹏多多4 小时前
Flutter使用screenshot进行截屏和截长图以及分享保存的全流程指南
android·前端·flutter
Carson带你学Android4 小时前
OpenClaw移动端要来了?Android官宣AI原生支持App Functions
android
拉不动的猪4 小时前
重温Vue异步更新队列
前端·javascript·面试
黄林晴4 小时前
Android 删了 XML 预览,现在你必须学 Compose 了
android