diff --git a/samples/ApiDemos/res/layout/service_start_arguments_controller.xml b/samples/ApiDemos/res/layout/service_start_arguments_controller.xml index f10a2c396..19b749802 100644 --- a/samples/ApiDemos/res/layout/service_start_arguments_controller.xml +++ b/samples/ApiDemos/res/layout/service_start_arguments_controller.xml @@ -43,5 +43,15 @@ android:text="@string/start3_service"> + + + + diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index fa4e61ced..a1f200089 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -117,9 +117,6 @@ Disconnected from remote service Failure calling remote service - "Started with arguments: " - Finished arguments, - stopping. Sample Service Start Arguments @@ -130,10 +127,13 @@ service can be started with arguments, and run until all arguments are processed. - Start with \"One\" - Start with \"Two\" - Start with \"Three\" - + Start \"One\" no redeliver + Start \"Two\" no redeliver + Start \"Three\" w/redeliver + Start failed delivery + Service created. + Service destroyed. + The one-shot alarm has gone off The repeating alarm has gone off diff --git a/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.java b/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.java index 5e1615876..f4f9af1fb 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.java @@ -27,6 +27,7 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.util.Log; import android.widget.Toast; @@ -41,9 +42,12 @@ import com.example.android.apis.R; * happen in the service. This is generally how background services should * interact with the user, rather than doing something more disruptive such as * calling startActivity(). + * + *

