使用Service、TimerTask和BroadcastReceiver来检查各种更新

发布于 2024-12-09 06:38:50 字数 3861 浏览 1 评论 0原文

我正在尝试创建一个执行以下操作的简单程序:

  1. 由我的活动 (UpdateServiceActivity) 启动的服务 (NewsService) 检查新闻。
  2. 如果发现新闻(NewsService),则会向接收者(NewsReceiver)发送广播。
  3. 接收到广播后,接收者 (NewsReceiver) 应通知活动 (UpdateServiceActivity) 有新闻。
  4. 收到通知后,活动 (UpdateServiceActivity) 获取消息并处理它们。

到目前为止,我只是在研究一个简单的例子。到目前为止,这是我的代码:

UpdateServiceActivity

public class UpdateServiceActivity extends Activity implements OnClickListener {
  private static final String TAG = "UpdateServiceActivity";
  Button buttonStart, buttonStop;
  BroadcastReceiver receiver;

  @Override
  public void onCreate( Bundle savedInstanceState ) {
    super.onCreate( savedInstanceState );
    setContentView( R.layout.main );

    buttonStart = (Button) findViewById( R.id.buttonStart );
    buttonStop = (Button) findViewById( R.id.buttonStop );

    buttonStart.setOnClickListener( this );
    buttonStop.setOnClickListener( this );

    receiver = new NewsReceiver();
  }

  @Override
  protected void onPause() {
    super.onPause();
    unregisterReceiver( receiver );
  }

  @Override
  protected void onResume() {
    super.onResume();
    registerReceiver( receiver, new IntentFilter() );
  }

  public void onClick( View src ) {
    switch( src.getId() ) {
      case R.id.buttonStart:
        Log.e( TAG, "onClick: starting service" );
        startService( new Intent( this, NewsService.class ) );
        break;
      case R.id.buttonStop:
        Log.e( TAG, "onClick: stopping service" );
        stopService( new Intent( this, NewsService.class ) );
        break;
    }
  }
}

NewsService

public class NewsService extends Service {
  public static final String NEWS_INTENT = "bs.kalender.news";
  private Timer timer = new Timer();

  @Override
  public IBinder onBind( Intent arg0 ) {
    return null;
  }

  @Override
  public void onStart( Intent intent, int startId ) {
    startService();
  }

  private void startService() {
    timer.scheduleAtFixedRate( new NewsChecker(), 0, 5000 );
  }

  private class NewsChecker extends TimerTask {
  @Override
  public void run() {
    Intent intent = new Intent( getApplicationContext(), NewsReceiver.class );
    sendBroadcast( intent );
  }
 }
}

NewsReceiver

public class NewsReceiver extends BroadcastReceiver {
  @Override
  public void onReceive( Context context, Intent intent ) {
    Toast.makeText( context, "Broadcast recieved! There is news!", Toast.LENGTH_SHORT).show();
  } 
}

Manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="bs.update"
  android:versionCode="1"
  android:versionName="1.0">
  <uses-sdk android:minSdkVersion="7" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
  <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
      <activity android:name=".UpdateServiceActivity"
              android:label="@string/app_name">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
      <service android:name=".NewsService" />
      <receiver android:name=".NewsReceiver" />
  </application>
</manifest>

我遇到的问题是:

  • 当点击“Home”按钮(应用程序转到后台,并在暂停时调用)NewsReceiver 不断触发 toast。我的理解是,一旦取消注册接收器,它就不能用于接收广播。
  • 即使我按下按钮停止 NewsService,TimerTask 也会继续运行并发布广播。

我做错了什么?这是对广播/接收工作原理的普遍误解吗?我是否步入正轨?应该做出哪些改变才能实现我的愿望?

I'm trying to create a simple program that does the following:

  1. A service (NewsService) started by my activity (UpdateServiceActivity) checks for news.
  2. If news are found (NewsService) sends a broadcast to a receiver (NewsReceiver).
  3. Upon receiving the broadcast the receiver (NewsReceiver) should notify the activity (UpdateServiceActivity) that there are news.
  4. Upon notification, the activity (UpdateServiceActivity) gets the news and handles them.

