大厂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 开发中非常重要的一部分,未来的发展将围绕着性能、安全、易用性等方面不断优化,为开发者提供更好的开发体验。

相关推荐
投笔丶从戎8 分钟前
Kotlin Multiplatform--03:项目实战
android·开发语言·kotlin
居然是阿宋26 分钟前
Android Canvas API 详细说明与示例
android
wuli玉shell3 小时前
spark-Schema 定义字段强类型和弱类型
android·java·spark
东风西巷3 小时前
BLURRR剪辑软件免费版:创意剪辑,轻松上手,打造个性视频
android·智能手机·音视频·生活·软件需求
JhonKI3 小时前
【MySQL】行结构详解:InnoDb支持格式、如何存储、头信息区域、Null列表、变长字段以及与其他格式的对比
android·数据库·mysql
ab_dg_dp5 小时前
Android 位掩码操作(&和~和|的二进制运算)
android
大学生小郑16 小时前
Go语言八股之channel详解
面试·golang
潜龙952716 小时前
第3.2.3节 Android动态调用链路的获取
android·调用链路
追随远方17 小时前
Android平台FFmpeg音视频开发深度指南
android·ffmpeg·音视频
撰卢18 小时前
MySQL 1366 - Incorrect string value:错误
android·数据库·mysql