Differences

This shows you the differences between two versions of the page.

Link to this comparison view

osp:lectures:lecture-3 [2015/10/25 09:47]
laura.gheorghe [Practical]
— (current)
Line 1: Line 1:
-===== 03 - Android Services ===== 
  
-  * Description: ​ 
-  * Practical part:  
- 
- 
-==== Lecture ==== 
- 
- 
-  *{{:​osp:​lectures:​lecture-3.pdf | Lecture Slides}} 
- 
-{{url>​http://​ocw.cs.pub.ro/​courses/​_media/​osp/​lectures/​lecture-3.pdf}} 
- 
- 
-==== Practical ==== 
- 
-=== Resources === 
- 
- 
- 
-=== Files === 
- 
-  * {{osp:​media:​lab-4.zip|}} 
- 
-=== Task 1 - Creating a started service === 
- 
-Create a new project with a blank main Activity. Add an EditText, a Button and a TextView to the Activity'​s layout. From the lab files, add the ''​PiComputer.java''​ class to your project. This class computes the number ''​PI''​ with variable precisions using the [[https://​en.wikipedia.org/​wiki/​Vi%C3%A8te%27s_formula|Viete series]]. Your first task is to create a started service that will receive a value for the precision, will create an instance of the ''​PiComputer''​ class giving it the received precision value and call the ''​compute()''​ method, retrieving the result. ​ 
- 
-Use the EditText to allow the user to input the precision and, when the user clicks the Button call ''​startService(Intent)''​ putting the precision value as an Extra in the Intent. 
- 
-Create a new class in your project that will extend the ''​Service''​ Android class. Add the new service to the ''​AndroidManifest.xml''​ file. Within the service, override the ''​onStartCommand()''​ method and, if the received intent has the precision value extra, trigger the computing mechanism. We will consider that computing the value of ''​PI''​ is a computationally intensive operation. Therefore, the ''​compute''​ method should be called from a separate thread. 
- 
-In order for the service to send the answer back to the Activity, create a ''​BroadcastReceiver''​ instance and, within it's ''​onReceive()''​ method, update the TextView within the Activity using the value in the ''​Intent''​ parameter. Then, create a new ''​IntentFilter''​ with a custom action and register the receiver with the newly created ''​IntentFilter'':​ 
- 
-<​code>​ 
-BroadcastReceiver mReceiver = new BroadcastReceiver() { 
-  @Override 
-  public void onReceive(Context context, Intent intent){ 
-    // get the value for PI from the intent 
-    // and set it as the TextView'​s text 
-  } 
-}; 
- 
-IntentFilter filter = new IntentFilter();​ 
-filter.addAction(customActionString);​ 
- 
-registerReceiver(mReceiver,​ filter); 
-</​code>​ 
- 
-Since we don't want to have the receiver registered after the Activity has been destroyed, override the ''​onDestroy()''​ method and unregister the receiver: 
-<​code>​ 
-public class MainActivity extends Activity { 
-  ... 
-  @Override 
-  public void onDestroy() { 
-    super.onDestroy();​ 
-    ​ 
-    unregisterReceiver(mReceiver);​ 
-  } 
-} 
-</​code>​ 
- 
-From the service, after the thread has finished computing the value for ''​PI''​ create a new ''​Intent'',​ put the value as an extra and call ''​sendBroadcast()'':​ 
-<​code>​ 
-Intent i = new Intent(); 
-// set the action of the intent to the one you previously used in the Activity 
-i.setAction(customActionString);​ 
-//put the computed value of PI as an extra 
-i.putExtra("​pi_value_extra",​ String.valueOf(computedPi));​ 
- 
-sendBroadcast(i);​ 
-</​code>​ 
-=== Task 2 - Running a foreground service === 
- 
-We want to have a new service notifying the user that our application is online, providing with a always-present notification which can be used to access the app at any time. 
- 
-For this, create a new class that also extends ''​Service''​ and add it to the ''​AndroidManifest.xml''​ accordingly. Override the ''​onStartCommand()''​ method of the newly created service. If the intent action is "​start_foreground"​ then show the notification and if the intent action is "​stop_foreground"​ hide the notification. 
- 
-The first step in creating a ''​Notification''​ is to construct an Intent which will be used to direct the user to the main Activity: 
-<​code>​ 
-Intent i = new Intent(this,​ MainActivity.class);​ 
-</​code>​ 
-Next, you need to create a ''​PendingIntent''​ using the ''​PendingIntent.getActivity()''​ method to which you will provide the previously created ''​Intent'':​ 
-<​code>​ 
-PendingIntent pending = PendingIntent.getActivity(this,​ 0, i, 0); 
-</​code>​ 
- 
-Finally, you can construct the ''​Notification''​ instance using the ''​Notification.Builder''​ factory: 
-<​code>​ 
-Notification.Builder notifBuilder = new Notification.Builder(this);​ 
-</​code>​ 
-Set the icon and the content text on the notification appropriately (the icon should usually be your app's icon and the text can be any String that you find is informative of the running state of the app). Afterwards, set the content intent on the notification so that, when the user clicks it, the pending intent you created earlier is called: 
-<​code>​ 
-notifBuilder.setContentIntent(pending);​ 
-</​code>​ 
- 
-Having set all required elements for the notification you can call the builder'​s ''​build()''​ method and get the notification:​ 
-<​code>​ 
-Notification notification = notifBuilder.build();​ 
-</​code>​ 
- 
-Now, depending on the action of the intent sent as a parameter to the ''​onStartCommand()''​ method call ''​startForeground()''​ or ''​stopForeground'':​ 
- 
-In the main Activity, add two more buttons, one used to show the notification and the other one to hide it. On each Button click create an intent, set the appropriate action and call ''​startService(intent)''​. 
- 
- 
-=== Task 3 - Moving from started service to bounded service === 
- 
-We now want to compute the value of ''​PI''​ using a bounded service instead of a started one. Therefore, create a third service and add it to the ''​AndroidManifest.xml''​. Within this service'​s class declare an inner class that extends ''​Binder''​. Add a method to the class that returns the current instance of the service class: 
-<​code>​ 
-public class BoundService extends Service { 
-  ... 
-  public class MyBinder extends Binder { 
-    BoundService getService() { 
-      return BoundService.this;​ 
-    } 
-  } 
-} 
-</​code>​ 
- 
-Since the ''​Binder''​ class returns the service instance, create a method for computing ''​PI''​ within the service: 
-<​code>​ 
-public void computePi(int precision, final Handler callback) { 
-  ... 
-} 
-</​code>​ 
-Within this method compute the value of ''​PI''​ on a separate thread using the given precision. When the thread has finished and you have obtained the computed value, use the ''​Handler''​ object to obtain a ''​Message'',​ create a new ''​Bundle''​ object, add the value to it and send the ''​Message'':​ 
-<​code>​ 
-// after the value of PI has been computed 
-Message msg = callback.obtainMessage();​ 
- 
-Bundle args = new Bundle(); 
-args.putDouble("​pi_value",​ value); 
-msg.setData(args);​ 
- 
-msg.sendToTarget();​ 
-</​code>​ 
- 
-Add a new service member variable to the Activity class and also a ''​ServiceConnection''​ member variable, which you must also implement: 
-<​code>​ 
-public class MainActivity extends Activity { 
-  private BoundService mService; 
-  ... 
-  ServiceConnection mConnection = new ServiceConnection() { 
-    @Override 
-    public void onServiceConnected(ComponentName component, IBinder service) { 
-      MyBinder mBinder = (MyBinder)service;​ 
-      mService = mBinder.getService();​ 
-    } 
-    ... 
-  }; 
-} 
-</​code>​ 
- 
-In the Activity'​s ''​onCreate()''​ method you need to bind to the service using the ''​ServiceConnection''​ member variable: 
-<​code>​ 
-Intent i = new Intent(this,​ BoundService.class);​ 
-bindService(i,​ mConnection,​ Context.BIND_AUTO_CREATE);​ 
-</​code>​ 
- 
-Since the service'​s compute method requires a ''​Handler''​ in order to send the result of the computation,​ create the Handler instance as well: 
-<​code>​ 
-Handler mHandler = new Handler() { 
-  @Override 
-  public void handleMessage(Message msg) { 
-    Bundle data = msg.getData();​ 
-    // if the data has the "​pi_value"​ key, retrieve it 
-    // and set it as the text of the TextView element 
-  } 
-} 
-</​code>​ 
- 
-Add a ''​Button''​ to the Activity, which on click will check if the service member variable is not null and call the ''​BoundService.computePi()''​ method: 
-<​code>​ 
-if (mService != null) { 
-  mService.computePi(precision,​ mHandler); 
-} 
-</​code>​ 
- 
-Since we won't need the service after the Activity has been destroyed, call ''​unbindService()''​ in the ''​onDestroy()''​ method: 
-<​code>​ 
-@Override 
-public void onDestroy() { 
-  ... 
-  unbindService(mConnection);​ 
-} 
-</​code>​ 
-=== Bonus: Task 4 - Connecting to a bounded service from another application === 
- 
-We now want to make a fourth service that will expose the ''​PiComputer''​ mechanism to external applications. Create a ''​IExtService.aidl''​ file in the ''​src/''​ folder of your application,​ within the same package as all other services. Within the ''​.aidl''​ file we will declare an interface which will be used by the system to allow external applications access to our internal service'​s functionality:​ 
-<​code>​ 
-package ndk.lab4.picomputer;​ // this is just an example, use the package name you have within your app 
- 
-interface IExtService { 
-  double computePi(int precision); 
-} 
-</​code>​ 
-Now, create a fourth service(you can call it ''​ExtService''​ for consistency) in your app and add it to the ''​AndroidManifest.xml''​. When declaring the service add an ''​android:​exported''​ field in the XML element. Also, add an ''<​intent-filter>''​ child element: 
-<​code>​ 
-<service 
-    android:​name="​.ExtService"​ 
-    android:​exported="​true">​ 
-    <​intent-filter>​ 
-        <action android:​name="​ndk.lab4.picompute"/>​ 
-    </​intent-filter>​ 
-</​service>​ 
-</​code>​ 
-Within the service implement the ''​IExtService.Stub''​ interface as a member variable and return the instance from the service'​s ''​onBind()''​ method. 
- 
-Create a new Android application that will act as a client for the service exposed by the first application. In this new application'​s main Activity layout put an EditText, a Button and a TextView. The EditText will be used to allow the user to input the desired precision, the TextView will display the result and the Button'​s click will trigger the call to the remote service. 
- 
-Copy the ''​.aidl''​ file from the first application to the new one's ''​src/''​ folder. Do not change the package name in the ''​.aidl''​ file, but instead create a package name in the second app that corresponds to the one in the app providing the service. 
- 
-In the MainActivity retain a member variable to the ''​IExtService''​ and create a local ''​ServiceConnection''​. When implementing the ''​ServiceConnection.onServiceConnected()''​ method, get a reference to the remote ''​IExtService''​ using the ''​IExtService.Stub.asInterface(IBinder)''​ construct. 
- 
-To connect to the remote service create an ''​Intent''​ variable, set the package to be the one of your original app and the action to be the one declared in that app's ''​AndroidManifest.xml'':​ 
-<​code>​ 
-Intent i = new Intent(); 
-i.setPackage(origAppPackageName);​ 
-i.setAction(remoteServiceAction);​ 
-</​code>​ 
- 
-Afterwards, you can call ''​bindService()''​ using the intent and the previously declared ''​ServiceConnection''​ instance. Now, when you want to compute the value of ''​PI''​ in the second application,​ call the ''​mRemoteService.compute()''​ method. 
osp/lectures/lecture-3.1445759274.txt.gz ยท Last modified: 2015/10/25 09:47 by laura.gheorghe
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0