webentwicklung-frage-antwort-db.com.de

Verwenden des Dienstes zum Ausführen des Hintergrunds und Erstellen einer Benachrichtigung

Ich möchte, dass meine App den Dienst startet, wenn auf die Schaltfläche geklickt wird und der Dienst im Hintergrund ausgeführt wird, um eine Benachrichtigung zu einer bestimmten Tageszeit anzuzeigen. Ich habe den folgenden Code, um dies zu tun. Aber es zeigt Fehler, die ich nicht verstehe. Ich bin neu in Android und dies ist meine erste App, die Service verwendet. Jede Hilfe wird gebeten. Vielen Dank im Voraus.

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

}

ErgebnisAktivität.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
44
user2648852

Die Frage ist relativ alt, aber ich hoffe, dass dieser Beitrag noch für andere relevant sein könnte.

TL; DR: Verwenden Sie AlarmManager, um eine Aufgabe zu planen, verwenden Sie IntentService, siehe Beispielcode hier ;

Worum geht es in dieser Testanwendung (und Anleitung):

Einfache helloworld App, die Ihnen alle 2 Stunden eine Benachrichtigung sendet. Klicken Sie auf Benachrichtigung - öffnet sekundäre Aktivität in der App; Löschen von Benachrichtigungsspuren.

Wann solltest du es benutzen:

Einmal müssen Sie eine Aufgabe auf einer geplanten Basis ausführen. Mein eigener Fall: Einmal am Tag möchte ich neue Inhalte vom Server abrufen, eine Benachrichtigung basierend auf den erhaltenen Inhalten erstellen und dem Benutzer anzeigen.

Was zu tun ist:

  1. Zuerst erstellen wir zwei Aktivitäten: MainActivity, das den Benachrichtigungsdienst startet, und NotificationActivity, das durch Klicken auf notification gestartet wird:

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

    und NotificationActivity ist eine beliebige Aktivität, die Sie erstellen können. NB ! Vergessen Sie nicht, beide Aktivitäten in AndroidManifest hinzuzufügen.

  2. Dann erstellen wir WakefulBroadcastReceiver Rundfunkempfänger, den ich im obigen Code NotificationEventReceiver nannte.

    Hier richten wir AlarmManager so ein, dass PendingIntent alle 2 Stunden (oder mit einer anderen Häufigkeit) ausgelöst wird, und legen die verarbeiteten Aktionen für diese Absicht in der Methode onReceive() fest. In unserem Fall - vorsichtigIntentService starten, was wir in den späteren Schritten spezifizieren werden. Dieses IntentService würde Benachrichtigungen für uns generieren.

    Außerdem würde dieser Receiver einige Hilfsmethoden wie das Erstellen von PendintIntents enthalten, die wir später verwenden werden

    NB1 ! Da ich WakefulBroadcastReceiver verwende, muss ich meinem Manifest zusätzliche Berechtigungen hinzufügen: <uses-permission Android:name="Android.permission.WAKE_LOCK" />

    NB2 ! Ich verwende die Weckversion des Rundfunkempfängers, um sicherzustellen, dass das Gerät während des Betriebs meines IntentService nicht wieder in den Ruhezustand wechselt. In der Hallo-Welt ist das nicht so wichtig (wir haben keinen langfristigen Betrieb in unserem Dienst, aber stellen Sie sich vor, Sie müssen während dieses Betriebs einige relativ große Dateien vom Server abrufen). Lesen Sie mehr über Device Awake hier .

    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);
        }
    }
    
  3. Jetzt erstellen wir ein IntentService, um tatsächlich Benachrichtigungen zu erstellen.

    Dort geben wir onHandleIntent() an, das sind Antworten auf NotificationEventReceivers Absicht, die wir in der startWakefulService -Methode übergeben haben.

    Wenn es sich um eine Löschaktion handelt, können wir sie beispielsweise in unserer Analyse protokollieren. Wenn es sich um Benachrichtigungsabsicht starten handelt, erstellen wir mit NotificationCompat.Builder Eine neue Benachrichtigung und zeigen sie mit NotificationManager.notify An. Beim Verfassen der Benachrichtigung legen wir außerdem ausstehende Absichten für das Klicken und Entfernen von Aktionen fest. Ziemlich einfach.

    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());
        }
    }
    
  4. Fast fertig. Jetzt füge ich auch einen Rundfunkempfänger für die Ereignisse BOOT_COMPLETED, TIMEZONE_CHANGED und TIME_SET hinzu, um meinen AlarmManager neu einzurichten, nachdem das Gerät neu gestartet wurde oder die Zeitzone geändert wurde (Benutzer aus den USA nach Europa geflogen, und Sie möchten keine Benachrichtigung erhalten Mitten in der Nacht, war aber klebrig an der Ortszeit :-)).

    NotificationServiceStarterReceiver.Java

    public final class NotificationServiceStarterReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            NotificationEventReceiver.setupAlarm(context);
        }
    }
    
  5. Wir müssen auch alle unsere Dienste, Rundfunkempfänger in AndroidManifest registrieren:

    <?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>
    

Das ist es!

Den Quellcode für dieses Projekt finden Sie hier . Ich hoffe, Sie finden diesen Beitrag hilfreich.

93

Ihr Fehler ist in UpdaterServiceManager in der Methode onCreate und showNotification.

Sie versuchen, notification aus Service using Activity Context Anzuzeigen. Wobei Every Service has its own Context, Nur das benutzt. Sie müssen nicht pass a Service an Activity's Context. Ich verstehe nicht, warum Sie einen bestimmten Activity's Context to show Notification. Benötigen.

Fügen Sie Ihre createNotification-Methode in UpdateServiceManager.class ein. Und CreateNotificationActivity entfernen nicht aus dem Dienst.

Sie können ein Anwendungsfenster/-dialogfeld nicht über einen Kontext anzeigen, der keine Aktivität ist. Versuchen Sie, eine gültige Aktivitätsreferenz zu übergeben

2
Xar E Ahmer