Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Service to run background and create notification

I want my app to start Service when the button is clicked and the Service should run in background to show a notification at a particular time of day. I have the following code to do this. But it shows errors which I don't understand. I am new to Android and this is my first app using Service. Any help would be appreciated. Thanks in advance.

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 
like image 729
user2648852 Avatar asked Dec 10 '13 17:12

user2648852


People also ask

How do I create a service that runs in the background?

Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main. xml. In the above code, we have taken text view, when user click on text view, it will start service and stop service.

What is background services in Android?

A background service performs an operation that isn't directly noticed by the user. For example, if an app used a service to compact its storage, that would usually be a background service.

Does intent service run in background?

IntentService runs outside the application in a background process, so the process would run even if your application is closed. Google now recommends using the JobIntentService, which is included as part of the support library. On pre-Android O devices, a normal IntentService will be dispatched in the background.


1 Answers

The question is relatively old, but I hope this post still might be relevant for others.

TL;DR: use AlarmManager to schedule a task, use IntentService, see the sample code here;

What this test-application(and instruction) is about:

Simple helloworld app, which sends you notification every 2 hours. Clicking on notification - opens secondary Activity in the app; deleting notification tracks.

When should you use it:

Once you need to run some task on a scheduled basis. My own case: once a day, I want to fetch new content from server, compose a notification based on the content I got and show it to user.

What to do:

  1. First, let's create 2 activities: MainActivity, which starts notification-service and NotificationActivity, which will be started by clicking notification:

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

    and NotificationActivity is any random activity you can come up with. NB! Don't forget to add both activities into AndroidManifest.

  2. Then let's create WakefulBroadcastReceiver broadcast receiver, I called NotificationEventReceiver in code above.

    Here, we'll set up AlarmManager to fire PendingIntent every 2 hours (or with any other frequency), and specify the handled actions for this intent in onReceive() method. In our case - wakefully start IntentService, which we'll specify in the later steps. This IntentService would generate notifications for us.

    Also, this receiver would contain some helper-methods like creating PendintIntents, which we'll use later

    NB1! As I'm using WakefulBroadcastReceiver, I need to add extra-permission into my manifest: <uses-permission android:name="android.permission.WAKE_LOCK" />

    NB2! I use it wakeful version of broadcast receiver, as I want to ensure, that the device does not go back to sleep during my IntentService's operation. In the hello-world it's not that important (we have no long-running operation in our service, but imagine, if you have to fetch some relatively huge files from server during this operation). Read more about 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);     } } 
  3. Now let's create an IntentService to actually create notifications.

    There, we specify onHandleIntent() which is responses on NotificationEventReceiver's intent we passed in startWakefulService method.

    If it's Delete action - we can log it to our analytics, for example. If it's Start notification intent - then by using NotificationCompat.Builder we're composing new notification and showing it by NotificationManager.notify. While composing notification, we are also setting pending intents for click and remove actions. Fairly Easy.

    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. Almost done. Now I also add broadcast receiver for BOOT_COMPLETED, TIMEZONE_CHANGED, and TIME_SET events to re-setup my AlarmManager, once device has been rebooted or timezone has changed (For example, user flown from USA to Europe and you don't want notification to pop up in the middle of the night, but was sticky to the local time :-) ).

    NotificationServiceStarterReceiver.java

    public final class NotificationServiceStarterReceiver extends BroadcastReceiver {      @Override     public void onReceive(Context context, Intent intent) {         NotificationEventReceiver.setupAlarm(context);     } } 
  5. We need to also register all our services, broadcast receivers in 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> 

That's it!

The source code for this project you can find here. I hope, you will find this post helpful.

like image 198
Konstantin Loginov Avatar answered Oct 21 '22 17:10

Konstantin Loginov