Android - Best way to sync SQLite with MySQL [duplicate] - android

This question already has answers here:
Synchronizing client-server databases
(6 answers)
Closed 8 years ago.
I'm working on a project containing web app and mobile app which records daily user's data. User are able to delete, update their data and they can use many devices to insert data.
I intend to develop this way:
User enter their data and then insert to SQLite. A service will start periodically (each 5hr or sth) to sync with MySQL using timestamp.
I did search for a sample using service and timestamp on the internet but I found nothing. It would be great if there are a sample or tutorial.
I'm new at Android and I have no idea what is the best way to develop an app like this. Thanks in advance.
----EDIT----
I consider using timestamp or synced. Which is more effective?

you could use volley library by google or any alternative libraries, it depends on how you want to send the data, the best approach is that you use JSON to make your life easier, get the data from sqlite that you like to sync with your backend and send it over JsonObjectRequest using volley, for example your request can look like this
jsonObjectRequest postForm = new JsonObjectRequest(Request.Method.POST, URL, YourJsonData,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
// here you can get your response.
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// here you can tell there is something went wrong.
}
});
u could add a new value which indicates whether the value has been sync or no from your local database. for example, lets say you have a table called student and this table has three columns which are ID, NAME and synced in above code when your response return success update that row synced column with true|false which indicates whether this row synced with your backend or no. and to get the data from your database you should do something like this.
public String getStudents() {
List<Student> students = new ArrayList<Student>();
String query = "SELECT * FROM student WHERE synced = 0";
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);
if (cursor.moveToFirst()) {
do {
Student st = new Student();
st.setId(cursor.getString(cursor.getColumnIndex(ID)));
st.setName(cursor.getString(cursor.getColumnIndex(NAME)));
st.setSynced(cursor.getInt(cursor.getColumnIndex(SYNCED)));
students.add(st);
} while (cursor.moveToNext());
}
db.close();
return new Gson().toJson(students);
}

The esieast way AFAIK is to use a lib like http://loopj.com/android-async-http/ or volley lije k0sh says to send data to a PHP script that will manage you mysql data.
You'll have to write a php( or java) script in your server to receive your data ( You should write a REST API)
To select your HTTP lib, you should look here:
What is the most robust HTTP library for android?
You should really care about how you are going to sync your datas, because it could drain your battery.
Here you will learn how to optimize your updates:
https://developer.android.com/training/efficient-downloads/index.html
Hope it helps!

Related

Retrieve specific rows from SQLite database on Android - For messaging part of application

I have spent some time researching possible answers on here for the problem as well as referring to the SQLite documentation and tutorials but I still cannot retrieve the specific rows from an SQLite database in an Android App I am writing.
I have a database of messages which I have retrieved from Twitter using the Twitter4j libraries and have stored then in the database, returning all the values and displaying them in a layout is working perfectly but when I try and get a specific chat thread it still returns all the values rather than just messages between the app's user and a specified contact.
I'm familiar with how logic operators work as I am part way through a Degree and have spent two years working with C++, however SQL is new to me.
It could be a case that I cant see the wood for the trees with this but below is the WHERE argument I am passing to the database query() method.
//Values to replace '?' in whereClause
String[] userArgs new String[] { senderName, recipientName };
//Argument to be passed to WHERE in query method
String whereClause = "(user_screen=? AND recipient_screen=?) OR (recipient_screen=? AND user_screen=?)"
The outcome I am trying to achieve is that any messages between the sender and recipient are shown without other messages from the database. The column headings in the database are below:
ID
MessageContent
Recipient
Sender
Time
Profile Image
I think that you want :-
String[] userArgs new String[] { senderName, recipientName, senderName, recipientName };
That is the WHERE clause has 4 parameters to be bound rather than the 2 parameters.

How structure a SQLite database for offline support?

