如何从广播接收器访问 Activity 的任何控件。不使用“静态”

Posted

技术标签:

【中文标题】如何从广播接收器访问 Activity 的任何控件。不使用“静态”【英文标题】:How to access any control of an Activity from a broadcast receiver. Without using "static" 【发布时间】:2020-02-01 06:16:09 【问题描述】:

我想从广播接收器访问活动的控件,而不使用“静态”或在它们之间传递任何数据。我只想像在静态中一样直接访问它,但没有将任何类定义为静态。

在此示例中,我尝试从广播接收器更改 TextView 控件的文本值。

namespace App2


[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity

    private BluetoothManager bluetoothManager;
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothDeviceReceiver _receiver;
    private TextView currentCharacterName;

    int count = 0;
    protected override void OnCreate(Bundle savedInstanceState)
    
        base.OnCreate(savedInstanceState);
        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.activity_main);
        Button ChangeTextButton = FindViewById<Button>(Resource.Id.button1);
        currentCharacterName = FindViewById<TextView>(Resource.Id.textView1);

        ChangeTextButton.Click += delegate 
            currentCharacterName.Text = string.Format("0 clicks!", count++);
        ;
        var appContext = Application.Context;

        bluetoothManager = (BluetoothManager)appContext.GetSystemService(BluetoothService);
        bluetoothAdapter = bluetoothManager.Adapter;
        if (bluetoothAdapter == null || !bluetoothAdapter.IsEnabled)
        
            bluetoothManager.Adapter.Enable();
        
        _receiver = new BluetoothDeviceReceiver();
        RegisterReceiver(_receiver, new IntentFilter(BluetoothDevice.ActionFound));
        bluetoothAdapter.StartDiscovery();
    

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] android.Content.PM.Permission[] grantResults)
    
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    

    private class BluetoothDeviceReceiver : BroadcastReceiver
    
        public override void OnReceive(Context context, Intent intent)
        
            var action = intent.Action;

            TextView currentCharacterName = MainActivity.FindViewById<TextView>(Resource.Id.textView1);
            if (action != BluetoothDevice.ActionFound)
            
                return;
            

            // Get the device
            var device = (BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);

            if (device.BondState != Bond.Bonded)
            
                Console.WriteLine($"Found device with name: device.Name and MAC address: device.Address");
            
        
    


问题出现在这里

TextView currentCharacterName = MainActivity.FindViewById<TextView>(Resource.Id.textView1);

错误 CS0120 非静态字段、方法或属性需要对象引用

它需要什么样的参考? MainActivity 不是参考吗?周围还有其他 MainActivity 吗?我在 MainActivity 中定义了这个接收器,所以每当 MainActivity 获得一个实例时,这个接收器就属于那个实例。我真的不明白这里发生了什么。我尝试使用传递给接收者的上下文,但我做不到。

【问题讨论】:

【参考方案1】:

你不能,也不应该。当 BroadcastReceiver 运行时,您无法保证 Activity 正在运行。如果您需要这样做,您的应用程序的架构就会出错。唯一可以的情况是,如果 BroadcastReceiver 已在 Activity 中注册,并且在这种有限的情况下,您可以将 BR 设为 Activity 的内部类(在这种情况下,它可以访问 Activity 的任何变量)或者可以通过当您通过其构造函数实例化它时的控件。但是任何一种方式都是危险的,因为如果你不小心取消注册,任何一种方式都可能导致它在活动完成后触发。

在任何情况下都不要将视图设为静态变量。不仅会出错(一次不必只有 1 个 Activity 实例),而且会导致整个 Activity 和所有关联变量的内存泄漏。

【讨论】:

0 是的,正如我在问题中提到的那样,我不想将它用作静态。所以 ; 1-)它的正确方法是什么? 2-) 有一个上下文传递给 BroadCastReceiver 。所以我认为该上下文属于 MainActivity ,不是吗?如果是这样,我们可以使用该上下文获取 TextWiev 控件的文本吗? 传递给广播接收器的上下文只是一个上下文。它不必是任何特定的上下文。很可能是它的应用程序上下文,但不要依赖于此。就像我说的 - 你可以做到这一点的唯一方法是如果接收者在活动中注册,然后你需要在构造函数中传递它。如果它是一个清单注册的接收者或注册在一个服务中,你根本不能。【参考方案2】:

通过在 BroadcastReceiver 中使用此代码,我可以得到我想要的结果;

TextView currentCharacterName = ((Activity)context).FindViewById<TextView>(Resource.Id.textView1);

我在 mainactivity 中创建了 Broadcastreceiver 并在 mainactivity 中注销它,以防 onPause 或其他与活动停用相关的状态。 我不认为这是最安全和最好的方法,但至少它会更改活动中文本视图的文本,而不会出现任何异常或错误。 最好的问候。

【讨论】:

如果您在MainActivity 中创建BroadcastReceiver,那么您可以在创建BroadcastReceiver 时传递对MainActivity 的引用(在构造函数中)。然后你可以在参考上调用FindViewById() 我在 MainActivity 内外都创建了 Broadcastreceiver ,它们都可以按需要工作。但是,一个问题出现在我面前,那个 Context 是从哪里来的 onReceive?显然,上下文属于我的 Mainactivity,但为什么呢?既然事件广播器不是我的 MainActivity ,为什么那个上下文属于我的 Mainactivity ? "_receiver = new BluetoothDeviceReceiver();"在创建 BluetoothDeviceReceiver 实例或注册期间,mainActiviy 的上下文是否传递给该实例?因为我没有手动传递任何引用。

以上是关于如何从广播接收器访问 Activity 的任何控件。不使用“静态”的主要内容,如果未能解决你的问题,请参考以下文章

手机卫士03_手势动作_广播接收者应用

acvity和fragment通过广播联系

如何将结果数据从广播接收器发送到活动

Android广播接收器和Activity间传递数据

应用程序关闭后,用于媒体控件的 Android 广播接收器停止工作

22 AndroidBroadcast广播机制