通过BroadcastReceiver 或 ViewModel与LiveData实现跨Activity的线程间通信及UI更新

问题背景如下:
按钮有两种状态,分别是出水、回水。如果当前状态是出水,当点击按钮时,会发送一条指令,在另一个线程中判断状态是否切换成功,如果已经切换为出水状态,则将按钮文字修改为”回水“。需要做到的就是能够在点击按钮后,自动更新按钮状态。另外需要注意的是,按钮和回复线程不在同一个Activity,这涉及到跨 Activity 的线程间通信以及 UI 更新。

该问题可以有两种方式去解决:
一、使用 BroadcastReceiver

  1. 在发送数据的 Activity 中发送广播
// 在发送数据的 Activity 中
private void sendUpdateBroadcast() {
    Intent intent = new Intent("UPDATE_ACTION");
    intent.putExtra("waterTemControlType", 1);  // 传递数据
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

btn1.setOnClickListener(v -> {
    send(new byte[]{0x01,0x10,0x00,0x00,0x00,0x03,0x06,0x00,0x03,0x00,0x04,0x00,0x05,0x23,0x42});
    Log.d("TAG", "onCreateView: " + UserSetDate.waterTemControlType);

    // 在后台线程中更新数据
    new Thread(() -> {
        UserSetDate.waterTemControlType = 1;

        // 发送广播通知其他 Activity 更新 UI
        sendUpdateBroadcast();
    }).start();
});
  1. 在接收数据的 Activity 中注册 BroadcastReceiver
public class ReceivingActivity extends AppCompatActivity {

    private Button btn1;
    private BroadcastReceiver updateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if ("UPDATE_ACTION".equals(intent.getAction())) {
                int waterTemControlType = intent.getIntExtra("waterTemControlType", 0);
                if (waterTemControlType == 1) {
                    btn1.setText("回水");
                }
            }
        }
    };

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

        btn1 = findViewById(R.id.btn1);

        // 注册 BroadcastReceiver
        LocalBroadcastManager.getInstance(this).registerReceiver(updateReceiver, new IntentFilter("UPDATE_ACTION"));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销 BroadcastReceiver
        LocalBroadcastManager.getInstance(this).unregisterReceiver(updateReceiver);
    }
}

二、使用 LiveData 和 ViewModel

  1. 创建 ViewModel
public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Integer> waterTemControlType = new MutableLiveData<>();

    public LiveData<Integer> getWaterTemControlType() {
        return waterTemControlType;
    }

    public void setWaterTemControlType(int value) {
        waterTemControlType.setValue(value);
    }
}
  1. 在发送数据的 Activity 中更新 ViewModel
public class SendingActivity extends AppCompatActivity {

    private SharedViewModel viewModel;

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

        viewModel = new ViewModelProvider(this).get(SharedViewModel.class);

        btn1.setOnClickListener(v -> {
            send(new byte[]{0x01,0x10,0x00,0x00,0x00,0x03,0x06,0x00,0x03,0x00,0x04,0x00,0x05,0x23,0x42});
            Log.d("TAG", "onCreateView: " + UserSetDate.waterTemControlType);

            // 在后台线程中更新数据
            new Thread(() -> {
                UserSetDate.waterTemControlType = 1;

                // 更新 ViewModel 中的数据
                viewModel.setWaterTemControlType(1);
            }).start();
        });
    }
}
  1. 在接收数据的 Activity 中观察 ViewModel
public class ReceivingActivity extends AppCompatActivity {

    private SharedViewModel viewModel;
    private Button btn1;

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

        btn1 = findViewById(R.id.btn1);
        viewModel = new ViewModelProvider(this).get(SharedViewModel.class);

        // 观察 ViewModel 中的数据
        viewModel.getWaterTemControlType().observe(this, value -> {
            if (value != null && value == 1) {
                btn1.setText("回水");
            }
        });
    }
}

在调试过程中,发现使用 LiveData 和 ViewModel 时,btn1按钮的文字并没有发生修改,最后发现在 Activity 和 Fragment 之间共享的ViewModel 实例不同导致的。

