一、activity与Fragment的通信有哪些?

        使用 ViewModel 进行通信

        在项目中我对于activity与Fragment的通信主要就是使用 ViewModel 进行通信

        它里面的核心是数据驱动。Activity 和 Fragment 不再直接交互,而是通过一个共享的 ViewModel 实例作为数据中心,来同步数据。下面是我自己打的代码示例

1. 创建共享的 ViewModel

首先,创建一个继承自 ViewModel 的类,并用 MutableLiveData 来封装你需要共享的数据。

// SharedViewModel.java
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class SharedViewModel extends ViewModel {
    // MutableLiveData 用于在 ViewModel 内部修改数据
    private final MutableLiveData<String> selectedItem = new MutableLiveData<>();

    // 提供一个公开方法,供外部调用以更新数据
    public void selectItem(String item) {
        selectedItem.setValue(item);
    }
    
    // 提供一个不可变的 LiveData 实例供外部观察
    public LiveData<String> getSelectedItem() {
        return selectedItem;
    }
}
2. Fragment A(数据发送方)

        在发送数据的 Fragment 中,通过其宿主 Activity 的范围(by activityViewModels()ViewModelProvider)获取 ViewModel 的实例,然后调用其方法来更新数据。

// FragmentA.java
public class FragmentA extends Fragment {
    
    // 获取共享的 ViewModel 实例
    private SharedViewModel sharedViewModel;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 使用 ViewModelProvider 获取同一个实例
        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_a, container, false);
        Button button = view.findViewById(R.id.send_button);
        button.setOnClickListener(v -> {
            // 当按钮被点击时,更新 ViewModel 中的数据
            sharedViewModel.selectItem("Data from Fragment A");
        });
        return view;
    }
}
3. Fragment B(数据接收方)

        在接收数据的 Fragment 中,也通过同样的方式获取 ViewModel 的实例,然后观察observeLiveData 的变化。

// FragmentB.java
public class FragmentB extends Fragment {

    private SharedViewModel sharedViewModel;
    private TextView textView;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_b, container, false);
        textView = view.findViewById(R.id.received_data_text_view);
        
        // 观察 ViewModel 中的数据变化
        sharedViewModel.getSelectedItem().observe(getViewLifecycleOwner(), item -> {
            // 当数据发生变化时,更新 UI
            textView.setText(item);
        });
        
        return view;
    }
}

        逻辑总结: 这个过程的核心是数据流。Fragment A 只负责更新 ViewModel 中的数据,Fragment B 和 Activity 只负责观察这些数据。它们之间没有直接引用。这种方式不仅彻底解耦了组件,还利用了 LiveData 的生命周期感知特性,自动处理了数据在屏幕旋转等配置变化时的持久化,有效防止了内存泄漏。它代表了现代 Android 开发中组件通信的最新趋势和最佳实践。

        使用接口进行通信的逻辑与代码示例

        这个核心就是通过通过定义一个接口作为通信契约,让 Fragment 不依赖于具体的 Activity 类型,进行解耦。

1. 定义通信接口(在 Fragment 内)

首先,在 Fragment 内部定义一个公共接口。这个接口就像一份协议,规定了 Fragment 想要传递给 Activity 的信息类型和方法。

// MyFragment.java
public class MyFragment extends Fragment {

    // 定义通信接口
    public interface OnMessageSendListener {
        void onMessageSent(String message);
    }

    private OnMessageSendListener listener;

    // ... 其他 Fragment 生命周期方法

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        // 检查宿主 Activity 是否实现了接口
        if (context instanceof OnMessageSendListener) {
            listener = (OnMessageSendListener) context;
        } else {
            // 如果没有实现,抛出异常,提醒开发者
            throw new RuntimeException(context.toString() + " must implement OnMessageSendListener");
        }
    }

    // Fragment 内部触发事件的方法
    private void triggerCommunication() {
        if (listener != null) {
            // 调用接口方法,传递数据
            listener.onMessageSent("Hello from Fragment!");
        }
    }
}
2. Activity 实现接口并处理数据

然后,你的 Activity 必须实现这个接口。这就像在说:“好的,我愿意遵守这份契约,并且知道如何处理来自 Fragment 的数据。”

