Using Service, TimerTask and BroadcastReceiver to

2019-04-09 20:47发布

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?

2条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-04-09 21:13
  • 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.

查看更多
Ridiculous、
3楼-- · 2019-04-09 21:21

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();
      }
   };
}
查看更多
登录 后发表回答