I'm trying to give the offline support in my app, so user can read the information without the internet also. I'm creating an app to show some packages from different companies. I have created my API and following database in the remote server.
companies -- id, name
duration-- id, type (type could be, monthly, weekly, yearly)
packages -- id, name, company_id, duration_id
My API is returning result as
{
"id":3,
"package":"premimum",
"company_id":6,
"duration_id": 5,
}
Now I want to store all information fetched from the API to the local database SQLite. Now I want to know how I should structure my local database? should I structure same as the remote database? should I create the three tables in the local database SQLite same as?
companies -- id, name
duration-- id, type (type mean package duration, monthly, yearly, daily)
packages -- id, name, company_id, duration_id
If yes then id will be different in local database and in the remote database, because both databases will generate own primary key how I can handle this? or something you can suggest me to keep the same ID? or should I keep the id different?
You don't necessarly need the internal Ids on your local database to match those of your cloud server database. For instance, if you use Parse as cloud server, Parse will generate its own internal object ID. Your app does not need to know them.
Having said that, I strongly suggest you have a common key attribute between your local object and cloud object, so that you can keep them synced.
For example you could add a package_id attribute on both your local and cloud object. This package_id will be the same on both side, so that you can update your local object when the remote object is updated and vice-versa.
For example, here is the code to create an object on the Parse server:
ParseObject gameScore = new ParseObject("GameScore");
gameScore.put("score", 1337);
gameScore.put("playerName", "Sean Plott");
gameScore.put("cheatMode", false);
gameScore.saveInBackground();
You can them retrieve this object from the app as follows:
ParseQuery<ParseObject> query = ParseQuery.getQuery("GameScore");
query.getInBackground("xWMyZ4YEGZ", new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (e == null) {
// object will be your game score
int score = object.getInt("score");
String playerName = object.getString("playerName");
boolean cheatMode = object.getBoolean("cheatMode");
} else {
// something went wrong
}
}
});
I suggest you read the documentation for more infos on storing object on a Parse server:
https://docs.parseplatform.org/android/guide/
Also, this lib could help you to sync data between local database and a Parse server:
https://github.com/ntoskrnl/DataSync

Parse query not returning new data on onResume

I'm developing a feature where users can join a room and I'm using Parse to hold the room's data. My problem is that when a new user joins a room and an existing user of that room resumes the room's activity, the existing user doesn't see the new user.
So here's an entry for my table where a user created a room but no one's joined yet.
Here's my GameOnSession class which is an extension of the ParseObject class.
#ParseClassName("GameOnSession")
public class GameOnSession extends ParseObject {
public JSONArray getParticipants() { return getJSONArray("participants"); }
public String getNumberOfParticipants() {
int getParticipantsNumber = getParticipants().length();
return String.valueOf(getParticipantsNumber);
}
public static ParseQuery<GameOnSession> getQuery() {
return ParseQuery.getQuery(GameOnSession.class);
}
}
This is my query that I run to check the number of people in the room.
ParseQuery<GameOnSession> query = ParseQuery.getQuery(GameOnSession.class);
query.whereEqualTo("objectId", QueryPreferences.getStoredSessionId(getActivity()));
query.findInBackground(new FindCallback<GameOnSession>() {
#Override
public void done(List<GameOnSession> objects, ParseException e) {
GameOnSession sess = objects.get(0);
Log.d("GAMEONSESSION", "Current # " + sess.getNumberOfParticipants());
}
});
So it returns what I expect: 0.
Now when a new user joins the room, the entry looks like this.
Then I press a button that runs the query above. And it still returns 0, when I expect 1.
I'm not sure what's the problem here, did I set up my query wrong?
In short, when User A creates a room, the Parse query returns number of users as 0 which is expected. When User B joins User A's room, Parse query STILL returns the numbers of users as 0 and NOT 1. This is unexpected and I'm not sure how to proceed.
So I found out the cause of why my application wouldn't correctly query the database. The reason was that I called Parse to "enableLocalDatastore" so whenever I tried a new query, the application would query from the cached result.
So what I did was disable local datastore and everything's working as expected.
Funny thing was, my team and I that's building this application enable local datastore in the beginning of the development cycle (6 months ago) to test a small feature and forgot to disable it. Technical debt is a PITA.
So if your Parse query is not returning what you expect even though the database clearly shows what you've done, check that you haven't enabled your local datastore!

Android application with Parse - How to respond to events on the server side

