I'm trying to create a simple program that does the following:
- A service (NewsService) started by my activity (UpdateServiceActivity) checks for news.
- If news are found (NewsService) sends a broadcast to a receiver (NewsReceiver).
- Upon receiving the broadcast the receiver (NewsReceiver) should notify the activity (UpdateServiceActivity) that there are news.
- 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="@dr开发者_如何学Pythonawable/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?
- 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 theTimer
)
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
.
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();
}
};
}
精彩评论