定期更新我的应用程序的用户界面

时间:2018-04-16 19:16:51

标签: java android alarmmanager

我希望我的Android应用根据REST服务的响应定期更新其UI。我无法在主线程上执行此操作,因为在主线程上访问网络不允许/不良做法。 SO和互联网的一般智慧是使用BroadcastReceiver和AlarmManager的组合。例如,这是建议here。我尝试了两种设计,我都无法工作:

  1. 定义一个扩展BroadcastReceiver的类作为MainActivity的内部类。
  2. 将外部类定义为相同的类。
  3. 使用(1)我得到这个运行时错误:

    <div class="wrapper">
    
    <label class="container">
      <input type="checkbox" checked="checked">
      <span class="checkmark"></span>
    </label>
    
    <label class="container">
      <input type="checkbox">
      <span class="checkmark"></span>
    </label>
    
    <label class="container">
      <input type="checkbox">
      <span class="checkmark"></span>
    </label>
    
    <label class="container">
      <input type="checkbox">
      <span class="checkmark"></span>
    </label>
    
    <label class="container">
      <input type="checkbox">
      <span class="checkmark"></span>
    </label>
    
    </div>

    问题是我无法弄清楚如何在MainActivity中访问我想要修改的视图。

    以下是(1)的示例实现:

    java.lang.RuntimeException: Unable to instantiate receiver com.dbs.alarm.MainActivity$AlarmReceiver: java.lang.InstantiationException: java.lang.Class<com.dbs.alarm.MainActivity$AlarmReceiver> has no zero argument constructor
    

    我还将此添加到我的AndroidManifest.xml中,考虑到我得到一个异常,它似乎已成功注册:

    package com.dbs.alarm;
    
    import android.app.AlarmManager;
    import android.app.PendingIntent;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.SystemClock;
    import android.support.v7.app.AppCompatActivity;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity {
    
        // I tried making this its own class, but then findViewById isn't accessible.
        public class AlarmReceiver extends BroadcastReceiver {
            @Override
            public void onReceive(Context context, Intent intent) {
                // I tried wrapping this in runOnUiThread() but it made no difference.
                TextView myTextView = findViewById(R.id.my_text);
                CharSequence myCharSequence = "Set from UpdateReceiver.onReceive()";
                myTextView.setText(myCharSequence);
            }
        }
    
        private void setRecurringAlarm(Context context) {
            Intent intent = new Intent(context, AlarmReceiver.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    
            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    context, 0, intent,
                    PendingIntent.FLAG_CANCEL_CURRENT);
            AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    
            alarmManager.setInexactRepeating(
                    AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    SystemClock.elapsedRealtime() + 1000,
                    1000, // Set so short for demo purposes only.
                    pendingIntent
            );
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            setRecurringAlarm(this);
        }
    }
    

4 个答案:

答案 0 :(得分:3)

由于您需要直接访问文本视图,因此选择接收器的内部类是正确的做法。但是,声明为内部类的BroadcastReceiver必须在清单中声明static,这样才能使其成为内部类的目的(至少在您的场景中) 。因此,我建议您在BroadcastReceiveronStart()生命周期方法中动态注册/取消注册onStop()

public class MainActivity extends AppCompatActivity {

    private BroadcastReceiver alarmReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // the "MainActivity.this" tells it to use the method from the parent class
            MainActivity.this.updateMyTextView();
        }
    };

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

    @Override
    protected void onStart() {
        super.onStart();
        final IntentFilter filter = new IntentFilter();
        filter.addAction("YOUR_ACTION_NAME"); 
        registerReceiver(alarmReceiver, filter);
    }

    @Override
    protected void onStop() {
        unregisterReceiver(alarmReceiver);
        super.onStop();
    }

    private void updateMyTextView(){
        final TextView myTextView = findViewById(R.id.my_text);
        if (myTextView != null){
            CharSequence myCharSequence = "Set from UpdateReceiver.onReceive()";
            myTextView.post(()-> myTextView.setText(myCharSequence));
        }
    }

    private void setRecurringAlarm(Context context) {
        Intent intent = new Intent("YOUR_ACTION_NAME");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                context, 0, intent,
                PendingIntent.FLAG_CANCEL_CURRENT);
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        alarmManager.setInexactRepeating(
                AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime() + 1000,
                1000, // Set so short for demo purposes only.
                pendingIntent
        );
    }
}

您还会注意到,在创建警报意图时,不是传入接收器类,而是将其更改为使用可用于定义意图过滤器的字符串("YOUR_ACTION_NAME"){{{ 1}}将用于收听广播。

至于在UI线程上运行更新的问题,您始终可以从视图中调用BroadcastReceiver以在UI线程上运行某些内容,或者像您尝试的那样使用活动post()runOnUiThread内。我使“更新”方法属于活动而不是广播接收器,因为它似乎在我的头脑中更有意义。

编辑:在撰写此答案时,我更专注于解决您在实施解决方案时遇到的问题,而不是实际尝试帮助解决执行定期UI更新的更大问题。 @Ashikee AbHi建议使用BroadcastReceiver而不是警报,这绝对是你应该考虑的事情。当您必须在不同的过程中通知某些内容时,警报/广播接收器非常棒,但如果所有内容都包含在单个活动中,则使用Handler会更加清晰。

答案 1 :(得分:1)

您可以使用Handler并递归调用它来执行定期操作。请检查以下内容 Repeat a task with a time delay?

如果要更新视图,则需要使用新的Handler(Looper.getMainLooper())初始化它

答案 2 :(得分:0)

以最简单的方式,您必须在后台线程中运行REST请求,如AsyncTask doInBackground,并将请求的结果发送到onPostExecute中的UI线程。你可以通过不同的方式做到这一点,但对我来说最方便的是使用Bus&#39; es,例如Otto

答案 3 :(得分:0)

好的,看看你的要求,我会说你正在提取一些数据,并且你想让你的应用知道已经提取了新数据,这样应用就可以进行必要的UI更改。我建议使用Local Broadcast Manager,它允许您在应用中发送广播。

可以很容易地找到实现,您可以检查this。 基本上,您的想法是从REST API获取数据,向您的应用广播已获取数据,并且需要响应此事件的每个活动都将有一个接收器,将会收到通知。