Infinite loop or idle code in IntentService class of Android - android

I'm asking this question because my Java knowledge is really low... I need
need to use this new API 27 USSD feature... Below if What I'm trying to do :
public class MyService extends IntentService {
// BEGIN of MyService Class properties ****
public static boolean jobInProgress = true;
private Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg); // I guess this will be on some message queue somewhere
}
};
TelephonyManager tm;
// END of properties *************************************
// BEGIN of MyService class abstract class methods implementation
class MyCallback extends TelephonyManager.UssdResponseCallback{
Context serviceContext;
MyCallback (Context serviceContext){
this.serviceContext = serviceContext;
}
public void onReceiveUssdResponse (TelephonyManager telephonyManager,
String request,
CharSequence response){
//Here since it's a System callback I guess my this.tm == telephonyManager parameter right ?
Toast.makeText(serviceContext, "Response from network is : " + response, Toast.LENGTH_LONG).show();
MyService.jobInProgress = false;
}
public void onReceiveUssdResponseFailed (TelephonyManager telephonyManager,
String request,
int failureCode){
Toast.makeText(serviceContext, "USSD request failed with code " + failureCode, Toast.LENGTH_LONG).show();
MyService.jobInProgress = false;
}
}
// END of abstract methods implementation******************
//BEGIN of MyService Class methods
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "Service is created.", Toast.LENGTH_LONG).show();
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service is destroyed", Toast.LENGTH_LONG).show();
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
doJob();
while(jobInProgress){
//I hang here to not call onDestroy to quickly...
}
}
private void doJob(){
//Get the instance of TelephonyManager
this.tm =(TelephonyManager)getSystemService(this.TELEPHONY_SERVICE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
//Don't know How to use sendUssdRequest second and thrid arguments. Below is what I have tried with no success
this.tm.sendUssdRequest("#105*2#",new MyCallback(this),myHandler);
}
//END of class methods*****************************
}
The golad I'm trying to achieve is to runn the USSD request and print the result in a Toast. When I launch the service, it says service created as expected, it goes into the doJob() method as expected, but after that, nothing else happens... The app does not even crash... Just as if after enterring doJob() no instructions was written...
Can you help me make this code work ?

The reason its not working is beacuse you are using Handler and infinite loop together.
When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it. So when you do following:
while(jobInProgress){
//I hang here to not call onDestroy to quickly...
}
It will block the worker Thread and also the Handler. As a result nothing happens.
The solution would be to use normal Service and avoid any looping.
Remember from Android O you cannot endlessly run your service in background. This approach will work if your app is in Foreground. Use foreground Service if you want to make it work reliably.

Here is how I solved it following Sagar's answer :
Instead of :
#Override
protected void onHandleIntent(#Nullable Intent intent) {
doJob();
while(jobInProgress){
//I hang here to not call onDestroy to quickly...
}
}
I did this :
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
doJob();
}
});
t.start();
while(jobInProgress){
//I hang here to not call onDestroy to quickly...
}
}
and my Handler changed to this :
private Handler myHandler = new Handler(Looper.getMainLooper())
public void handleMessage(Message msg) {
super.handleMessage(msg); // I guess this will be on some message queue somewhere
}
};

Related

Firebase: onDisconnect() in a service

