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

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?

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

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?

Related

Android - Access Servlet AsyncPost task response from fellow classes

I have an app connected to a Java Servlet backend by means of an AsyncPost task. The task returns a String to the client representing a json object serialized with Gson.
It works almost fine, the problem is that I'm unable to access the Servlet response message from the class instantiating the call to the ServletPostAsyncTask: ListViewPrenota.class.
The project is structured as follows:
Both within the Servlet and the Client I created the two classes, Tour.class and Tours.class to store my data:
Tour class:
public class Tour {
// some simple int/string/list fields
}
Tours class:
public class Tours {
private List<Tour> tours;
// ...
}
On Client side, in a ServletPostAsyncTask.class, I receive the aforementioned Gson object within doInBackGround(). Within onPostExecute() I deserialize it, this way:
class ServletPostAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private Context context;
Tours tours;
#Override
protected String doInBackground(Pair<Context, String>... params) {
//connect to Servlet and get the serialized Gson object
}
#Override
protected void onPostExecute(String jsonResponse) {
tours = (new Gson().fromJson(jsonResponse, Tours.class));
}
}
Now, from ListViewPrenota.class in Client I am calling the ServletPostAsyncTask:
ServletPostAsyncTask s = new ServletPostAsyncTask();
s.execute(new Pair<Context, String>(ListViewPrenota.this, "tours"));
Tours ttours = s.tours;
Tour tour = ttours.getTours().get(0);
Problem: I receive a java.lang.NullPointerException pointing to Tour tour = ttours.getTours().get(0);
What is the reasong preventing me to access the newly received Tours object from other classes than ServletPostAsyncTask?
Thank you very much
the problem is you are thinking that code runs serially, if you want to use stuff returned from the AsycTask you need to use it in onPostExecute or have a callback that sends the data after it is done
doInBackground(){
//do heavy work
}
onPostExecute(Data data){
//handle data
//send data via interface to activity or class that needs the data
//or just put everything that needs the data in here
}
Ok, it works. Here's what I was able to come up with:
Callback interface:
interface CallBack {
void callBackMethod(Tours tours);//do job
}
Caller class:
class ServletPostAsyncTask extends AsyncTask<Pair<Context, String>, Tours, String>{
private Context context;
Tours tours;
public ListViewPrenota listViewPrenota;
public ServletPostAsyncTask(ListViewPrenota listView){
this.listViewPrenota = listView;
}
#Override
protected String doInBackground(Pair<Context, String>... params) {
//communicate with Servlet and get a HttpResponse
}
#Override
protected void onPostExecute(String jsonResponse) {
tours = (new Gson().fromJson(jsonResponse, Tours.class));
//the callback starts a thread updating the UI in ListViewPrenota
listViewPrenota.callBackMethod(tours);
Toast.makeText(
context,
"Connected. \nTours size: "+ tours.getTours().size(),
Toast.LENGTH_LONG).show();
}
}
The callback interface's implementation within ListViewPrenota:
public class ListViewPrenota extends FragmentActivity implements CallBack{
private ProgressDialog m_ProgressDialog = null;
private Runnable viewOrders;
private TourAdapter m_adapter;
ListView listView;
private ArrayList<Tour> m_tours =null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view_prenota);
listView = (ListView) findViewById(R.id.list);
m_tours = new ArrayList<Tour>();
m_adapter = new TourAdapter(this, R.layout.list_row, m_tours);
listView.setAdapter(m_adapter);
getActionBar().setDisplayHomeAsUpEnabled(true); //pulsante drawer
getActionBar().setHomeButtonEnabled(true); //pulsante dietro
ServletPostAsyncTask spat = new ServletPostAsyncTask(ListViewPrenota.this);
String status = spat.getStatus().toString();
spat.execute(new Pair<Context, String>(ListViewPrenota.this,"tours"));
}
public void callBackMethod(final Tours tours){
System.out.println("I've been called back");
viewOrders = new Runnable(){
#Override
public void run() {
getOrders(tours);
}
};
Thread thread = new Thread(null, viewOrders, "MagentoBackground");
thread.start();
m_ProgressDialog = ProgressDialog.show(
ListViewPrenota.this,
"Please wait...",
"Retrieving data ...",
true);
}
public void getOrders(Tours tours){
try{
m_tours = new ArrayList<>();
m_tours.addAll(tours.getTours());
Thread.sleep(2000);
Log.i("ARRAY", "" + m_tours.size());
} catch (Exception e) {
Log.e("BACKGROUND_PROC", e.getMessage());
}
//add tours to the adapter
runOnUiThread(returnRes);
}
private Runnable returnRes = new Runnable() {
#Override
public void run() {
if(m_tours != null && m_tours.size() > 0){
m_adapter.notifyDataSetChanged();
for(int i=0;i<m_tours.size();i++)
m_adapter.add(m_tours.get(i));
}
m_ProgressDialog.dismiss();
m_adapter.notifyDataSetChanged();
}
};
If there's a better way to do it I accept further suggestions.
In the mean time, thank you very much

