I have the following code, in which I try to connect to google.com and parse the text on that site:
package com.example.parsetest;
import java.io.IOException;
import org.jsoup.*;
import org.jsoup.nodes.Document;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
Thread downloadThread = new Thread() {
public void run() {
Document doc;
try {
doc = Jsoup.connect("http://google.com/").get();
String title = doc.title();
TextView console = (TextView) findViewById(R.id.textview1);
console.setText(title);
} catch (IOException e) {
e.printStackTrace();
}
}
};
}
My issue is that I'm unsure as to whether I've created my new thread properly, and where I'm supposed to call downloadThread.start() from - am I supposed to create a new class? Or do I call it from my onCreate method?
Yes you need to call downloadThread.start(). You cannot update ui from a background thread. Use runOnUiThread
public class MainActivity extends Activity {
TextView console;
String title;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
console = (TextView) findViewById(R.id.textView1);
new Thread() {
public void run() {
Document doc;
try {
doc = Jsoup.connect("http://google.com/").get();
title = doc.title();
runOnUiThread( new Runnable()
{
public void run()
{
console.setText(title); // set text on the ui thread
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
As other have suggested using asynctask is easier.
http://developer.android.com/reference/android/os/AsyncTask.html
Using AsyncTask
public class MainActivity extends Activity {
TextView console;
String title;
ProgressDialog pd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pd = new ProgressDialog(this);
pd.setMessage("Jsoup parsing...");
console = (TextView) findViewById(R.id.textView1);
new TheTask().execute();
}
class TheTask extends AsyncTask<Void,String,String>
{
#Override
protected String doInBackground(Void... arg0) {
// TODO Auto-generated method stub
Document doc;
try {
doc = Jsoup.connect("http://google.com/").get();
title = doc.title();
}
catch(Exception e)
{
e.printStackTrace();
}
return title;
}
#Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
pd.dismiss();
console.setText(title);
}
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
pd.show();
}
}
}
check out android AsyncTask usage example
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
the download should be taken place at doInBackground
I'm unsure as to whether I've created my new thread properly
Yes you have created Thread in right way but inside run method you are updating TextView text which is not valid because only main thread updated ui elements instead of any other thread. you should use runOnUiThread for updating TextView :
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
TextView console = (TextView) findViewById(R.id.textview1);
console.setText(title);
}
});
suggested way is use AsyncTask
where I'm supposed to call downloadThread.start() from
you should call downloadThread.start() in onCreate of Activity after setContentView
Related
There are an application that process many operations in an AsyncTask that has a runOnUIThread call.
The application has the code to show an indeterminate ProgressBar when the process it's doing in background, but the progress bar isn't shown always, in one cases yes, in other cases no.
With this model of (bad structure) code, how can I sure that the progress bar always is shown
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.TextView;
public class AsyncTestActivity extends ActionBarActivity {
private ProgressDialog pd = null;
private void loadingBar(String titulo){
pd = ProgressDialog.show(this, titulo, "Please wait ...", true, false);
}
private void closeLoading(){
pd.dismiss();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_test);
TextView textView = (TextView) findViewById(R.id.textView);
Task async = new Task(textView);
async.execute("");
}
private class Task extends AsyncTask<String, Void, String> {
private TextView tv;
public Task(TextView tv) {
this.tv = tv;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
loadingBar("Warning");//I want to show this always
}
#Override
protected String doInBackground(String... params) {
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
for(int i = 0; i <= 10; i++) {
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
return "End";
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
tv.setText(result);
closeLoading();
}
}
}
I, can't change the structure (AsyncTask/runOnUIThread)the code because is big and I have few time to do this. The goal is that the progress bar always is ahowed. I had been reading that this is a bad practice, but the code was made by another person.
Did you ever try to call bringToFront() inside your onPreExecute():
#Override
protected void onPreExecute() {
super.onPreExecute();
loadingBar.bringToFront();
loadingBar("Warning");//I want to show this always
}
(Untested code)
or if you want to make sure, you can include your progressBar as your Task parameter then directly execute bringToFront();
public Task(TextView tv, ProgressBar pB) {
this.tv = tv;
this.pb = pB.bringToFront();
}
hope it helps
Call loadingBar("Warning"); before async.execute("");.
If it's not showing, there is a problem with your ProgressDialog. Can you try edit loadingBar() method like this? :
ProgressDialog pd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_test);
//init progress
pd = new ProgressDialog(this);
//
TextView textView = (TextView) findViewById(R.id.textView);
Task async = new Task(textView);
async.execute("");
}
private void loadingBar(String titulo){
pd.show();
pd.setTitle(titulo);
}
I am making an app that counts up every second based on rate of pay, and as it is written now, it crashes on startup.
Am I doing the thread section wrong?
I am kind of new to android, so I am a bit unclear on the onCreate method in general. Any clarification about that and how it relates to my code would be helpful as well.
The button is supposed to start the count. I think it's crashing due to the t.start() line, but I don't know how to trigger the event.
package com.example.terik.myapplication;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView text2;
text2 = (TextView) findViewById(R.id.textView2);
Thread t = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
runOnUiThread(new Runnable() {
#Override
public void run() {
updateTextView();
}
});
}
} catch (InterruptedException e) {
}
}
};
t.start();
}
private void updateTextView() {
TextView text2;
double update;
double rateofPay = 9.00;
text2 = (TextView)findViewById(R.id.textView2);
CharSequence newtime = text2.getText();
int number = Integer.parseInt(newtime.toString());
update = number+ rateofPay;
text2.setText((int) update);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
One problem was a NumberFormatException when trying to parse a Double as an Integer.
The other problem was trying to call setText() with an int on this line:
text2.setText((int) update);
This fixed it:
private void updateTextView() {
TextView text2;
double update;
double rateofPay = 9.00;
text2 = (TextView)findViewById(R.id.textView2);
CharSequence newtime = text2.getText();
double number = Double.parseDouble(newtime.toString());
update = number+ rateofPay;
text2.setText(String.valueOf(update));
}
Edit:
Here's how you would only start the Thread when you click the Button. First make the Thread t an instance variable, so that it can be accessed in the button click run() method (you might want to re-name that method too!).
I just tested this, it worked for me:
public class MainActivity extends ActionBarActivity {
Thread t;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView text2;
text2 = (TextView) findViewById(R.id.textView2);
t = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
runOnUiThread(new Runnable() {
#Override
public void run() {
updateTextView();
}
});
}
} catch (InterruptedException e) {
}
}
};
}
public void run(View v) {
t.start();
}
private void updateTextView() {
TextView text2;
double update;
double rateofPay = 9.00;
text2 = (TextView)findViewById(R.id.textView2);
CharSequence newtime = text2.getText();
double number = Double.parseDouble(newtime.toString());
update = number+ rateofPay;
text2.setText(String.valueOf(update));
}
//.........
Edit 2:
As #BladeCoder mentioned in the comments, a Thread is really over-kill for this. Using a Handler and postDelayed() is really the best route for something like this.
Also, it would be better to make the TextView an instance variable so that you don't create a new reference every time you update it.
I tested this version as well:
public class MainActivity extends ActionBarActivity {
TextView text2;
Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text2 = (TextView) findViewById(R.id.textView2);
handler = new Handler();
}
Runnable updateText = new Runnable(){
#Override
public void run(){
updateTextView();
handler.postDelayed(this, 1000);
}
};
public void run(View v) {
handler.postDelayed(updateText, 1000);
}
private void updateTextView() {
double update;
double rateofPay = 9.00;
CharSequence newtime = text2.getText();
double number = Double.parseDouble(newtime.toString());
update = number+ rateofPay;
text2.setText(String.valueOf(update));
}
//.............
Instead of using thread to achieve this, you can achieve the same effect with a "neat way" using the Timer class.
Check this stackoverflow answer Android timer? How-to?
Regarding the onCreate method I suggest you to check the activity life cycle
Called when the activity is first created. This is where you should do all of your normal static set up: create views, bind data to lists, etc. This method also provides you with a Bundle containing the activity's previously frozen state, if there was one.
Always followed by onStart().
Check the documentation for further info http://developer.android.com/reference/android/app/Activity.html#onCreate(android.os.Bundle)
Is it okay to do this? Creating another MainAcitivity since my original MainAcitivity is tied on the v4.view.pager activity_main. Although, this code doesn't post errors, this class seems to not be reflecting on the output. I'm using a tab pager adapter. Do I need to refresh or something? (It doesn't do anything that even the clickAction on this class doesn't work.
public class MainActivity2 extends Activity {
String url = "http://blog.compassion.com/living-in-the-philippines-rural-life/";
ProgressDialog mProgressDialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_fragment);
TextView titlebutton = (TextView) findViewById(R.id.TextView01);
titlebutton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
// Execute Title AsyncTask
new Title().execute();
}
});
}
private class Title extends AsyncTask<Void, Void, Void> {
String body;
String title;
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog = new ProgressDialog(MainActivity2.this);
mProgressDialog.setTitle("Android Basic JSoup Tutorial");
mProgressDialog.setMessage("Loading...");
mProgressDialog.setIndeterminate(false);
mProgressDialog.show();
}
#Override
protected Void doInBackground(Void... params) {
try {
Document document = Jsoup.connect(url).get();
Elements elements = document.select("p");
title = document.title();
for(Element elements123 : elements){
body+=elements123.text();
System.out.println(elements123.text());
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
TextView txttbody = (TextView) findViewById(R.id.textView2);
txttbody.setMovementMethod(new ScrollingMovementMethod());
txttbody.setText(title +"\n"+body);
mProgressDialog.dismiss();
}
}
}
I’m using ksoap to call a web service.
When Application starts or when i scroll to end of page Application fetches data from Web Service correctly and it shows data as well as i want, But When I want to show Progress Dialog in a AsyncTask it doesn’t work correctly. It shows Progress Dialog after PostExecute in a blik of eyes. So, What's wrong in my code ? I can't understand where is my problem. ;-)
I have a web service class called ElvatraWebService:
Here is a Method witch is fetch latest post with Asynctasks
private String GetLastPublicPosts(int counter){
...
...
...
...
return resTxt; //Returns JSON String
}
public String getLatestPost(int counter, Activity a){
CallLatestPost task = new CallLatestPost(counter,a);
try {
strJSON = task.execute("getLatest").get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Log.i("sss", "--" + strJSON);
return strJSON;
}
private class CallLatestPost extends AsyncTask<String, Void, String> {
private int Counter = 0;
private ProgressDialog dialog;
private Activity activity;
private Context context;
public CallLatestPost (int c,Activity actt) {
super();
this.Counter = c;
activity = actt;
context = actt;
dialog = new ProgressDialog(this.context);
}
#Override
protected String doInBackground(String... params) {
ElvatraWebService elws = new ElvatraWebService();
String tmp = elws.GetLastPublicPosts(this.Counter);
//Log.i("strJSON",strJSON);
return tmp;
}
#Override
protected void onPostExecute(String result) {
//Set response
super.onPostExecute(result);
if (dialog.isShowing()) {
dialog.dismiss();
}
}
#Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(context);
dialog.setMessage("Connecting...");
//dialog.setIndeterminate(true);
dialog.setCancelable(true);
dialog.show();
Log.i("###Async", "=== " + "PreExec");
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
I have a Class called post. It will call getLatestPost from Elvatrawebservice.
Then it will convert JSON String to JsonArray with PostAdapter class. PostAdapter is a class extends BaseAdapter.
In Main Activity I call a local Method called LoadContent in onCreate method.
Here is MainActivity Class
public class MainActivity extends Activity {
String displayText="";
public TextView tv;
public ListView lv;
public ProgressBar pg;
int theCounter=20;
String[] strTAG = new String[] {"title","photo","date","desc","linkurl"};
//int[] intField = new int[] {R.id.title,R.id.imgPost, R.id.Date, R.id.desc, R.id.link};
ListAdapter sa;
List<HashMap<String,Object>> list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recentposts);
lv = (ListView) findViewById(R.id.list);
lv.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
final int lastItem = firstVisibleItem + visibleItemCount;
if(lastItem == totalItemCount) {
//load more data
// Load content of listview
LoadContent();
}
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void onDestroy() {
super.onDestroy(); // Always call the superclass
// Stop method tracing that the activity started during onCreate()
android.os.Debug.stopMethodTracing();
}
public void LoadContent(){
lv = (ListView) findViewById(R.id.list);
Post p = new Post(theCounter);
theCounter+=20;
if (p.GetLatestPosts(lv,MainActivity.this))
{
//tv.setText("true");
}
}
}
You have
task.execute("getLatest").get();
You should not call get() as this blocks the ui thread waiting for the result making it asynctask asynchronous no more. Just use
task.execute("getLatest");
You can return result in doInBackground and update ui in onPostExecute.
I want to make a thread which will change the layout of my activity ... I have 2 layouts : welcomepage and activity_main ...
The goal of thread: when I launch my application , the welcomepage layout will be visible in only 5 sec and after that the layout again will be activity_main ...
I wrote the code as below:
package com.example.tripolimazad;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
public TextView counter = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.welcomepage);
counter = (TextView) findViewById(R.id.Counter);
Thread th=new Thread(){
#Override
public void run(){
try
{
Thread.sleep(10000);
setContentView(R.layout.activity_main);
}catch (InterruptedException e) {
}
}
};
th.start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
but it doesnt work !!! have anyone any solution plz !
You cannot change the UI on a non-UI thread, however in an Activity, you can use the runOnUiThread method:
runOnUiThread(new Runnable() {
#Override
public void run() {
setContentView(R.layout.activity_main);
}
});
This seems very strange though, to have this in your onCreate.
You may also try to use CountDownTimer something like:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.welcomepage);
//display the logo during 5 secondes,
new CountDownTimer(5000,1000){
#Override
public void onTick(long millisUntilFinished){}
#Override
public void onFinish(){
//set the new Content of your activity
MainActivity.this.setContentView(R.layout.main);
}
}.start();
//...
}
See Displaying logo for few seconds at application start for more.
Alternatively you can use a handler to avoid creating a new thread as following:
getWindow().getDecorView().getHandler().postDelayed(new Runnable() {
#Override public void run() {
setContentView(R.layout.activity_main);
}
}, 10000);
UI related operations must be done on UI thread only and must not be done in non-UI thread as you are doing. But you can update UI from non-UI thread as follows:
activityContext.runOnUiThread(new Runnable() {
#Override
public void run() {
setContentView(R.layout.activity_main);
}
});
However a better approach for the situation you mentioned is to use an Async task, provided by the Android.You can try following :
/*
* Activity/Thread to display the **welcomepage**
* when the app is started.
*/
public class SplashActivity extends Activity {
// how long until we go to the next activity
protected int _splashTime = 5000; // 5 seconds
// thread to display welcome page
private Thread splashTread;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_welcome_page_layout);
// thread for displaying the WelcomeScreen
splashTread = new Thread() {
#Override
public void run() {
try {
synchronized (this) {
// wait 5 sec
wait(_splashTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// finish the current splashactivity
finish();
// start MainActivity as next activity
startActivity(new Intent(SplashActivity.this, MainActivity.class));
}
}
};
// start the thread.
splashTread.start();
}
}