// MyActivity.java
public class MyActivity extends AppCompatActivity implements MyFragment.OnMessageSendListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 假设这里将 MyFragment 添加到了 Activity
        getSupportFragmentManager().beginTransaction()
                .add(R.id.fragment_container, new MyFragment())
                .commit();
    }

    // 实现接口中定义的方法
    @Override
    public void onMessageSent(String message) {
        // 在 Activity 中处理从 Fragment 接收到的数据
        Toast.makeText(this, "收到来自 Fragment 的消息: " + message, Toast.LENGTH_SHORT).show();
    }
}

逻辑总结: 这个过程非常清晰:Fragment 不关心它的宿主是谁,它只知道需要一个实现了 OnMessageSendListener 接口的对象。只要 Activity 遵守了这个约定,通信就能安全地进行。这种方式彻底解耦了 Fragment 和 Activity,提高了 Fragment 的可复用性。

二、String类与List,Set,Map类

StringStringBuilderStringBuffer 三者作用、意义和区别。

        String 是最基础的字符串类,它最大的特点是不可变性。这意味着一旦创建,它的值就无法改变。任何对 String 的修改操作,比如拼接,都会在内存中创建一个全新的 String 对象,而旧的对象则会被回收。因此,String 适用于那些不需要频繁修改的场景,它的不可变性也保证了线程安全,使其成为一个可靠的数据类型。然而,如果需要进行大量的字符串操作,尤其是在循环中,频繁创建新对象的开销会严重影响程序性能。

        为了解决 String 的性能问题,Java 引入了 StringBuilderStringBuffer。这两者都属于可变的字符串,它们在内部维护一个动态的字符数组,可以直接在原有对象上进行修改,而无需创建新的对象。这使得它们在进行字符串拼接、插入等操作时,效率远高于 String。其中,StringBuilder 的性能最高,因为它没有线程同步(synchronized)的开销,这使得它在单线程环境下成为处理字符串修改的首选工具。

        StringBufferStringBuilder 的功能几乎完全相同,但关键区别在于线程安全性StringBuffer 的所有方法都是线程同步的,这意味着在多线程环境中,它可以确保同一时间只有一个线程能访问它,从而避免了数据不一致的问题。然而,这种同步机制会带来额外的性能开销,所以在单线程环境下,使用 StringBuffer 是不必要的,会比 StringBuilder 慢。因此,在选择时,应根据项目的具体需求来决定:单线程用 StringBuilder,多线程用 StringBuffer,而 String 则用于处理不需要修改的简单字符串。


        ListSetMap 是 Java 集合框架中最基础也是最重要的三种接口,它们都用于存储对象,但各自有独特的存储方式、行为和适用场景。

List(列表)

        List 是一种有序、可重复的集合。你可以把它想象成一个数组,每个元素都有一个对应的索引,因此你可以通过索引来精确地访问、添加或删除元素。由于它允许重复元素,你可以多次添加同一个对象。在实际应用中,当你需要一个元素的顺序很重要,或者需要存储重复数据时,List 是你的首选。常见的实现类有 ArrayListLinkedList,前者基于数组,随机访问速度快;后者基于链表,插入和删除操作更高效。

Set(集合

        Set 是一种无序、不可重复的集合。它的行为更像一个数学意义上的集合,所有元素都是唯一的。当你试图向一个 Set 中添加一个已经存在的元素时,添加操作会失败。正因为这种独一无二的特性,Set 非常适合用来去重。例如,如果你需要统计一篇文章中不重复的单词数量,Set 就能轻松胜任。常见的实现类有 HashSetTreeSetHashSet 提供了最快的查找速度,而 TreeSet 则会保持元素的排序。

Map(映射)

        Map 是一种键值对的集合。它存储的不是单个元素,而是由“键(Key)”和“值(Value)”组成的配对。每个键都是唯一的,并且与一个值相关联。你可以通过键来快速查找、更新或删除对应的值,就像查字典一样。Map 非常适合用于存储需要通过某种唯一标识来检索的数据,比如一个用户的 ID 对应其个人信息,或者一个学生的学号对应其成绩。常见的实现类有 HashMapTreeMapHashMap 提供了无序的快速存取,而 TreeMap 则会按键的自然顺序进行排序。

Logo

为武汉地区的开发者提供学习、交流和合作的平台。社区聚集了众多技术爱好者和专业人士,涵盖了多个领域,包括人工智能、大数据、云计算、区块链等。社区定期举办技术分享、培训和活动,为开发者提供更多的学习和交流机会。

更多推荐