So far I'm just working on a simple example. Here is my code so far:

UpdateServiceActivity

public class UpdateServiceActivity extends Activity implements OnClickListener {
  private static final String TAG = "UpdateServiceActivity";
  Button buttonStart, buttonStop;
  BroadcastReceiver receiver;

  @Override
  public void onCreate( Bundle savedInstanceState ) {
    super.onCreate( savedInstanceState );
    setContentView( R.layout.main );

    buttonStart = (Button) findViewById( R.id.buttonStart );
    buttonStop = (Button) findViewById( R.id.buttonStop );

    buttonStart.setOnClickListener( this );
    buttonStop.setOnClickListener( this );

    receiver = new NewsReceiver();
  }

  @Override
  protected void onPause() {
    super.onPause();
    unregisterReceiver( receiver );
  }

  @Override
  protected void onResume() {
    super.onResume();
    registerReceiver( receiver, new IntentFilter() );
  }

  public void onClick( View src ) {
    switch( src.getId() ) {
      case R.id.buttonStart:
        Log.e( TAG, "onClick: starting service" );
        startService( new Intent( this, NewsService.class ) );
        break;
      case R.id.buttonStop:
        Log.e( TAG, "onClick: stopping service" );
        stopService( new Intent( this, NewsService.class ) );
        break;
    }
  }
}

NewsService

public class NewsService extends Service {
  public static final String NEWS_INTENT = "bs.kalender.news";
  private Timer timer = new Timer();

  @Override
  public IBinder onBind( Intent arg0 ) {
    return null;
  }

  @Override
  public void onStart( Intent intent, int startId ) {
    startService();
  }

  private void startService() {
    timer.scheduleAtFixedRate( new NewsChecker(), 0, 5000 );
  }

  private class NewsChecker extends TimerTask {
  @Override
  public void run() {
    Intent intent = new Intent( getApplicationContext(), NewsReceiver.class );
    sendBroadcast( intent );
  }
 }
}

NewsReceiver

public class NewsReceiver extends BroadcastReceiver {
  @Override
  public void onReceive( Context context, Intent intent ) {
    Toast.makeText( context, "Broadcast recieved! There is news!", Toast.LENGTH_SHORT).show();
  } 
}

Manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="bs.update"
  android:versionCode="1"
  android:versionName="1.0">
  <uses-sdk android:minSdkVersion="7" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
  <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
      <activity android:name=".UpdateServiceActivity"
              android:label="@string/app_name">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
      <service android:name=".NewsService" />
      <receiver android:name=".NewsReceiver" />
  </application>
</manifest>

The problems I run in to are:

  • When hitting the 'Home'-button (the App goes to the background, and on pause is called) the NewsReceiver keeps firing toasts. I was of the understanding that once I unregister the receiver, it shouldn't be availible for receiving broadcast.
  • Even if I hit the button to stop the NewsService, the TimerTask keeps running and posting broadcast.

What am I doing wrong? Is it a general misunderstanding of how Broadcasting/Receiving works? Am I on track and what should be changed to accomplish what I desire?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

書生途 2024-12-16 06:38:50
  • 您正在创建并取消注册一个新的 Receiver 实例,但这对您在清单文件中注册的接收器没有影响。尝试将其从清单中删除。
  • 您永远不会取消计时器,这就是它不断触发的原因。停止服务不会自动停止您创建的线程(例如 Timer 使用的线程)。

通常,您应该将 AlarmManagerIntentService 一起使用code> 安排重复的后台任务。 Timer 不可靠,并且不太适合 Android 框架。此外,如果手机处于睡眠状态,Timer 将不会执行。您可以使用 AlarmManager 让闹钟唤醒手机来执行(这对于新闻更新服务是否是一个好主意是另一回事)。

  • You are creating and unregistering a new Receiver instance, but that has no effect on the receiver you have registered in the manifest file. Try removing it from the manifest.
  • You never cancel the Timer that's why it keeps firing. Stopping the service doesn't automatically stop threads you have created (such as the one used by the Timer)