AsyncTask handle message recieved by another class and update progress

My problem is this;
I have a AsyncTask that works fine, and on doInBackground() it calls a new class that sync my data to a web service using REST service, i don't have everything on a unique class because i need the same content sync for different activitys and it's easier this way.
What i need is, on the sync procedure, i can get the number of "contacts" and everytime it downloads a contact, removes 1 from the "contacts" lenght, so, i nedd to show on the progress dialog the length of contact and refresh everytime it downloads a new "contact"
hre's my code for the AsyncTask:
public class syncContentTask extends AsyncTask<String, String, Boolean> {
private ProgressDialog mprogress;
private Context context;
//token for JSON header to authenticate
String authToken;
public syncContentTask(Context cxt, String token) {
this.context = cxt;
mprogress = new ProgressDialog(context);
authToken = token;
}
protected void onPreExecute() {
mprogress = ProgressDialog.show(context, "Sync", "Sync in progress...");
}
#Override
protected Boolean doInBackground(String... params) {
syncData syncData = new syncData();
syncData.syncData(context, authToken);
publishProgress(progress);
return true;
}
protected void onProgressUpdate(String... progress) {
//mprogress.setProgress(Integer.parseInt(progress[0]));
}
protected void onPostExecute(Boolean result) {
if (result) {
mprogress.dismiss();
}
}
}
In the Sync Data class i have functions that handles the HttpRequest and database stuff...
can anyone help??
You need to create a listener for your data progress and have it update the progress bar. Right now it looks like this line:
syncData.syncData(context, authToken);
blocks and no updates are provided to your progress indicator until it is done. So, you need something like:
MyListener listener = new MyListener(context);
SyncData syncData = new syncData(listener);
And in your listener have callback methods like myListener.downloadStarted() , myListener.updateProgressBar(int progress) and myListener.downloadCompleted() that your syncData class calls as the download progresses.
For example:
public abstract class SDScanAdapter implements SDScanListener {
public void startScan() {
}
public void updateScanProgress(int scanItemsTotal, int scanItemsCompleted) {
}
public void scanComplete() {
}
}
Then create a listener class:
public class ScanListener extends SDScanAdapter {
#Override
public void scanComplete(String contactName, String action) {
runOnUiThread(scanComplete);
}
#Override
public void startScan() {
runOnUiThread(startScan);
}
#Override
public void updateScanProgress(int scanItemsTotal,
int scanItemsCompleted) {
if (scanCountTotal != scanItemsTotal) {
scanCountTotal = scanItemsTotal;
progressBar.setMax(scanCountTotal);
}
if (scanCountUpdate != scanItemsCompleted) {
scanCountUpdate = scanItemsCompleted;
runOnUiThread(updateScanProgress);
}
}
}
And then for this example you need Runnables (startScan, scanComplete and updateScanProgress) that perform UI tasks, like updating the progress bar. In your case, you may also want to load some of the results, etc.
Then in your AsyncTask you do:
ScanListener listener = new ScanListener();
SyncData syncData = new syncData(listener);
Assuming the SDScanListener class and AsyncTask are all in your Activity. Also, your SyncData calss will need to have a SDScanListener variable that is set when it instantiates. Then, while it does its job, calls are made to the listener methods like:
scanListener.startScan();
And while it progresses, it calls the other methods (and corresponding parameters are passed in).

How to wait for a function in the doBackground to return in android?

