I am new to android development and software programming in general and believe I have a threading issue in my app. What the app does is searches for two sets of results based on two queries to an api and stores each set of results in its own list. A new list is generated containing only the elements that are in both lists. The app runs in a virtual device on my desktop but hangs on my Galaxy Nexus. I am using arraylist for this but I am wondering if perhaps hashset would be faster at accomplishing this type of operation. Below is my main activity. getfirst and secondID are done in an asynctask as well as getfirst and secondtitle in order to prevent networkonmainthread exception. Is that the best way to thread this application? Thanks for any help.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.totlayout);
//set the UI elements
searchOne = (EditText) findViewById(R.id.searchOne);
searchTwo = (EditText) findViewById(R.id.searchTwo);
findMovies = (Button) findViewById(R.id.findMovies);
searchOne.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
//make person search url1
final StringBuilder personSearchURLOne = new StringBuilder(getName.getName1(searchOne));
searchURLOne = personSearchURLOne.toString();
return false;
}
});
searchTwo.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
//make person search url2
final StringBuilder personSearchURLTwo = new StringBuilder(getName.getName2(searchTwo));
searchURLTwo = personSearchURLTwo.toString();
return false;
}
});
}
public void getData(String searchURLOne, String searchURLTwo){
try{
//get ID 1
idOne = new getFirstID().execute(searchURLOne).get();
Log.d("JSONArray idOne", idOne.toString());
//get ID 2
idTwo = new getSecondID().execute(searchURLTwo).get();
Log.d("JSONArray idTwo", idTwo.toString());
//make credit search url1
final StringBuilder creditURLOne = new StringBuilder(buildCreditURL.getCreditURLOne(idOne));
final String creditOne = creditURLOne.toString();
Log.d("creditOne contains", creditOne);
//make credit search url2
final StringBuilder creditURLTwo = new StringBuilder(buildCreditURL.getCreditURLTwo(idTwo));
final String creditTwo = creditURLTwo.toString();
//get array of tiles for credit url 1
titleOne = new getFirstTitle().execute(creditOne).get();
Log.d("titleOne Contains", titleOne.toString());
//get array of titles for credit url 2
titleTwo = new getSecondTitle().execute(creditTwo).get();
//parse out common films into new array
myCommonFilms = new ArrayList<String>(commonFilms.getCommonFilms(titleOne, titleTwo));
}
catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}
}
public void displayResults(View view) throws InterruptedException, ExecutionException{
//do something in response to button
getData(searchURLOne, searchURLTwo);
Intent intent = new Intent(this, DisplayResultsActivity.class).putStringArrayListExtra("myCommonFilmsList", myCommonFilms);
startActivity(intent);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.totlayout, menu);
return true;
}
}
I think it's the correct way : using AsyncTask is an easy way to prevent your app for freezing the UI.
But I don't really understand what the following line do.
idOne = new getFirstID().execute(searchURLOne).get();
Actually, I never see it before. Anyway, how many records are you managing in your ArrayList ? 10, 100, 1000, 1 000 000 ?
If you're working with a lot of records, your phone will take a certain amount of time to accomplish the operations and you'll not be able to reduce it. The only solution, if the set is too large, is to trying to reduce it maybe directly on your server or when you're building your ArrayList.
I'm not necessarily following where the AsyncTask is created and put into action but it seems like it would be a good idea to put the entire getData method on the AsyncTask. That aside, if it works on the and but not on the device my 1st suspicion would be network access. Make sure your device has access not only to the internet but to the service that it needs to run the remote query on. It's very common to misunderstand that phones cannot see the same host computers as our desktops can. Also, if you're phone is roaming on Wifi check that the Wifi network will allow the service to be accessed. Is the remote query service a web service? Is it a DBMS SQL call you are trying to run? does the query require a specific network port?
this one seems to block the UI thread:
titleOne = new getFirstTitle().execute(creditOne).get();
better use a new thread for doing the long job , and once it finishes , notify the UI thread about the new data .
you can use asyncTask to make it easier for you . if you prefer the normal threads ,either use a handler (and use handler.post) or use runOnUiThread once the thread has finished.
if you use a hanlder , don't forget to create it outside the of thread itself.
as a new android developer , you should watch the google IO videos . they can help a lot . watch videos from 2010 to 2012 .
Related
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 this piece of code that runs alright when I put it in Eclipse, but for some reason it does not want to execute when I put it in an activity's onCreate method in Android studio.
Here is the code:
public class ItemListView extends AppCompatActivity{
String itemURL;
int listSize;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_item_list_view);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Bundle itemData = getIntent().getExtras();
if(itemData==null){
return;
}
//Gets URL from search bar
itemURL = itemData.getString("itemURL");
try {
Document doc = Jsoup.connect("https://www.amazon.com/s/ref=nb_sb_ss_c_1_6?url=search-alias%3Daps&field-keywords=rx+390&sprefix=rx+390%2Caps%2C166&crid=2MTUBA4KGNY06").get();
String link = doc.select("h2#s-result-count").first().text();
System.out.println(link);
System.out.println(link.substring(1));
if (link.substring(1, 2).equals("-")) {
System.out.println("run1");
listSize = Integer.parseInt(link.substring(2, 3));
System.out.println(listSize);
try {
listSize = Integer.parseInt(link.substring(2, 4));
System.out.println(listSize);
} catch (Exception e) {
}
} else {
System.out.println("run2");
listSize = Integer.parseInt(link.substring(0, 1));
System.out.println(listSize);
try {
listSize = Integer.parseInt(link.substring(0, 2));
System.out.println(listSize);
} catch (Exception e) {
}
}
} catch (Exception e) {
}
System.out.println("listSize: " +listSize);
...
}
}
I need listSize to create a variable array depending on the URL, but when I print the value to make sure it's working it always gives me 0. I have tried running the code in a separate Java Class with AsyncTask and it works, but by the time the code executes in onPostExecute, it's too late since the class above has already tried to initialize the array.
Any advice would be appreciated, thanks!
What you need is a callback to allow you to initialize variable onPostExecute:
interface OnCallCompleteCallBack {
void onCallComplete(int listSize);
}
In your AsyncTask do this:
public class MyTask extends AsyncTask < ... > {
// Maintain a ref to callback
OnCallCompleteCallBack callback;
MyTask(OnCallCompleteCallBack callback) {
this.callback = callback;
}
...
#Override
protected void onPostExecute(Void aVoid) {
if (callback != null) {
callback.onCallComplete(listSize);
}
}
}
Make your Activity implement OnCallCompleteCallBack and start the AsyncTask like this:
new MyTask(this).execute();
You can then use the value inside your activity's implementation of onCallComplete()
Before I answer, just a few observations:
Naming your activity ItemListView seems wrong (an Activity is not a View).
You should never perform networks calls on the UI thread.
If you want to log, you should use Log instead of System.out.println().
And now to the answer. You should execute the code that fetches the data in an AsyncTask (as you mentions it works). Once the data is fetched, you should update array and at that point update the UI in onPostExecute().
Android works pretty well using the MVC (Model-View–Controller) pattern and your problem is a classic case where you need to update the model using data from the server and update the views accordingly. The activity represents controller in this case.
Please go through the topic in android developer site Android Developer, Read the section "Introducing Network Operations on a Separate Thread" - To avoid creating an unresponsive UI, don't perform network operations on the UI thread. By default, Android 3.0 (API level 11) and higher requires you to perform network operations on a thread other than the main UI thread; if you don't, a NetworkOnMainThreadException is thrown.
Thanks Ashish
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 trying to connect to .NET 4.0 webservice I created for receiving SOAP-calls from Android-devices, now hosted on local IIS for testing purposes.
I found out that ksoap2 would be an excellent class library for doing what i want to do. Downloaded the .jar package from https://code.google.com/p/ksoap2-android/ and started pounding the keyboard in ecstacy... with my fingers.
The amount of information being sent is from few kilobytes to few megabytes.
What is working
HttpTransportSE.call(String, SoapSerializationEnvelope)-method works perfectly while still in Eclipse's Android emulator, sending the call to webservice hosted in local IIS. Even tested that the webservice receives empty calls from trying to open the service address from a web browser in the same local area network.
What doesn't work
When I copy the .apk-file to an Android device, install it, start it and trying to make the call, the whole program freezes without making the call.
As you can see from a code block presented some lines after that possible errors are being taken into account: In emulated environment a successful call returns a SoapPrimitive-object or flows into the correct catch block generating an error message for the user according to the current situation.
Then on live Android device, program loses it's responsivity forever and has to be terminated from application menu.
What have i tried
I removed the call from the asynchronous method, and tried calling it straight from an anonymous inner function assigned for a button click-event.
Tried not trying to get a response, just making the call.
Tried getting a logcat-program for the device to see what's happening behind the UI, found two, they needed root access, which i don't have in the device. This is why i don't have any logcats to show you, and showing the emulator logcat would probably(?) be useless because it works fine there.
Not trying to connect to localhost.
Tried installing the program on older Lenovo-tablet running Android 4.2.2 and on brand new Samsung Galaxy Tab, both would have the same problem while otherwise working well.
The code
Here's the asynchronous method for making the call in device/emulator, where variables str_URL and soapRequest are a correct service address (checked) and a well formed SoapObject respectively:
#Override
protected WebServiceResult doInBackground(Void... v) {
WebServiceResult _ret;
SoapSerializationEnvelope soapEnvelope= new SoapSerializationEnvelope(SoapEnvelope.VER11);
soapEnvelope.dotNet=true;
soapEnvelope.setAddAdornments(false);
soapEnvelope.setOutputSoapObject(soapRequest);
HttpTransportSE conn = new HttpTransportSE(str_URL);
conn.setXmlVersionTag("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
conn.debug = true;
try {
conn.call(str_ACTION, soapEnvelope);
SoapObject o = (SoapObject)soapEnvelope.getResponse();
_ret = new WebServiceResult(o, WebServiceResultEnum.ok);
} catch (NetworkOnMainThreadException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.keskeytys);
} catch (HttpResponseException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.httpVirhe);
} catch (XmlPullParserException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.vaara_muoto);
} catch (SocketTimeoutException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.aikakatkaisu);
} catch (Exception e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.keskeytys);
}
return _ret;
}
Thank you in advance!
Is it possible you are doing something like this:
YourAsyncTask task = new YourAsyncTask();
WebServiceResult result = task.doInBackground();
Because that would be wrong, completely wrong. If you call doInBackground() directly it will run in the same Thread and not in a new one. You need to start the AsyncTask with execute() like this:
YourAsyncTask task = new YourAsyncTask();
task.execute();
You need to implement the AsyncTask like this:
public class ExampleTask extends AsyncTask<Void, Void, WebServiceResult> {
public interface FinishedListener {
public void onFinished(WebServiceResult result);
}
private final FinishedListener finishedListener;
public ExampleTask(FinishedListener listener) {
this.finishedListener = listener;
}
#Override
protected WebServiceResult doInBackground(Void... params) {
WebServiceResult result = ...;
return result;
}
#Override
protected void onPostExecute(WebServiceResult result) {
super.onPostExecute(result);
if(this.finishedListener != null) {
this.finishedListener.onFinished(result);
}
}
}
And if you implemented it that way you can use it like this:
ExampleTask task = new ExampleTask(new ExampleTask.FinishedListener() {
#Override
public void onFinished(WebServiceResult result) {
// This will be called if the task has finished
}
});
task.execute();
It seems that I had declared the minimum SDK as 14 and target SDK as 17 in AndroidManifest.xml. I didn't use any fancy things in newer sdk's so i lowered the target SDK to the same level as minimum SDK, 14. I also had an Avast! Antivirus service running on the tablet which i removed.
This solved my problem. It could be that probably the Avast! antivirus-program wanted to block all communications from applications not downloaded from Play-store. I don't know if changing the target SDK had much effect really.
Well, I had the same question as you. When it goes to the method transport.call, it pauses, and for a while, it throws a timeout problem. At first, I thought maybe the network was poor, but the server logcat shows it is not the problem. The request was fine and the response was good. My business process is like below:
First, I get a list from the server through ksoap inner a child thread, then cycle the list, send a ksoap request based on every item of the list. It means it will send another list.size() request. When debugging in a real device the above problems occured. I solved it by starting a new child thread after getting the list and making all the list.size requests in the new child thread. So, ksoap use in android may cause thread block which leads to ioexception. So when you put it in a new thread, it escapes from the parent catch exception and works fine.
I'm trying to write a simple application to display chart data. I want to display some data as soon as the user loads the page, so I'm getting data & drawing tables inside of the Runnable as described in the gwt-visualization Getting Started.
Things seem to work alright, except charts tend to get loaded more than once. Below is my onModuleLoad().
private final StatisticsServiceAsync statisticsService = GWT.create(StatisticsService.class);
GWTBeanFactory factory = GWT.create(GWTBeanFactory.class);
DataTable locationData;
AnnotatedTimeLine atl;
GeoMap usMap;
TextBox storeField;
Button log10Button;
DateRange durationChartRange;
String eusrJson = null;
Button b;
HTML last1000Html;
public void onModuleLoad() {
storeField = new TextBox();
storeField.setText("Enter a store");
storeField.addKeyDownHandler(new MyKeyHandler());
b = new Button("Get Stats!");
log10Button = new Button("Show Log10 Scale");
log10Button.addClickHandler(new Log10ClickHandler());
b.addClickHandler(new MyClickHandler());
last1000Html = new HTML();
getLast1000Avg();
Runnable onLoadCallback = new Runnable() {
public void run() {
storeDurationData = DataTable.create();
storeDurationDataLog10 = DataTable.create();
RootPanel.get("storeDurationDiv").add(storeField);
RootPanel.get("storeDurationDiv").add(b);
RootPanel.get("storeDurationDiv").add(log10Button);
RootPanel.get("storeDurationDiv").add(last1000Html);
log10Button.setVisible(false);
// Get initial Data
getAvgByRegion();
getLast1000Avg();
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
#Override
public boolean execute() {
getLast1000Avg();
return true;
}
}, 5000);
}
};
// Load the visualization api, passing the onLoadCallback to be called
// when loading is done.
VisualizationUtils.loadVisualizationApi(onLoadCallback, AnnotatedTimeLine.PACKAGE);
VisualizationUtils.loadVisualizationApi(onLoadCallback, GeoMap.PACKAGE);
}
All of the "simple" elements seem to get populated correctly, as the Button, HTML, and TextBox all get placed appropriately (which used to be inside of run, they're where they are now as a result of debugging previous errors). However, the GeoMap gets placed twice, and looking at the logging you can tell that the Runnable's run is being executed at least twice, which seems reasonable, but I don't know how to keep it from adding twice.
I'm probably screwing up something with the Async stuff, but I'm new and confused. Below is my getAvgByRegion() method:
private void getAvgByRegion() {
statisticsService.getEusrForRegion(new AsyncCallback<String>() {
#Override
public void onFailure(Throwable caught) {
System.out.println(":(");
}
#Override
public void onSuccess(String result) {
createLocTable();
DataTable dt = parseEusrLocations(result);
usMap = new GeoMap(dt, createGeoMapOptions());
usMap.setSize("800px", "600px");
RootPanel.get("storeDurationDiv").add(usMap);
}
});
}
Any advice on how best to work with GWT is welcome.
So, you call VisualizationUtils.loadVisualizationApi twice, so the onLoadCallback will be run twice (I don't know GWT Google Apis, this is a supposition).
onLoadCallback calls getAvgByRegion, so that one will get called twice too; and it gets data and in the callback creates a new GeoMap and adds it to the RootPanel.get("storeDurationDiv"), so you get two GeoMaps on the screen.
The other widgets(storeField, etc.) are created only once, so adding them repeatedly is not a problem (except performance-wise), as they'll first be removed from their current parent before being added to the new one (which in this case is the same)