Hope you are doing well.
I'm developing an android app, and i need to make synchronization between the local SQL and the database on the server.
I'm using PHP on the server side(JSON for retrieving date).
My problem is when i launch the app for the first time , is dowloads the data located on the server, then when i launch it for the second time , the data is dowloaded again, sometimes it happens and sometimes NOT!
And when adding new data , it is downloaded , but the problem still happens, the data is duplicated.
When i try the app on my mobile, the data is downloaded once, but when adding new data, it isn't downloaded!
Here is my code.
protected Void doInBackground(Void... uri) {
myDatabaseHandler.openToRead();
Cursor cursor = myDatabaseHandler.queueBranchId();
try {
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setSoTimeout(params, 0);
HttpClient httpClient = new DefaultHttpClient(params);
//prepare the HTTP GET call
HttpGet httpget = new HttpGet("http://restaurants.bugs3.com/branches.php");
//get the response entity
HttpEntity entity = httpClient.execute(httpget).getEntity();
if (entity != null) {
//get the response content as a string
String response = EntityUtils.toString(entity);
//consume the entity
entity.consumeContent();
// When HttpClient instance is no longer needed, shut down the connection manager to ensure immediate deallocation of all system resources
httpClient.getConnectionManager().shutdown();
//return the JSON response
JSONObject object = new JSONObject(response.trim());
JSONArray jsonArray = object.getJSONArray("branches");
if(jsonArray != null) {
for(int i = 0 ; i < jsonArray.length() ; i++) {
JSONObject object1 = (JSONObject) jsonArray.get(i);
int server_branch_id = object1.getInt("server_branch_id");
String branch_name = object1.getString("branch_name");
double branch_lat = object1.getDouble("branch_lat");
double branch_lon = object1.getDouble("branch_lon");
double distance = object1.getDouble("distance");
String restaurant_name = object1.getString("restaurant_name");
boolean exist = false;
if (cursor.moveToFirst())
{
do
{
try
{
if (server_branch_id == cursor.getInt(cursor.getColumnIndex(DatabaseHandler.KEY_LOCAL_BRANCH_ID)))
{
exist = true;
}
}
catch(Exception e)
{
}
}while(cursor.moveToNext());
cursor.close();
}
myDatabaseHandler.close();
if (!exist)
{
myDatabaseHandler.openToWrite();
myDatabaseHandler.insertBranchWithID(server_branch_id, branch_name, restaurant_name, branch_lat, branch_lon, distance);
myDatabaseHandler.close();
}
}}catch (Exception e) {
e.printStackTrace();
}
return null;
}
There could be some workarounds for this. I can suggest two different ways (Assuming you have a specific id for whatever item you download):
Keep track of last id at local sql, on app start, ask server if there is newer id and if there is download them. By this way you can avoid replication.
Use primary key constraint on table and for insertion use insertWithOnConflict(..., CONFLICT_IGNORE). This method will not duplicate any same ids and insert if there is something different.
Hope this helps
Related
I'm doing a REST API in Android with my GET method using Slim framework. I didn't have any problem about retrieving all the values of a table, for example, cars.
The problem comes when I try to access to the name of a Car by it's id. I have the method on Slim framework created and it works perfectly:
GET METHOD
$app->get("/cars/:idCar",function($idCar) use($app)
{
try{
$connection = getConnection();
$dbh = $connection->prepare("SELECT name FROM cars WHERE idCar = ?");
$dbh->bindParam(1,$idCar);
$dbh->execute();
$car = $dbh->fetchObject();
$connection = null;
header("HTTP/1.1 200");
header("Content-Type:application/json; charset=utf-8");
echo json_encode($car,JSON_UNESCAPED_UNICODE );
}catch(PDOException $e)
{
echo "Error: " . $e->getMessage();
}
});
And if I put the url on my browser:
http://IP of my computer/project/cars/1
it returns to me:
{"name":"Car1"}
So in the GET method there isn't any problem.
The problem it's when I try to execute HttpGet, because I get stuck of what I have to do. What I have right now:
HttpGet method inside the AsyncTask
class FindCar extends AsyncTask<Integer, Integer, Void> {
protected void onPreExecute(){
}
protected Void doInBackground(Integer... idCar) {
String url = "http://IP of my computer/project/cars/" + idCar[0].intValue();
HttpClient httpClient = new DefaultHttpClient();
HttpGet method = new HttpGet(url);
method.setHeader("content-type", "application/json");
try {
HttpResponse resp = httpClient.execute(method);
String respStr = EntityUtils.toString(resp.getEntity());
JSONArray respJSON = new JSONArray(respStr);
for (int i = 0; i < respJSON.length(); i++) {
JSONObject obj = respJSON.getJSONObject(i);
String name = obj.getString("name");
}
} catch (Exception ex) {
}
return null;
}
protected void onProgressUpdate(){
}
protected void onPostExecute(){
}
}
And it gives to me some questions:
Should I create a JSONArray just for one attribute?
What is the best practice to get the name in my AsyncTask?
After getting the name by the id of my Car that I pass to my AsyncTask, how can I return it out of the AsyncTask?
Should I create a NameValuePair to set the value of my id?
Thanks in advance!
You do not need the JSONArray (since there is no square brackets in the responce)! Please try instead of
JSONArray respJSON = new JSONArray(respStr);
for (int i = 0; i < respJSON.length(); i++) {
JSONObject obj = respJSON.getJSONObject(i);
String name = obj.getString("name");
}
the following:
JSONObject respJSON = new JSONObject(respStr);
String name = respJSON.getString("name");
As for returning the result - return it from doInBackground to onPostExecute method then store it somewhere (you can show it on the screen since onPostExecute runs on UI thread). To return value from doInBackground change last type param of AsyncTask<Integer, Integer, Void> from Void to other type.
Best practice is do not use AsyncTask and do not parse JSON manually (when it is possible) :) Retrofit would be a good choice
i use something like this:
public function getCarsById($id) {
$stmt = $this->conn->prepare("SELECT name FROM cars WHERE idCar = ?");
$stmt->bind_param("s", $id);
if ($stmt->execute()) {
$car= $stmt->get_result()->fetch_assoc();
$stmt->close();
return $car;
} else {
return NULL;
}
}
Regarding the java class, i use Retrofi that is incredible fast and can be used Synchronous and Asynchronous.
I have a JSON array sent from my SQL server via PHP in the following format which I am finding difficult to parse without encountering errors.
[
{
"placename": "place1",
"latitude": "50",
"longitude": "-0.5",
"question": "place1 existed when?",
"answer1": "1800",
"answer2": "1900",
"answer3": "1950",
"answer4": "2000",
"correctanswer": "1900"
},
{
"placename": "place2",
"latitude": "51",
"longitude": "-0.5",
"question": "place2 existed when?",
"answer1": "800",
"answer2": "1000",
"answer3": "1200",
"answer4": "1400",
"correctanswer": "800"
},
{
"placename": "place3",
"latitude": "52",
"longitude": "-1",
"question": "place 3 was established when?",
"answer1": "2001",
"answer2": "2005",
"answer3": "2007",
"answer4": "2009",
"correctanswer": "2009"
}
]
I have verified my JSON at JSONLint and it comes up as valid. I have also used log code to print out my JSON in the Eclipse app debugger after my HTTP client has processed it and that also works fine (it shows the JSON as above so I know it has downloaded correctly).
I'm trying to fit the JSON Parser into the following activity but all my attempts thus far have either contained too many errors to run or have simply returned no results because of JSON parsing errors.
Here is the code of the main activity. The code for this activity is adapted from NewThinkTank.com (Android Development 15) and I'm trying to tweak it for my needs but the structure of the JSON used in the example is very different to mine.
I was hoping someone could suggest some code, or give me some pointers, as to how I could go about parsing this JSON array properly. I am fairly new to Android programming so this is a fairly steep task to figure out on my own.
Thanks for your time.
public class MainActivity extends Activity {
// The JSON REST Service I will pull from
static String dlquiz = "http://exampleserver.php";
// Will hold the values I pull from the JSON
static String placename = "";
static String latitude = "";
static String longitude = "";
static String question = "";
static String answer1 = "";
static String answer2 = "";
static String answer3 = "";
static String answer4 = "";
static String correctanswer = "";
#Override
public void onCreate(Bundle savedInstanceState) {
// Get any saved data
super.onCreate(savedInstanceState);
// Point to the name for the layout xml file used
setContentView(R.layout.main);
// Call for doInBackground() in MyAsyncTask to be executed
new MyAsyncTask().execute();
}
// Use AsyncTask if you need to perform background tasks, but also need
// to change components on the GUI. Put the background operations in
// doInBackground. Put the GUI manipulation code in onPostExecute
private class MyAsyncTask extends AsyncTask<String, String, String> {
protected String doInBackground(String... arg0) {
// HTTP Client that supports streaming uploads and downloads
DefaultHttpClient httpclient = new DefaultHttpClient(new BasicHttpParams());
// Define that I want to use the POST method to grab data from
// the provided URL
HttpPost httppost = new HttpPost(dlquiz);
// Web service used is defined
httppost.setHeader("Content-type", "application/json");
// Used to read data from the URL
InputStream inputStream = null;
// Will hold the whole all the data gathered from the URL
String result = null;
try {
// Get a response if any from the web service
HttpResponse response = httpclient.execute(httppost);
// The content from the requested URL along with headers, etc.
HttpEntity entity = response.getEntity();
// Get the main content from the URL
inputStream = entity.getContent();
// JSON is UTF-8 by default
// BufferedReader reads data from the InputStream until the Buffer is full
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 8);
// Will store the data
StringBuilder theStringBuilder = new StringBuilder();
String line = null;
// Read in the data from the Buffer untilnothing is left
while ((line = reader.readLine()) != null)
{
// Add data from the buffer to the StringBuilder
theStringBuilder.append(line + "\n");
}
// Store the complete data in result
result = theStringBuilder.toString();
} catch (Exception e) {
e.printStackTrace();
}
finally {
// Close the InputStream when you're done with it
try{if(inputStream != null)inputStream.close();}
catch(Exception e){}
}
//this allowed me to verify the JSON download in the debugger
Log.v("JSONParser RESULT ", result);
// JSON parsing needs to happen here...
return result;
}
protected void onPostExecute(String result){
// Gain access so I can change the TextViews
TextView line1 = (TextView)findViewById(R.id.line1);
TextView line2 = (TextView)findViewById(R.id.line2);
TextView line3 = (TextView)findViewById(R.id.line3);
// Change the values for all the TextViews
line1.setText("Place Name: " + placename);
line2.setText("Question: " + question);
line3.setText("Correct Answer: " + correctanswer);
}
}
}
Check this answer out: How to parse JSON in Android
You'll be using:
JSONArray array = new JSONArray(result);
From there, you'll loop through and get each JSONObject:
for(int i = 0; i < array.length(); i++)
{
JSONObject obj = array.getJSONObject(i);
//now, get whatever value you need from the object:
placename = obj.getString("placename");
//or if on the MainUI thread you can set your TextView from here:
yourTextView.setText(obj.getString("placename"));
}
Good luck!
I have reviewed all of the documentation for CWAC-endlessAdapteras well as the demo projects. I do understand how it works and mostly where everything goes. But I have a several questions on how to handle some things with how I am currently doing it now (I have yet to find any working example of this).
Here is a typical AsyncTask I use (cleaned up a bit):
class ReviewTask extends AsyncTask<String, String, Void> {
#Override
protected Void doInBackground(String... params) {
ArrayList<NameValuePair> param = new ArrayList<NameValuePair>();
param.add(new BasicNameValuePair("username", userName));
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url_select);
try {
httpPost.setEntity(new UrlEncodedFormEntity(param));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
} catch (Exception e) {
}
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
result = sb.toString();
} catch (Exception e) {
}
return null;
}
protected void onPostExecute(Void v) {
String review, newdate, item, rating, cat;
try {
JSONArray jArray = new JSONArray(result);
JSONObject json_data = null;
for (int i = 0; i < jArray.length(); i++) {
json_data = jArray.getJSONObject(i);
newdate = json_data.getString("date");
review = json_data.getString("review");
item = json_data.getString("item");
rating = json_data.getString("rating");
cat = json_data.getString("category");
reviews.add(review);
itemslist.add(item);
datelist.add(newdate);
ratings.add(rating);
cats.add(cat);
}
}
Profile[] p = new Profile[reviews.size()];
int index = 0;
for (String i : reviews) {
p[index] = new Profile(reviews.get(index), datelist.get(index),
itemslist.get(index), ratings.get(index),
cats.get(index));
index++;
}
if (getActivity() != null) {
adapter = new ProfileAdapter(getActivity(), p);
setListAdapter(adapter);
}
}
}
In this task, I get all the data from MySQL Database via php. The SQL query I wrote gathers all data at once. Is this correct to still handle this way?
Also, I call this task in the onCreateView in my ListFragment. But it looks like the task needs to be called in cacheInBackground()?
Lastly, it sounds like I have to set the adapter in onActivityCreated like this:
// from Example
if (adapter==null) {
items=new ArrayList<Integer>();
for (int i=0;i<25;i++) { items.add(i); }
adapter=new DemoAdapter(items);
adapter.setRunInBackground(false);
}
setListAdapter(adapter);
I don't understand or see where there is a constructor for DemoAdapter(items), and based on the fact that I am passing an Array of Objects, would I do something like DemoAdapter(object[])? And it's ok if it is null, because gathering the data actually happens in the adapter, correct?
Last relevant note is, all of my adapters are in a class outside of the Fragment where they are set.
The SQL query I wrote gathers all data at once. Is this correct to still handle this way?
That is up to you. However, doing it this way, you do not need EndlessAdapter, as you already have all your data. The point behind EndlessAdapter is to support situations where you do not "gather all data at once", but rather wish to gather a portion of the data, and gather another portion only when the user scrolls far enough.
Also, I call this task in the onCreateView in my ListFragment. But it looks like the task needs to be called in cacheInBackground()?
That is up to you. If you wish to use your own AsyncTask called whenever you want, that is fine. This is covered in the documentation:
If you would prefer that EndlessAdapter not run its own AsyncTask, then call setRunInBackground(false). In this mode, your cacheInBackground() method will be called on the main application thread. It is up to you to arrange to do the work on your own background thread, then call onDataReady() when you want the adapter to update to reflect the newly added data. Note that appendCachedData() will not be used in this scenario.
I don't understand or see where there is a constructor for DemoAdapter(items)
There isn't one, as the demos did not require one.
based on the fact that I am passing an Array of Objects, would I do something like DemoAdapter(object[])?
That is up to you.
And it's ok if it is null, because gathering the data actually happens in the adapter, correct?
Again, that is up to you.
However, as I pointed out earlier, since you do not need EndlessAdapter, I would recommend that you just stop using it.
Is there a way to send android local HTML forms via ajax to remote php server? (local means the files are in my device) My scenario is this: In my app, I have an html files in my android device and is loaded in a webview, i also have the javascript file in my device. What i want to do is to send the html forms data to a remote server. In my current situation, its not sending any data, I've check the javascript and php and the code is fine, and it's working on iOS version of the app. I've tried other workarounds and what I've observed is that, when i load html file in webview using local files (e.g. webview.loadUrl("file://"+ Environment.getExternalStorageDirectory()+"/android_asset/list.html"), the android is looking for all other related files (e.g. formsprocessor.php) locally, though in javascript/ajax all necessary arguments in it's functions are supplied properly. The errors i've encountered are: FileNotFound: content://packagename.com/formsprocessor.php & Unknown chronium error: -6.
Is there a way or what is the best way to do this?
Thanks, Clint.
This solve my problem:
Used a javascripthandler, and in my javascript i call the function from the handler. So basically, the android handled the upload of data to server using httppost. Here's the codes;
the handler:
final class IJavascriptHandler{
IJavascriptHandler(){}
public void sendJSONToAndroid(String text){
if(!Config.canConnect((ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE), home) && dialogNoConnFlag == false) {
dialogNoConnFlag = true;
Config.notificationMsg(Config.ERRORNOCONN,home, Config.TITLE1 + " " + Config.TITLE6);
return;
}
try {
Log.v("SendToServer","Send JSON to Server");
String url = "";
JSONObject json_data = new JSONObject(text);
JSONArray names= json_data.names();
JSONArray values = json_data.toJSONArray(names);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
for(int i = 0 ; i < values.length(); i++){
Log.v("Good",names.getString(i).toString());
if(names.getString(i).equals("url")) {
url = json_data.getString(names.getString(i)).toString();
}
nameValuePairs.add(new BasicNameValuePair( names.getString(i).toString(), json_data.getString(names.getString(i)).toString()));
}
Config.uploadToServer(nameValuePairs, url);
}
catch (JSONException e)
{
Config.notificationMsg(Config.ERRORMSG + e.getMessage(), (Activity) home, Config.TITLE1 + " " + Config.TITLE6);
}
}
}
the httppost:
public static String uploadToServer(List<NameValuePair> nameValuePairs, String url){
if(Session.isordinaryHost)
{
httpclient = new DefaultHttpClient();
}
else
{
httpclient = new MyHttpClient().getNewHttpClient();
((AbstractHttpClient) httpclient).getCredentialsProvider().setCredentials(
new AuthScope(Session.siteIp, 443),
new UsernamePasswordCredentials(Session.siteUsername, Session.sitePassword));
}
httppost = new HttpPost(url);
try
{
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
// Execute HTTP Post Request
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
EntityUtils.toString(entity);
}
catch (ClientProtocolException e)
{
return e.getMessage();
}
catch (IOException e)
{
return e.getMessage();
}
return null;
}
the javascript:
function CheckCompleteRecords() {
DB.transaction(function(tx) {
tx.executeSql(SelectCompleteForUploadStatement, [], function(tx, result) {
Dataset = result.rows;
for (var i = 0, item = null; i < Dataset.length; i++) {
item = Dataset.item(i);
var a = createJSON(item['FormName'],item['UserID'],item['Image1'],item['Image2'],item['Image3'],item['Image4'],item['Image5'],item['Field1'],item['Field2'],item['Field3'],item['Field4'],item['Field5'],item['Field6'],item['Field7'],item['Field8'],item['Field9'],item['Field10'],item['Field11'],item['Field12'],item['Field13'],item['Field14'],item['Field15'],item['Field16'],item['Field17'],item['Field18'],item['Field19'],item['Field20'],item['Field21'],item['Field22'],item['Field23'],item['Field24'],item['Field25'],item['Field26'],item['Field27'],item['Field28'],item['Field29'],item['Field30'],item['Field31'],item['Field32'],item['Field33'],item['Field34'],item['Field35'],item['Field36'],item['Field37'],item['Field38'],item['Field39'],item['Field40'],item['Field41'],item['Field42'],item['Field43'],item['Field44'],item['Field45'],item['Field46'],item['Field47'],item['Field48'],item['Field49'],item['Field50'],item['Field51'],item['Field52'],item['Field53'],item['Field54'],item['Field55'],item['Field56'],item['Field57'],item['Field58'],item['Field59'],item['Field60'],item['Field61'],item['Field62'],item['Field63'],item['Field64'],item['Field65'],item['Field66'],item['Field67'],item['Field68'],item['Field69'],item['Field70'],item['Field71'],item['Field72'],item['Field73'],item['Field74'],item['Field75'],item['Field76'],item['Field77'],item['Field78'],item['Field79'],item['Field80'],item['Field81'],item['Field82'],item['Field83'],item['Field84'],item['Field85'],item['Field86'],item['Field87'],item['Field88'],item['Field89'],item['Field90'],item['Field91'],item['Field92'],item['Field93'],item['Field94'],item['Field95'],item['Field96'],item['Field97'],item['Field98'],item['Field99'],item['Field100'],item['CurrentDateTime'],item['Geolocation'],item['BarCode']);
window.cpjs.sendJSONToAndroid(a);
showStuff('SendServerBtn');
window.location = "senttoserver://app_action";
}
});
});
}
In my application, I have a food activity in which the user enters his/her food, and the app requests the food, by the name entered by the user, from a MYSQL database. In the case that the entered food not exist, the string returned by the database should be null.
Currently, when this happens, an exception to occurs since the null value cannot be parsed to a JSON array. My question is: "Is there a way to prevent my app from force closing? Can I handle the exception and display a toast notifying the user that the requested food was not found?" I would like to prevent the app from crashing, and, rather, fail gracefully.
Please help me.
I've shown the relevant code in my application..
private class LoadData extends AsyncTask<Void, Void, String>
{
private JSONArray jArray;
private String result = null;
private InputStream is = null;
private String entered_food_name=choice.getText().toString().trim();
protected void onPreExecute()
{
}
#Override
protected String doInBackground(Void... params)
{
try {
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://10.0.2.2/food.php");
nameValuePairs.add(new BasicNameValuePair("Name",entered_food_name));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs,"UTF-8"));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
//convert response to string
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"utf-8"),8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
is.close();
result =sb.toString();
result = result.replace('\"', '\'').trim();
}
catch(Exception e){
Log.e("log_tag", " connection" + e.toString());
}
return result;
}
#Override
protected void onPostExecute(String result)
{
try{
String foodName="";
int Description=0;
jArray = new JSONArray(result); // here if the result is null an exeption will occur
JSONObject json_data = null;
for (int i = 0; i < jArray.length(); i++) {
json_data = jArray.getJSONObject(i);
foodName=json_data.getString("Name");
.
.
.
.
.
}
catch(JSONException e){
**// what i can do here to prevent my app from crash and
// make toast " the entered food isnot available " ????**
Log.e("log_tag", "parssing error " + e.toString());
}
}
}
This will fix your code:
jArray = (result == null) ? new JSONArray() : new JSONArray(result);
Now that you have an empty JSONArray, you will be able to test for null JSONObjects later in your program. Many of the JSON methods return a JSONObject if one is found, of null if none exists.
You might also want to initialize your JSONObject with the no-argument JSON constructor, rather than simply setting it to null. It will avoid problems when passing it to other JSON methods (such as using it in a constructor to a JSONArray():
JSONObject json_data = new JSONObject();
Finally, if you're still getting JSONExceptions, it's because you're not actually passing a valid JSON string to the constructor. You can print out the value of result to the log:
Log.d("JSON Data", result);
You may see some SQL error text or if you retrieve from a web server, then an HTTP error code (404 is common if you don't have your url correct).
If your result does look like JSON, then you can verify whether it's actually valid JSON or not using the JSONLint validator. It will help you catch any errors you may have, especially if you're formatting the JSON yourself.
Are you looking to capture the Exception and log it (remotely) to aid in crash reporting and debugging? I've used this package to remotely capture Exceptions and it works pretty good:
http://code.google.com/p/android-remote-stacktrace/