Singleton Android Not Able To Access Data - java

I am trying to take data from one class, put it in a singleton, and then proceed to access that data in another class.
Here is my singleton:
public class DataTransferrer {
private static DataTransferrer instance = null;
private ArrayList<Transients> transientList;
private DataTransferrer(){
transientList = new ArrayList<>();
}
public static synchronized DataTransferrer getInstance(){
if(instance == null) {instance = new DataTransferrer();}
return instance;
}
public ArrayList<Transients> getTransients(){return this.transientList;}
public void setTransients(ArrayList<Transients> t){transientList = t;}
public void printAll(){
for(Transients t : transientList){
Log.d("DEBUG DA",t.getDa().toString());
}
}
}
Here is my method that adds data to an arrayList which is then set to the arrayList within the singleton to then be used elsewhere:
public class JSONParser {
// URL to get contacts JSON
private static String url = "http://pi.cs.oswego.edu/~lpatmore/getAllTransients.php";
ArrayList<Transients> transientList;
private Transients t;
public JSONParser(){
transientList = new ArrayList<>();
}
public void execute(){
new GetTransients().execute();
}
/**
* Async task class to get json by making HTTP call
*/
private class GetTransients extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... arg0) {
HttpHandler sh = new HttpHandler();
// Making a request to url and getting response
String jsonStr = sh.makeServiceCall(url);
Log.d(TAG, "Response from url: " + jsonStr);
if (jsonStr != null) {
try {
Log.d(TAG, "NOT NULL DEBUG");
JSONObject jsonObj = new JSONObject(jsonStr);
// Getting JSON Array node
JSONArray transients = jsonObj.getJSONArray("result");
// looping through All Transients
for (int i = 0; i < transients.length(); i++) {
JSONObject c = transients.getJSONObject(i);
String author = c.getString("author");
String transientId = c.getString("transientId");
String dateAlerted = c.getString("dateAlerted");
String datePublished = c.getString("datePublished");
float right_asencsion = (float) c.getDouble("right_asencsion");
float declination = (float) c.getDouble("declination");
t = new Transients(author, transientId, dateAlerted, datePublished, right_asencsion, declination);
// adding trans to trans list
transientList.add(t);
}
DataTransferrer.getInstance().setTransients(transientList);
DataTransferrer.getInstance().printAll();
} catch (final JSONException e) {
Log.e(TAG, "Json parsing error: " + e.getMessage());
}
} else {
Log.e(TAG, "Couldn't get json from server.");
}
return null;
}
}
}
And I try to access it in another class like:
for(int i = 0; i < DataTransferrer.getInstance().getTransients().size(); i++){
System.out.println("DEBUG ADDED TRANSIENT");
Float ra = DataTransferrer.getInstance().getTransients().get(i).getR();
Float dec = DataTransferrer.getInstance().getTransients().get(i).getD();
transients.add(new Transient("Transient", names[i], GeocentricCoordinates.getInstance(ra, dec)));
}
Whenever I debug it checking to see in the other class, I am trying to access the arraylist, but in the other class it's like there was never an array list added to getTransients() method.
I tested this exact setup in Eclipse and it works perfectly but not in Android Studio.

I tested this exact setup in Eclipse and it works perfectly but not in
Android Studio.
Basically, you have got multithreading issue i.e., your DataTransferrer class is NOT threadsafe which is causing the issue i.e., there is a race condition between multiple threads and your singleton instance is NOT actually singleton (multiple objects are being created for DataTransferrer class).
So when you calling setTransients() from one thread, it is actually storing the Transients in one instance of DataTransferrer and then when you are trying retrieve the Transients from a different thread it is using different instance (object) of DataTransferrer.
You can create threadsafe singleton instance for your DataTransferrer class as shown below:
public class DataTransferrer {
private static final DataTransferrer instance = new DataTransferrer();
private ArrayList<Transients> transientList = new ArrayList<>();
//private constructor, so instantiation NOT allowed
private DataTransferrer() {}
public static synchronized DataTransferrer getInstance(){
return instance;
}
public ArrayList<Transients> getTransients(){
return this.transientList;
}
public void setTransients(ArrayList<Transients> t){
transientList = t;
}
public void printAll(){
for(Transients t : transientList){
Log.d("DEBUG DA",t.getDa().toString());
}
}
}
Also, note that in the above code, the instance will be created eagerly during the startup (initializing) time and when you call setTransients and getTransients that same instance (singleton) will be used. Instead of eager creation, if you need to create the instance lazily, you can look here.

Related

Global Variable appending multiple copies of data while updating

