diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index fb416ed78..6615d711b 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -218,7 +218,7 @@ - @@ -227,7 +227,7 @@ - @@ -235,6 +235,19 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index 3ab923b90..c7bb84e9c 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -109,6 +109,10 @@ Connected to local service Disconnected from local service + App/Service/Messenger Service + This demonstrates how you can communicate with + a remote service using Messenger. + Remote service has started Remote service has stopped Sample Remote Service diff --git a/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java b/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java index 4a4f32428..859969c0c 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java @@ -16,22 +16,14 @@ package com.example.android.apis.app; -import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; -import android.content.ComponentName; -import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; -import android.os.Bundle; import android.os.IBinder; import android.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; import android.widget.Toast; // Need the following import to get access to the app resources, since this @@ -40,8 +32,8 @@ import com.example.android.apis.R; /** * This is an example of implementing an application service that runs locally - * in the same process as the application. The {@link Controller} - * and {@link Binding} classes show how to interact with the + * in the same process as the application. The {@link LocalServiceActivities.Controller} + * and {@link LocalServiceActivities.Binding} classes show how to interact with the * service. * *

Notice the use of the {@link NotificationManager} when interesting things @@ -49,6 +41,7 @@ import com.example.android.apis.R; * interact with the user, rather than doing something more disruptive such as * calling startActivity(). */ +//BEGIN_INCLUDE(service) public class LocalService extends Service { private NotificationManager mNM; @@ -110,7 +103,7 @@ public class LocalService extends Service { // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, - new Intent(this, Controller.class), 0); + new Intent(this, LocalServiceActivities.Controller.class), 0); // Set the info for the views that show in the notification panel. notification.setLatestEventInfo(this, getText(R.string.local_service_label), @@ -120,127 +113,5 @@ public class LocalService extends Service { // We use a layout id because it is a unique number. We use it later to cancel. mNM.notify(R.string.local_service_started, notification); } - - // ---------------------------------------------------------------------- - - /** - *

Example of explicitly starting and stopping the local service. - * This demonstrates the implementation of a service that runs in the same - * process as the rest of the application, which is explicitly started and stopped - * as desired.

- * - *

Note that this is implemented as an inner class only keep the sample - * all together; typically this code would appear in some separate class. - */ - public static class Controller extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.local_service_controller); - - // Watch for button clicks. - Button button = (Button)findViewById(R.id.start); - button.setOnClickListener(mStartListener); - button = (Button)findViewById(R.id.stop); - button.setOnClickListener(mStopListener); - } - - private OnClickListener mStartListener = new OnClickListener() { - public void onClick(View v) { - // Make sure the service is started. It will continue running - // until someone calls stopService(). The Intent we use to find - // the service explicitly specifies our service component, because - // we want it running in our own process and don't want other - // applications to replace it. - startService(new Intent(Controller.this, - LocalService.class)); - } - }; - - private OnClickListener mStopListener = new OnClickListener() { - public void onClick(View v) { - // Cancel a previous call to startService(). Note that the - // service will not actually stop at this point if there are - // still bound clients. - stopService(new Intent(Controller.this, - LocalService.class)); - } - }; - } - - // ---------------------------------------------------------------------- - - /** - * Example of binding and unbinding to the local service. - * This demonstrates the implementation of a service which the client will - * bind to, receiving an object through which it can communicate with the service.

- * - *