Generally, you should use AlarmManager with an IntentService to schedule repeating background tasks. A Timer is unreliable and doesn't fit too well with the Android framework. Also, a Timer won't execute if the phone is asleep. You can have alarms wake up the phone to execute (whether that is a good idea for a news-updating service is another matter) with AlarmManager.

姐不稀罕 2024-12-16 06:38:50

编辑

这与 Nikolay Elenkov 关于使用 ScheduledThreadPoolExecutor 的建议相结合,提供了一个非常好的解决方案。


我找到了一个比使用服务和广播接收器更适合我的目的的解决方案。当应用程序未运行时,我不需要检查新闻/更新,因此使用服务会太过分,只会使用比需要更多的数据。我发现使用处理程序是正确的方法。这是完美运行的实现:

Avtivity:

public class UpdateServiceActivity extends Activity implements OnClickListener {
   private Handler handlerTimer;
   private Runnable newsHandler;

   @Override
   public void onCreate( Bundle savedInstanceState ) {
      handlerTimer = new Handler();
      newsHandler = new NewsHandler( handlerTimer, this );
      handlerTimer.removeCallbacks( newsHandler );
   }

   @Override
   protected void onPause() {
      handlerTimer.removeCallbacks( newsHandler );
      super.onPause();
   }

   @Override
   protected void onResume() {
      handlerTimer.postDelayed( newsHandler, 5000 );
      super.onResume();
   }
}

NewsHandler

public class NewsHandler implements Runnable {
   private static final int THERE_IS_NEWS = 999;
   private Handler timerHandler;
   private Handler newsEventHandler;
   private Context context;

   public NewsHandler( Handler timerHandler, Context context ) {
      this.timerHandler = timerHandler;
      this.newsEventHandler = new NewsEventHandler();
      this.context = context;
   }

   public void run() {
      Message msg = new Message();
      msg.what = THERE_IS_NEWS;
      newsEventHandler.sendMessage( msg );
      timerHandler.postDelayed( this, 5000 );
   }

   private class NewsEventHandler extends Handler { 
      @Override
      public void handleMessage( Message msg ) {
         if( msg.what == THERE_IS_NEWS )
               Toast.makeText( context, "HandlerMessage recieved! There is news!", Toast.LENGTH_SHORT ).show();
      }
   };
}

EDIT

This in combination with Nikolay Elenkov suggestion about using ScheduledThreadPoolExecutor provided a very nice solution.


I found a solution that fits my purpose better than using a Service and BroadcastReceiver. I have no need to check for news/updates when the app is not running, so using a service would be overkill and just use more data than needed. I found that using Handlers was the way to go. Here is the implementation that works perfectly:

Avtivity:

public class UpdateServiceActivity extends Activity implements OnClickListener {
   private Handler handlerTimer;
   private Runnable newsHandler;

   @Override
   public void onCreate( Bundle savedInstanceState ) {
      handlerTimer = new Handler();
      newsHandler = new NewsHandler( handlerTimer, this );
      handlerTimer.removeCallbacks( newsHandler );
   }

   @Override
   protected void onPause() {
      handlerTimer.removeCallbacks( newsHandler );
      super.onPause();
   }

   @Override
   protected void onResume() {
      handlerTimer.postDelayed( newsHandler, 5000 );
      super.onResume();
   }
}

NewsHandler

public class NewsHandler implements Runnable {
   private static final int THERE_IS_NEWS = 999;
   private Handler timerHandler;
   private Handler newsEventHandler;
   private Context context;

   public NewsHandler( Handler timerHandler, Context context ) {
      this.timerHandler = timerHandler;
      this.newsEventHandler = new NewsEventHandler();
      this.context = context;
   }

   public void run() {
      Message msg = new Message();
      msg.what = THERE_IS_NEWS;
      newsEventHandler.sendMessage( msg );
      timerHandler.postDelayed( this, 5000 );
   }

   private class NewsEventHandler extends Handler { 
      @Override
      public void handleMessage( Message msg ) {
         if( msg.what == THERE_IS_NEWS )
               Toast.makeText( context, "HandlerMessage recieved! There is news!", Toast.LENGTH_SHORT ).show();
      }
   };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文