Good evening.
I have an activity like this. In handleMessage I have access to largeText field and can change it, but I can't do smth whith stringLinks field, or each other field which is not a UI element(like TextView, Button, EditText, etc).
How can I add to stringLinks ?
public class AboutUsActivity extends Activity {
Handler h;
TextView largeText;
List<String> stringLinks;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
stringLinks = new ArrayList<String>();
largeText = (TextView) findViewById(R.id.textView1);
h = new Handler() {
public void handleMessage(android.os.Message msg) {
HtmlParser parser;
StringBuilder result = new StringBuilder();
try {
parser = new HtmlParser(String.valueOf(msg.getData()));
List<TagNode> links = parser.getContentByClassName("ab");
for (Iterator<TagNode> iterator = stringLinks.iterator(); iterator
.hasNext();) {
TagNode divElement = (TagNode) iterator.next();
result.append(divElement.getText().toString());
}
} catch (Exception e) {
e.printStackTrace();
}
largeText.setText(newhtml); //Work
stringLinks.add(newhtml); //doesn't work
}
};
MyHttpClientUsage connect = new MyHttpClientUsage(h);
try {
connect.getInfoAbout();
} catch (HttpException e) {
e.printStackTrace();
}
}
}
I dont know if I understood you correctly.
If i did, you can use AsyncTask class
Check out their main too methods, doInBackground that executes something in a separate thread and onPostExecute that runs in UI Thread. They can comunicate with each other passing objects, and you can also publish the progress of your task.
It really looks like SwingWorker (maybe you used it if you programmed Swing applications).
Related
I'm currently working on my first Android application.
The application accesses a database to get some informations that I want to print on the screen. To send requests and get answers on the network, I need to use a new thread (I'll name it "N thread"), different from the UI Thread. This part is ok.
Now, I want to modify the variable eventList to get the values stored in a collection, in the N thread.
public class MainActivity extends AppCompatActivity {
public List<Event> eventList = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* I fill the list in an other thread */
new Thread(new Runnable() {
public void run(){
eventList = new WebService().getEvents(); //returns a list
}
// if I check here, eventList contains elements
}).start();
/* I check the result */
TextView respView = (TextView) findViewById(R.id.responseView);
if(eventList != null)
{
respView.setText("Ok");
} else {
respView.setText("Not ok");
}
...
}
The problem is : eventList is not modified. How can modify this variable and print it from the UI thread ?
Thank you for your help.
You can use runOnUiThread function or Handler to update UI from other thread. I suggest you reading the below tutorial first: AndroidBackgroundProcessing
Try this
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params)
{
eventList = new WebService().getEvents();
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView respView = (TextView) findViewById(R.id.responseView);
if(eventList != null)
{
respView.setText("Ok");
} else {
respView.setText("Not ok");
}
}
});
}
}.execute();
private class EventsDownloader extends AsyncTask<Void, Void, Void> {
protected Long doInBackground(Void... params) {
eventList = new WebService().getEvents()
}
protected void onPostExecute(Void result) {
TextView respView = (TextView) findViewById(R.id.responseView);
if(eventList != null)
{
respView.setText("Ok");
} else {
respView.setText("Not ok");
}
}
}
This AsyncTask does what you want, the doInBackground runs on a thread and the 'onPostExecute' runs on the UI thread, and it's only called after the doInBackground finishes. This class is "managed" by the OS. To run it you just need to instantiate it and call 'execute'. I recommend doing something like this
The thing with your code is that the thread runs at the same time as the rest of your code (the calls to the setText), this means when it runs the setText the Thread is still getting the events.
I am working on an Android project in which I have to connect to a server, retrieve a list of restaurants and display it in a ListView. The situation is when the app is started, for testing, I am starting another Intent and running a thread inside it. Inside the thread, I am retrieving data from a server which I would like to display it on the ListView. But the handler method which should get the data and display it, is not working.
Unfortunately I am getting no errors to isolate and deal with the problem, and as passing data between threads is a core function, I am getting lost in the so many methods it calls in between. Kindly have a look what I am doing wrong.
MainActivity class :
public class MyActivity extends Activity {
RestaurantServiceImpl restaurantService = new RestaurantServiceImpl();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button) findViewById(R.id.standortermitteln);
//Below starts the new view
Intent listScreen = new Intent(getApplicationContext(),RestaurantList.class);
}
RestaurantList.java :
public class RestaurantList extends Activity {
String url1 = "http://192.168.178.40:8080/restaurant/listing";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.restos);
ListView listView = (ListView) findViewById(R.id.restaurantList);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Log.d("Are we in listRestaurants","Checking");
try {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Collections.singletonList(new MediaType("application", "json")));
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
restTemplate.getMessageConverters().add(new GsonHttpMessageConverter());
ResponseEntity<Restaurant[]> responseEntity = restTemplate.exchange(url1, HttpMethod.GET, requestEntity, Restaurant[].class);
Restaurant[] restaurantList = responseEntity.getBody();
for(Restaurant restaurant : restaurantList){
// I am able to print these messages,
Log.d(restaurant.getRestaurantName(), "Restaurant name");
}
} catch (Exception e) {
Log.d("We are in stacktrace",e.toString());
e.printStackTrace();
}
}
});
thread.setPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// I am not reaching here to set the ListView
Handler messageHandler = new Handler(){
public void handleMessage(Message message){
try {
Restaurant[] restaurants = (Restaurant[]) message.obj;
for(Restaurant restaurant : restaurants){
Log.d(restaurant.getRestaurantName(), "Restaurant name");
}
if(restaurants.length<1){
Log.d("Restaurants from thread are empty"," check");
}
for(Restaurant restaurant : restaurants){
Log.d(restaurant.getRestaurantName(), "Restaurant name in messageHandler");
}
ArrayAdapter<Restaurant> adapter = new ArrayAdapter<>(getApplicationContext(),android.R.layout.simple_list_item_1,restaurants);
listView.setAdapter(adapter);
}catch (Exception e){
e.printStackTrace();
}
}
};
}
}
Add queueNewRestaurants(restaurantList); to the end of the first thread.
private static final int NEW_RESTAURANTS = 1;
public void queueNewRestaurants(Restaurant[] restaurants) {
android.os.Message message = messageHandler.obtainMessage(
NEW_RESTAURANTS,
restaurants
);
message.sendToTarget();
}
As you are sending the message to the handler from the thread, it will not call the handler. Add following line to your thread.
handleMessage.sendMessage(new Message());
Edit:
Add object
Message message =new Message();
message.obj=arrayobject;
You are creating a Handler but never sending anything to it. In this case I would recommend that once you have your restaurantList in your worker thread, you either post a runnable to a Handler that you create as part of your activity being constructed, or you use runOnUiThread.
Option 1.
public class RestaurantList extends Activity {
String url1 = "http://192.168.178.40:8080/restaurant/listing";
Handler mHandler = new Handler();
...
...
...
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
...
...
...
final Restaurant[] restaurantList = responseEntity.getBody();
mHandler.post(new Runnable() {
public void run() {
// update UI here
}
});
Option 2.
call
runOnUiThread(new Runnable(.....));
instead of mHandler.post(.....)
Option 3.
Use an AsyncTask, do the HTTP work in doInBackground() and update your UI in onPostExecute(). Using an AsyncTask you don't have to care about creating any thread at all, it's done for you.
In this particular case I would probably go for option 3.
Except for Handler, you also can use java API inside your thread run():
runOnUiThread(new Runnable() {
public void run() {
//communicate with main thread here, send the result data to main thread
}
});
i need to check a variable for change, if the change happens i will update a textview.
so i created a new thread with a while loop for this purpose. i check the variable every 1 second, via Thread.sleep()
this thread created and started in onCreate(). so it's one time.
the problem is every time i flip my phone (from vertical to horizontal or ...) a new thread will be created.
here is my code:
public class HomeActivity extends Activity
{
private final static int LOAD_SUCCESSFULL = 1;
private final long LOCATION_THREAD_SLEEP = 1000;
private boolean flag = false;
static TextView lat;
static TextView lon;
static Location currentLocation = null;
Thread locationThread;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.new_home2);
this.getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.new_home_titlebar);
lat = (TextView)findViewById(R.id.t2rt3);
lon = (TextView)findViewById(R.id.t2rt4);
/* FindLocation class is a helper class that find location via simcard or gps in separate thread,
same problem happen with this thread also, it create multiple thread, for ease of work i
commented this part.
*/
//FindLocation fn = new FindLocation(this);
locationThread = new Thread(null, loadLocation, "loadLocationHomePage");
locationUpdater();
}
private static Handler locationUpdateHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch(msg.what)
{
case LOAD_SUCCESSFULL:
lat.setText(Double.toString(currentLocation.getLatitude()));
lon.setText(Double.toString(currentLocation.getLongitude()));
//stopThread();
break;
}
}
};
private Runnable loadLocation = new Runnable()
{
public void run()
{
//boolean flag = false;
while(!flag)
{
if(Data.currLocation != null)
{
currentLocation = new Location(Data.currLocation);
Message msg = locationUpdateHandler.obtainMessage(LOAD_SUCCESSFULL);
locationUpdateHandler.sendMessage(msg);
//return;
flag = true;
//return;
}
else
{
try
{
Thread.sleep(LOCATION_THREAD_SLEEP);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
};
public void locationUpdater()
{
//Thread locationThread = new Thread(null, loadLocation, "loadLocationHomePage");
locationThread.start();
}
so how i can solve this?
Actually the problem is that EveryTime you flip the phone a new instance of Activity is created and because of this you on every rotation you get a call on onCreate() where you are blindly creating a new Thread and Starting the new Thread.
This is the default behavior of every Activity but we can change this re-creation of Activity by stating an attribute in AndroidManifest file for the Activity
<activity
android:name="yourPackage.ActivityName"
android:configChanges="keyboardHidden|orientation|screenSize"
</activity>
This will prevent from creation of Activity on orientation change.
You will also get these orientation event if you override
#Override
public void onConfigurationChanged(Configuration newConfig) {}
Hope this will solve this problem without implementing such a complex logic which may broke in some other uses case.
I think you aren't perhaps going about this in the most efficient way possible.
But if your question is simply, how do i prevent multiple worker threads from being spawned, you should look into a UIless fragment.
http://www.vogella.com/articles/AndroidFragments/article.html#headlessfragments1
i don't know why android doing this. if i putted my code in onResume(), then this behavior make sence but in my case, i don't know.
anyway i found a dirty solution. the idea is finding list of all thread and search them for mine, if it existed prevent to create another.
public boolean checkThreadExist()
{
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for(int i = 0; i < threadArray.length ; i++)
{
if(threadArray[i].getName().equalsIgnoreCase("loadLocationHomePage"))
return true;
}
return false;
}
updated onCreate() :
if(checkThreadExist())
{
}
else
{
locationThread = new Thread(null, loadLocation, "loadLocationHomePage");
locationUpdater();
}
I have a thread that needs to be receiving data all the time from the network and I want this data to be displayed to an EditText object.
Obviously, I can't access the UI EditText from within my receiving thread; what I read is that I can use AsyncTask but reading the example in Painless Threading it seems to me that I have to be done with receiving the data before I can be able to post the results to the UI component.
I can't use post or postDelayed as both will be run over the UI thread and I can't block the UI to receive the data; I need to keep receiving the data all the time.
What other options do I have?
Use LocalBroadcastManager, your Activity containing TextView will start listening for broadcast:
public class MyActivity extends Activity {
private TextView mTextView;
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getStringExtra("actionType");
if(action.equals("updateTextView")){
mTextView.setText("whatever you want to set");
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Start listening, you can put it on onResume too
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter(MyActivity.class.getSimpleName()));
mTextView = (TextView) findViewById(R.id.something);
}
}
So whenever your Thread receive something that needs to update the screen, call this:
Intent intent = new Intent(MyActivity.class.getSimpleName());
intent.putExtra("actionType", "updateTextView");
// Once this is called, your broadcast receiver in MyActivity should receive it and start processing
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
Also remember to unregister it in onDestroy or onPause.
*side note: you need to import android support v4 library, and you can pass simple String or object over by intent using Intent.putExtra("","") and Inteng.getExtra("");
Another way is to implement a data listener interface.
public interface DataListener{
void onUpdateData(MyData data);
}
You activities that contain the UI components that need to be updated will implement this interface. It will specify what need to do with updated data.
You may want to keep all instances these data listener interface somewhere in your app.
I assume that you have a different thread to handle network sending/receiving actions. On receiving data, you just call:
dataListenerInstance.onUpdateData(data)
Then it will activate the handler that you have implemented in your activity.
In MainActivity call AsyncTask but make #Override method onPostExecute(..)
public class MainActivity extends ActionBarActivity{
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(Utils.isNetworkAvailable(this)) {
DownloadFileFromURL downloader = new DownloadFileFromURL(){
#Override
protected void onPostExecute(Integer file_content) {
onCompleteLoad();
}
};
downloader.execute(new String[]{file_url, fileName});
...
onCompleteLoad(); - will be call in UI thread of MainActivity. You don't need even implements Interface!
Secon way more suitable for server solutions, but can also be used on the client it is Callable
public class DoGetSize implements Callable<Integer> {
private final String file_url;
private int lenghtOfFile = -1;
public DoGetSize(String file_url) {
this.file_url = file_url;
}
public Integer call() {
try {
URL url = new URL(file_url);
URLConnection connection = url.openConnection();
connection.connect();
lenghtOfFile = connection.getContentLength();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return lenghtOfFile;
}
}
And call this like:
FutureTask<Integer> task = new FutureTask(new DoGetSize(file_url));
ExecutorService es = Executors.newSingleThreadExecutor();
es.submit (task);
try {
Integer result = task.get();
File file = new File(fileName);
if(file.length() != result.intValue()) {
// Do something
...
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
You can send and receive any object in such a way
Full example see on github: https://github.com/app-z/OffLineShop/blob/master/app/src/main/java/net/appz/offlineshop/offlineshop/MainActivity.java
You can do this using a simple delegation.
class NonUIThread {
private NonUIThreadDelegate delegate; //NonUIThreadDelegate can be an interface or an object that has access to your UI thread like an Activity
public void setDelegate(NonUIThreadDelegate delegate) {
this.delegate = delegate;
}
private void doSomthing() {
//do something and at the end:
delegate.someMethodThatUpdatesThatComponent();
}
}
class TheUIThread implements NonUIThreadDelegate /*assuming you've decided to make NonUIThreadDelegate an interface*/ { // the "delegator"
/*
your code
*/
private void initiateNonUIThread() {
NonUIThread nonUIThread;
/*do whatever needed*/
nonUIThread.setDelegate(this);
nonUIThread.start();
}
public void someMethodThatUpdatesThatComponent() { //will be called by the non ui thread
//update the UI
}
}
It's explained a little better (of course using AsincTask) in here: Simple Delegation Pattern in Android
My application has a ViewFlipper with 3 ViewGroups in it. Each ViewGroup interaction is dependent on data from a database. I'm using an AsyncTask to read from a database and return a Cursor when it's done. Before the AsyncTask is executed, I just want to display a single View in the ViewFlipper saying "Loading data, please wait.", is this possible somehow?
Show the progress dialog in your onPreExecute() and dismiss it in the onPostExecute(). Something like this,
private class MyAsyncTask extends AsyncTask<Integer, Integer, Integer[]> {
private ProgressDialog myWait = null;
// This is on the UI thread itself
protected void onPreExecute() {
myWait = new ProgressDialog(MainActivity.this);
myWait.setMessage("Loading data, please wait");
myWait.setCancelable(false);
myWait.show();
}
// Separate worker thread is used here
protected Integer[] doInBackground(Integer...params) {
//do the database loading
return <your result - goes to onPostExecute>;
}
// This is on the UI thread itself
protected void onPostExecute(Integer[] resultCell) {
if (myWait != null) {
myWait.dismiss();
}
}
}
yes you can make use of progressDialog. Do it like this,
progressDiaolg=ProgressDialog.show(Activity.this,"","Loading Images...");
final Thread t= new Thread(new Runnable() {
public void run() {
Log.i("Inside Thread", "Downloading Images...");
downloadImages();
handler.sendEmptyMessage(0);
}
});
t.start();
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
try {
progressDiaolg.dismiss();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
};
I don't have idea with Asynctask. So try modifying this snippet accordingly.