For applications targeting Android 1.5 or beyond, you may want consider + * using the android.app.IntentService class, which takes care of all the + * work of creating the extra thread and dispatching commands to it. */ -public class ServiceStartArguments extends Service -{ +public class ServiceStartArguments extends Service { private NotificationManager mNM; private Intent mInvokeIntent; private volatile Looper mServiceLooper; @@ -58,16 +62,22 @@ public class ServiceStartArguments extends Service public void handleMessage(Message msg) { Bundle arguments = (Bundle)msg.obj; - String txt = getResources() - .getString(R.string.service_arguments_started); - txt = txt + arguments.getString("name"); - Log.i("ServiceStartArguments", "Message: " + msg + ", " + txt); + String txt = arguments.getString("name"); + + Log.i("ServiceStartArguments", "Message: " + msg + ", " + + arguments.getString("name")); - showNotification(); + if ((msg.arg2&Service.START_FLAG_REDELIVERY) == 0) { + txt = "New cmd #" + msg.arg1 + ": " + txt; + } else { + txt = "Re-delivered #" + msg.arg1 + ": " + txt; + } + + showNotification(txt); - // Normally we would do some work here... for our sample, we will - // just sleep for 10 seconds. + // Normally we would do some work here... for our sample, we will + // just sleep for 5 seconds. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { @@ -78,6 +88,8 @@ public class ServiceStartArguments extends Service } } + hideNotification(); + Log.i("ServiceStartArguments", "Done with #" + msg.arg1); stopSelf(msg.arg1); } @@ -88,14 +100,19 @@ public class ServiceStartArguments extends Service public void onCreate() { mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); + Toast.makeText(this, R.string.service_created, + Toast.LENGTH_SHORT).show(); + // This is who should be launched if the user selects our persistent // notification. mInvokeIntent = new Intent(this, ServiceStartArgumentsController.class); // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's - // main thread, which we don't want to block. - HandlerThread thread = new HandlerThread("ServiceStartArguments"); + // main thread, which we don't want to block. We also make it + // background priority so CPU-intensive work will not disrupt our UI. + HandlerThread thread = new HandlerThread("ServiceStartArguments", + Process.THREAD_PRIORITY_BACKGROUND); thread.start(); mServiceLooper = thread.getLooper(); @@ -103,25 +120,45 @@ public class ServiceStartArguments extends Service } @Override - public void onStart(Intent intent, int startId) { + public int onStartCommand(Intent intent, int flags, int startId) { Log.i("ServiceStartArguments", "Starting #" + startId + ": " + intent.getExtras()); Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; + msg.arg2 = flags; msg.obj = intent.getExtras(); mServiceHandler.sendMessage(msg); Log.i("ServiceStartArguments", "Sending: " + msg); + + // For the start fail button, we will simulate the process dying + // for some reason in onStartCommand(). + if (intent.getBooleanExtra("fail", false)) { + // Don't do this if we are in a retry... the system will + // eventually give up if we keep crashing. + if ((flags&START_FLAG_RETRY) == 0) { + // Since the process hasn't finished handling the command, + // it will be restarted with the command again, regardless of + // whether we return START_REDELIVER_INTENT. + Process.killProcess(Process.myPid()); + } + } + + // Normally we would consistently return one kind of result... + // however, here we will select between these two, so you can see + // how they impact the behavior. Try killing the process while it + // is in the middle of executing the different commands. + return intent.getBooleanExtra("redeliver", false) + ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onDestroy() { mServiceLooper.quit(); - // Cancel the persistent notification. - mNM.cancel(R.string.service_arguments_started); + hideNotification(); // Tell the user we stopped. - Toast.makeText(ServiceStartArguments.this, R.string.service_arguments_stopped, + Toast.makeText(ServiceStartArguments.this, R.string.service_destroyed, Toast.LENGTH_SHORT).show(); } @@ -133,10 +170,7 @@ public class ServiceStartArguments extends Service /** * Show a notification while this service is running. */ - private void showNotification() { - // In this sample, we'll use the same text for the ticker and the expanded notification - CharSequence text = getText(R.string.service_arguments_started); - + private void showNotification(String text) { // Set the icon, scrolling text and timestamp Notification notification = new Notification(R.drawable.stat_sample, text, System.currentTimeMillis()); @@ -149,9 +183,16 @@ public class ServiceStartArguments extends Service notification.setLatestEventInfo(this, getText(R.string.service_start_arguments_label), text, contentIntent); + // We show this for as long as our service is processing a command. + notification.flags |= Notification.FLAG_ONGOING_EVENT; + // Send the notification. // We use a string id because it is a unique number. We use it later to cancel. - mNM.notify(R.string.service_arguments_started, notification); + mNM.notify(R.string.service_created, notification); + } + + private void hideNotification() { + mNM.cancel(R.string.service_created); } } diff --git a/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArgumentsController.java b/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArgumentsController.java index 2c53ff4f4..9d79e2e05 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArgumentsController.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArgumentsController.java @@ -21,14 +21,13 @@ package com.example.android.apis.app; import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import android.os.Process; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import com.example.android.apis.R; -import java.util.HashMap; - /** * Example of explicitly starting the {@link ServiceStartArguments}. */ @@ -46,26 +45,51 @@ public class ServiceStartArgumentsController extends Activity { button.setOnClickListener(mStart2Listener); button = (Button)findViewById(R.id.start3); button.setOnClickListener(mStart3Listener); + button = (Button)findViewById(R.id.startfail); + button.setOnClickListener(mStartFailListener); + button = (Button)findViewById(R.id.kill); + button.setOnClickListener(mKillListener); } private OnClickListener mStart1Listener = new OnClickListener() { public void onClick(View v) { startService(new Intent(ServiceStartArgumentsController.this, - ServiceStartArguments.class).putExtra("name", "One")); + ServiceStartArguments.class) + .putExtra("name", "One")); } }; private OnClickListener mStart2Listener = new OnClickListener() { public void onClick(View v) { startService(new Intent(ServiceStartArgumentsController.this, - ServiceStartArguments.class).putExtra("name", "Two")); + ServiceStartArguments.class) + .putExtra("name", "Two")); } }; private OnClickListener mStart3Listener = new OnClickListener() { public void onClick(View v) { startService(new Intent(ServiceStartArgumentsController.this, - ServiceStartArguments.class).putExtra("name", "Three")); + ServiceStartArguments.class) + .putExtra("name", "Three") + .putExtra("redeliver", true)); + } + }; + + private OnClickListener mStartFailListener = new OnClickListener() { + public void onClick(View v) { + startService(new Intent(ServiceStartArgumentsController.this, + ServiceStartArguments.class) + .putExtra("name", "Failure") + .putExtra("fail", true)); + } + }; + + private OnClickListener mKillListener = new OnClickListener() { + public void onClick(View v) { + // This is to simulate the service being killed while it is + // running in the background. + Process.killProcess(Process.myPid()); } }; }