I am making a project in which i have to
configure realtime internet connection status
of one client app even if app is in background.
I Have to display it on
ServerSide that weather specific device is connected to internet or
not
I am using Firebase to perform this scenerio but it's not working.
ConnectionService
public class ConnectionService extends Service implements ConnectivityReciever.ConnectivityRecieverListner {
public static final int notify = 5000;
private Handler mHandler=new Handler();
private Timer mTimer = null;
FirebaseDatabase db;
DatabaseReference dbRef;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
dbRef=FirebaseDatabase.getInstance().getReference(Common.DEVICE_NAME).child("online");
MyApplication.getInstance().setConnectivtyListner(ConnectionService.this);
boolean isConnected=ConnectivityReciever.isConnected();
checkConnection(isConnected);
if(mTimer!=null){
mTimer.cancel();
}else{
mTimer=new Timer();
mTimer.scheduleAtFixedRate(new TimeDisplay(),0,notify);
}
}
private void checkConnection(boolean isConnected) {
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(ConnectionService.this, "Service is destroyed", Toast.LENGTH_SHORT).show();
}
#Override
public void onNetworkConnectionChanged(boolean isConnected) {
if(isConnected){
dbRef.onDisconnect().setValue("true");
Toast.makeText(ConnectionService.this, "online", Toast.LENGTH_SHORT).show();
}
if(!isConnected){
dbRef.onDisconnect().setValue(ServerValue.TIMESTAMP);
Toast.makeText(ConnectionService.this, "offline", Toast.LENGTH_SHORT).show();
}
}
//class TimeDisplay for handling task
class TimeDisplay extends TimerTask {
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable() {
#Override
public void run() {
MyApplication.getInstance().setConnectivtyListner(ConnectionService.this);
boolean isConnected=ConnectivityReciever.isConnected();
checkConnection(isConnected);
// display toast
Toast.makeText(ConnectionService.this, "Service is running", Toast.LENGTH_SHORT).show();
}
});
}
private void checkConnection(boolean isConnected) {
if(isConnected){
dbRef.setValue("true");
Toast.makeText(ConnectionService.this, "online", Toast.LENGTH_SHORT).show();
}
if(!isConnected){
dbRef.child("online").onDisconnect().setValue(ServerValue.TIMESTAMP);
Toast.makeText(ConnectionService.this, "offline", Toast.LENGTH_SHORT).show();
}
}
}
}
Applicaiton
public class MyApplication extends Application {
public static MyApplication mInstance;
DatabaseReference dbRef;
#Override
public void onCreate() {
super.onCreate();
mInstance=this;
Common.DEVICE_NAME = android.os.Build.MODEL;
FirebaseApp.initializeApp(this);
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
public static synchronized MyApplication getInstance(){
return mInstance;
}
public void setConnectivtyListner(ConnectivityReciever.ConnectivityRecieverListner listner){
ConnectivityReciever.connectivityReceiverListener = listner;
}
}
I have tried all of methods but nothing worked.
Help will be arreciated.
Thanks
if you have registered your service with menifest.xml & your returning start_sticky, it should work but there's an alternative way also, you can write an api to ping the server(call it in a service after a specific interval), and save the time stamp with TRUE(if isConnected), so now the timestamp is saved along with boolean value true, let's suppose now the connection is OFF or cell is dead, since no updation will be sent to server, right?
now it comes the magic of reciver/admin app(where you want to show this online status graphically), now write another API that will fetch that saved response with timestamp & also get the current time when this fetching API will be triggered, find the difference b/w these two time stamps & impose a condition here `
(here difference is in seconds cuz we are using timestamps)
if(timeDifference>60)`
{
echo "true";
}
else
echo "false";
in this way you can get real time online/offline status
on reciver app you ca set
if(response.equals("true"))
{
textView.setText("ONLINE")
}
else
{
textView.setText("OFFLINE")
}
further you can read this post answer too
Save Response to Server When Network Connection goes OFF
Have you registered your Service within your Manifest.xml?
And for your service to be "sticky" you should override your onStartCommand to return START_STICKY

Android sending messages between fragment and service

I have a fragment with a button. When clicked it tells a service to start polling sensors and then insert the sensor data into a database on a background thread. When the button is pushed again, the service will stop. When the Stop button is pushed, there may still be tasks in the executor queue that is inserting into the DB, so during this time I want to display a progress dialog, and dismiss it once the entire queue is clear. The fragment with the button looks like this:
public class StartFragment extends Fragment implements View.OnClickListener {
Button startButton;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_start, container, false);
startButton = (Button) view.findViewById(R.id.startButton);
startButton.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
if (recording has not yet started){
mainActivity.startService(new Intent(mainActivity, SensorService.class));
} else {
//I want to display a progress dialog here when the service is told to stop
//Once all executor task queue is clear, I want to dismiss this dialog
mainActivity.stopService(new Intent(mainActivity, SensorService.class));
}
}
}
When the button is clicked the first time, the following service will start:
public class SensorService extends Service implements SensorEventListener {
public static final int SCREEN_OFF_RECEIVER_DELAY = 100;
private SensorManager sensorManager = null;
private WakeLock wakeLock = null;
ExecutorService executor;
Runnable insertHandler;
private void registerListener() {
//register 4 sensor listeners (acceleration, gyro, magnetic, gravity)
}
private void unregisterListener() {
sensorManager.unregisterListener(this);
}
public BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive("+intent+")");
if (!intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
return;
}
Runnable runnable = new Runnable() {
public void run() {
Log.i(TAG, "Runnable executing...");
unregisterListener();
registerListener();
}
};
new Handler().postDelayed(runnable, SCREEN_OFF_RECEIVER_DELAY);
}
};
public void onSensorChanged(SensorEvent event) {
//get sensor values and store into 4 different arrays here
//insert into database in background thread
executor.execute(insertHandler);
}
#Override
public void onCreate() {
super.onCreate();
//get sensor manager and sensors here
PowerManager manager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
//Executor service and runnable for DB inserts
executor = Executors.newSingleThreadExecutor();
insertHandler = new InsertHandler();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startForeground(Process.myPid(), new Notification());
registerListener();
wakeLock.acquire();
return START_STICKY;
}
#Override
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
if (executor.isTerminated()){
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
//Once the queue is clear, I want to send a message back to the fragment to dismiss the progress dialog here
}
}
class InsertHandler implements Runnable {
public void run() {
//get sensor values from 4 arrays, and insert into db here
}
}
So I want to display the dialog on the 2nd button press. Then once it is pressed again, service will stop, and I want to wait until the queue is clear and then send a dismiss event back to the fragment to dismiss the progress dialog.
Showing the dialog is easy. I can just add progress dialog code in the onClick method of the fragment, before stopService is called
I'm having difficulty with figuring out how to send a message back in onDestroy of the SensorService to dismiss that dialog
Whats the best way of doing this without resorting to external libraries?
Is there some way that the BroadcastReceiver I'm using in SensorService can be used? Or maybe it's better to create a new Handler in the fragment and somehow pass it through to the service so it can send a message back to the fragment?
EDIT:
I have tried the following based on one of the answers below:
Added a MessageHandler class to my fragment class:
public static class MessageHandler extends Handler {
#Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case 0:
stopDialog.dismiss();
break;
case 1:
stopDialog = new ProgressDialog(mainActivity);
stopDialog.setMessage("Stopping...");
stopDialog.setTitle("Saving data");
stopDialog.setProgressNumberFormat(null);
stopDialog.setCancelable(false);
stopDialog.setMax(100);
stopDialog.show();
break;
}
}
}
Created a new instance of MessageHandler in my fragment (tried placing this in a variety of places...same results):
public static Handler messageHandler = new MessageHandler();
The service is then started from my fragment using:
Intent startService = new Intent(mainActivity, SensorService.class);
startService.putExtra("MESSENGER", new Messenger(messageHandler));
getContext().startService(startService);
In my SensorService BroadcastReceiver I create the messageHandler:
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
Then I show the dialog at the very beginning of SensorService onDestroy:
sendMessage("SHOW");
and dismiss it at the very end of that same method:
sendMessage("HIDE");
My sendMessage method looks like this:
public void sendMessage(String state) {
Message message = Message.obtain();
switch (state) {
case "SHOW":
message.arg1 = 1;
break;
case "HIDE" :
message.arg1 = 0;
break;
}
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
So I can start the Service OK, but when I press it again to stop, I get this:
java.lang.RuntimeException: Unable to stop service com.example.app.SensorService#21124f0: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.os.Messenger.send(android.os.Message)' on a null object reference
and its referring to Line 105 of SensorService where I have messageHandler.send(message)
Thoughts on what might be wrong?
In activity:
protected BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, final Intent intent) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(intent.hasExtra("someExtraMessage")){
doSomething(intent.getStringExtra("someExtraMessage"));
}
}
});
}
};
#Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("message-id"));
}
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
public void doSomething(){
//...
}
Then somewhere from service:
Context context = BamBamApplication.getApplicationContext(); // Can be application or activity context.
// BamBamApplicaiton extends Application ;)
Intent intent = new Intent("message-id");
intent.putExtra("someExtraMessage", "Some Message :)");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
Actually you are doing wrong from the very beginning :) all the services are running on main thread, so here you must better start all hard processing to async task to move this in background otherwise you will stuck your app, or you will get sudden unexpected crashes.
Here are you sample of async task that parses json api response in background with Typed result by parameter.
class ParseJsonInBackground<T> extends AsyncTask<String, Void, ApiResponseModel<T>> {
private ProcessResponse<T> func;
private Type inClass;
public ParseJsonInBackground(ProcessResponse<T> f, Type inClass){
this.func = f;
this.inClass = inClass;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected ApiResponseModel<T> doInBackground(String... json) {
Gson gson = new Gson();
try {
ApiResponseModel<T> result = (ApiResponseModel<T>) gson.fromJson(json[0], inClass);
return result;
}catch(Exception e){
ApiResponseModel<T> result = new ApiResponseModel<T>();
result.data = null;
result.success = false;
result.error = new ArrayList<>();
result.error.add(new ErrorModel(0, "Parsing error", "Parsing error"));
return result;
}
}
#Override
protected void onPostExecute(ApiResponseModel<T> result) {
Utils.hideLoadingProgress(mContext);
if(result != null && func != null){
if(result.success){
func.onSuccess(result);
}else{
func.onError(result);
}
}
}
}
and sample how to call:
new ParseJsonInBackground<T>(responseFunc, inClass).execute(json.toString());
make attention! - don't use any views in processing coz this will stuck main thread, make database processing in similar async task, don't write to often to database make recording with transactions.
I would propose doing this via Handler messages: you send a message from the Service to your Activity which has to register as a callback handler (implement http://developer.android.com/reference/android/os/Handler.Callback.html). Use a custom message code (message.what) and listen for it. Keep in mind to send this to the main looper of your application (from the service).
You may also check this comment which illustrates this kind of interaction with some more code: https://stackoverflow.com/a/20595215/4310905
It turns out that the code in the Edit of my original question works, but I have to shuffle around some of my code:
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
The above needs to be moved to onStartCommand of SensorService instead of being in the BroadcastReceiver

Service Process - Ram Memory increases with time. How to fix it?

I am making an Android app which runs a simple Service in the background.
Nothing fancy but the service toasts a msg every 5 secs confirming that it is running in the background, even when the App activity is terminated.
But when i checked the task manager, i found that the process is utilizing 4MB of ram initially but later keeps on increasing with time.
I want to know that if there is any way i can stop the extra memory usage and keep it to a bare minimum, since i know i am not doing any heavy work in the background.
Any help will be appreciated!
Thanks.
P.S. I will post the service code below.
public class BgmService extends Service {
public Handler mHandler = new Handler();
public BgmService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "Service has started!!!", Toast.LENGTH_SHORT).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mHandler.post(mtask);
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service was Killed!!!", Toast.LENGTH_SHORT).show();
mHandler.removeCallbacks(mtask);
}
public Runnable mtask = new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Service is Running!!!", Toast.LENGTH_SHORT).show();
mHandler.postDelayed(mtask, 4000);
}
};
}
As #PunK_l_RuLz told, your Runnable is getting created after every 4000 mili seconds. So, you can create a subclass of Runnable and use single object of this class for every Toast :
public class PostRunnable extends Runnable {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Service is Running!!!", Toast.LENGTH_SHORT).show();
mHandler.postDelayed(mRunnable, 4000);
}
}
Use above class like :
PostRunnable mRunnable;
if(mRunnable != null) {
mHandler.post(mRunnable);
} else {
mRunnable = new PostRunnable();
mHandler.post(mRunnable);
}
you are calling the mTask from inside your mTask, every time a new object of Runnable is created and as your Service holds the reference of every runnable object created your memory goes on increasing. I think using a Timer with TimerTask might solve your problem. Hope this helps
Have you tried using a timer instead of a post delay?
It's odd though, I can't see why it would leak.

