Подтвердить что ты не робот

Использование службы для запуска фона и создания уведомлений

Я хочу, чтобы мое приложение запускало службу, когда нажимается кнопка, и служба должна работать в фоновом режиме, чтобы показать уведомление в определенное время суток. Для этого у меня есть следующий код. Но он показывает ошибки, которые я не понимаю. Я новичок в 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
4b9b3361

Ответ 1

Вопрос относительно старый, но я надеюсь, что этот пост все же может быть релевантен для других.

TL; DR: используйте AlarmManager для планирования задачи, используйте IntentService, см. пример кода здесь;

Что это за тест-приложение (и инструкция):

Простое приложение helloworld, которое отправляет вам уведомление каждые 2 часа. Нажатие на уведомление - открывает вторичную активность в приложении; удаление дорожек уведомлений.

Когда вы должны использовать его:

Как только вам нужно выполнить какую-либо задачу по расписанию. Мой собственный случай: один раз в день я хочу получать новый контент с сервера, составлять уведомление на основе содержимого, которое я получил, и показывать его пользователю.

Что делать:

  • Сначала создайте 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 для запуска PendingIntent каждые 2 часа (или с любой другой частотой) и укажите обработанные действия для этого намерения в методе onReceive(). В нашем случае - бодрствуйте запустите IntentService, который мы укажем на последующих шагах. Этот IntentService будет генерировать уведомления для нас.

    Кроме того, этот приемник будет содержать некоторые вспомогательные методы, такие как создание PendintIntents, которые мы будем использовать позже

    NB1! Поскольку я использую WakefulBroadcastReceiver, мне нужно добавить в свой манифест дополнительное разрешение: <uses-permission android:name="android.permission.WAKE_LOCK" />

    NB2! Я использую его в бодрствовавшей версии широковещательного приемника, так как хочу, чтобы устройство не возвращалось спать во время моей операции IntentService. В привет-мире это не так важно (у нас нет долгой работы в нашем сервисе, но представьте себе, если вам нужно извлечь некоторые относительно огромные файлы с сервера во время этой операции). Подробнее об устройстве Awake здесь.

    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:

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

Что это!

Исходный код этого проекта можно найти здесь. Надеюсь, вы найдете эту статью полезной.

Ответ 2

Ваша ошибка в UpdaterServiceManager в методе onCreate и showNotification.

Вы пытаетесь показать notification от Service using Activity Context. Если Every Service has its own Context, просто использовать это. Вам не нужно pass a Service an Activity Context. Я не понимаю, зачем вам нужен конкретный Activity Context to show Notification.

Поместите свой метод createNotification в UpdateServiceManager.class. И удалить CreateNotificationActivity не из Сервиса.

Вы не можете отобразить окно/диалог приложения через контекст, который не является активностью. Попробуйте передать действительную ссылку на действия