I'm using Retrofit2 and DBFlow. I have a problem when I save my data into my database. Certain data are duplicated when I'm calling the thread twice at the same time. The problem is my List because this variable is final. And I have to set final because I need to use this List in my thread.
Then, there is a way to remove the final to my List and replace by something ?
Retrofit onResponse()
public void onResponse(Call<AdminPictures> call, Response<AdminPictures> response) {
AdminPictures apResponse = response.body();
// Here is my list
final List<PictureInfos> pictureInfos = apResponse.getPicturesList();
new Thread(new Runnable() {
#Override
public void run() {
try {
// The List is used here
for (PictureInfos infos : pictureInfos) {
if(!infos.exists()){
infos.save();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
move the declaration of the list to the top of the class and make a class variable...
List<PictureInfos> pictureInfos....
Make it a class variable.
Otherwise, instead of anonymous class; move your code into a separate class. Then pass the variable using constructor
Please try this
synchronized(this) {
for (PictureInfos infos : pictureInfos) {
if(!infos.exists()){
infos.save();
}
}}
What I'm trying, is to first let insert a thread data, meanwhile block another.
Maybe you have request twice and response twice,so thread is excuted twice.
Maybe this is silly, but have you tried clearing the variable at the end of the for cycle?
...
for (PictureInfos infos : pictureInfos) {
if(!infos.exists()) {
infos.save();
}
}
pictureInfos.clear();
...
Related
I am using ActivityScenarioRule for Espresso UI Testing and I wanted to get access to the method getStringArray(), calling which requires the Activity . So, is there any way to retrieve the Activity by the ActivityScenarioRule , maybe something similar to getActivity in ActivityTestRule.
#Rule
public ActivityScenarioRule activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);
I am not using ActivityTestRule, because it is deprecated!
Since it appears you're using Java, here's how you'd do it:
#Rule
ActivityScenarioRule<MainActivity> activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);
#Test
public void test() {
activityScenarioRule.getScenario().onActivity(activity -> {
// use 'activity'.
});
}
Please read the documentation for more info on these new ways of interacting with the activity under test.
For anyone who wants Activity, but that without need to re-write all tests to run on UI-thread, a fairly straightforward Java way to get it:
Waiting for UI
Assume you want to test if a dialog is shown after some delay, the onActivity(...) hook runs on UI-thread, which means waiting in there would cause the dialog to be nerver shown.
In such cases you need to keep a strong-reference to ActivityScenario (as that prevents Activity close).
Test should wait for onActivity(...) hook to be called, then keep passed Activity's reference.
Finally, move test logic out of onActivity(...) hook.
Example
private ActivityScenario mActivityScenario;
#After
public void tearDown() throws Exception {
if (mActivityScenario != null) {
mActivityScenario.close();
}
mActivityScenario = null;
}
#Override
public Activity getActivity() {
if (mActivityScenario == null) {
mActivityScenario = ActivityScenario.launch(getActivityClassForScenario());
}
return tryAcquireScenarioActivity(mActivityScenario);
}
protected static Activity tryAcquireScenarioActivity(ActivityScenario activityScenario) {
Semaphore activityResource = new Semaphore(0);
Activity[] scenarioActivity = new Activity[1];
activityScenario.onActivity(activity -> {
scenarioActivity[0] = activity;
activityResource.release();
});
try {
activityResource.tryAcquire(15000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail("Failed to acquire activity scenario semaphore");
}
Assert.assertNotNull("Scenario Activity should be non-null", scenarioActivity[0]);
return scenarioActivity[0];
}
Espresso states the following:
At the same time, the framework prevents direct access to activities
and views of the application because holding on to these objects and
operating on them off the UI thread is a major source of test
flakiness.
When there is no other way I use the following method to get an arbitrary activity from an ActivityScenarioRule. It uses onActivity mentioned in the accepted answer:
private <T extends Activity> T getActivity(ActivityScenarioRule<T> activityScenarioRule) {
AtomicReference<T> activityRef = new AtomicReference<>();
activityScenarioRule.getScenario().onActivity(activityRef::set);
return activityRef.get();
}
Any onView(...) code inside onActivity led to a timeout in my testcases. So, I extracted the activity and used it with success outside the onActivity. Beware tho! See the statement above.
#Test
fun checkForUpdate() {
val scenario = ActivityScenario.launch(MainActivity::class.java)
scenario.onActivity {
UpdateTool.checkForUpdate(it)
}
}
After calling a batch.commit or docRef.update (part of it calls FieldValue.serverTimeStamp to update time of submission), I call finish(); to go back to previous activity that loads a recycleView of the list of documents that was updated.
I get this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.Date com.google.firebase.Timestamp.toDate()' on a null object reference`
I suspect it's that FieldValue.servertimeStamp takes more time to compute and the app crashes. However, the same field where recyclerView is pulling the datetime from already have an old value.
I'm not sure why the old value is not retrieved, but crashes on null instead.
Q1) Does FieldValue.servertimeStamp make the field null until new datetime is computed?
My guess is, this particular call is waiting for an answer from Firebase server, thus taking more time but other calls are done locally first on the device before updating in the cloud. Some of your insights is appreciated.
In the mean time, as a work-around to stop this asynchronous error, I have used a Thread loop with Thread.sleep while waiting for onCompleteSuccess to respond:
FirestoreFieldUpdated = false;
Thread myThread = new Thread(
new Runnable() {
#Override
public void run() {
try {
while (!FirestoreFieldUpdated) { //db.updateFields will change FirestorefieldUpdated to true uponSuccess
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
finish();
}
}
}
);
myThread.start();
Q2) Is there a more elegant way or better way to do this? Or to enable synchronicity only for this particular transaction of updating datetime?
EDIT (added on more details on what I'm trying to do):
I am trying to call this method from AddNewOrder.java:
public void updateFields(String actionDateField) {
Map<String, Object> updates = new HashMap<>();
updates.put(actionDateField, FieldValue.serverTimestamp());
updateSubmit.update(updates);
}
from a class outside (AddNewOrder.java):
db = new DatabaseHelper(getApplicationContext());
db.updateFields("OrderDate");
finish();
Finish(); will then pass me back to the previous activity that calls RecyclerView:
Query query = mFirestore
.collection("Org")
.document(RootCollection)
.collection("Stores")
.document(StoreID)
.collection("Orders")
.whereGreaterThan("OrderVerified", "")
.limit(queryLimit);
mAdapter = new OrdersAdapter(query, FulfilmentActivity.this) {
#Override
protected void onError(FirebaseFirestoreException e) {
// Show a snackbar on errors
Snackbar.make(findViewById(android.R.id.content),
"Error: check logs for info.", Snackbar.LENGTH_LONG).show();
}
};
mAdapter.setQuery(query);
mOrdersRecycler.setAdapter(mAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mOrdersRecycler.setLayoutManager(layoutManager);
In OrdersAdapter.java, I have this:
Orders orders = snapshot.toObject(Orders.class);
submitDate.setText(FORMAT.format(orders.getOrderDate()));
in public void bind.
The above is the line that NullPointerException appeared on.
Orders.java:
public class Orders {
private Timestamp OrderDate;
public Orders(Timestamp orderDate) { this.OrderDate = orderDate; }
public java.util.Date getOrderDate() { return OrderDate.toDate(); }
}
How do I fix this properly?
First of all, if you're working with threading in order to deal with Firestore, you're almost certainly doing the wrong thing. All Firestore APIs (actually, all Firebase APIs) are asynchronous, and require no threading on the part of your app.
Q1 - there is no intermediate null value in a document that's going to be created with a timestamp. The server interprets the server timestamp token immediately and writes a Timestamp object to the document atomically.
Q2 - I can't really tell what you're trying to accomplish with this code. It's way out of bounds of what you would normally do to write to Firestore. If you want to know if a document has changed in Firestore, you attach a listener to a reference to that document, and the listener is invoked when the document is seen to change. Again, there's no need for threading in this case, because the callbacks are all asynchronous. As long as the listener is added, it will be called.
serverTimeStamp() indeed becomes Null in the cache before getting a confirm response from the server: https://github.com/firebase/firebase-js-sdk/issues/192
Refer to this for solution: https://firebase.google.com/docs/reference/android/com/google/firebase/firestore/Document
I fixed this by adding this:
DocumentSnapshot.ServerTimestampBehavior behavior = ESTIMATE;
Date date = snapshot.getDate("OrderDate", behavior);
I have phone contact numbers list stored in an array and called contactsString[]
and in an online database registered users numbers
I want to count how many registered users are there
and there is my code
for (i=0;i<contactsString.length-1;i++){
Phone phone=new Phone();
phone.phone=contactsString[i]
WebService.getInstance().getApi().checkNumber(phone).enqueue(new Callback<MainResponse>() {
#Override
public void onResponse(Call<MainResponse> call, Response<MainResponse> response) {
if (response.body().status==1){
availableUsers++;
}
}
#Override
public void onFailure(Call<MainResponse> call, Throwable t) {
}
});
}
my problem is the web service response is delayed so it don't count and availableUsers is printed it's initial value which is 0
I would try better sending an array of Phone objects. In this way you would get the correct answer in 1 call.
I would never do this in the way you implemented: imagine you have 500 contacts: you will be doing 500 calls to your server. Now imagine you have 100000 users with 500 contacts each
Try to customize your api call in this format. Which uses async task class.
private void phoneContact() {
new AsyncTask<String,Void,String>() {
#Override
protected String doInBackground(String ... params) {
try {
Platform http = Url_Contacts;
JSONObject resp = http.search(what,where);
Log.d(TAG, "Response: " + resp.toString());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return "";
}
}.execute();
}
Make sure that your service works well and the format of json with value status in there.
In onResponse, run on UIThread to update your View with the availableUsers.
The enqueue method is asynchronous. So your code should respect the multithreaded nature of it.
There are many approaches you can take:
Replace enqueue() method with execute(). But that makes all the calls synchronous. If you call it in UI Thread then whole app can stutter. Probably you will get NetworkOnMainThreadException. Not a good approach anyway.
Use RxAndroid or RxJava with Observer pattern.
Simple solution. Create a variable int callsFinished = 0;. In onResponse increment that variable. Then if that callsFinished == contactsString.length that means all calls have been done.
In your activity add a listener
void onAllCallsFinished(int availableUsers) {
//do what you want with availableUsers information
}
Call onAllCallsFinished(availableUsers) when callsFinished == contactsString.length.
There you can do what you want with that data. Update a view, call another service.
I'm using a bit of code to help me with pulling data from the web called WebRequest (https://github.com/delight-im/Android-WebRequest). It provides a callback for retrieving asynchronous data. However I can't update my ArrayAdapter because I get an error "Non-Static method 'notifyDataSetChanged()' cannot be referenced from a static context"
Now, I've seen a number of similar questions here. Some suggest putting the notifyDataSetChanged command in the Adapter class itself. Some suggest using a Handler and some suggest using a Loader. However I have had no luck actually implementing these solutions. Here's my code:
new WebRequest().get().to(stringSQLUrl).executeAsync(new WebRequest.Callback() {
public void onSuccess(int responseCode, String responseText) {
try {
DataHistory = CsvUtil.fromCsvTable(responseText);
DataHistory.remove(0); //Removes header row
} catch (Exception e) {
Log.e("Main pullWebData","Error converting from CsvTable: " + e.getMessage());
}
DataAdapter.notifyDataSetChanged(); // <-- ERROR HERE
runOnUiThread(new Runnable() {
#Override
public void run() {
DataAdapter.notifyDataSetChanged(); // <-- ALSO ERROR HERE
}
});
}
public void onError() {
Log.e("Main pullWebData","Error pulling data from web");
}
});
I also defined this Handler in my activity thinking I could call it and it would update the ArrayAdapter, but I got the same error here:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
WeightAdapter.notifyDataSetChanged();
}
};
Lastly I created a method inside the Adapter definition to notify itself, but calling that gave me the same static/non-staic error:
public void updateMe() {
this.notifyDataSetChanged();
}
Long story short - there are a ton of questions seemingly about this same topic and lots of advice, but I have been unsuccessful in implementation. Can anyone show me exactly how I'd implement this?
Thank you!
One other thing: I was considering switching from Web data to an Azure SQL DB, but that would also use a callback and I presume have the same issue?
You can only call static methods using ClassName.methodName(); However, notifyDataSetChanged() is not a static method. i.e. notifyDataSetChanged() works depending on the instance of your adapter.
To make sure that this works, you should use notifyDataSetChanged() on the object of the custom adapter.
If you have something like :
DataAdapter customAdapter = new DataAdapter(//params);
listView.setAdapter(customAdapter);
You should call :
customAdapter.notifyDataSetChanged();
I cannot think of a proper way to wait for an object to appear. I am writing a camera app. After taking a picture, I am writing GPS Data into the exif tags. I have to wait for the location object to appear, before writing. My quick and dirty fix is to start a new thread and use a while loop to "wait" for the object:
private static class MyRunnable implements Runnable {
private final String imagePath;
private final String thumbPath;
MyRunnable(final String anImagePath, String aThumbPath) {
this.imagePath = anImagePath;
this.thumbPath = aThumbPath;
}
public void run() {
while (mCurrentLocation == null) {
//do nothing
}
try {
writeExifTags(imagePath);
writeExifTags(thumbPath);
}
catch (NullPointerException e) {
Log.i(TAG, "NullPointerException");
}
}
}
This works but the empty while-loop looks very ugly. I think of some kind of handler for the object but cannot think of a way to use handlers to check for the existence of mCurrentLocation. Anyone with a flash of wit? :) (Yes the try/ catch block is obsolete now ^^)
You could try using an AsyncTask?