1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.apis.app; 18 19 import android.app.Activity; 20 import android.app.Notification; 21 import android.app.NotificationManager; 22 import android.app.PendingIntent; 23 import android.app.Service; 24 import android.content.Intent; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.HandlerThread; 28 import android.os.IBinder; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.Process; 32 import android.util.Log; 33 import android.view.View; 34 import android.view.View.OnClickListener; 35 import android.widget.Button; 36 import android.widget.Toast; 37 38 import com.example.android.apis.R; 39 40 /** 41 * This is an example of implementing an application service that runs locally 42 * in the same process as the application. The {@link Controller} 43 * class shows how to interact with the service. 44 * 45 * <p>Notice the use of the {@link NotificationManager} when interesting things 46 * happen in the service. This is generally how background services should 47 * interact with the user, rather than doing something more disruptive such as 48 * calling startActivity(). 49 * 50 * <p>For applications targeting Android 1.5 or beyond, you may want consider 51 * using the {@link android.app.IntentService} class, which takes care of all the 52 * work of creating the extra thread and dispatching commands to it. 53 */ 54 public class ServiceStartArguments extends Service { 55 private NotificationManager mNM; 56 private Intent mInvokeIntent; 57 private volatile Looper mServiceLooper; 58 private volatile ServiceHandler mServiceHandler; 59 60 private final class ServiceHandler extends Handler { 61 public ServiceHandler(Looper looper) { 62 super(looper); 63 } 64 65 @Override 66 public void handleMessage(Message msg) { 67 Bundle arguments = (Bundle)msg.obj; 68 69 String txt = arguments.getString("name"); 70 71 Log.i("ServiceStartArguments", "Message: " + msg + ", " 72 + arguments.getString("name")); 73 74 if ((msg.arg2&Service.START_FLAG_REDELIVERY) == 0) { 75 txt = "New cmd #" + msg.arg1 + ": " + txt; 76 } else { 77 txt = "Re-delivered #" + msg.arg1 + ": " + txt; 78 } 79 80 showNotification(txt); 81 82 // Normally we would do some work here... for our sample, we will 83 // just sleep for 5 seconds. 84 long endTime = System.currentTimeMillis() + 5*1000; 85 while (System.currentTimeMillis() < endTime) { 86 synchronized (this) { 87 try { 88 wait(endTime - System.currentTimeMillis()); 89 } catch (Exception e) { 90 } 91 } 92 } 93 94 hideNotification(); 95 96 Log.i("ServiceStartArguments", "Done with #" + msg.arg1); 97 stopSelf(msg.arg1); 98 } 99 100 }; 101 102 @Override 103 public void onCreate() { 104 mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); 105 106 Toast.makeText(this, R.string.service_created, 107 Toast.LENGTH_SHORT).show(); 108 109 // This is who should be launched if the user selects our persistent 110 // notification. 111 mInvokeIntent = new Intent(this, Controller.class); 112 113 // Start up the thread running the service. Note that we create a 114 // separate thread because the service normally runs in the process's 115 // main thread, which we don't want to block. We also make it 116 // background priority so CPU-intensive work will not disrupt our UI. 117 HandlerThread thread = new HandlerThread("ServiceStartArguments", 118 Process.THREAD_PRIORITY_BACKGROUND); 119 thread.start(); 120 121 mServiceLooper = thread.getLooper(); 122 mServiceHandler = new ServiceHandler(mServiceLooper); 123 } 124 125 @Override 126 public int onStartCommand(Intent intent, int flags, int startId) { 127 Log.i("ServiceStartArguments", 128 "Starting #" + startId + ": " + intent.getExtras()); 129 Message msg = mServiceHandler.obtainMessage(); 130 msg.arg1 = startId; 131 msg.arg2 = flags; 132 msg.obj = intent.getExtras(); 133 mServiceHandler.sendMessage(msg); 134 Log.i("ServiceStartArguments", "Sending: " + msg); 135 136 // For the start fail button, we will simulate the process dying 137 // for some reason in onStartCommand(). 138 if (intent.getBooleanExtra("fail", false)) { 139 // Don't do this if we are in a retry... the system will 140 // eventually give up if we keep crashing. 141 if ((flags&START_FLAG_RETRY) == 0) { 142 // Since the process hasn't finished handling the command, 143 // it will be restarted with the command again, regardless of 144 // whether we return START_REDELIVER_INTENT. 145 Process.killProcess(Process.myPid()); 146 } 147 } 148 149 // Normally we would consistently return one kind of result... 150 // however, here we will select between these two, so you can see 151 // how they impact the behavior. Try killing the process while it 152 // is in the middle of executing the different commands. 153 return intent.getBooleanExtra("redeliver", false) 154 ? START_REDELIVER_INTENT : START_NOT_STICKY; 155 } 156 157 @Override 158 public void onDestroy() { 159 mServiceLooper.quit(); 160 161 hideNotification(); 162 163 // Tell the user we stopped. 164 Toast.makeText(ServiceStartArguments.this, R.string.service_destroyed, 165 Toast.LENGTH_SHORT).show(); 166 } 167 168 @Override 169 public IBinder onBind(Intent intent) { 170 return null; 171 } 172 173 /** 174 * Show a notification while this service is running. 175 */ 176 private void showNotification(String text) { 177 // Set the icon, scrolling text and timestamp 178 Notification notification = new Notification(R.drawable.stat_sample, text, 179 System.currentTimeMillis()); 180 181 // The PendingIntent to launch our activity if the user selects this notification 182 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 183 new Intent(this, Controller.class), 0); 184 185 // Set the info for the views that show in the notification panel. 186 notification.setLatestEventInfo(this, getText(R.string.service_start_arguments_label), 187 text, contentIntent); 188 189 // We show this for as long as our service is processing a command. 190 notification.flags |= Notification.FLAG_ONGOING_EVENT; 191 192 // Send the notification. 193 // We use a string id because it is a unique number. We use it later to cancel. 194 mNM.notify(R.string.service_created, notification); 195 } 196 197 private void hideNotification() { 198 mNM.cancel(R.string.service_created); 199 } 200 201 // ---------------------------------------------------------------------- 202 203 /** 204 * Example of explicitly starting the {@link ServiceStartArguments}. 205 * 206 * <p>Note that this is implemented as an inner class only keep the sample 207 * all together; typically this code would appear in some separate class. 208 */ 209 public static class Controller extends Activity { 210 @Override 211 protected void onCreate(Bundle savedInstanceState) { 212 super.onCreate(savedInstanceState); 213 214 setContentView(R.layout.service_start_arguments_controller); 215 216 // Watch for button clicks. 217 Button button = (Button)findViewById(R.id.start1); 218 button.setOnClickListener(mStart1Listener); 219 button = (Button)findViewById(R.id.start2); 220 button.setOnClickListener(mStart2Listener); 221 button = (Button)findViewById(R.id.start3); 222 button.setOnClickListener(mStart3Listener); 223 button = (Button)findViewById(R.id.startfail); 224 button.setOnClickListener(mStartFailListener); 225 button = (Button)findViewById(R.id.kill); 226 button.setOnClickListener(mKillListener); 227 } 228 229 private OnClickListener mStart1Listener = new OnClickListener() { 230 public void onClick(View v) { 231 startService(new Intent(Controller.this, 232 ServiceStartArguments.class) 233 .putExtra("name", "One")); 234 } 235 }; 236 237 private OnClickListener mStart2Listener = new OnClickListener() { 238 public void onClick(View v) { 239 startService(new Intent(Controller.this, 240 ServiceStartArguments.class) 241 .putExtra("name", "Two")); 242 } 243 }; 244 245 private OnClickListener mStart3Listener = new OnClickListener() { 246 public void onClick(View v) { 247 startService(new Intent(Controller.this, 248 ServiceStartArguments.class) 249 .putExtra("name", "Three") 250 .putExtra("redeliver", true)); 251 } 252 }; 253 254 private OnClickListener mStartFailListener = new OnClickListener() { 255 public void onClick(View v) { 256 startService(new Intent(Controller.this, 257 ServiceStartArguments.class) 258 .putExtra("name", "Failure") 259 .putExtra("fail", true)); 260 } 261 }; 262 263 private OnClickListener mKillListener = new OnClickListener() { 264 public void onClick(View v) { 265 // This is to simulate the service being killed while it is 266 // running in the background. 267 Process.killProcess(Process.myPid()); 268 } 269 }; 270 } 271 } 272 273