使用服务来运行后台并创build通知
我希望我的应用程序在单击button时启动服务,并且服务应该在后台运行,以在一天中的特定时间显示通知。 我有以下代码来做到这一点。 但它显示我不明白的错误。 我是Android的新手,这是我使用服务的第一个应用程序。 任何帮助,将不胜感激。 提前致谢。
AndroidManifest.xml中
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.newtrial" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.newtrial.CreateNotificationActiviy" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.example.newtrial.ResultActivity" android:label="@string/title_activity_result" > </activity> <service android:enabled="true" android:name=".UpdaterServiceManager" /> </application> </manifest>
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
这个问题相对比较老,但是我希望这个post可能对其他人有用。
TL; DR:使用AlarmManager来安排一个任务,使用IntentService,在这里看到示例代码;
这个testing应用程序(和指令)是关于什么的:
简单的helloworld应用程序,每2小时发送一次通知。 点击通知 – 在应用程序中打开辅助活动; 删除通知跟踪。
什么时候应该使用它:
一旦你需要按计划运行一些任务。 我自己的情况:每天一次,我想从服务器获取新内容,根据我得到的内容撰写通知并显示给用户。
该怎么办:
-
首先,我们创build两个活动:MainActivity,它启动通知服务和NotificationActivity,它将通过单击通知来启动:
activity_main.xml中
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> <Button android:id="@+id/sendNotifications" android:onClick="onSendNotificationsButtonClick" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Start Sending Notifications Every 2 Hours!" /> </RelativeLayout>
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中。
-
然后让我们创build
WakefulBroadcastReceiver
广播接收器,我在上面的代码中调用了NotificationEventReceiver 。在这里,我们将设置
AlarmManager
以每2小时(或以任何其他频率)触发PendingIntent
,并在onReceive()
方法中为此意图指定处理的操作。 在我们的例子中 – 清醒地开始IntentService
,我们将在后面的步骤中指定。 这个IntentService
会为我们生成通知。另外,这个接收器将包含一些辅助方法,比如创buildPendintIntents,我们稍后会使用它
NB1! 当我使用
WakefulBroadcastReceiver
,我需要在我的清单中添加额外的权限:<uses-permission android:name="android.permission.WAKE_LOCK" />
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); } }
-
现在让我们创build一个
IntentService
来实际创build通知。在那里,我们指定了
onHandleIntent()
,这是对我们在startWakefulService
方法中传递的NotificationEventReceiver的意图的响应。如果是删除操作 – 例如,我们可以将其logging到我们的分析中。 如果它是开始通知的意图 – 然后通过使用
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,一旦设备重新启动或时区已经改变(例如,用户从美国飞到欧洲,你不想popup通知在半夜,但粘到当地时间:-))。
NotificationServiceStarterReceiver.java
public final class NotificationServiceStarterReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { NotificationEventReceiver.setupAlarm(context); } }
-
我们还需要在AndroidManifest中注册我们所有的服务,广播接收器:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="klogi.com.notificationbyschedule"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".notifications.NotificationIntentService" android:enabled="true" android:exported="false" /> <receiver android:name=".broadcast_receivers.NotificationEventReceiver" /> <receiver android:name=".broadcast_receivers.NotificationServiceStarterReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.TIMEZONE_CHANGED" /> <action android:name="android.intent.action.TIME_SET" /> </intent-filter> </receiver> <activity android:name=".NotificationActivity" android:label="@string/title_activity_notification" android:theme="@style/AppTheme.NoActionBar"/> </application> </manifest>
而已!
这个项目的源代码,你可以在这里find。 我希望你会发现这个post很有帮助。
您的错误在onCreate和showNotification方法中的UpdaterServiceManager中。
您正尝试Service using Activity Context
显示来自Service using Activity Context
notification
。 而Every Service has its own Context,
只需使用那个。 您不需要pass a Service an Activity's Context.
我不明白为什么你需要一个特定的Activity's Context to show Notification.
把你的createNotification方法放在UpdateServiceManager.class中 。 并从服务中删除CreateNotificationActivity 。
您不能通过不是活动的上下文显示应用程序窗口/对话框。 尝试传递有效的活动参考