我希望我的应用程序在单击按钮时启动服务,并且服务应在后台运行以在一天中的特定时间显示通知。我有以下代码来执行此操作。但它显示了我不理解的错误。我是Android新手,这是我第一个使用Service的应用。任何帮助,将不胜感激。提前谢谢。
的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
答案 0 :(得分:89)
问题相对较旧,但我希望这篇文章可能与其他人有关。
TL; DR:使用AlarmManager来安排任务,使用IntentService,查看示例代码here;
简单的helloworld应用程序,每2小时向您发送一次通知。点击通知 - 在应用中打开辅助活动;删除通知曲目。
一旦您需要按计划运行某项任务。 我自己的情况:每天一次,我想从服务器上获取新内容,根据我收到的内容撰写通知并将其显示给用户。
首先,让我们创建两个活动: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中。
然后让我们创建WakefulBroadcastReceiver
广播接收器,我在上面的代码中调用 NotificationEventReceiver 。
在此,我们将设置AlarmManager
每2小时(或使用任何其他频率)触发PendingIntent
,并在onReceive()
方法中指定此意图的已处理操作。在我们的例子中 - 清醒启动IntentService
,我们将在后面的步骤中指定。此IntentService
会为我们生成通知。
此外,此接收器将包含一些辅助方法,如创建PendintIntents,稍后我们将使用
NB1!当我使用WakefulBroadcastReceiver
时,我需要在清单中添加额外权限:<uses-permission android:name="android.permission.WAKE_LOCK" />
NB2!我使用它的唤醒版本的广播接收器,因为我想确保设备在IntentService
的操作期间不会重新进入睡眠状态。在hello-world中,它并不重要(我们的服务中没有长时间运行的操作,但想象一下,如果在此操作期间必须从服务器获取一些相对较大的文件)。详细了解Device Awake here。
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()
,这是对{em> 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中注册我们所有的服务,广播接收器:
<?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>
您可以找到此项目的源代码here。我希望,你会发现这篇文章很有用。
答案 1 :(得分:2)
您的错误发生在onCreate和showNotification方法的UpdaterServiceManager中。
您正试图从notification
展示Service using Activity Context
。而Every Service has its own Context,
只是使用它。您不需要pass a Service an Activity's Context.
我不明白为什么您需要特定的Activity's Context to show Notification.
将 createNotification方法放在UpdateServiceManager.class 中。 删除CreateNotificationActivity 不是来自服务。
您无法通过非活动的上下文显示应用程序窗口/对话框。尝试传递有效的活动参考