Search code examples
androidservicebroadcastreceiver

android service causing ANR


I've an app that from a boot receiver starts a service which periodically deletes rows from a table on the phone's Sqlite DB. The BootReceiver works fine. The service that it instantiates has an infinate loop that is paused by a Thread.sleep for 30 secs(for testing), eventually it will be set to every couple of hrs or so. It does delete the rows but i'm not convinced it's every 30 secs and also eventually it causes an ANR. The way it's all implemented seems a little crude. Is there a better way to implement what i'm doing? If not how can i stop the ANR as i thought services don't have to be Asynchronous, just network calls etc.

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyBootReceiver extends BroadcastReceiver {   

    @Override
    public void onReceive(Context context, Intent intent) {

     Intent myIntent = new Intent(context, SendOutstandingTransactions.class);
     myIntent.setAction("com.carefreegroup.startatboot.MyService");
     context.startService(myIntent);
        
        
    }

}




import org.joda.time.DateTime;

import android.app.Service;
import android.content.Intent;
import android.database.Cursor;
import android.os.IBinder;
import android.util.Log;

public class SendOutstandingTransactions extends Service {

    private static final String TAG = SendOutstandingTransactions.class.getSimpleName();
    NfcScannerApplication nfcscannerapplication;
    Cursor c;
    
    @Override
    public void onCreate() {
        Log.e(TAG, "inside onCreate of SendOutstandingTransactions");
        nfcscannerapplication = (NfcScannerApplication)getApplication();
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "inside onDestroy of SendOutstandingTransactions");
        
        super.onDestroy();
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        Log.e(TAG, "inside onStart of SendOutstandingTransactions");
        
        do{
        DateTime now = new DateTime();
        //nfcscannerapplication.loginValidate.deleteTransactionsOlderThanThreeDays(now);
        nfcscannerapplication.loginValidate.deleteTableTransactions();
        Log.e(TAG, "just called housekeeping method in service");
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        }while(true); 
            
        

        
    }// end of onStart

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

}

AndroidManifest File :

<service android:name=".SendOutstandingTransactions" >
            <intent-filter>
                <action android:name="com.carefreegroup.startatboot.MyService" />
            </intent-filter>
        </service>

        <receiver
            android:name=".MyBootReceiver"
            android:enabled="true"
            android:exported="false" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

How i start the service

// get a Calendar object with current time
             Calendar cal = Calendar.getInstance();
             // add 5 minutes to the calendar object
             cal.add(Calendar.MINUTE, 1);
             Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
             intent.putExtra("alarm_message", "sending outstanding transactions");
             // In reality, you would want to have a static variable for the request code instead of 192837
             PendingIntent sender = PendingIntent.getBroadcast(getApplicationContext(), 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
             
             // Get the AlarmManager service
             AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
             //am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
             //86400000 = 24 hours
             //43200000 = 12 hours
             //3600000 = 1hr
             //1800000 = 30 mins
            // 600000 = 10 mins
             //300000 = 5 mins
             
             am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 300000 , sender);

The AlarmReceiver:

public class AlarmReceiver extends BroadcastReceiver {
 
 @Override
 public void onReceive(Context context, Intent intent) {
     
    
   try {
       
     Bundle bundle = intent.getExtras();
     String message = bundle.getString("alarm_message");
    // Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
     Intent myIntent = new Intent(context, SendOutstandingTransactions.class);
     myIntent.setAction("com.carefreegroup.rr3.startatboot.MyService");
     context.startService(myIntent);
     
    } catch (Exception e) {
     Toast.makeText(context, "There was an error somewhere, but we still received an alarm", Toast.LENGTH_SHORT).show();
     e.printStackTrace();
 
    }
 }
 
}

The Service:

 public class SendOutstandingTransactions extends IntentService {
        
        private static final String TAG = SendOutstandingTransactions.class.getSimpleName();
        
instance variables
        
        @Override
        public void onCreate() {
            super.onCreate();
            
            
        }
    
    
    
    
    
        @Override
        protected void onHandleIntent(Intent intent) {
            
    //do something
    
        }//end of onHandleIntent
    
    
    
    
        public SendOutstandingTransactions() {
            super("SendOutstandingTransactions");
            
        }

Solution

  • Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.

    Source: http://developer.android.com/reference/android/app/Service.html

    Solution:

    Move your code to a dedicated thread. This can be done by using, for instance the AsyncTask class.