我希望我的应用程序在单击按钮时启动服务,并且服务应在后台运行以在一天中的特定时间显示通知.我有以下代码来执行此操作.但它显示了我不理解的错误.我是Android新手,这是我第一个使用Service的应用.任何帮助,将不胜感激.提前致谢.
AndroidManifest.xml中
CreateNotificationActiviy.java
package com.example.newtrial; import android.os.Bundle; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class CreateNotificationActiviy extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.create_notification_activiy); Button b=(Button)findViewById(R.id.button1); b.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub startService(new Intent(CreateNotificationActiviy.this, UpdaterServiceManager.class)); } }); } public void createNotification(View view) { // Prepare intent which is triggered if the // notification is selected Intent intent = new Intent(this, ResultActivity.class); PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0); // Build notification // Actions are just fake Notification noti = new Notification.Builder(this) .setContentTitle("Notification Title") .setContentText("Click here to read").setSmallIcon(R.drawable.ic_launcher) .setContentIntent(pIntent) .build(); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // hide the notification after its selected noti.flags |= Notification.FLAG_AUTO_CANCEL; notificationManager.notify(0, noti); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.create_notification_activiy, menu); return true; } }
UpdaterServiceManager.java
package com.example.newtrial; import java.util.Calendar; import java.util.Timer; import java.util.TimerTask; import android.app.AlertDialog; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.util.Log; import android.view.View; public class UpdaterServiceManager extends Service { private final int UPDATE_INTERVAL = 60 * 1000; private Timer timer = new Timer(); private static final int NOTIFICATION_EX = 1; private NotificationManager notificationManager; CreateNotificationActiviy not; public UpdaterServiceManager() { not=new CreateNotificationActiviy(); } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public void onCreate() { // code to execute when the service is first created super.onCreate(); Log.i("MyService", "Service Started."); showNotification(); } public void showNotification() { final Calendar cld = Calendar.getInstance(); int time = cld.get(Calendar.HOUR_OF_DAY); if(time>12) { not.createNotification(null); } else { AlertDialog.Builder alert=new AlertDialog.Builder(this); alert.setMessage("Not yet"); alert.setTitle("Error"); alert.setPositiveButton("OK", null); alert.create().show(); } } @Override public void onDestroy() { if (timer != null) { timer.cancel(); } } @Override public int onStartCommand(Intent intent, int flags, int startid) { return START_STICKY; } private void stopService() { if (timer != null) timer.cancel(); } }
ResultActivity.java
package com.example.newtrial; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.widget.TextView; public class ResultActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_result); TextView tv=(TextView)findViewById(R.id.textView1); tv.setText("After notification is clicked" ); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.result, menu); return true; } }
logcat的
12-10 12:14:04.286: I/Process(872): Sending signal. PID: 872 SIG: 9 12-10 12:14:11.774: I/MyService(893): Service Started. 12-10 12:14:12.094: D/AndroidRuntime(893): Shutting down VM 12-10 12:14:12.094: W/dalvikvm(893): threadid=1: thread exiting with uncaught exception (group=0x414c4700) 12-10 12:14:12.124: E/AndroidRuntime(893): FATAL EXCEPTION: main 12-10 12:14:12.124: E/AndroidRuntime(893): java.lang.RuntimeException: Unable to create service com.example.newtrial.UpdaterServiceManager: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application 12-10 12:14:12.124: E/AndroidRuntime(893): at android.app.ActivityThread.handleCreateService(ActivityThread.java:2587) 12-10 12:14:12.124: E/AndroidRuntime(893): at android.app.ActivityThread.access$1600(ActivityThread.java:141) 12-10 12:14:12.124: E/AndroidRuntime(893): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1338) 12-10 12:14:12.124: E/AndroidRuntime(893): at android.os.Handler.dispatchMessage(Handler.java:99) 12-10 12:14:12.124: E/AndroidRuntime(893): at android.os.Looper.loop(Looper.java:137) 12-10 12:14:12.124: E/AndroidRuntime(893): at android.app.ActivityThread.main(ActivityThread.java:5103) 12-10 12:14:12.124: E/AndroidRuntime(893): at java.lang.reflect.Method.invokeNative(Native Method) 12-10 12:14:12.124: E/AndroidRuntime(893): at java.lang.reflect.Method.invoke(Method.java:525) 12-10 12:14:12.124: E/AndroidRuntime(893): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737) 12-10 12:14:12.124: E/AndroidRuntime(893): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 12-10 12:14:12.124: E/AndroidRuntime(893): at dalvik.system.NativeStart.main(Native Method) 12-10 12:14:12.124: E/AndroidRuntime(893): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application 12-10 12:14:12.124: E/AndroidRuntime(893): at android.view.ViewRootImpl.setView(ViewRootImpl.java:563) 12-10 12:14:12.124: E/AndroidRuntime(893): at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:269) 12-10 12:14:12.124: E/AndroidRuntime(893): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69) 12-10 12:14:12.124: E/AndroidRuntime(893): at android.app.Dialog.show(Dialog.java:281) 12-10 12:14:12.124: E/AndroidRuntime(893): at com.example.newtrial.UpdaterServiceManager.showNotification(UpdaterServiceManager.java:65) 12-10 12:14:12.124: E/AndroidRuntime(893): at com.example.newtrial.UpdaterServiceManager.onCreate(UpdaterServiceManager.java:41) 12-10 12:14:12.124: E/AndroidRuntime(893): at android.app.ActivityThread.handleCreateService(ActivityThread.java:2577) 12-10 12:14:12.124: E/AndroidRuntime(893): ... 10 more
Konstantin L.. 93
问题相对较旧,但我希望这篇文章可能与其他人有关.
TL; DR:使用AlarmManager来安排任务,使用IntentService,在这里查看示例代码;
简单的helloworld应用程序,每2小时向您发送一次通知.单击通知 - 在应用程序中打开辅助活动; 删除通知曲目.
一旦您需要按计划运行某些任务.我自己的情况:每天一次,我想从服务器获取新内容,根据我收到的内容撰写通知并将其显示给用户.
首先,让我们创建两个活动:MainActivity,它启动通知服务和NotificationActivity,它将通过单击通知启动:
activity_main.xml中
MainActivity.java
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onSendNotificationsButtonClick(View view) { NotificationEventReceiver.setupAlarm(getApplicationContext()); } }
和NotificationActivity是您可以提出的任何随机活动.NB!不要忘记将这两个活动添加到AndroidManifest中.
然后让我们创建WakefulBroadcastReceiver
广播接收器,我在上面的代码中调用了NotificationEventReceiver.
在这里,我们将设置AlarmManager
为PendingIntent
每2小时(或使用任何其他频率)触发,并在onReceive()
方法中指定此意图的已处理操作.在我们的例子中 - 清醒启动IntentService
,我们将在后面的步骤中指定.这IntentService
会为我们生成通知.
此外,这个接收器将包含一些辅助方法,如创建PendintIntents,我们稍后将使用它
NB1!在我使用时WakefulBroadcastReceiver
,我需要在清单中添加额外权限:
NB2!我使用它的唤醒版广播接收器,因为我想确保,设备在我IntentService
的操作期间不会再回到睡眠状态.在hello-world中,它并不重要(我们的服务中没有长时间运行的操作,但想象一下,如果在此操作期间必须从服务器获取一些相对较大的文件).了解更多关于设备屏幕这里.
NotificationEventReceiver.java
public class NotificationEventReceiver extends WakefulBroadcastReceiver { private static final String ACTION_START_NOTIFICATION_SERVICE = "ACTION_START_NOTIFICATION_SERVICE"; private static final String ACTION_DELETE_NOTIFICATION = "ACTION_DELETE_NOTIFICATION"; private static final int NOTIFICATIONS_INTERVAL_IN_HOURS = 2; public static void setupAlarm(Context context) { AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); PendingIntent alarmIntent = getStartPendingIntent(context); alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, getTriggerAt(new Date()), NOTIFICATIONS_INTERVAL_IN_HOURS * AlarmManager.INTERVAL_HOUR, alarmIntent); } @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Intent serviceIntent = null; if (ACTION_START_NOTIFICATION_SERVICE.equals(action)) { Log.i(getClass().getSimpleName(), "onReceive from alarm, starting notification service"); serviceIntent = NotificationIntentService.createIntentStartNotificationService(context); } else if (ACTION_DELETE_NOTIFICATION.equals(action)) { Log.i(getClass().getSimpleName(), "onReceive delete notification action, starting notification service to handle delete"); serviceIntent = NotificationIntentService.createIntentDeleteNotification(context); } if (serviceIntent != null) { startWakefulService(context, serviceIntent); } } private static long getTriggerAt(Date now) { Calendar calendar = Calendar.getInstance(); calendar.setTime(now); //calendar.add(Calendar.HOUR, NOTIFICATIONS_INTERVAL_IN_HOURS); return calendar.getTimeInMillis(); } private static PendingIntent getStartPendingIntent(Context context) { Intent intent = new Intent(context, NotificationEventReceiver.class); intent.setAction(ACTION_START_NOTIFICATION_SERVICE); return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } public static PendingIntent getDeleteIntent(Context context) { Intent intent = new Intent(context, NotificationEventReceiver.class); intent.setAction(ACTION_DELETE_NOTIFICATION); return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } }
现在让我们创建一个IntentService
实际创建通知.
在那里,我们指定onHandleIntent()
哪个响应NotificationEventReceiver的意图我们传入startWakefulService
方法.
如果是删除操作 - 我们可以将其记录到我们的分析中,例如.如果它是开始通知意图 - 那么通过使用NotificationCompat.Builder
我们正在撰写新通知并显示它NotificationManager.notify
.在撰写通知时,我们还设置了单击和删除操作的待处理意图.相当容易.
NotificationIntentService.java
public class NotificationIntentService extends IntentService { private static final int NOTIFICATION_ID = 1; private static final String ACTION_START = "ACTION_START"; private static final String ACTION_DELETE = "ACTION_DELETE"; public NotificationIntentService() { super(NotificationIntentService.class.getSimpleName()); } public static Intent createIntentStartNotificationService(Context context) { Intent intent = new Intent(context, NotificationIntentService.class); intent.setAction(ACTION_START); return intent; } public static Intent createIntentDeleteNotification(Context context) { Intent intent = new Intent(context, NotificationIntentService.class); intent.setAction(ACTION_DELETE); return intent; } @Override protected void onHandleIntent(Intent intent) { Log.d(getClass().getSimpleName(), "onHandleIntent, started handling a notification event"); try { String action = intent.getAction(); if (ACTION_START.equals(action)) { processStartNotification(); } if (ACTION_DELETE.equals(action)) { processDeleteNotification(intent); } } finally { WakefulBroadcastReceiver.completeWakefulIntent(intent); } } private void processDeleteNotification(Intent intent) { // Log something? } private void processStartNotification() { // Do something. For example, fetch fresh data from backend to create a rich notification? final NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setContentTitle("Scheduled Notification") .setAutoCancel(true) .setColor(getResources().getColor(R.color.colorAccent)) .setContentText("This notification has been triggered by Notification Service") .setSmallIcon(R.drawable.notification_icon); PendingIntent pendingIntent = PendingIntent.getActivity(this, NOTIFICATION_ID, new Intent(this, NotificationActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(pendingIntent); builder.setDeleteIntent(NotificationEventReceiver.getDeleteIntent(this)); final NotificationManager manager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(NOTIFICATION_ID, builder.build()); } }
几乎完成了.现在我还为BOOT_COMPLETED,TIMEZONE_CHANGED和TIME_SET事件添加了广播接收器,以重新设置我的AlarmManager,一旦设备重启或时区已经改变(例如,用户从美国飞到欧洲,你不想弹出通知在半夜,但当地时间很粘:-)).
NotificationServiceStarterReceiver.java
public final class NotificationServiceStarterReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { NotificationEventReceiver.setupAlarm(context); } }
我们还需要在AndroidManifest中注册我们所有的服务,广播接收器:
您可以在此处找到此项目的源代码.我希望,你会发现这篇文章很有帮助.