Note that this is implemented as an inner class only keep the sample - * all together; typically this code would appear in some separate class. - */ - public static class Binding extends Activity { - private boolean mIsBound; - private LocalService mBoundService; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.local_service_binding); - - // Watch for button clicks. - Button button = (Button)findViewById(R.id.bind); - button.setOnClickListener(mBindListener); - button = (Button)findViewById(R.id.unbind); - button.setOnClickListener(mUnbindListener); - } - - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - // This is called when the connection with the service has been - // established, giving us the service object we can use to - // interact with the service. Because we have bound to a explicit - // service that we know is running in our own process, we can - // cast its IBinder to a concrete class and directly access it. - mBoundService = ((LocalService.LocalBinder)service).getService(); - - // Tell the user about this for our demo. - Toast.makeText(Binding.this, R.string.local_service_connected, - Toast.LENGTH_SHORT).show(); - } - - public void onServiceDisconnected(ComponentName className) { - // This is called when the connection with the service has been - // unexpectedly disconnected -- that is, its process crashed. - // Because it is running in our same process, we should never - // see this happen. - mBoundService = null; - Toast.makeText(Binding.this, R.string.local_service_disconnected, - Toast.LENGTH_SHORT).show(); - } - }; - - private OnClickListener mBindListener = new OnClickListener() { - public void onClick(View v) { - // Establish a connection with the service. We use an explicit - // class name because we want a specific service implementation that - // we know will be running in our own process (and thus won't be - // supporting component replacement by other applications). - bindService(new Intent(Binding.this, - LocalService.class), mConnection, Context.BIND_AUTO_CREATE); - mIsBound = true; - } - }; - - private OnClickListener mUnbindListener = new OnClickListener() { - public void onClick(View v) { - if (mIsBound) { - // Detach our existing connection. - unbindService(mConnection); - mIsBound = false; - } - } - }; - } } +//END_INCLUDE(service) diff --git a/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.java b/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.java new file mode 100644 index 000000000..a6c3fd822 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.apis.app; + +import com.example.android.apis.R; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.Toast; + +public class LocalServiceActivities { + /** + *

Example of explicitly starting and stopping the local service. + * This demonstrates the implementation of a service that runs in the same + * process as the rest of the application, which is explicitly started and stopped + * as desired.

+ * + *