I have created a class named "Global Services" which I use to save my data globally and access them in a different activity. But when I am calling the set() method, instead of overview the existing data instead it is appending that data. Below is my code.
I have even tried to remove the instance but still, it is appending the new data instead of overwriting.
import java.util.ArrayList;
import java.util.List;
public class GlobalServices {
private static GlobalServices instance;
String partner, leadsResponse;
List<Leads> assignedList = new ArrayList<>();
List<Leads> unAssignedList = new ArrayList<>();
List<Inventory> listInventory = new ArrayList<>();
private GlobalServices() {}
public static GlobalServices getInstance() {
if (instance == null) {
instance = new GlobalServices();
}
return instance;
}
public static void destory() {
instance = null;
}
public String getPartner() {
return partner;
}
public String getLeadsResponse() {
return leadsResponse;
}
public List<Leads> getAssignedList() {
return assignedList;
}
public List<Leads> getUnAssignedList() {
return unAssignedList;
}
public List<Inventory> getListInventory() {
return listInventory;
}
public void setPartner(String partner) {
this.partner = partner;
}
public void setLeadsResponse(String leadsResponse) {
this.leadsResponse = leadsResponse;
}
public void setAssignedList(List<Leads> assignedList) {
this.assignedList = assignedList;
}
public void setUnAssignedList(List<Leads> unAssignedList) {
this.unAssignedList = unAssignedList;
}
public void setListInventory(List<Inventory> listInventory) {
this.listInventory = listInventory;
}
}
The problem is that you're just assigning new references to your lists in GlobalServices but not creating new lists. This means as soon as you modify this reference from another place in your code, it will be reflected in the GlobalServices list as well. All you have to do is:
public void setAssignedList(List<Leads> assignedList) {
this.assignedList = new ArrayList<>(assignedList);
}
public void setUnAssignedList(List<Leads> unAssignedList) {
this.unAssignedList = new ArrayList<>(unAssignedList);
}
public void setListInventory(List<Inventory> listInventory) {
this.listInventory = new ArrayList<>(listInventory);
}
This way a new copy will be created in memory for each list and the data will be overwritten.
Sorry if I was wrong, but your code here is not a problem.
The problem might come from other part of your application.
The data you set might be the data that extend your current data.
Example you have
GlobalServices instance = GlobalServices.getInstance()
List<Inventory> listInventory1 = new ArrayList<>();
listInventory1.add(new Inventory());
instance.setListInventory(listInventory1); // now your inventory have one item
// In some where else in your project
List<Inventory> listInventory2 = instance.getListInventory(); // lisInventorys.size() equals 1
// Then you add more data to listInventory2 by mistake
listInventory2.add(new Inventory()); // listInventory2.size() equals 2
// Then you set back listInventory2 to your global service
instance.setListInventory(listInventory2); // now your inventory have two item
So, the data had been actually overwrite, it data just been extended by accident.

Check if data exists in database and insert new if not found using multiple threads?

My requirement is something like this:
I have written a code which checks for data in database and if data is not found, it consumes third party rest API in order to get data. Some logic is performed and finally obtained data is stored in database.
public void exampleMethod(){
MyObject myObj = getObjFromDatabase(); //getting from db
if(myObj==null){ //not found in db
myObj = getObjFromThirdParty(); //consuming rest api
//some logic here in case myobj is not in db....
}else{
//some other logic here in case myobj is in db...
}
saveObjInDatabase(myObj); //saving it in database
}
I need it to be saved in database just once. Getting response from third party API takes some time and this method gets executed from multiple threads.
Now, the problem is I need it to be saved in database just once, but before one thread could save data in database another thread gets null from database, logic that should get executed only when "data is not in db" gets executed and saves same data more than once. (I am using mongoDB to store data)
How can I solve this problem? Thank you.
What you are asking about is caching. Here is an example that should work decently. It synchronizes on getObj in case the object needs to be loaded. If the object is fresh then getObj returns very quickly and hardly blocks the other threads, but if it needs to load the object then the other threads will wait until the object is loaded.
public class Test {
// used when you want to refresh the cached object
private static boolean refreshNext = false;
// a static reference to your object that all threads can use
private static MyObj cachedObj = null;
private static void message(String msg) {
System.out.println(
System.currentTimeMillis() + " : " + Thread.currentThread().getName() + " : " + msg);
}
private static void sleep(long milli) {
try { Thread.sleep(milli); } catch (Exception e) { }
}
// represents the object stored in the db
private static MyObj myObjInDb = null;
private static void saveObjInDb(MyObj obj) {
// TODO do real saving to db and handle errors
message("storing in db...");
myObjInDb = obj;
}
private static MyObj loadObjFromDb() {
// TODO do real loading from db and handle errors
message("getting from db...");
sleep(1000);
return myObjInDb;
}
private static MyObj loadObjFromVendor() {
// TODO do real fetching from vendor and handle errors
message("getting from vendor...");
sleep(2000);
return new MyObj();
}
private static MyObj loadObj() {
message("loading object...");
MyObj obj = loadObjFromDb();
if (obj == null) {
message("db didn't have it.");
obj = loadObjFromVendor();
saveObjInDb(obj);
}
return obj;
}
/** Returns the object, first loading and caching if needed. */
public static synchronized MyObj getObj() {
// only one thread can get the object at a time, in case it needs to be loaded.
if (cachedObj == null || refreshNext) {
// load and cache the object
cachedObj = loadObj();
refreshNext = false;
}
return cachedObj;
}
public static void exampleMethod() {
MyObj obj = getObj();
message(obj.toString());
// ... do stuff with obj
}
private static class MyObj {
public final String data = "I have data!";
#Override public String toString() { return data; }
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++)
new Thread(Test::exampleMethod).start();
}
}

