Where to put in Android code to communicate with server over http

250 Views Asked by At

The question is how to communicate with an Android phone to a server, so that if the Activity is left and the call in the Activity was not successful to repeat the transaction once again automatically. Just now I use the AsyncTask of Android to communicate with the server:

new AsyncTask<String, Void, List<String>>() {

    @Override
    protected void onPreExecute(
       showWaitDialog();        
    }

    @Override
    protected void onPostExecute(List<String> msgList) {
       //here I put the handling after the POST ie. error and success handling
       hideWaitDialog();

       if (msgList.isEmpty() {
          //success handling --> starting an new Activity
       } else {
          errorView.setText (...);
          errorLayout.setVisibility (View.VISIBLE); 
       }
    } 

    @Override
    protected List<String> doInBackground(String... params) {
       List<String> msgs = new ArrayList<String>();
       try{
          //for example submitting an JSONObject
          JSONObject result = HttpUtils.sendHttpPost(
              AppConstants.WEB_URL, jsonObject);
          //error handling on the result
          boolean hasErrors = JsonResult.isOk(result);
          if (hasErrors) {
              // adding errors to msgs list
              String[] errorMessages = JsonResult.getErrorMessages (result,...);           
              fillList (msgs, errorMessages);
              return msgs;
          }
       } catch (CommunicationError er) {
           msgs.add (er...);
       }
       return msgs;
    }
 } 

The problem with this approach is, that if I don't have a successful transmission of the data I must stay in the same Activity. Until now I show an error message to the user and he is in charge to submit by a button again the results to the server. What I'm looking for is some Activity that remains persistent in the memory which runs later in the case that the transmission wasn't made.

As an application case I use this to dynamically upload pictures for a Waypoint in a map if I pressed that waypoint. In some case it can happens that the connection to the mobile service provider isn't available (mountains, forest, far apart from antenna). Then I want to leave the map Activity and switch to the detail view of this waypoint. In the success case I put the picture into my model classes and make an serialization. If the user clicks again on the same waypoint the picture is not loaded again. In the non success case I don't want to wait that the user clicks against on the waypoint to retrieve the image. In fact I need a background task, some sort of a queue that pictures of waypoints that are already visited on couldn't be retrieved are loaded until the communication part gives back a positive result and the image can be written into the model. The next time the user is pressing the Waypoint the picture will be then present.

Are there any best practices for making such a code implementation? Is there any example around? Is there a better way of doing this?

2

There are 2 best solutions below

0
On BEST ANSWER

Yes, you need to Implement Intent Service for this requirement

According to the developers website

The IntentService class provides a straightforward structure for running an operation on a single background thread.

For complete details and working source code, Go through the Android Docs

1
On

Thanks to the answer of David.

I just read after the suggestion the tutorial at

[1] http://code.tutsplus.com/tutorials/android-fundamentals-intentservice-basics--mobile-6183

After my tests I prefered a Service (not an IntentService)

and created a service: SubmissionService

public class SubmissionIntentService extends Service {

    private List<PendingMessage> pMsgList = new CopyOnWriteArrayList<PendingMessage>();
    private Handler handler = new Handler();
    private boolean hasAppStopped = false;
    private Runnable runner;


    public SubmissionIntentService() {
      super();
      Log.d (TAG, "Service created...");
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
      PendingMessage pMessage = (PendingMessage) intent.getParcelableExtra(AppConstants.MESSAGE_OBJECT);
      synchronized (pMsgList) {
         pMsgList.add(pMessage);
      }
      if (runner == null) {
         handler.postDelayed(runner = initializeRunnable(), 500);
      }
      return Service.START_NOT_STICKY;
   }

    private void runAsLongAppIsActive (Runnable runner) {
        if (!hasAppStopped) {
           handler.postDelayed (runner, SOME_INTERVAL_CONSTANT); 
        }
    }

    private Runnable initializeRunnable() {
        Runnable result;
        result = new Runnable() {

             @Override
             public void run() {
                if (pMsgList.isEmpty()) {
                   runAsLongAppIsActive (this);
                   return; 
                }

                PendingMessage[] pMArray = null;

                synchronized(pMsgList) {
                    pMArray = pMsgList.toArray (new PendingMessage[pMsgList.size()]);
                } 
                if (pMArray==null || pMArray.length==0) {
                   runAsLongAppIsActive (this);
                   return;
                }
                Log.d (TAG, "Message List size is actually :"+pMArray.length);

                for (PendingMessage pM: pMArray) {
                     try {
                        JSONObject jsonMess = JSONSendMessage.buildOutput (pM);
                        JSONObject result = HttupUtils.sendHttpPost (WEB_URL, jsonMess);

                        boolean hasErrors = JSONResult.isOk (result);
                        if (hasErrors) {
                           //TODO: error handling in case of transmission
                           //don't remove the message from the queue
                           runAsLongAppIsActive(this); 
                           return; 
                        }
                        //remove pending transmission of the queue if success
                        synchronized (pMsgList) {
                           pMsgList.remove (pM);
                        } 
                        //inform over receiver if activity is shown
                        Intent broadcastIntent = new Intent();
                        //put data in intent
                        sendBroadcast (intent);

                        //more important
                        WayPointModel model = ModelInstance.getWayPointModel();
                        model.addToModel (pM, result);
                        model.store();

                     } catch (Exception e) {
                        continue; //try to send other messages
                     }
                }
                runAsLongAppIsActive (this);
             }

           };
        return result;
    }
}

   @Override
   public void onDestroy() {
      hasAppStopped = true;
      handler.removeCallbacks (runner);
      super.onDestroy();
   }

}

Further I added a ResponseReceiver:

public class ResponseReceiver extends BroadcastReceiver {
   public static final String ACTION_RESP = "MESSAGE_PROCESSED";

   @Override
   public void onReceive(Context context, Intent intent) {
      //work in progress...
   }
}

and in the Activity where I want to be informed about events:

public class SomeActivity extends Activity {    
  private ResponseReceiver receiver;

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

    IntentFilter filter = new IntentFilter(ResponseReceiver.ACTION_RESP);
    filter.addCategory(Intent.CATEGORY_DEFAULT);
    receiver = new ResponseReceiver();
    registerReceiver(receiver, filter);
    ...
 }

}

and finally to send messages over Http:

Intent msgIntent = new Intent(this, SubmissionIntentService.class);
msgIntent.putExtra(...);
startService(msgIntent);

don't forget to declare the service in your manifest:

<service android:name="ch.xxx.app.service.SubmissionIntentService" />

Observations: - I called the method startService(...) from different Activities. The constructor is only called once. ==> I have just on instance of the service for all Activities (exactly what I need).

What I don't get until now: - Putting back data to the Activity. What is if the Activity is at the moment no shown?