Launching a new Activity from a Handler

I have a client/server application that needs to be able to launch different Activities. I have a working TCP thread that runs continuously in the background and a working handler in my MainAcitivty which the TCP thread uses to send messages. The problem is getting that handler to launch anything other than strings. My TCP thread creates an object of the MainActivity on start up so it can access my handler which it must do since my handler is not static. Everything works fine if I run it from a button on my MainActivity but I get nullpointexceptions on everything when launched from my handler. I believe it dislikes my Context but I can’t find a work around. Thanks
Handler TCP_handler = new Handler()
{
#Override
public void handleMessage(Message msg) {
Message.obtain();
Bundle bundle = msg.getData();
switch( msg.what ){
case 1:
// this stuff works
String aResponse1 = bundle.getString("messageStringL1");
String aResponse2 = bundle.getString("messageStringL2");
if(aResponse1 != null)
textViewLineOne.setText(aResponse1);
if(aResponse2 != null)
textViewLineTwo.setText(aResponse2);
break;
case 2:
// Method 1
// nullpointer exception error
Intent i = new Intent(MainActivity.this, IdleScreen.class);
startActivity(i);
// Method 2
// nullpointer exception error
Toast.makeText(MainContextSaved, "This is Toast!!!", Toast.LENGTH_SHORT).show();
// Method 3
// this launches but can only write to the MainActivty textview
runOnUiThread(IdleScreenUI);
break;
}
}
};
private Runnable IdleScreenUI = new Runnable() {
#Override
public void run() {
// this is the new screen I want to display
setContentView(R.layout.idlescreen ); // nullpointer exception error
// this is a textview in the MainActivity and it works
// textViewLineOne.setText("hello");
// null pointer exception error
Toast.makeText(MainContextSaved, "This is Toast!!!", Toast.LENGTH_SHORT).show();
}
};
My TCP thread creates an object of the MainActivity on start up.
Even if you create the object of the activity , that is not a real activity context. thats why your unable to start the other activity.
If I understood your problem correctly, when you try to start the other activity from handler, the MainActivity is in foreground(in stack).
Assuming that you have launched the MainActivity and your TCP operations are done in background.
If your background TCP operations are done from a service,then when the MainActivity is started you can bind to the service and share the activity context to the service.
So now with the MainActivity context you can send Message to the handler.
Here is a sample I created..
CustomService.java
public class CustomService extends Service {
private final IBinder mIBinder = new LocalBinder();
// temporary handler
private Handler mHandler = new Handler();
// context to hold MainActivity handler
private Context mActivityContext = null;
#Override
public int onStartCommand(Intent intent, int flag, int startId) {
// for testing Iam sending an empty message to the handler after 10 seconds
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (mActivityContext != null) {
((MainActivity) mActivityContext).TCP_handler.sendEmptyMessage(2);
}
}
}, 10000);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mIBinder;
}
public void setActivityContext(Activity activityContext) {
mActivityContext = activityContext;
}
public class LocalBinder extends Binder {
public CustomService getInstance() {
return CustomService.this;
}
}
}
Now , you can start the service from activity and bind a service connection.
MainActivity.java
public class MainActivity extends ActionBarActivity {
CustomService customService = null;
TextView textViewLineOne;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// start the service, even if already running no problem.
startService(new Intent(this, CustomService.class));
// bind to the service.
bindService(new Intent(this,
CustomService.class), mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
customService = ((CustomService.LocalBinder) iBinder).getInstance();
// pass the activity context to the service
customService.setActivityContext(MainActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
customService = null;
}
};
#Override
protected void onDestroy() {
super.onDestroy();
if (customService != null) {
// Detach the service connection.
unbindService(mConnection);
}
}
// Add your handler code stuff here..
}
Handler class doesn't have a startActivity() method, does it!
You can use a static context and store the value of the activity in it in onCreate() then call context.startActivity()
Intent mIntent = new Intent(context,YourActivity.class);
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(mIntent);