How do I call a Local Variable from Outside of its Method in Java

public void main(String[] args) throws Exception {
URL url = new URL("https://api.coinmarketcap.com/v1/global/");
InputStreamReader reader = new InputStreamReader(url.openStream());
MyData data = new Gson().fromJson(reader, MyData.class);
}
public static class MyData {
String total_market_cap_usd;
}
So what I need is to call this local variable "data" in a different class. So what I need is to somehow create a global variable with the same value as this local one, the problem is that the class this local variable is made from doesn't allow anything but final before it so could someone please help me out here. I'm really stumped and I haven't been able to find any resources to help me.
I'm sorry if I sound like a complete noob here because my experience in lower level coding languages is limited.
Based on your requirement you mentioned in comment, I tried achieving. See if it helps.
public class ChatBot {
String response;
public static void main(String[] args) throws Exception {
ChatBot c1 = new ChatBot();
String qry1 = c1.getResponse("total_market_cap_usd");
System.out.println("response: "+qry1);
}
public static class MyData {
String total_market_cap_usd;
String total_24h_volume_usd;
}
public String getResponse(String query) {
URL url = null;
try {
url = new URL("https://api.coinmarketcap.com/v1/global/");
InputStreamReader reader = new InputStreamReader(url.openStream());
MyData data = new Gson().fromJson(reader, MyData.class);
switch (query) {
case "total_market_cap_usd":
response = data.total_24h_volume_usd;
break;
case "total_24h_volume_usd":
response = data.total_24h_volume_usd;
break;
default:
throw new IllegalArgumentException("Query not recognized!");
}
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
}
Here is your answer :
https://docs.oracle.com/javase/tutorial/java/javaOO/thiskey.html
Since you wish to use your variable in a different class,you can use extends keyword as:
public class SubClass extends SuperClass{
//you can override the implementation
//#Override
public void getVariable(){
<datatype> var = this.localVariableOfPreviousClass;
}
}

UI Thread blocking during API calls - is AsyncTask usage correct?

I'm rather new to creating apps for Android, but what I am trying to essentially do is sync an API (using Volley) completely to my App in the first instance, and then periodically thereafter (I gather Volley creates new threads itself).
Whilst the app is syncing with the API the main UI Thread is getting blocked (I'm seeing a lot of "Skipped frames" in the logs). I have tried running the code to get the data and create in the database within an AsyncTask thread, and while I can see that the AsyncTask threads were created the Main UI thread still is sluggish.
I was thinking that since the initial call is within an AsyncTask, any sub ones will be within the same AsyncTask thread?
I'm also not 100% sure if the anothe rpage exists, whether I can execute another AsyncTask query from within doInBackground(), or if I should just call doInBackground inself?
I hope the below will be enough to get some help/pointers, but if any more is required, I will gladly update this question.
MainActivity
public class MainActivity extends AppCompatActivity {
public SQLiteHandler sqlh;
public DatabaseManager dbm;
public API API;
private Handler handler=new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
...
API = new API(getApplicationContext());
sqlh = new SQLiteHandler(getApplicationContext());
dbm = new DatabaseManager();
dbm.initializeInstance(sqlh);
...
// start 5 seconds after loading, then run every minute
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run(){
getChanges("thisApiUrl");
API.processSyncOut();
handler.postDelayed(this, 60000);
}
},5000);
...
}
private class getChangesTask extends AsyncTask<String, Void, Void>(){
#Override
protected Void doInBackground(String... params) {
String url = params[0];
final SQLiteHandler sqlh = new SQLiteHandler(getApplicationContext());
final DatabaseManager dm = new DatabaseManager();
dm.initializeInstance(sqlh);
JSONObject response = API.doAction(url, new API.VolleyActionCallback() {
#Override
public void onActionSuccess(Object response) {
....
for (Map.Entry<String, JsonElement> entry : entrySet) {
....
HashMap<String, String> addRecord = new HashMap();
addRecord.put("id", dataObj.get("id").getAsString());
addRecord.put("data", dataObj.get("data").getAsString());
addRecord.put("updated_at", dataObj.get("updated_at").getAsString();
dm.createRecord(addRecord);
....
}
if (!json.getString("next").equals("")) {
// if another page exists, go to it
getAPIChangesTask task = new getAPIChangesTask();
task.execute(json.getString("next"));
}
....
}
});
}
}
private Void getChanges(String url){
getChangesTask task = new getChangesTask();
task.execute(url);
}
}
DatabaseManager
Just a side question, but can a writeable database also do reading at the same time? Thinking that I could probably combine both methods below?
public class DatabaseManager {
private int rOpenCounter;
private int wOpenCounter;
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase rDatabase;
private SQLiteDatabase wDatabase;
public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}
public static synchronized DatabaseManager getInstance() {
if (instance == null) {
throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
" is not initialized, call initializeInstance(..) method first.");
}
return instance;
}
public synchronized SQLiteDatabase getReadableDatabase() {
rOpenCounter++;
if(rOpenCounter == 1) {
// Opening new readable database
rDatabase = mDatabaseHelper.getReadableDatabase();
}
return rDatabase;
}
public synchronized SQLiteDatabase getWritableDatabase() {
wOpenCounter++;
if(wOpenCounter == 1) {
// Opening new writable database
wDatabase = mDatabaseHelper.getWritableDatabase();
}
return wDatabase;
}
public synchronized void closeReadableDatabase() {
rOpenCounter--;
if(rOpenCounter == 0) {
// Closing readable database
rDatabase.close();
}
}
public synchronized void closeWritableDatabase() {
wOpenCounter--;
if(wOpenCounter == 0) {
// Closing writable database
wDatabase.close();
}
}
public Long createRecord(HashMap<String, String> dataTable){
String id = dataTable.containsKey("id") ? dataTable.get("id") : "";
String data = dataTable.containsKey("data") ? dataTable.get("data") : "";
String updatedAt = dataTable.containsKey("updated_at") ? dataTable.get("updated_at") : "";
// we don't want ID to be set, as probably will be therefore updating something
if (!id.isEmpty()) {
SQLiteDatabase db = DatabaseManager.getInstance().getWritableDatabase();
ContentValues values = new ContentValues();
values.put("data", data);
values.put("updated_at", updatedAt);
// insert row
Long insertId = db.insert("this_table", null, values);
DatabaseManager.getInstance().closeWritableDatabase();
return insertId;
}
return null;
}
}
}

