a simple android service
This is a small service example in Android. A Service is a running thread which is detached from the physical view of the application. It is of a great help to keep track of feeds or webservices while the application is not present and even if it was. My objective is to show a service works, and how one is able to start it and stop it at any time, and how it can be assigned by a device event such as AIRPLANE mode to turn it on and off as well. Aside from running the service, I am hooking up a thread which runs a simple loop. At times the loop dies before the service stops and it was a good thing to show what to do in case you want to start the thread to execute again.
So here is a simple description, and I will also address basic knowledge for others who are new to Android.
We work with a configuration file, which has set permissions on the device.
Here is the full code, just unzip it and you can import it to your workspace.
We work with a configuration file, which has set permissions on the device.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <!--?xml version="1.0" encoding="utf-8"?--> <!-- all it means is there were other activities, this is the initial view --> <!-- we notify the device, this is our class which will be used as a service --> <!-- We set our Broadcaster class to handle intents coming from the device such as being booted or having the device gone over airplane mode on or off --> <!-- We want the user to know we want the application to access the internet, and also get notification when the phone has booted. --> |
Services deal with intents. Intents are ways for us to get notifications by listeners. Think if you can in ActionScript of an EventDispatcher and object’s reading when an event is dispatched. Well, the device can send these notifications through intents which we set our application to listen to. Here we set our Broadcaster to get intents notifying when the device has been booted or the user has switched from airplane mode on and off.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | package com.flexnroses.android.services; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class Broadcaster extends BroadcastReceiver { public Broadcaster() { } //getting the intents from device booting or airplane mode (on/off) //such cases we deal with MyService to start it or turn it off. @Override public void onReceive(Context context, Intent intent ) { Log.d( "Broadcaster", "got onReceived " + intent.getAction() ); //if airplane mode.. if( intent.getAction().equals( Intent.ACTION_AIRPLANE_MODE_CHANGED ) ) { if( intent.getBooleanExtra("state", false ) == true ) { context.stopService( new Intent( context, MyService.class ) ); } else { context.startService( new Intent( context, MyService.class ) ); } } else if( intent.getAction().equals( Intent.ACTION_BOOT_COMPLETED ) ) { context.startService( new Intent( context, MyService.class ) ); } } } |
Broadcaster now will start and stop our service. Later on, our main activity “MyActivity” will do the same with its buttons as well.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | package com.flexnroses.android.services; import android.app.Service; import android.content.Intent; import android.os.AsyncTask.Status; import android.os.IBinder; import android.util.Log; /** * * @author juan.mendez @ flexnroses.com * * Our service class is going to set aside another thread (bgThread) which would * run a loop, and will be also knocking back this class and notifying * of current updates. We just wanted to run it as another thread instead * of having it running in the same thread as the one where this service is running. * * I wanted to also show here how the service is able to send broadcasts of * the udpates coming from bgThread. */ public class MyService extends Service { public static final String TAG = "MyService"; private BgThread tweet; private boolean _running; //this property is access by the thread when it runs, just double checking //the service is no longer running, so it won't either. @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); Log.d(TAG, "onCreate" ); this._running = true; tweet = new BgThread( this ); } @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); Log.d(TAG, "onDestroy" ); //3 making sure Thread is void this._running = false; tweet.cancel(true ); } @Override public void onLowMemory() { Log.d(TAG, "onLowMemory" ); // TODO Auto-generated method stub super.onLowMemory(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand " + intent.toString() + " " + Integer.toString(startId) + " " + tweet.getStatus() + " canceled?:" + tweet.isCancelled() ); //get the state of our intent Status status = tweet.getStatus(); //is asynctask running? then don't attempt to execute twice.. if( status.equals(Status.PENDING) ) { tweet.execute( 0, 10 ); } else if( status.equals(Status.FINISHED) ) { //AsyncTask is BgThread's super class.. therefore this should only be done once. //if you want to reuse it, follow this http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html if( tweet.isCancelled() == false ) tweet.cancel(true); tweet = new BgThread( this ); tweet.execute( 0, 10 ); } //find more at http://developer.android.com/reference/android/app/Service.html#onStartCommand(android.content.Intent, int, int) return Service.START_STICKY; } //find more at http://developer.android.com/reference/android/app/Service.html#onBind(android.content.Intent) @Override public IBinder onBind(Intent arg0) { return null; } //as our bgThread is knocking back, we are here //notifying any broadcast receivers of what's happening. //this idea reminded me of EventDispatcher, and even handler from ActionScript. public void setProgress( Integer progress ) { Intent intent = new Intent( MyService.TAG ); intent.putExtra( "percent", progress ); this.sendBroadcast(intent); } //is the service running or not. if not running, its bgThread shouldn't either public boolean is_running() { return _running; } } |
Service sets aside another thread ( using AsyncTask ) in such way it will iterate an array and calculate progress which is placed back in the service. The service broadcasts the progress wrapped in an intent.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | package com.flexnroses.android.services; import java.util.ArrayList; import android.os.AsyncTask; import android.util.Log; public class BgThread extends AsyncTask <Integer, Integer, ArrayList > { private MyService service; public BgThread( MyService service ) { this.service = service; } /** * running function,, we are looping over and pausing for 600 miliseconds. * don't go so much into the details of this operation.. */ @Override protected ArrayList doInBackground(Integer... list) { int min = list[0]; int max = list[1]; ArrayList col = new ArrayList(); for( int i = min; i < max; i++ ) { try { if( this.isCancelled() == false || this.service.is_running() == true ) { Thread.sleep( 600 ); } else { break; } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } col.add( i ); this.publishProgress( i, min, max ); } this.cancel( true ); return col; } @Override protected void onCancelled() { Log.e("BgThread", "CANCELED" ); // TODO Auto-generated method stub super.onCancelled(); } @Override protected void onPostExecute(ArrayList result) { // TODO Auto-generated method stub super.onPostExecute(result); } @Override protected void onPreExecute() { // TODO Auto-generated method stub super.onPreExecute(); } @Override protected void onProgressUpdate(Integer... values) { int current = values[0]; int min = values[1]; int max = values[2]; int percent = (int) ( (current-min) * 100 / ( max - min ) ); // TODO Auto-generated method stub super.onProgressUpdate(percent); //in my case i know who the service is and i am passing the percent.. this.service.setProgress( percent ); Log.d("BgThread", Integer.toString( percent ) ); } } |
Here is our activity, which is similar in Flex to spark.components.View, we have two buttons, one starts and one stops the service. This activity is tuned to the service’s broadcast intents, and is getting that progress and placing it in its textview.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | package com.flexnroses.android.services; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; /** * * @author juan.mendez @ flexnroses.com * * Our activity is set to also broadcast notifications coming from the service. * We also have the ability to use btnStart and btnStop to start or stop the service. */ public class MyActivity extends Activity implements OnClickListener { private Button btnStart; //button used for starting the service private Button btnStop; //button used to stop the service private TextView txtView; //textfield used to show the notifications from service private ServiceReceiver receiver; //helps us to get access to service notifications. /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //get reference of these gui's in the layout btnStart = (Button) this.findViewById(R.id.btnStartService); btnStop = (Button) this.findViewById(R.id.btnStopService ); txtView = (TextView) this.findViewById( R.id.textView ); btnStart.setOnClickListener(this); btnStop.setOnClickListener(this); this.receiver = new ServiceReceiver(); } @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); //we don't want to get updates while we pause for reasons such as //leaving the screen. this.unregisterReceiver( this.receiver ); } @Override protected void onResume() { super.onResume(); //we are back in the screen.. let's keep up tuned to the service IntentFilter filter = new IntentFilter( MyService.TAG ); this.registerReceiver(this.receiver, filter); } @Override public void onClick(View view ) { Button button = (Button) view; //we request to start/stop the service if( button == this.btnStart ) { this.startService( new Intent(this, MyService.class ) ); } else if( button == this.btnStop ) { this.stopService( new Intent(this, MyService.class ) ); } } /** * this class is assigned to get the service getting back the feedback. * we use super.this to get access of MyActivity. * @author juan.mendez @ flexnroses.com * */ private class ServiceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { MyActivity.this.txtView.setText( "bgThread's progress: " + intent.getExtras().getInt("percent", 0) + "%" ); } } } |
I hope this tutorial was helpful and clear. Thank you.
( i have created a simple video, the phone is starting, and you can see the Broadcater doing its work and then service begins and so its thread.. )
Wow, exactly what i was looking for – had nearly lost hope, had tried everything i could think of…
Thanks
i am glad you found it useful