I have a asyc task that stores objects in cloudmine. Now I want to wait for the confirmation from cloudmine that the objects are stored. But before I get the conversation the postExecute method starts, which gives the wrong toast message. Main problem is that the cloudmine task itself is async and so I want doInBacground to wait for the cloudmine async to finish. Nothing I have tried has worked.
Here is the code:
public class createCloudmineLot extends AsyncTask<Void,Void,Boolean>
{
private boolean success;
private boolean inputFault;
protected void onPostExecute(Boolean arg0)
{
System.out.println("postExecute sucess value: " + success);
if(inputFault)
{
t = Toast.makeText(AddParking.this, "No Input!Please press enter after each input",Toast.LENGTH_LONG);
t.show();
}
else
if(arg0)
{
t = Toast.makeText(AddParking.this, "Parking Lot was added:" + success,Toast.LENGTH_LONG);
t.show();
}
else
{
t = Toast.makeText(AddParking.this, "Unable to add Parking Lot" + success,Toast.LENGTH_LONG);
t.show();
}
return;
}
#Override
protected Boolean doInBackground(Void... params)
{
// TODO Auto-generated method stub
CMApiCredentials.initialize(APP_ID, API_KEY, getApplicationContext());//initializing Cloudmine connetion
SimpleCMObject parkingLot = new SimpleCMObject();
if(name.equals("") | address.equals("") | pricing.equals("") | hours.equals("") | latitude.equals("") | longitude.equals(""))
{
inputFault = true;
return null;
}
parkingLot.add("name", name);
parkingLot.add("address",address);
parkingLot.add("pricing",pricing);
parkingLot.add("hours",hours);
double lat = Double.parseDouble(latitude);
double lon = Double.parseDouble(longitude);
CMGeoPoint locationCoordinates = new CMGeoPoint(lat,lon);
parkingLot.add("location",locationCoordinates);
parkingLot.save(new ObjectModificationResponseCallback()
{
public void onCompletion(ObjectModificationResponse response)
{
success = response.wasSuccess();
System.out.println("doInbackground: "+ success);
}
});
return success;
}//end of doBackground
}//end on AsyncTask
This is when I try with Broadcast Receivers. Using the receiver, nothing happens.
public class EditParking extends Activity
{
private ListView lotList;
private ArrayList<String> lots;
private ArrayAdapter<String> arrayAdpt;
private class ReceiveBroadcastActivity extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
// TODO Auto-generated method stub
System.out.println("received");
ArrayList<String> lots = intent.getStringArrayListExtra("ParkingLots");
ListView lotlist = (ListView)findViewById(R.id.lotList);
ArrayAdapter<String> arrayAdpt = new ArrayAdapter<String>(EditParking.this,android.R.layout.simple_list_item_1,lots);
lotList.setAdapter(arrayAdpt);
System.out.println("lots: "+ lots);
}
};
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.editparking_layout);
lotList = (ListView)findViewById(R.id.lotList);
lots = new ArrayList<String>();
lots.add("hello");
arrayAdpt = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,lots);
lotList.setAdapter(arrayAdpt);
arrayAdpt.setNotifyOnChange(true);
CMApiCredentials.initialize(APP_ID, API_KEY, getApplicationContext());//initializing Cloudmine connection
CMStore store = CMStore.getStore();
store.loadAllApplicationObjects(new CMObjectResponseCallback()
{
public void onCompletion(CMObjectResponse response)
{
for(CMObject object : response.getObjects())
{
// do something with each object
SimpleCMObject lot = (SimpleCMObject)object;
String lotInfo = lot.getString("name") + "\n" + lot.getString("address") +
"\nPrice: " + lot.getString("pricing") + "\nHours: " + lot.getString("hours");
System.out.println(lotInfo);
}
Intent broadcastIntent = new Intent();
lots.add("world");
broadcastIntent.putStringArrayListExtra("ParkingLots", lots);
broadcastIntent.setAction("com.cs275.findparking.broadcast");
sendBroadcast(broadcastIntent);
}
The problem is that the call you are doing inside the doInBackground is also an asynchronous call, you need to realize that by the time the execution thread reaches the end of doInBackground and returns an object, the onPostExecute will be called, so basically the problem in your case is that since your call is also asynchronous, its not blocking the thread and it moves inmediately to the end of the doInBackground method and the "callback" you receive from that call is executed way later than the whole asynctask did it's job, so, that said there's different ways to approach the issue.
1.- You could force the worker thread in doInBackground to wait for your callback to be executed(this requires a perfect understanding of java threads and the wait/notify methods)
2.- A simple approach would be, not to rely on onPostExecute method, and in stead send a Broadcast Receiver message from the callback method of your call, which will be handled in your activity whenever is done..
Regards!

Parse.com Android API and Android Dialog

I have placed the parse method inside onCreate method. But my problem is how to show the Android Loading... Dialog??
Parse.initialize(this, "a", "b");
ParseQuery query = new ParseQuery("Category");
query.findInBackground(new FindCallback() {
#Override
public void done(List<ParseObject> catObjects, ParseException arg1) {
Log.d("Catlength", String.valueOf(catObjects.size()));
for(int i =0; i<catObjects.size(); i++){
Log.d("lengthName"+String.valueOf(i), String.valueOf(catObjects.get(i).getInt("Id")));
Category category = new Category();
category.Name= catObjects.get(i).getString("CatName");
category.id= catObjects.get(i).getInt("Id");
categories.add(category);
}
if(categories.size()>0){
setListAdapter(new CategoryArrayAdapter(CategoryListActivity.this, R.layout.row_category, categories));
}
else{
Toast.makeText(CategoryListActivity.this, "Our servers are busy. Hit refresh..", 3000).show();
}
}
});
Everything works fine in the above code but I couldn't figure out how to show the Dialog.
I'm unable to use AsycTask also as parse sdk invokes its own thread in the background and before the findInBackground execution finishes, the doInBackground completes the Asyc thread. That's why I invoked it in the main thread.
As the result I always get no results in my ArrayList.
Can someone please enlighten me.
I was in the same situation regarding the progress dialog, tried a few tricks and finally just declared a ProgressDialog class member:
protected ProgressDialog proDialog;
then created two methods:
protected void startLoading() {
proDialog = new ProgressDialog(this);
proDialog.setMessage("loading...");
proDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
proDialog.setCancelable(false);
proDialog.show();
}
protected void stopLoading() {
proDialog.dismiss();
proDialog = null;
}
and called startLoading() before the background operation and stopLoading()
inside the background operation after I got the the results.
startLoading();
ParseUser.logInInBackground(userName.getText().toString(), hashedPass, new LogInCallback() {
public void done(ParseUser user, ParseException e) {
if (user != null) {
Log.d(Constants.TAG, "User Loged in.");
ParseManager.sCurrentUser = user;
stopLoading();
finish();
} else {
stopLoading();
invalidCreds();
}
}
});
if you want to use AsyncTask don't call findInBackground() you can use find().
you can check it out in the api https://parse.com/docs/android/api/com/parse/ParseQuery.html#find()
hope this helps.
It's easy to get the progress of both uploads and downloads using ParseFile by passing a ProgressCallback to saveInBackground and getDataInBackground. For example:
byte[] data = "Working at Parse is great!".getBytes();
ParseFile file = new ParseFile("resume.txt", data);
file.saveInBackground(new SaveCallback() {
public void done(ParseException e) {
// Handle success or failure here ...
}
}, new ProgressCallback() {
public void done(Integer percentDone) {
// Update your progress spinner here. percentDone will be between 0 and 100.
}
});

Using the GCM service to communicate between 2 applications

I'm trying to create 2 applications that communicate by the GCM service.
Let's assume that I'm trying to sends string from app A to B and then from B to A.
I'm very new to the GCM service and I'm alittle bit confused. Every time you see myApiCode I replaced it in my original code with the api code.Here is the A code:
public class MainActivity extends Activity
{
private final String myApiKey = "903137756997";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
GCMRegistrar.register(this, "myApiCode");
} else {
Log.v("INFORMATION", "Already registered");
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
And here is the GCMIntentService:
public class GCMIntentService extends GCMBaseIntentService
{
private final String myApiKey = "903137756997";
public GCMIntentService()
{
super("123");
}
...
#Override
protected void onMessage(Context arg0, Intent arg1)
{
Log.d("GCM", "RECIEVED A MESSAGE");
System.out.println("123123123123");
}
...
}
That code that I've attached would be app A and now I will attach the code of app B:
The following code is a service called from the main activity:
public void onCreate()
{
super.onCreate();
Sender sender = new Sender(myApiKey);
Message message = new Message.Builder().build();
Result result = null;
try {
result = sender.send(message, "123", 5);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (result.getMessageId() != null) {
String canonicalRegId = result.getCanonicalRegistrationId();
if (canonicalRegId != null) {
// same device has more than on registration ID: update database
}
} else {
String error = result.getErrorCodeName();
if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
// application has been removed from device - unregister database
}
}
}
I have to mention that both of the apps are running without exceptions but looks like nothing happens.. I guess I have done something wrong with the keys because I still can't understand how app B will find app A.
You have to override the onRegistered method in GCMIntentService. This will be called when the GCM server returns a registration ID that was prompted by your call to GCMRegistrar.register.
Your implementation of this method should upload the String argument to a server you control, and the server can then send out messages targetting the ID you uploaded.
Also you should not push messages directly between apps this way, because it will require you to ship your private API key out in the app package in order to send messages.

Categories