Note that this is implemented as an inner class only keep the sample + * all together; typically this code would appear in some separate class. + */ + public static class Controller extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.local_service_controller); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.start); + button.setOnClickListener(mStartListener); + button = (Button)findViewById(R.id.stop); + button.setOnClickListener(mStopListener); + } + + private OnClickListener mStartListener = new OnClickListener() { + public void onClick(View v) { + // Make sure the service is started. It will continue running + // until someone calls stopService(). The Intent we use to find + // the service explicitly specifies our service component, because + // we want it running in our own process and don't want other + // applications to replace it. + startService(new Intent(Controller.this, + LocalService.class)); + } + }; + + private OnClickListener mStopListener = new OnClickListener() { + public void onClick(View v) { + // Cancel a previous call to startService(). Note that the + // service will not actually stop at this point if there are + // still bound clients. + stopService(new Intent(Controller.this, + LocalService.class)); + } + }; + } + + // ---------------------------------------------------------------------- + + /** + * Example of binding and unbinding to the local service. + * This demonstrates the implementation of a service which the client will + * bind to, receiving an object through which it can communicate with the service.

+ * + *

Note that this is implemented as an inner class only keep the sample + * all together; typically this code would appear in some separate class. + */ + public static class Binding extends Activity { + private boolean mIsBound; + +// BEGIN_INCLUDE(bind) + private LocalService mBoundService; + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. Because we have bound to a explicit + // service that we know is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + mBoundService = ((LocalService.LocalBinder)service).getService(); + + // Tell the user about this for our demo. + Toast.makeText(Binding.this, R.string.local_service_connected, + Toast.LENGTH_SHORT).show(); + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + // Because it is running in our same process, we should never + // see this happen. + mBoundService = null; + Toast.makeText(Binding.this, R.string.local_service_disconnected, + Toast.LENGTH_SHORT).show(); + } + }; + + void doBindService() { + // Establish a connection with the service. We use an explicit + // class name because we want a specific service implementation that + // we know will be running in our own process (and thus won't be + // supporting component replacement by other applications). + bindService(new Intent(Binding.this, + LocalService.class), mConnection, Context.BIND_AUTO_CREATE); + mIsBound = true; + } + + void doUnbindService() { + if (mIsBound) { + // Detach our existing connection. + unbindService(mConnection); + mIsBound = false; + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + doUnbindService(); + } +// END_INCLUDE(bind) + + private OnClickListener mBindListener = new OnClickListener() { + public void onClick(View v) { + doBindService(); + } + }; + + private OnClickListener mUnbindListener = new OnClickListener() { + public void onClick(View v) { + doUnbindService(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.local_service_binding); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.bind); + button.setOnClickListener(mBindListener); + button = (Button)findViewById(R.id.unbind); + button.setOnClickListener(mUnbindListener); + } + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.java b/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.java new file mode 100644 index 000000000..a21763e3e --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.apis.app; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; +import android.widget.Toast; + +import java.util.ArrayList; + +// Need the following import to get access to the app resources, since this +// class is in a sub-package. +import com.example.android.apis.R; +import com.example.android.apis.app.RemoteService.Controller; + +/** + * This is an example of implementing an application service that uses the + * {@link Messenger} class for communicating with clients. This allows for + * remote interaction with a service, without needing to define an AIDL + * interface. + * + *

Notice the use of the {@link NotificationManager} when interesting things + * 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(). + */ +//BEGIN_INCLUDE(service) +public class MessengerService extends Service { + /** For showing and hiding our notification. */ + NotificationManager mNM; + /** Keeps track of all current registered clients. */ + ArrayList mClients = new ArrayList(); + /** Holds last value set by a client. */ + int mValue = 0; + + /** + * Command to the service to register a client, receiving callbacks + * from the service. The Message's replyTo field must be a Messenger of + * the client where callbacks should be sent. + */ + static final int MSG_REGISTER_CLIENT = 1; + + /** + * Command to the service to unregister a client, ot stop receiving callbacks + * from the service. The Message's replyTo field must be a Messenger of + * the client as previously given with MSG_REGISTER_CLIENT. + */ + static final int MSG_UNREGISTER_CLIENT = 2; + + /** + * Command to service to set a new value. This can be sent to the + * service to supply a new value, and will be sent by the service to + * any registered clients with the new value. + */ + static final int MSG_SET_VALUE = 3; + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REGISTER_CLIENT: + mClients.add(msg.replyTo); + break; + case MSG_UNREGISTER_CLIENT: + mClients.remove(msg.replyTo); + break; + case MSG_SET_VALUE: + mValue = msg.arg1; + for (int i=mClients.size()-1; i>=0; i--) { + try { + mClients.get(i).send(Message.obtain(null, + MSG_SET_VALUE, mValue, 0)); + } catch (RemoteException e) { + // The client is dead. Remove it from the list; + // we are going through the list from back to front + // so this is safe to do inside the loop. + mClients.remove(i); + } + } + break; + default: + super.handleMessage(msg); + } + } + } + + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + @Override + public void onCreate() { + mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); + + // Display a notification about us starting. + showNotification(); + } + + @Override + public void onDestroy() { + // Cancel the persistent notification. + mNM.cancel(R.string.remote_service_started); + + // Tell the user we stopped. + Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show(); + } + + /** + * When binding to the service, we return an interface to our messenger + * for sending messages to the service. + */ + @Override + public IBinder onBind(Intent intent) { + return mMessenger.getBinder(); + } + + /** + * 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.remote_service_started); + + // Set the icon, scrolling text and timestamp + Notification notification = new Notification(R.drawable.stat_sample, text, + System.currentTimeMillis()); + + // The PendingIntent to launch our activity if the user selects this notification + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, + new Intent(this, Controller.class), 0); + + // Set the info for the views that show in the notification panel. + notification.setLatestEventInfo(this, getText(R.string.remote_service_label), + text, contentIntent); + + // Send the notification. + // We use a string id because it is a unique number. We use it later to cancel. + mNM.notify(R.string.remote_service_started, notification); + } +} +//END_INCLUDE(service) diff --git a/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.java b/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.java new file mode 100644 index 000000000..8d0ac8c16 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.java @@ -0,0 +1,178 @@ +package com.example.android.apis.app; + +import com.example.android.apis.R; +import com.example.android.apis.app.LocalServiceActivities.Binding; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +public class MessengerServiceActivities { + /** + * Example of binding and unbinding to the remote service. + * This demonstrates the implementation of a service which the client will + * bind to, interacting with it through an aidl interface.

+ * + *

Note that this is implemented as an inner class only keep the sample + * all together; typically this code would appear in some separate class. + */ + public static class Binding extends Activity { +// BEGIN_INCLUDE(bind) + /** Messenger for communicating with service. */ + Messenger mService = null; + /** Flag indicating whether we have called bind on the service. */ + boolean mIsBound; + /** Some text view we are using to show state information. */ + TextView mCallbackText; + + /** + * Handler of incoming messages from service. + */ + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MessengerService.MSG_SET_VALUE: + mCallbackText.setText("Received from service: " + msg.arg1); + break; + default: + super.handleMessage(msg); + } + } + } + + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + /** + * Class for interacting with the main interface of the service. + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, + IBinder service) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. We are communicating with our + // service through an IDL interface, so get a client-side + // representation of that from the raw service object. + mService = new Messenger(service); + mCallbackText.setText("Attached."); + + // We want to monitor the service for as long as we are + // connected to it. + try { + Message msg = Message.obtain(null, + MessengerService.MSG_REGISTER_CLIENT); + msg.replyTo = mMessenger; + mService.send(msg); + + // Give it some value as an example. + msg = Message.obtain(null, + MessengerService.MSG_SET_VALUE, this.hashCode(), 0); + mService.send(msg); + } catch (RemoteException e) { + // In this case the service has crashed before we could even + // do anything with it; we can count on soon being + // disconnected (and then reconnected if it can be restarted) + // so there is no need to do anything here. + } + + // As part of the sample, tell the user what happened. + Toast.makeText(Binding.this, R.string.remote_service_connected, + Toast.LENGTH_SHORT).show(); + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + mService = null; + mCallbackText.setText("Disconnected."); + + // As part of the sample, tell the user what happened. + Toast.makeText(Binding.this, R.string.remote_service_disconnected, + Toast.LENGTH_SHORT).show(); + } + }; + + void doBindService() { + // Establish a connection with the service. We use an explicit + // class name because there is no reason to be able to let other + // applications replace our component. + bindService(new Intent(Binding.this, + MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); + mIsBound = true; + mCallbackText.setText("Binding."); + } + + void doUnbindService() { + if (mIsBound) { + // If we have received the service, and hence registered with + // it, then now is the time to unregister. + if (mService != null) { + try { + Message msg = Message.obtain(null, + MessengerService.MSG_UNREGISTER_CLIENT); + msg.replyTo = mMessenger; + mService.send(msg); + } catch (RemoteException e) { + // There is nothing special we need to do if the service + // has crashed. + } + } + + // Detach our existing connection. + unbindService(mConnection); + mIsBound = false; + mCallbackText.setText("Unbinding."); + } + } + // END_INCLUDE(bind) + + /** + * Standard initialization of this activity. Set up the UI, then wait + * for the user to poke it before doing anything. + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.messenger_service_binding); + + // Watch for button clicks. + Button button = (Button)findViewById(R.id.bind); + button.setOnClickListener(mBindListener); + button = (Button)findViewById(R.id.unbind); + button.setOnClickListener(mUnbindListener); + + mCallbackText = (TextView)findViewById(R.id.callback); + mCallbackText.setText("Not attached."); + } + + private OnClickListener mBindListener = new OnClickListener() { + public void onClick(View v) { + doBindService(); + } + }; + + private OnClickListener mUnbindListener = new OnClickListener() { + public void onClick(View v) { + doUnbindService(); + } + }; + } +}