2011-10-12 30 views
4

我試圖創建一個簡單的程序,執行以下操作:使用服務,TimerTask的和廣播接收器來檢查各種更新

  1. 我的活動啓動的服務(NewsService)(UpdateServiceActivity)檢查的消息。
  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 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'按鈕(在App轉到後臺,並且暫停被調用)的NewsReceiver保持發射吐司。我的理解是,一旦我註銷接收者,就不應該接收廣播。
  • 即使我點擊按鈕來停止NewsService,TimerTask仍會繼續運行併發布廣播。

我在做什麼錯?這是對廣播/接收如何工作的普遍誤解嗎?我的軌道上,應該改變什麼來完成我的願望?

回答

1

編輯

這與尼古拉Elenkov建議有關使用的ScheduledThreadPoolExecutor組合提供了一個非常好的解決方案。


我發現一個解決方案比使用Service和BroadcastReceiver更適合我的目的。當應用程序沒有運行時,我不需要檢查新聞/更新,因此使用服務將會過度殺傷,只是使用比需要的更多的數據。我發現使用Handlers是要走的路。這裏是一個完美的作品實現:

活性起着:

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(); 
     } 
    }; 
} 
+0

這將在UI線程上運行,因此如果您必須執行實際的網絡I/O,則會出現問題。如果你只需要這個工作,而應用程序可見,你可以看看'AsyncTask'或啓動一個處理線程。 –

+0

您能否提供一個提供相同功能的示例實現?我考慮過使用AsynTask,但我發現這樣做並不適合Android框架(安排特定任務以給定間隔運行,然後應該在UI上發佈結果)。 AsyncTask適合一次性執行,但只要我的Activity可見,我就需要它運行(例如,任務在onPause中停止並在onResume中重新啓動)。 – kaspermoerch

+1

您可以使用ScheduledThreadPoolExecutor定期運行'NewsHandler'並將消息發佈到'NewsEventHandler'來更新UI(您不需要'postDelayed()',但是您需要啓動/停止執行程序暫停/恢復)。或者,如果要使用處理程序執行此操作,請使用類似於'HandlerThread thread = new HandlerThread(「HT」)的後臺線程運行它。 Handler handlerTimer = new Handler(thread.getLooper());' –

5
  • 您正在創建和取消註冊一個新的Receiver實例,但這對您在清單文件中註冊的接收器沒有影響。嘗試從清單中刪除它。
  • 你永遠不會取消Timer這就是爲什麼它繼續射擊。停止該服務不會自動停止,你已經創建的線程(如由Timer使用的)

一般來說,你應該使用AlarmManagerIntentService安排重複後臺任務。 Timer是不可靠的,不適合Android框架。另外,如果手機處於睡眠狀態,Timer將不會執行。您可以通過AlarmManager讓鬧鐘喚醒手機執行(這對於新聞更新服務來說是個好主意)。

+0

在沒有接收到廣播的所有清單結果中移除它。我將查看AlarmManager。 – kaspermoerch