Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I am developing a client-server application which requires remote database connection.
I know that tutorials on the web and more people are using PHP to interact with MySQL. But, I am not so good at PHP and my prior experience is with Core Java, Swing and JDBC.
Can anyone guide me if it is possible to connect remote MySQL database using JAVA JDBC APIs in Android application?
Basically: you can connect to your MySQL (or whatever you use) server, but you should not do this directly from your Android application.
Reasons:
Android applications can be decompiled, and the client will have credentials to access to your database. If using the right hacking tools like Backtrack, then this malicious client can access, connect and exploit the data in your database.
If your application is for clients all around the world, then the clients should open and maintain a connection to your database per operation or set of operations. Opening a physical database connection takes a lot of time, even when your pc client is in a LAN next to the database engine server. Now, imagine opening a connection from a country in the other side of the world e.g. China or Japan or from a country in South America like Brazil or Peru (where I live).
For these 2 reasons I can come up with, it's a bad idea even trying to connect to MySQL or any other database engine directly from your phone device.
How to solve this problem? Use a service oriented architecture where you will have at least two applications:
Service provider application. This application will create and publish web services (preferably RESTful) and may establish policies to consume the web services like user authentication and authorization. This application will also connect to the database and execute CRUD operations against it.
Service consumer application. This would be your Android (or any other mobile) application.
From your question, you're focusing on the point 1. As I've said in my comments, you can create a Web application in Java, create a RESTful service there, which boils down to a POJO (plain old java object) that has a method per service. In this method, since it's plain Java after all, you can add other functionality like JDBC usage.
Here's a kickoff example using Jersey, Jackson (JSON library) and JDBC:
#Path("/product")
public class ProductRestService {
#GET
#Path("/list")
#Produces(MediaType.APPLICATION_JSON)
public List<Product> getProducts() {
List<Product> productList = new ArrayList<>();
Connection con = ...; //retrieve your database connection
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, name FROM product");
while (rs.next()) {
Product product = new Product();
product.setId(rs.getInt("id"));
product.setName(rs.getString("name"));
productList.add(product);
}
//ALWAYS close the resources
rs.close();
stmt.close();
conn.close();
return productList;
}
}
You can check for further configurations of the Java web application in a tutorial like mkyong's or Vogella's or any other of your like (it's too much info to place in this answer).
Note that then this application can evolve into a layered application, and the JDBC code will go in a DAO class, and then the ProductRestService class will access to the database through this DAO class. Here's another kickoff example:
public class ProductDao {
public List<Product> getProducts() {
List<Product> productList = new ArrayList<>();
Connection con = ...; //retrieve your database connection
//the rest of the code explained above...
return productList;
}
}
#Path("/product")
public class ProductRestService {
#GET
#Path("/list")
#Produces(MediaType.APPLICATION_JSON)
public List<Product> getProducts() {
ProductDao productDao = new ProductDao();
return productDao.getProducts();
}
}
And you can apply other changes to this project as well as is evolving.
Can you say me what PHP does here? (if I develop with PHP)
Instead of writing the Service provider application in Java (as shown above), you can do it in PHP. Or in Python, Ruby, C#, Scala or any other programming language that provides this technology to you. Again, I'm not sure what kind of tutorial you're reading, but this should be explained somewhere and explain that for the purposes of that tutorial you will create the services using PHP. If you feel more comfortable writing these services in Java rather than in PHP or any other language, there's no problem. Your android app doesn't really care which technology is used to produce the web services, it will only care about consuming the services and that the data from them can be consumed.
It is possible to do it but not recommended. I have done it before as I was in the same boat as you so I will share some code.
I used this jdbc jar specifically: https://www.dropbox.com/s/wr06rtjqv0q1vgs/mysql-connector-java-3.0.17-ga-bin.jar?dl=0
now for the code:
package com.example.test.databaseapp;
import java.sql.DriverManager;
import java.sql.SQLException;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
public class MainActivity extends Activity {
static final String url = "jdbc:mysql://x.x.x.x:xxxx/DBNAME";
static final String user = "client";
static final String pass = "password";
public static List<objClass> objList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Download(MainActivity.this, internalUrl).execute(); //async task for getting data from db
}
}
Now for my async task:
public class Download extends AsyncTask<Void, Void, String> {
ProgressDialog mProgressDialog;
Context context;
private String url;
public Download(Context context, String url) {
this.context = context;
this.url = url;
}
protected void onPreExecute() {
mProgressDialog = ProgressDialog.show(context, "",
"Please wait, getting database...");
}
protected String doInBackground(Void... params) {
try {
Class.forName("com.mysql.jdbc.Driver");
java.sql.Connection con = DriverManager.getConnection(url, user, pass);
java.sql.Statement st = con.createStatement();
java.sql.ResultSet rs = st.executeQuery("select * from table");
list = new ArrayList<objClass>();
while (rs.next()) {
String field= rs.getString("field");
MainActivity.playerList.add(new objectClass(field));
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return "Complete";
}
protected void onPostExecute(String result) {
if (result.equals("Complete")) {
mProgressDialog.dismiss();
}
}
}
Make sure to include internet permissions in the manifest.
Feel free to ask more questions about my code if you have any.
You can't access a MySQL DB from Android natively. EDIT: Actually you may be able to use JDBC, but it is not recommended (or may not work?) ... see Android JDBC not working: ClassNotFoundException on driver
See
http://www.helloandroid.com/tutorials/connecting-mysql-database
http://www.basic4ppc.com/forum/basic4android-getting-started-tutorials/8339-connect-android-mysql-database-tutorial.html
Android cannot connect directly to the database server. Therefore we
need to create a simple web service that will pass the requests to the
database and will return the response.
http://codeoncloud.blogspot.com/2012/03/android-mysql-client.html
For most [good] users this might be fine. But imagine you get a hacker that gets a hold of your program. I've decompiled my own applications and its scary what I've seen. What if they get your username / password to your database and wreak havoc? Bad.
Related
I have created an app which is relied on my local server which fetch profile image and information about user..Code works fine without any problem but when I change my data in the local server (for example profile picture )the updated profile is not reflecting in the application until activity is restarted but this should not be happened because live data should reflect the change immediately as soon as changes occurred in the database.
below is the code of live data class
private MutableLiveData<Profile> profileMutableLiveData;
public void init(String token){
if (profileMutableLiveData!=null){
return;
}
repository=Repository.getInstance();
profileMutableLiveData=repository.getProfile(token);
}
public LiveData<Profile> getProfile(){
return profileMutableLiveData;
}
here is my Repository code
public class Repository {
private static Repository instance;
public static Repository getInstance(){
if (instance==null){
instance=new Repository();
}
return instance;
}
public MutableLiveData<Profile> getProfile(String token){
MutableLiveData<Profile> data=new MutableLiveData<>();
RetrofitApi retrofitApi=RetrofitInstance.getInstance();
Call<Profile> call=retrofitApi.getProfile(token);
call.enqueue(new Callback<Profile>() {
#Override
public void onResponse(Call<Profile> call, Response<Profile> response) {
Profile profile=response.body();
if (response.isSuccessful()){
data.setValue(profile);
}
}
#Override
public void onFailure(Call<Profile> call, Throwable t) {
}
});
return data;
}
}
Code in main activity to observe changes....
actually I am showing profile image in navigation drawer ... like telegram app
viewModelClass = new ViewModelProvider(this).get(ViewModelClass.class);
viewModelClass.init(token);
viewModelClass.getProfile().observe(this, new Observer<Profile>() {
#Override
public void onChanged(Profile profile) {
Picasso.get().load("http://192.168.43.216:8000" + profile.getProfile_photo()).into(profileImage);
fName = profile.getFirst_name();
lName = profile.getLast_name();
image = profile.getProfile_photo();
nameView.setText("Hello " + profile.getFirst_name());
}
});
}
The code is working fine but I want the data must be updated as soon as changes made in my server...
but data is updated when I restart the activity or opening app again after closing the activity...
May be the problem - is that you begin to observe in your activity one instance of MutableLiveData, and then you replace it with another one.
In your ViewModel:
profileMutableLiveData=repository.getProfile(token);
you override it instead of setting new value with "postValue"
In your Repository:
MutableLiveData<Profile> data=new MutableLiveData<>();
you make another instance of LiveData
You can try to change your return value from a Repository to a "Profile" and set it as a new value of MutableLiveData in your ViewModel with "postValue"
UPDATED
I've read your question more carefully. I think my answer above wouldn't give you what you expect (in case you expect Retrofit should update LiveData instantly like ROOM does)
So my thoughts:
You expect too much using LiveData+Retrofit. Just using them doesn't mean you'll get on-line updates of your data on your server. To achieve that you have to change mechanism of your interaction with your server, not just fix few lines in code you've shown.
There is mechanism LiveData+ROOM that works with local DB (Sqlite) in a way, that you expect from LiveData+Retrofit. But there is no magic there. Room is using mechanic, that built-in in Sqlite for notifying (triggering) when there are some changes in DB tables occur. But Retrofit doesn't implement similar mechanism with Rest Api and actually it's not its responsibility.
To achieve what you want you can look at several possibilities:
To use some Cloud Service API, that contains that built-in mechanism for notifying your device when data changes (Firebase, for example)
To implement some kind of periodic synchronisation of your app data with server. After this synchronisation you'll have all data on device and depending on where you put your data you could observe changes with LiveData+Room or FileObserver.
To simplify your case and refresh your data from the server at activity explicitly after click on Button "Refresh" on your activity. In that case you can implement steps that I wrote at first version of my answer.
I am fetching data from web service and storing it to SQLite database. The records I'm fetching are more in number so it takes too much time to fetch the data. I want to fetch this data such that while fetching data I can use the app. That is I want to fetch data in the background like we download large files. While downloading we can do the rest of task. How should I implement it in my app?
you should call web service from asyncktask which works on background.
#Override
protected String doInBackground(String... params) {
//call your webservice from here;
return ;
}
In the beginning I was using my custom Asynctask, but fetch information from a web service was very slow. Now I use Retrofit, and the time is very short. My best advice is that implements Retrofit.
Retrofit
already there are lot of answers similar to this is available, anyway here is the sample code.
create a innerClass name as MyAsyncClass
class MyAsyncClass{
#Override
protected Object doInBackground(Void... params) {
// write your method to fetch data from network
}
#Override
protected void onPostExecute(Object output) {
// do what ever you want to do with 'output' data
}
}
now you can call this class like this
MyAsyncClass myAsyncClass = new MyAsyncClass();
myAsyncClass.execute();
and here you can open some new activity.
Intent intent = new Intent(this, yourActivityName.class);
startActivity(intent);
I am currently a beginner developer working on a few side projects trying to make my way into the world of android java development.
My question today however applies to most OOP driven concepts.
Let me get to the point. My Android application has some activities and methods that end up having very heavy database queries, this makes it quite slow especially because i am using an ORM (SugarORM) to make things faster (Development wise) and less buggy.
I have already found a solution to this and it seems to be working quite well, however, I thought it would be best to ask about it before integrating it through out the application. Just to know if this is generally a bad practice or not.
Unfortunately, google wasn't very helpful, mostly because the keywords needed to search for a similar question would always take me to the Null Object Pattern :|
Example of my implementation:
private List<Book> mBooks;
public List<Book> getBooks() {
if (this.mBooks == null)
this.mBooks = SugarRecord.listAll(Book.class);
return this.mBooks;
}
public List<Book> onBookListUpdated() {
this.mBooks = null;
}
As you can see this ensures that the query is only executed once (at least until the list is expected to have changed).
What I would need to know is if this makes sense, how many programmers would actually do something like this and if it is a thing what's it called?
Furthermore if it is 'ok' to do, would it be a good idea to wrap this logic in a class?
Say something like:
public class FastList<T> {
public interface iFastList<TT> {
List<TT> reloadList();
}
public FastList(iFastList<T> inCallback) {
this.callback = inCallback;
}
private iFastList<T> callback
private List<T> currentList;
public List<T> getList() {
if (this.currentList == null)
this.currentList = this.callback.reloadList();
return this.currentList;
}
public void onListChanged() {
this.currentList = null;
}
}
Thanks in advance to all that will take time to answer.
Lazy Intitailization
its very popular in objective-c
- (NSMutableArray *) myArray {
if(!_myArray) {
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
I'm using Google's mobile backend starter for a project and I want to set the key name myself for some of entities instead of using the auto-generated one.
If I were doing this without the backend I could do something like it describes in the datastore documentation which creates an employee entity with the key name "asalieri":
Entity employee = new Entity("Employee", "asalieri");
Here's the code I'm using to create the entity. I've been trying to use the CloudEntity.setId() function. Upc is a string and it doesn't work when I use a hardcoded string either.
CloudEntity avg = new CloudEntity("Aggregate");
avg.setId(upc);
avg.put("averagePrice", sum/count);
insertAverage(avg);
private void insertAverage(CloudEntity avg) {
CloudCallbackHandler<CloudEntity> handler = new CloudCallbackHandler<CloudEntity>() {
#Override
public void onComplete(final CloudEntity result) {
Toast.makeText(AddProduct.this, "Average updated.", Toast.LENGTH_LONG).show();
}
#Override
public void onError(final IOException exception) {
handleEndpointException(exception);
}
};
// execute the insertion with the handler
getCloudBackend().insert(avg, handler);
}
When I run the app everything works fine except that the new entity doesn't have the custom ID that I set.
The only thing I can think of is that setId() isn't supposed to do what I think it does but I've been digging through the code and haven't been able to find another way to do what I want.
Does anyone know why this isn't working?
I'm a Googler on the MBS project. I recreated your issue and first glance shows this as a bug on our side. I'll edit my response with updates.
Would this workaround be ok until we push a fix?
avg.put("samId", upc)
I have created a database in my application with 5 tables. my database is being updated from different threads. When i see the log i can see that there are database locked exception while opening the database if it is already open.
One of my friend suggested me to always use content provider to avoid this issue. According to him content provider manages concurrency issues on its own?
Is it a good practice to use content provider if we don't want to share data to other applications?
I think using a read-write lock is enough in most cases.
Suppose you have written the following,
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class MyDatabase extends SQLiteOpenHelper
{
private static final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
private static void beginReadLock()
{
rwLock.readLock().lock();
}
private static void endReadLock()
{
rwLock.readLock().unlock();
}
private static void beginWriteLock()
{
rwLock.writeLock().lock();
}
private static void endWriteLock()
{
rwLock.writeLock().unlock();
}
then you can do your task like the following.
public static void doSomething()
{
SQLiteDatabase sldb = null;
try
{
beginReadLock();
MyDatabase mydb = new MyDatabase();
sldb = mldb.getReadableDatabase();
......
}
catch (Exception e)
{
......
}
finally
{
if (sldb != null)
{
try
{
sldb.close();
}
catch (Exception e) {}
}
endReadLock();
}
}
Enclose read operations with beginReadLock() and endReadLock(). Likewise, enclose write operations with beginWriteLock() and endWriteLock().
Months ago, by the solution described above, I could solve my own database-lock issue where multiple threads were trying to read/write-open a database simultaneously.
The problem is that you use several database connections to your database. Thus, several threads try to update your table simultaneously and all these threads have different connections to your database.
To avoid this problem in all your threads you need to use the same connection to the database, i.e. all your threads should use the same connection to the database (that is represented by SQLiteDabase object).
Moreover, so as there is a file block on a sqlite file you'll not improve the performance of database upgrade using several threads (it's better to use only one thread to work with database). If you want to use several threads, you should use the same connection to the database and in this case Android will manage locks.
The discussion of this problem you can find here: http://touchlabblog.tumblr.com/post/24474398246/android-sqlite-locking and here: What are the best practices for SQLite on Android?