如果在调试过程中遇到问题,问题原因可能如下:
1.LiveData 数据没有正确更新:确保你在正确的地方更新了 LiveData 数据。
2.Fragment 的生命周期:确保 Fragment 在观察 LiveData 时处于活动状态。
3.ViewModel 的作用域:确保你使用的 ViewModel 是正确的,并且在 Activity 和 Fragment 之间共享相同的 ViewModel 实例。

排查步骤如下:

  1. 确保 LiveData 数据正确更新
    确保在更新 LiveData 数据的地方,你已经正确地调用了 setValue 或 postValue 方法。例如:
viewModel.setWaterTemControlType(1);
  1. 确保 Fragment 正确注册了观察者
    确保你在 Fragment 的 onCreateView 或 onViewCreated 方法中注册了观察者,而不是在 onCreate 中,因为 Fragment 在 onCreate 中可能还没有完全初始化。下面是一个正确的示例:
public class YourFragment extends Fragment {

    private SharedViewModel viewModel;
    private Button btn1;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_your, container, false);
        btn1 = view.findViewById(R.id.btn1);

        // 获取 ViewModel
        viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);

        // 注册观察者
        viewModel.getWaterTemControlType().observe(getViewLifecycleOwner(), value -> {
            Log.d("TAG", "onCreateView进来了: ");
            if (value != null && value == 1) {
                btn1.setText("回水");
            }
        });

        return view;
    }
}

注意:使用 getViewLifecycleOwner() 来注册观察者,以确保观察者的生命周期与 Fragment 的视图生命周期一致。这可以避免在视图被销毁后尝试更新 UI 的问题。

  1. 确保 ViewModel 的作用域
    确保你在 Activity 和 Fragment 中使用的是同一个 ViewModel 实例。如果你在 Activity 和 Fragment 中分别创建了不同的 ViewModel 实例,它们之间的 LiveData 更新是不会相互影响的。

通常,你可以通过 ViewModelProvider(requireActivity()) 在 Fragment 中获取 Activity 作用域的 ViewModel 实例,确保 Fragment 和 Activity 使用同一个 ViewModel 实例。

  1. 检查 Fragment 的生命周期
    确保 Fragment 没有被销毁或隐藏,这会导致观察者的回调无法触发。在 Fragment 被创建和视图被创建时注册观察者,避免在 Fragment 被销毁或视图被销毁时更新 UI。

总结
使用 BroadcastReceiver 是一种简单的方式,适合在 Activity 之间进行通知和数据传递。它是基于广播的机制,适合传递较为简单的数据和触发事件。
使用 LiveData 和 ViewModel 是现代 Android 开发中的推荐方式,适合在同一进程内的不同 Activity 或 Fragment 之间共享数据和状态。它更为强大和灵活,特别是对于较复杂的应用架构和数据观察场景。

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android中,服务和UI线程是两个不同的线程,因此从服务中更新UI可以有几种方法。以下是一些方法: 1. 使用BroadcastReceiver:从服务中发送广播,UI线程中的BroadcastReceiver接收广播并更新UI。 2. 使用Handler:从服务中创建一个Handler对象,然后使用post()或sendMessage()方法将更新消息发送到UI线程的Handler中。 3. 使用LiveData:在ViewModel中创建一个LiveData对象,然后从服务中更新LiveDataLiveData可观察,因此任何观察它的UI组件都会自动更新。 以下是使用LiveData从服务中更新ViewModelLiveData的示例代码: 在ViewModel中创建LiveData对象: ``` public class MyViewModel extends ViewModel { private MutableLiveData<String> mData = new MutableLiveData<>(); public LiveData<String> getData() { return mData; } public void setData(String data) { mData.setValue(data); } } ``` 在服务中更新LiveData: ``` public class MyService extends Service { private MyViewModel mViewModel; @Override public void onCreate() { super.onCreate(); mViewModel = ViewModelProviders.of(this).get(MyViewModel.class); } private void updateData(String data) { mViewModel.setData(data); } } ``` 在UI组件中观察LiveData: ``` public class MyActivity extends AppCompatActivity { private MyViewModel mViewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mViewModel = ViewModelProviders.of(this).get(MyViewModel.class); mViewModel.getData().observe(this, new Observer<String>() { @Override public void onChanged(String data) { // 更新UI视图 } }); } } ``` 当服务调用updateData()方法时,LiveData会自动通知所有观察它的UI组件进行更新
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值