I'm trying to write an android application, and I chose to handle the server side with parse.
Suppose I have three tables, TableA, TableB and TableC. I want to call a server-side function each time a row is added to one of my tables, that is: call fa when a row is added to TableA, fb when a row is added to TableB and fc when a row is added to TableC.
I've read the Parse Javascript Guide for server-side code from beginning to end, but could not find any useful information. Would much appreciate any help :)
As far as I understand from your question that, you want to call separate functions for your table add row operations. So, in android side you can differentiate which table is currently used. So based on used table, you can trigger cloud function via ;
ParseCloud.callFunctionInBackground("tableAFunction", params, new FunctionCallback<Object>() {
#Override
public void done(Object response, ParseException exc) {
Log.e("cloud code example", "response: " + response);
}
});
Via this approach, you can call different cloud function based on table selection. In addition, you can send parameters via filling the params hash table (key-value structure.) Hope this helps.
Regards.

Master Sync Database

Hi am syncing my database with server for any incremental values on click of a button. Below is the code which checks all the values and inserts if the data is missing but from android perspective is there any optimized technique to do the sync
//new fields
public void updatenewfileds(ArrayList<String> s) {
ArrayList<String> da = new ArrayList<String>();
try {
String manu = "select MANUFACTURERID from ManufacturerDesc";
Cursor cc = mDb.rawQuery(manu,null);
Log.d("Cursor count", "Count =" + cc.getCount());
if (cc != null && cc.getCount() > 0) {
if (cc.moveToFirst());
do {
da.add(cc.getString(cc.getColumnIndex("MANUFACTURERID")));
System.out.println("here got all alreday avilable ids"+ cc.getString(cc.getColumnIndex("MANUFACTURERID")));
} while (cc.moveToNext());
cc.close();
} else {
cc.close();
}
// need to add only missing data
for(int i = 0; i<da.size(); i++){
boolean flag = false;
System.out.println(flag);
for(int j=0; j<i; j++){
if(da.get(i).equals(s.get(i*2))){
flag = true;
break;
}
}
if(flag == false){
String sql = "insert into ManufacturerDesc values('"+ s.get(i*2)+"','"+ s.get(i*2+1)+"');";
System.out.println("item inserted into db"+ s.get(i*2) +"******" + s.get(i*2+1) );
mDb.execSQL(sql);
}
}
} catch (SQLException mSQLException) {
Log.e(TAG, "getTestData >>" + mSQLException.toString());
throw mSQLException;
}
}
This would be my suggestion, the [] are just to emphasize, as I might get back to it:
Design your Android database tables like: { _id, [server_id], .. your data .. }
On all your tables on the server add a [time_changed] timestamp.
Whenever your device gets synced with the server, the server should additionally send a last sync timestamp e.g. System.currentTimeMilliseconds() (letting the server do this to avoid relying on synced clocks). This timestamp is stored on the android device and used whenever requesting a new sync.
When the server receives a sync request the stored last sync timestamp is yet again handed to the server from the device. Now a simple query can extract all the relevant added data since the timestamp (minus some constant to ensure you get everything). For example SELECT * FROM Mydata WHERE (time_changed > (last_sync-5000)); 5000 being 5 seconds.
Now as you receive data from the server, remember to add the [server_id], which is just the autoincremented _id from the server. This enables you to deduce whether some of thee received rows are known (which is likely with the minus 5 seconds above).
The deduction is a simple query on the device e.g: Do I already have a row with [server_id], if not we add it, if yes then skip it.
With this method you avoid to send more and more information over time, as you only send the rows that are changed after the last sync (plus a bit more).
If you edit rows on your server, simply update time_changed again to reflect the edit . Then it will automatically be included and overwritten on the device during the next sync.
If you plan on doing a lot of database operations on the android device, I would suggest trying MotoDev, it can be plugged in to eclipse and has some nice database features. Including a database perspective and automatic generation of ContentProviders (nice classes to make database operations simple).
Giving a full explanation or guide to do all this is way out of the scope of this answer. It should merely give you an idea as to how it can be done, and if you wish to improve your syncing process, you now have some guidance.
Regarding mechanism as autoincrement and timestamp on a database, there is plenty of examples to find on the interwebz.
Happy coding :)

Categories

Resources