Stopping Service results in orphaned thread

I am continuing to study from the book "Pro Android 2," working through the Service example that consists of two classes: BackgroundService.java and MainActivity.java. The MainActivity class is shown below and has a couple buttons. The unbind button, unbindBtn, stops the Service but doesn't appear to do much else like kill the thread the Service started.
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.d(TAG, "starting service");
Button bindBtn = (Button)findViewById(R.id.bindBtn);
bindBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Intent backgroundService = new Intent(MainActivity.this, com.marie.mainactivity.BackgroundService.class);
startService(backgroundService);
}
});
Button unbindBtn = (Button)findViewById(R.id.unbindBtn);
unbindBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
stopService(new Intent(MainActivity.this, BackgroundService.class));
}
});
}
}
The documentation says "if your service is going to do any CPU intensive work or blocking operations..., you should create a new thread within the service to do that work." And that's exactly what the BackgroundService class does below. As you can see below I've added a while(true) loop in the thread's run() method to see what happens to the thread when I stop the Service.
public class BackgroundService extends Service {
private NotificationManager notificationMgr;
#Override
public void onCreate() {
super.onCreate();
notificationMgr = NotificationManager)getSystemService(NOTIFICATION_SERVICE);
displayNotificationMessage("starting Background Service");
Thread thr = new Thread(null, new ServiceWorker(), "BackgroundService");
thr.start();
}
class ServiceWorker implements Runnable
{
public void run() {
// do background processing here...
long count = 0;
while (true) {
if (count++ > 1000000)
{
count = 0;
Log.d("ServiceWorker", "count reached");
}
}
//stop the service when done...
//BackgroundService.this.stopSelf();
}
}
#Override
public void onDestroy()
{
displayNotificationMessage("stopping Background Service");
super.onDestroy();
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void displayNotificationMessage(String message)
{
Notification notification = new Notification(R.drawable.note, message, System.currentTimeMillis());
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
notification.setLatestEventInfo(this, "Background Service", message, contentIntent);
notificationMgr.notify(R.id.app_notification_id, notification);
}
}
When I press the unbind button, unbindBtn, in the MainActivity class I trust the Service in this example will be stopped. But from what I can see in logcat the thread that was started by the Service continues to run. It's like the thread is now some kind of orphan with no apparent way to stop it. I've seen other source code use a while(true) loop in a thread's run() method. This seems bad unless a way to break out of the loop is provided. Is that typically how it's done? Or are there other ways to kill a thread after the Service that started it has stopped?
You should provide a 'running' boolean.
while(running) {
//do your stuff
}
You want to make it something that you can update. Perhaps your Service's onDestroy() method should call a stopProcessing() method on your Runnable, which will set the 'running' boolean to false.

Categories

Resources