I have an AsyncTask extended class that listens to a port in the background.
My problem is that when I try to add the text that I receive from the socket into a TextView on the UI, the app stops because I'm doing it from outside of the activity thread. What do I need to do to my class to be able to edit the TextView?
Here is the code:
public class Receive_String extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
try {
TextView text_ShowString=(TextView)findViewById(R.id.textView_ShowString);
ServerSocket conn = new ServerSocket(35316);
Socket listen=conn.accept();
BufferedReader input = new BufferedReader(new InputStreamReader(listen.getInputStream()));
String message = input.readLine();
text_Notificari.append(message);
conn.close();
} catch (IOException e) {
e.printStackTrace();}
}
return null;
}
}
Update in onPostExecute(). If you continously reading from port and want to make changes in main UI do it in onProgressUpdate() .
Eg : Make text_ShowString and message global
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
text_ShowString.setText(message);
}
Move your update-the-UI code to onPostExecute() of your AsyncTask.
Related
I'm trying to load some JSON inside my Android App. On the MainActivity I have created one AsyncTask to download the JSON and to Parse it. Everything here works, but I have a problem to put everything inside a ListView.
I have created a Model (with 6 Strings) and the Adapter.
The problem is, I can't update the List with the new content inside the "doInBackground" function, and I don't know how to put everything inside the list.
If your Adapter is set up properly to listen to a List as its data source, then you will simply need to change the elements in the List and then notify the Adapter by calling one of the notify methods, such as adapter.notifyDataSetChanged().
However, since this is modifying UI elements, this will need to be run on the UI thread. The doInBackground() method of an AsyncTask is run on a separate thread than the UI thread so we need to do one of two things:
Wait until we're done in the separate thread and then notify the adapter
Tell the UI thread to notify the adapter
The first is easily done if we call adapter.notifyDataSetChanged() in the onPostExecuted method of an AsyncTask.
The second is easily done if we have a reference to an Activity object, by calling the runOnUiThread(Runnable) method.
Hope this helps.
doInBackground() method didn't have access to UI thread, so you can't do it. In your case you should update your ListView in onPostExecute method or you can use for this runOnUiThread() method directly in your doInBackground() method.
You can't update or put data in listview in doInBackground() method. You have to assign Adpter to Listview, onCreate() method of MainActivity and onPostExecute() Method update listview. I have posted below code snippet will be helpful.
public class MainActivity extends AppCompatActivity {
String json_string;
JSONObject jsonObject;
JSONArray jsonArray;
ContactAdapter contactAdapter;
ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new BackegroundTask().execute();
listView = (ListView)findViewById(R.id.listview);
contactAdapter = new ContactAdapter(this,R.layout.row_layout);
listView.setAdapter(contactAdapter);
}
class BackegroundTask extends AsyncTask<Void,Void,String>
{
String json_url;
String JSON_STRING;
#Override
protected void onPreExecute() {
json_url = "http://10.0.2.2/webapp/index.php";
}
#Override
protected String doInBackground(Void... voids) {
try {
URL url = new URL(json_url);
HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection();
httpURLConnection.setDoOutput(true);
InputStream inputStream = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
while ((JSON_STRING = bufferedReader.readLine()) != null){
stringBuilder.append(JSON_STRING+"\n");
}
bufferedReader.close();
inputStream.close();
httpURLConnection.disconnect();
return stringBuilder.toString().trim();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String result) {
json_string = result;
try {
jsonObject = new JSONObject(json_string);
jsonArray = jsonObject.getJSONArray("server_response");
int count = 0;
String id,username;
while (count<jsonArray.length()){
JSONObject job = jsonArray.getJSONObject(count);
id = job.getString("id");
username = job.getString("username");
Contacts contacts = new Contacts(id,username);
contactAdapter.add(contacts);
contactAdapter.notifyDataSetChanged();
count++;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
I am beginner in Android and I need some help. So, I have a procedure with sub-procedures inside. How can I finish one before starting a new one. Here is a code to better understand:
public void onCellLocationChanged(CellLocation lokacija) {
super.onCellLocationChanged(lokacija);
location = (GsmCellLocation) Phone.getCellLocation();
textCellId.setText(String.valueOf(location.getCid() % 65536));
textCellLac.setText(String.valueOf(location.getLac()));
String JSON_URL_string=JSON_URL + "?cellid=" + String.valueOf(location.getCid()%65636);
getJSON(JSON_URL_string);
myJSONString = textCellNameSakriven.getText().toString();
ParseJSON(myJSONString);
}
Problem is that myJSONString is empty, cause textCEllNameSkriven is also empty. That textView textCellNameSkriven is made when getJSON(JSON_URL_string) is finished. If I run debugger and go step by step, app goes directly from getJSON(JSON_URL_string) row to the next one and the next etc
Edit: Maybe the problem is that onPostExecute is not finished before starting ParseJSON. Here is also a code for getJSON:
private void getJSON(String url) {
class GetJSON extends AsyncTask<String, Void, String> {
ProgressDialog loading;
#Override
protected void onPreExecute() {
super.onPreExecute();
loading = ProgressDialog.show(MainActivity.this, "Please Wait...", null, true, true);
}
#Override
protected String doInBackground(String... params) {
String uri = params[0];
BufferedReader bufferedReader = null;
try {
URL url = new URL(uri);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
StringBuilder sb = new StringBuilder();
bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String json;
while ((json = bufferedReader.readLine()) != null) {
sb.append(json + "\n");
}
return sb.toString().trim();
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
loading.dismiss();
textCellNameSakriven.setText(s);
}
}
GetJSON gj = new GetJSON();
gj.execute(url);
}
Since you are running a async task it practically runs on a different thread, so your getJson method need not wait for post execute and can return after starting the async task, so you can never be sure in this way that parseJson gets executed after textView is populated. You are running into classic race condition issue.
Your issue could be easily solved, if you have a callback which is called after postExecute is done, and you can handle parseJson there
So, something simple like , create interface MyCallback
public interface MyCallback {
public OnReadJsonDone();
}
Let your activity implement this MyCallback
public MainActivity implements MyCallback
{
...........
#Override
public OnReadJsonDone(){
parseJson();
}
Now change signature of getJson to
getJSON(string json, final MyCallback callback) {
Now in onpostexecute
//call OnReadJsonDone
callback.OnReadJsonDone()
So, all you now need is while calling getJSon pass this as second param
getJSON(JSON_URL_string,this);
Haven't tested this but you get the idea
I'm a new Android developer and I have a question. After users login, I need to get some data from an external URL and display them on one of my activities. But I've been reading and found this:
When your activity comes back to the foreground from the stopped state, it receives a call to onRestart(). The system also calls the
onStart() method, which happens every time your activity becomes
visible (whether being restarted or created for the first time).
And this
Caution: Your activity will be destroyed and recreated each time the
user rotates the screen. When the screen changes orientation, the
system destroys and recreates the foreground activity because the
screen configuration has changed and your activity might need to load
alternative resources (such as the layout).
Here and here.
So, it looks like I should not get the data I need on the onCreate method. Then where? In a previous activity and saving the data in the phone memory? That doesn't sound good to me.
Thanks in advance.
edit
I'm using AsyncTask as suggested, but everytime I switch the phone orientation, onCreate method is called.
My MainActivity:
public class MainActivity extends Activity implements AsyncResponse {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ReadJSONTask jsonTask = new ReadJSONTask();
jsonTask.delegate = this;
jsonTask.execute("http://www.myweb.com/myscript.php");
}
#Override
public void processFinish(String output) {
Toast.makeText(getApplicationContext(), output, Toast.LENGTH_LONG).show();
}
}
ReadJSONTask class:
public class ReadJSONTask extends AsyncTask<String, Void, String> {
public AsyncResponse delegate = null;
public String res;
public Boolean finish = false;
#Override
protected String doInBackground(String... url) {
String response = null;
String adres = url[0];
URL url_HTTPRequest = null;
try {
url_HTTPRequest = new URL(adres);
response = transfer(url_HTTPRequest);
} catch (MalformedURLException e) {
Log.e("URL ERROR", "MalformedURLException");
} catch (Exception e) {
Log.e("URL ERROR", "exc");
}
return response;
}
#Override
protected void onPostExecute(String result) {
Log.d("mylog", "result= " + result);
delegate.processFinish(result);
}
public String transfer(URL url) throws IOException {
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// inputStream = url.openStream();
InputStream inputStream = urlConnection.getInputStream();
BufferedReader bin = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String line = bin.readLine();
StringBuffer sb = new StringBuffer();
while (line != null) {
sb.append(line);
sb.append("\r\n");
line = bin.readLine();
}
inputStream.close();
return sb.toString();
}
}
And the interface is just:
public interface AsyncResponse {
void processFinish(String output);
}
As I said, everytime I switch the phone orientation all the async process is performed (and the Toast shows up). That's exactly what I wanted to avoid in the first place.
you dont need Stop/Resume activities you can use AsyncTask class and doInBackGround method when get data from external url and show to user process Dialog for waiting
Never ever try to get the data from URL on Main thread. Always use AsyncTask for getting the data from URL
You can write in the same activity, but not in the main thread. Maybe AsyncTask will help.
I want to start an AsyncTask inside another AsyncTask.
I'm trying to do this by starting the second AsyncTask in 'onPostExecute' of the first AsyncTask. The result is that the second AsyncTask starts after the first has finished.
How can I solve this?
Thank you
here's the code
private class Parse extends AsyncTask<Void, Void, Void>{
Document doc = null;
Element son = null;
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
try {
doc = Jsoup.connect("MY_URL").get();
son= doc.body();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
for(CONDITION){
//MY_CODE
new DownloadImageTask().execute();
}
}
}
This is the second AsyncTask
private class DownloadImageTask extends AsyncTask<String, Void, String> {
Bitmap bitmap;
ImageView image = new ImageView(Events.this);
#Override
protected void onPreExecute() {
};
protected String doInBackground(String... urls) {
try {
URL url = new URL("MY_URL");
bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
First, please refer to the oficial documentation to have a best understanding about AsyncTask: http://developer.android.com/reference/android/os/AsyncTask.html
The onPostExecute runs in the main thread, after the background task finishes. This method is designed this way to be used to comunicate with the UI thread to handle the execution.
If you want to run the second AsyncTask with your background code, or just after it started, you have to do this on the doInBackground method.
In some way I do understand the Handler, but I'm not sure what to do with the params and how to let the code wait until the job is done in the background. I want the UI to be normally working and in the background I want to do an exchange rate calculation.
I have the following:
I call new getOnlineExchangeRate().execute(""); //Get Exchange Rate in BG
After that I want to have a result=amount*exchangerate, but the code is not waiting for the result.
Can somebody tell me how the calculation waits till we have an exchangerate. Do I have to send some params and how would that look?
.
.
.
.
.
public double getYahooExchangeRate(String ER){
double exchangerate=0;
try {
s = getJson("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.xchange%20where%20pair%20in%20(%22"+ER+"%22)&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=");
//s = getJson("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.xchange%20where%20pair%20in%20(%22"+val[from]+val[to]+"%22)&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=");
JSONObject jObj;
jObj = new JSONObject(s);
String exResult = jObj.getJSONObject("query").getJSONObject("results").getJSONObject("rate").getString("Rate");
exchangerate=Double.parseDouble(exResult);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
ALS.Toast(myContext.getString(R.string.conversionerror), false);
}
catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
ALS.Toast(myContext.getString(R.string.conversionerror), false);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
ALS.Toast(myContext.getString(R.string.conversionerror), false);
}
return exchangerate;
}
public String getJson(String url)throws ClientProtocolException, IOException {
StringBuilder build = new StringBuilder();
HttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse response = client.execute(httpGet);
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
String con;
while ((con = reader.readLine()) != null) {
build.append(con);
}
return build.toString();
}
public class getOnlineExchangeRate extends AsyncTask<String, Void, String> {
#Override
protected void onPostExecute(String result) {
// execution of result of Long time consuming operation
ALS.Toast(myContext.getString(R.string.exchangeratesupdated), true);
}
#Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
// perform long running operation operation
getYahooExchangeRate(USDEUR);
return null;
}
I think your problem is in this line:
#Override
protected String doInBackground(String... params) {
getYahooExchangeRate(USDEUR);
return null;
You want to return the result of getYahooExchangeRate and not null :)
So change this and the return-value should be a double. So change this to:
#Override
protected Double doInBackground(String... params){
return getYahooExchangeRate(USDEUR);
}
You also have to change your class header:
public class getOnlineExchangeRate extends AsyncTask<String, Void, Double> {
AsyncTask<Params, Progress, Result>
The generic part tells the AsyncTask which Informationstypes are handled.
The first is the type for the params of doInBackground(Params... )
The second is the type of the progress-Information
The last explains which type is returned by doInBackground(), so it changes the method-header from
protected Result doInBackground(Params... params){ };
to
protected double doInBackground(Params... params){};
To bring back the Result i would use and Observer oder Callback-Pattern.
Edit: changed double to Double, because primitives cannot be used for Generics.
the code is not waiting for the result. Can somebody tell me how the calculation waits till we have an exchangerate. Do I have to send some params and how would that look?
You could use AsyncTask#get() to force the code to wait, but this blocks the main thread until the AsyncTask completes which defies the purpose of using an asynchronous task.
It is best to design your Activity to proceed without the exchange rate, just like my mail app loads allowing me to compose messages and read old messages while the new messages are being fetched. When the asynchronous data loads then you can update your UI with the new information. (I believe this is what you are trying to do.)
To add on to user1885518 code, you should use your AsyncTask as a subclass in your Activity like this:
public class MainActivity extends Activity {
private class getOnlineExchangeRate extends AsyncTask<Void, Void, Double> {
#Override
protected Double doInBackground(Void... params) {
return getYahooExchangeRate(params[0]);
}
#Override
protected void onPostExecute(Double rate) {
// Do something with rate
}
}
...
}
Once you know which exchange rate you want, call:
new getOnlineExchangeRate().execute(USDEUR); //Get Exchange Rate in BG
Now when you have gotten the rate from online, the code calls onPostExecute() with your desired rate. Inside on onPostExceute() you can call whatever method you want in your ACtivity to calculate result=amount*exchangerate and display result wherever it is appropriate.