Transferring ArrayList to Global ArrayList in Android

I have created Global Variables that are required at different steps in the entire Application Cycle. Below is the GlobalVariable.java created:
import android.app.Application;
public class GlobalVariable extends Application {
private String globalVariableOne;
public String getGlobalVariableOne() {
return globalVariableOne;
}
public void setGlobalVariableOne(String globalVariableOne) {
this.globalVariableOne = globalVariableOne;
}
}
Then I am easily able to Get and Set this Global Variable from any activity using the below code.
final GlobalVariable globalVariable = (GlobalVariable) getApplicationContext();
//To Set the globalVariableOne Value in an Activity
globalVariable.setGlobalVariableOne("My Value");
//To Get the globalVariableOne Value in a
String readGlobalVariableOne = globalVariable.getGlobalVariableOne();
Now I have an ArrayList localArrayList created by using the below code in my Activity.
List<String> localArrayList = new ArrayList<String>();
int maxLength = "10";
for (int i = 0; i < maxLength; i++) {
String valueOne = "Value "+i;
String valueTwo = "Value "+i;
localArrayList.add(valueOne));
localArrayList.add(valueTwo);
}
I want this ArrayList to be stored as a Global ArrayList accessible and editable by any Activity within my Application. I am not sure how to do it.
Can anyone help me in editing my GlobalVariable.java to define the Global ArrayList and guide me on how to Get and Set this Global ArrayList?
It should become like this:
public class GlobalVariable extends Application {
private String globalVariableOne;
private List<String> globalArrayList;
public String getGlobalVariableOne() {
return globalVariableOne;
}
public void setGlobalVariableOne(String globalVariableOne) {
this.globalVariableOne = globalVariableOne;
}
public List<String> getGlobalArrayList() {
return globalArrayList;
}
public void setGlobalArrayList(List<String> globalArrayList) {
this.globalArrayList = globalArrayList;
}
}
Then in you code you can do:
GlobalVariable myAppClass = (GlobalVariable)getApplicationContext();
//saving the list
myAppClass.setGlobalArrayList(/*put here the list to save*/);
//getting the list
List<String> globalArrayList = myAppClass.getGlobalArrayList();
But i really don't like this approach of putting every global variable inside the custom Application class...

Categories