I am working on an Android app that uses Jsoup. Early on in development, I "worked around" having to implement any kind of threading because I just wanted to get the bulk of the code completed before tackling threading. I am now attempting to use AsyncTask, but I am still getting the NetworkOnMainThreadException error. I have read plenty of tutorials and SO posts on AsyncTask, but still can seem to identify the problem. When I add the StrictMode... code, the app works as desired except for the UI lockup when loading the data using Jsoup. If anyone could show me what I am doing wrong pertaining to AsyncTask, I would appreciate it. (P.S. I know there is plenty of code redundancy to be cleaned up, but I want to get AsyncTask working first)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/***This is the work around used***/
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
/******/
up = new TreeMap<Double, String[]>();
c1 = "example.com/1";
//instansiate textviews (6)
doc1 = doc;
c2 = "example.com/2";
//instansiate textviews (6)
doc2 = doc;
c3 = "example.com/3";
//instansiate textviews (6)
doc3 = doc;
// instansiate textviews(16)
new Download().execute(c1,c2,c3);
}
private class Download extends AsyncTask<String, Integer, String[][]> {
#Override
protected String[][] doInBackground(String... urls){
out = new String[7][3];
try {
doc = Jsoup.connect(urls[0]).data().get();
//days, times, and cs arrays created and filled
String[] out1arr = {days[0], times[0], cs[0]};
//...all 7
String[] out7arr = {days[6], times[6], cs[6]};
String[][] outarrs = {out1arr,out2arr,out3arr,out4arr,out5arr,out6arr,out7arr};
for (int i= 0; i < out.length; i++){
out[i] = outarrs[i];
}
} catch (IOException e1) {
e1.printStackTrace();
}
return (out);
}
#Override
protected void onProgressUpdate(Integer... progress){
}
#Override
protected void onPostExecute(String[][] result){
Do(/*textviews(6)*/, c1, a, outa, "example1"); //a is previously instantiated double array, outa is preiously instantiated string array
Do(/*textviews(6)*/, c2, b, outb, "example2");
Do(/*textviews(6)*/, c3, c, outc, "example3");
upc00.setText(getUpc()[0][0]);
//setText for all 16
upc32.setText(getUpc()[3][2]);
}
private void Do(TextView t, TextView u, TextView v, TextView w, TextView x, TextView y,String webpage, double[] darr, String[] sarr, String show){
t.setText(doInBackground(webpage)[0][0]);
//...all 6
y.setText(doInBackground(webpage)[1][2]);
for (int i =0; i < darr.length; i++){
darr[i] = tis[i];
up.put(darr[i], out[i]);
}
}
}
private ArrayList<String[]> getMap(){
//...
return s;
}
private String[][] getUpc(){
//...
return upc;
}
The framework calls doInBackground you should not call it yourself. Your code makes a call from onPostExecute which is called by the framework on the UI thread. So effectively your calls run on the UI thread.
Move your fetching logic all into the doInBackgound method. The onPostExecute method should be used to deliver the results to the caller.
Related
Okay so I made module for searching medicines and display items in a listview, I am using JDBC here. The main issue is when I place an underscore_ in the searchview, It kind of makes a bug where the listview items duplicate themselves.
Based on my observation; this bug only occurs when the underscore is inserted simultaneously, but if pressed at slower interval, It doesn't duplicate items anyway.
Here is what it looks without anything on the SearchView:
This is when underscores are placed at slower interval
And even if underscores doesn't exist in the listview it still displays it, but when I add even more underscores, they started to disappear one by one starting from the bottom.
And here's the main problem; when Underscores are placed simultaneously
Here are my code for the search in AsyncTask
private class searchUnfiltered extends AsyncTask<String, String, String> {
String z = "";
String fromSearchView = sv.getQuery().toString().trim();
boolean isSuccess = false;
String about;
#Override
protected void onPreExecute() {
myArrayList2.clear();
myArrayList.clear();
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
try {
Connection con = connectionClass.CONN();
if (con == null) {
z = "Please check your internet connection";
} else {
String querySearch = "select name from medicines where name like '%"+ fromSearchView +"%' order by name ASC";
Statement stmt1 = con.createStatement();
ResultSet rs1 = stmt1.executeQuery(querySearch);
if(rs1 != null) {
while (rs1.next()) {
myArrayList2.add(rs1.getString("name"));
}
}
}
} catch (Exception ex) {
isSuccess = false;
z = "Exceptions" + ex;
}
return z;
}
#Override
protected void onPostExecute(String s) {
if(!isSuccess && !z.equals("")) {
Toast.makeText(getBaseContext(), z, Toast.LENGTH_LONG).show();
}
ListAdapter listAdapter = new ArrayAdapter<>(MedicineSearch.this, android.R.layout.simple_list_item_1, myArrayList2);
lv.setAdapter(listAdapter);
}
}
Whenever value is changed in search view the searchUnfiltered will be triggered, please tell me if what I'm doing is efficient or if you have any better suggestions, it would be very helpful.
That is a race condition reason you are clearing the data onPreExecute then doInBackground is working typing 5 _ at a fast pace will clear 5 times before you get any results back.
as a Solution move:
myArrayList2.clear();// if this didn't work try myArrayList2 = new ArrayList<>();
from onPreExecute to doInBackground.
Trying to convert a Arraylist of strings into one big comma separated string.
However when I use the
String joined = TextUtils.join(", ", participants);
Debugger shows me size of 4 for participants however the joined value as "" therefore empty
private ArrayList<String> participants;
Not sure what is going wrong?
UPDATE:
List<String> list = new ArrayList<>();
list.add("Philip");
list.add("Paul Smith");
list.add("Raja");
list.add("Ez");
String s = TextUtils.join(", ", list);
This works when I have a list that I manually populate however below is how the code is working right now.
In the onCreate()
callApi(type);
String s = TextUtils.join(", ", participants);
getSupportActionBar().setTitle(s);
In callAPI():
JSONArray participantsR = sub.getJSONArray("referralParticipants");
Log.e("Participants length ", String.valueOf(participantsR.length()));
for (int i = 0; i < participantsR.length(); i++)
{
JSONObject object = participantsR.getJSONObject(i);
String firstname = (String) object.get("fullName");
participants.add(firstname);
Log.e("Times", String.valueOf(i));
}
I'm trying to reproduce your error and am unable to. Here is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_temp);
List<String> list = new ArrayList<>();
list.add("Philip Johnson");
list.add("Paul Smith");
list.add("Raja P");
list.add("Ezhu Malai");
String s = TextUtils.join(", ", list);
Log.d(LOGTAG, s);
}
My output is Philip Johnson, Paul Smith, Raja P, Ezhu Malai as expected.
Are you importing the correct TextUtils class?
android.text.TextUtils;
Given the new information, here is my approach:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_temp);
callApi(type, new OnResponseListener<List<String>>() {
#Override public void onResponse(List<String> list) {
getSupportActionBar().setTitle(TextUtils.join(", ", list));
}
});
}
I don't know what networking library you're using, but you may have to define OnResponseListener as an interface. It's very easy:
public interface OnResponseListener<T> {
public void onResponse(T response);
}
You will then need to modify your callApi function to take an instance of OnResponseListener> and call it's onResponse method after completing the call.
I would recommend looking into the Volley library, and reading the Android documentation about simple network calls.
I use StringUtils.join from Apache Common Utilities.
The code is super-simple just the way you wanted,
StringUtils.join(participants,", ");
Works flawlessly for me.
EDIT
As requested, here is the StringUtils.java file for those who just want to use this single utility class and not the entire library.
I don't know what TextUtils does. This will do it.
StringBuffer sb = new StringBuffer();
for (String x : participants) {
sb.append(x);
sb.append(", ");
}
return sb.toString();
Easy enough, just use that.
Try with kotlin
val commaSeperatedString = listOfStringColumn.joinToString { it ->
"\'${it.nameOfStringVariable}\'" }
// output: 'One', 'Two', 'Three', 'Four', 'Five'
I am writing an android application, I use Aysnctask to get the current weather using openWeatherAPI. How could I call the Aysnctask class.
this is the Aysnctask class I wrote:
private class getWeather extends AsyncTask<String[], Void, String[]>{
#Override
protected String[] doInBackground(String[]... params) {
// TODO Auto-generated method stub
try {
String Url1="http://api.openweathermap.org/data/2.5/weather?lat="+currentLatitude+"&lon="+currentLongitude;
s1=getJson(Url1);
if(s1!=null){
JSONObject jObj1 = new JSONObject(s1);
Tem= jObj1.getJSONObject("main").getDouble("temp");
pressure=jObj1.getJSONObject("main").getDouble("pressure");
humm=jObj1.getJSONObject("main").getDouble("humidity");
wind=jObj1.getJSONObject("wind").getDouble("speed");
desc=jObj1.getJSONObject("weather").getDouble("description");
double tem_c=Tem-273.15;
String t=Double.toString(tem_c);
results[0]=t;
results[1]=Double.toString(pressure);
results[2]=Double.toString(humm);
results[3]=Double.toString(wind);
results[4]=Double.toString(desc);
}
} catch (Exception e) {
// TODO: handle exception
}
return results;
}//do in background
#Override
protected void onPostExecute(String[] results) {
temp.setText(results[0]+"°C");
hum.setText(results[1]+ "%");
press.setText(results[2]+ " hPa");
windSpeed.setText(results[3]+ " mps");
condDescr.setText(results[4]);
}
}
when I press a button, I want to get the weather of current latitude and longitude, but I don't know how to execute the class
try this
getWeather gt= new getWeather();
gt.execute(new String{currentLatitude,currentLongitude});
Per the android docs here, which have plenty of examples, you would do:
new GetWeather().execute(currentLatitude,currentLongitude);
I would personally shy away from holding a reference to it but rather send in what you need or use a listener. Also, please capitalize you classes :)
You access both of them like so in doInBackGround
String latitutde= params[0];
String longitude = params[1];
An example on the docs which you can look at is:
new DownloadFilesTask().execute(url1, url2, url3);
As you can see, pass however many parameters you need and it'll just be accessible in the array from the first generic parameter you mention. In your case its a String array, so you'd send only Strings.
Try this..
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
new getWeather(currentLatitude,currentLongitude).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[]{null});
else
new getWeather(currentLatitude,currentLongitude).execute(new String[]{null});
and also add constructor to your async task
String currentLatitude;
String currentLongitude;
// constructor
#SuppressWarnings("rawtypes")
public getWeather(String currentLatitude, String currentLongitude) {
this.currentLatitude = currentLatitude;
this.currentLongitude = currentLongitude;
}
EDIT 1:
You cannot parse double to string like this String t=Double.toString(tem_c);
you need to parse like below codes.
String t = String.valueOf(tem_c);
results[0] = t;
results[1] = String.valueOf(pressure);
results[2] = String.valueOf(humm);
results[3] = String.valueOf(wind);
results[4] = String.valueOf(desc);
I have a json function that connects to a database and returns a result. It does this about 15 times or for how many comments there are in the database. The json function is inside a while loop, and repeats itself until all the comments have been taken from the database or until it reached 15 comments. The problem is when the app loads the comments it does it during the onCreate part of the app. I want the app to load and then the json function to load in the back. I know I can do this with an asynctask but I am not familiar with them. So I was hoping someone would be able to tell me how to place this code into a asynctask.
UserFunctions CollectComments = new UserFunctions();
JSONObject json = CollectComments.collectComments(usernameforcomments, offsetNumber);
int commentCycle = 1;
// check for comments
try {
if (json.getString(KEY_SUCCESS) != null) {
registerErrorMsg.setText("");
String res2 = json.getString(KEY_SUCCESS);
if(Integer.parseInt(res2) == 1){
String numberOfComments = json.getString(KEY_NUMBER_OF_COMMENTS);
String offsetNumberDb = db.getOffsetNumber();
int numberOfComments2 = Integer.parseInt(numberOfComments) - Integer.parseInt(offsetNumberDb);
offsetNumber = offsetNumberDb;
//if comment number is less than 15 or equal to 15
if(numberOfComments2 <= 15){
while (commentCycle <= numberOfComments2){
JSONObject json2 = CollectComments.collectComments(usernameforcomments, offsetNumber);
TextView commentView = new TextView(this);
commentView.setText(json2.getString(KEY_COMMENT));
LinearLayout.LayoutParams commentViewParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
commentViewParams.setMargins(20, 10, 20, 20);
commentView.setBackgroundResource(R.drawable.comment_bg);
commentView.setTextColor(getResources().getColor(R.color.black));
commentBox.addView(commentView, commentViewParams);
verify2 = verify2 + 1;
offsetNumber = json2.getString(KEY_OFFSET_NUMBER);
commentCycle = commentCycle + 1;
}//end while
}//end if comment number is less than or equal to 15
}//end if key is == 1
else{
// Error in registration
registerErrorMsg.setText(json.getString(KEY_ERROR_MSG));
}//end else
}//end if
} //end try
catch (JSONException e) {
e.printStackTrace();
}//end catch
All this code works but I want it running in the background not during the apps oncreate some one please try putting this into a asynctask or at least help me understand how to do so.
You should put your while loop in a new Thread or Async Task. Here is how is will work
public class JsonWork extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
//your while loop goes here
}
}
Just before the while loop in your current code you should call new JsonWork().execute(). So that it would execute the while loop in a new AsyncTask Thread.
As the title says really. I have two columns. I want to put them into textviews so I did it. However only the bottom two results, one from each column gets shown. Very odd. Here is my code: http://pastebin.com/qNgfHfT3
The parsing/onPostExecute is towards the bottom where the issue is.
One thing to note: The logs labeled "work" & "dontwork" show all my results, however the logs in the onPostExecute (Google & Google1) only show the last result so I presume the error is in the transfer from parsing to displaying.
Would really appreciate any help here. Thanks.
If you are receiving a JSON response I'd suggest you to parse it by using Gson. It's strongly recommendable as long as you can parse the whole thing in a pair of lines.
Note that creating a proper object it is as easy as doing the following:
YourObject object = gson.fromJson(responseReader, YourObject.class);
or even if you are retrieving a list of items:
Type listType = new TypeToken<List<YourObject>>() {}.getType();
List<YourObject> objects = gson.fromJson(responseReader, listType);
Here's an example that fits exactly your needs
After the process is done you'll have your object (or list of objects) available in an accesible variable.
EDIT:
First your Asynctask should have the following params:
public class HttpTask extends AsyncTask<Void, Void, ArrayList<Driver>> {
and your doInBackground method will need to pass that array to your onPostExecute:
#Override
protected ArrayList<Driver> doInBackground(Void... params) {
For the rest, I take it when the JSon parsing starts.
//PARSING JSON DATA
try {
JSONObject json_data;
Driver d;
jArray = new JSONArray(result);
int l = jArray.length();
if(l>0){
ArrayList<Driver> drivers = newArrayListList<Driver>();
for (int i = 0; i < l; i++) {
json_data = jArray.getJSONObject(i);
d = new Driver(json_data.optString("Driver_full_name"), json_data.optString("Drives_for"));
drivers.add(d);
Log.i("work", returnString);
Log.i("dontwork", somethingelse);
}
} catch (JSONException e1) {
Log.d("DB", "Error somewhere");
CurrentSeasonDrivers_DriverName.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(CurrentSeasonDrivers_DriversName, "Could not parse data so shut up", Toast.LENGTH_LONG).show();
}
});
}
return drivers;
}
protected void onPostExecute(ArrayList<Drivers>... drivers) {
Log.i("Google", returnString);
Log.i("Google1", somethingelse);
String firstDriverName = drivers.get(0).name;
String firstDriverDrivesFor = drivers.get(0).drivesfor;
String secondDriverName = drivers.get(1).name;
TextView drivername = (TextView) findViewById(R.id.DriverName);
drivername.setText(firstDriverName);
TextView drivesfor = (TextView) findViewById(R.id.DrivesFor);
drivesfor.setText(firstDriverDrivesFor);
}
With this and an object for your driver will complete the circle.
public class Driver{
public String name;
public String drivesfor;
public Driver(String _name, String _drivesfor){
name = _name;
drivesfor = _drivesfor;
}
}
I guess you can take over from here.
Let me know about your progress.