Cleber Pifer
Cleber Pifer

Reputation: 1

Android Not Responding throws in Service

I'm developing an app that should track user location from time to time (e.g. every 20 seconds) and at each minute send those locations to a web services. To achieve that, I've created a producer consumer structure where each task is an android service started by AlarmManagers/BroadcastReceivers. The application also has a UI developed with Phonegap and JQuery Mobile to do secondary tasks and send some data to the services(e.g. username). The problem occurs when the service responsible by the network communication get stuck into getInputStream() method and throws an Android Not Responding dialog. I understand that lengthy operations should not be performed into the UI Thread, but I'm not sure how to solve this problem given my approach, any clue?

Below is my app manifest and some code:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.fcl"
          android:versionCode="1"
              android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="17" />

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"   />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme"
    android:configChanges="orientation|keyboardHidden" 
    android:allowBackup="false">
    <activity
        android:name="com.android.fcl.MainActivity"
        android:label="@string/app_name" 
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service 
        android:name="com.android.fcl.GPSLoggerService"
        android:label="@string/gps_logger_service">
    </service>
    <service 
        android:name="com.android.fcl.GPSHttpService"
        android:label="@string/gps_http_service">
    </service>
    <receiver android:name="com.android.fcl.GPSLoggerReceiver" />
    <receiver android:name="com.android.fcl.GPSHttpReceiver" />
</application>

</manifest>

Javascript:

function goToPage() {
    window.ServiceControl.startService();
    $.mobile.changePage('address.html', {transition: 'flip'});
    $.mobile.hidePageLoadingMsg();
}

Activity:

public class MainActivity extends DroidGap {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.init();

        if (android.os.Build.VERSION.SDK_INT > 9) {
            StrictMode.ThreadPolicy policy = new    StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
        }

        ServiceControl service = ServiceControl.getInstance();
        service.setActivity(this);
        service.setView(appView);
        appView.addJavascriptInterface(service, "ServiceControl");

        ServiceStorage serviceStorage = ServiceStorage.getInstance();
        serviceStorage.createDatabase(this);
        super.loadUrl("file:///android_asset/www/login.html");
    }    
}

ServiceControl.java:

public class ServiceControl {

    private AlarmManager gpsAlarmManager;
    private PendingIntent gpsPendingIntent;
    private AlarmManager httpAlarmManager;
    private PendingIntent httpPendingIntent;

    public void startService() {
        if (this.activity != null) {
            Intent gpsIntent = new Intent(this.activity, GPSLoggerReceiver.class);
            Intent httpIntent = new Intent(this.activity, GPSHttpReceiver.class);
            if (this.gpsAlarmManager == null) {
                gpsAlarmManager = (AlarmManager) activity.getSystemService(Service.ALARM_SERVICE);
                gpsPendingIntent = PendingIntent.getBroadcast(activity, 0, gpsIntent, 0);
                gpsAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), GPS_MIN_TIME, gpsPendingIntent);
            }
            if (this.httpAlarmManager == null) {
                httpAlarmManager = (AlarmManager) activity.getSystemService(Service.ALARM_SERVICE);
                httpPendingIntent = PendingIntent.getBroadcast(activity, 0, httpIntent, 0);
                httpAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), HTTP_MIN_TIME, httpPendingIntent);
            }
    }
}

BroadcastReceiver:

public class GPSHttpReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent service = new Intent(context, GPSHttpService.class);
        context.startService(service);
    }
}

Service:

public class GPSHttpService extends Service {

    private PowerManager pm = null;
    private PowerManager.WakeLock wl = null;

    private HttpURLConnection httpConnection = null;

    private synchronized void setWakeLock() {
        ServiceControl serviceControl = ServiceControl.getInstance();
        DroidGap activity = serviceControl.getActivity();
        pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        wl.acquire();
    }

    private String sendPostMessage(String message) {
        byte [] buffer = null;
        String response = null;
    URL url = null;
        DataOutputStream outputStream = null;
        InputStreamReader inputStream = null;

        buffer = message.getBytes();
        try {
            url = new URL(URI);
            httpConnection = (HttpURLConnection) url.openConnection();
            httpConnection.setDoOutput(true);
            httpConnection.setDoInput(true);
            httpConnection.setInstanceFollowRedirects(false);
            httpConnection.setRequestMethod(FCLString.POST_REQUEST_METHOD);
            httpConnection.setRequestProperty(FCLString.CONTENT_TYPE_PROPERTY, FCLString.CONTENT_TYPE_APP_JSON);
            httpConnection.setRequestProperty(FCLString.CHARSET_PROPERTY, FCLString.UTF8_FORMAT);
            httpConnection.setConnectTimeout(CONN_TIMEOUT);
            httpConnection.setRequestProperty(FCLString.CONTENT_LENGTH_PROPERTY, String.valueOf(buffer.length));
            httpConnection.setUseCaches(false);
            outputStream = new DataOutputStream(httpConnection.getOutputStream());
            outputStream.write(buffer);
            outputStream.flush();
            outputStream.close();
            inputStream = new InputStreamReader(httpConnection.getInputStream());
            response = GPSMessage.readResponseStream(inputStream);
       } catch (MalformedURLException e) {
           Log.d(TAG, FCLString.MALFORMED_URL);
       } catch (IOException e) {
           Log.d(TAG, FCLString.IO_EXCEPTION);
           Log.d(TAG,  e.getMessage());
       } finally {
           if (httpConnection != null) {
               httpConnection.disconnect();
            }
       }
       return response;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        ServiceStorage serviceStorage = ServiceStorage.getInstance();
        JSONObject json= null;
        String message = null;
        String response = null;
        boolean status = false;

        super.onStartCommand(intent, flags, startId);
    setWakeLock();
        json = serviceStorage.getRecords();
        if ((json != null) && (json.length() > 0)) {
            message = GPSMessage.prepareMessage(json);
            if (message != null) {
                if (hasInternetConnection()) {
                    response = sendPostMessage(message);
                    status = GPSMessage.evaluateResponse(response);
                    if (status) {
                        serviceStorage.removeRecords(json);
                    }
                } 
            }
        }
        this.stopSelf();
        return Service.START_NOT_STICKY;
     }
}

Upvotes: 0

Views: 947

Answers (2)

Santa
Santa

Reputation: 11545

From the reference doc on Android services:

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.

Upvotes: 3

kabuko
kabuko

Reputation: 36302

You could consider using IntentService instead of just a regular Service as your work seems relatively self-contained. That will take care of doing your work on a separate thread.

Upvotes: 0

Related Questions