Android : Sharing data from adapter and activity - java

I have a main activity where I create an adapter for a arraylist data.
I read the news from a website using a separate thread with Jsoup.
In onCreate() i have
this.newsItemAdapter = new NewsItemAdapter(this,
R.layout.newsitem_row,NewsItemAdapter.getAllNews());
parseThread.start();
this.newsItemAdapter.notifyDataSetChanged();
When i read from the adapter, i get empty list. This is because the thread is not completed yet. Any idea as to how i should proceed?
I cannot do notifyDataSetChanged inside my thread because it is not the owner of the adapter.

You need to notify after the seperate thread finishes, and invoke notifyDataSet on UI-thread, one possible method is to use handler,
you can define a handler in activity, invoke notifyDataSet change in handleMessage, such as
handler = new android.os.Handler() {
#Override
public void handleMessage(Message msg) {
newsItemAdapter.notifyDataSetChanged();
}
}
and in the thread run method, you need to send a message to the handler,
public void run() {
// add this to the end
Message msg = new Message()
handler.sendMessage(msg);
}
or else you can use AsyncTask instead of seperate thread, use jsoup in task's doInBackground method, and notifyDataSet in onPostExecute.
The handler document is http://developer.android.com/reference/android/os/Handler.html, and AsyncTask's is http://developer.android.com/reference/android/os/AsyncTask.html.

Pass the adapter to your thread in its constructor. Then your thread can call notifyDataSetChanged.

Related

Android how to update (UI thread) from other classes (really?)

you may know about Google Cloud Messaging
The problem is that when a gcm message triggers by the server, my application receives a bundle from google play services, this happen at GcmBroadcastReceiver.java. Here i can send this data to other classes in order to append some info from the server.. well. I got stuck when i try to update, for example, some views in the UI thread.
HOW I CAN DO THIS?
Imagine that MainActivity.java is the UI thread when i declare the views, etc.
I tried to create here a public static method which can be called directly by GcmBroadcastReceiver.java by this way: MainActivity.*updateUI*(args..), but it throws this exception:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Can anyone try to explain me this? i also know about asyncTask but i cant imagine how it works. I also find some pages explaining events that are fired by the UI thread it self like runnables that do some task in background. Im searching something like this:
MainActivity extends Activity{
...
protected void onCreate(Bundle blabla)..{
setContentView(R.layout.blabla);
registerSomeEvent(this);
}
private void handleEvent(Bundle ...){
... do stuff with the data provided in the UI thread
}
}
And here at GcmBroadcastReceiver, when gcm push some data, trigger that magic event in order to perform updates at the UI thread with some views like ListViews or TextView
One way is to use use LocalBroacastManager. For how to implement is, there is a great example on how to use LocalBroadcastManager?.
LocalBroadcast Manager is a helper to register for and send broadcasts of Intents to local objects within your process. The data you are broadcasting won't leave your app, so don't need to worry about leaking private data.`
Your activity can register for this local broadcast. From the GCMBroadcastReceiver, you send a local broadcast when you receive something in GcmBroadcastReceiver. Inside your Activity you can listen to the broadcast. This way if the activity is in the forefront/is active, it will receive the broadcast otherwise it won't. So, whenever you receive that local broadcast, you may do the desired action if activity is open. This is like saying to the activity that "Hey Activity, I've received a message. Do whatever you want with it".
If you want to do for the whole app, then you can make all your activities extend an abstract activity. And inside this abstract activity class you can register it for this 'LocalBroadcast'. Other way is to register for LocalBroadcast inside all your activities (but then you'll have to manage how you'll show the message only once).
You can use Handlers in your MainActivity in order to comunicate with UI Thread.
Communicating with the UI Thread
public class MainActivity extends Activity{
public static final int NEW_DATA_AVAILABLE = 0;
public static final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MainActivity.NEW_DATA_AVAILABLE:
String newData = msg.getData().getString(MyClass.DATA);
//Do some stuff with newData
break;
}
}
};
}
and in your non Activity class
public class MyClass implements Runnable{
Thread thread;
public final static String DATA = "new_data";
public MyClass(){
thread = new Thread(this);
thread.start();
}
#Override
public void run() {
while(true){
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
Message msg = mHandler.obtainMessage(MainActivity.NEW_DATA_AVAILABLE);
Bundle bundle = new Bundle();
bundle.putString(DATA, "We have received new data");
msg.setData(bundle);
MainActivity.handler.sendMessage(msg);
}
}
}

ArrayAdapter refreshing data

I am building an application which is dynamically populating/depopulating arraylist depending on Futuretask thread which communicates with server.
Problem is that when i add new object into my arrayadapter and in any way i call notifyDataSetChanged or setNotifyOnChange
(for example after adding item, i also tried make some postadd function which calls notifyDataSetChanged, or even tried adding setNotifyOnChange in adapter constructor, or before adding (adapter.setNotifyOnChange(true)))
in this Futuretask it simply freezes the thread and causes application malfunction. Without notifyonchange when i slide with my finger to call ArrayAdapter's getView() it works, but that is an undesired behavior.
Note: I am accessing the adapter by static variable reference.
Note#2: I dont think creating new adapter over and over again is a smart choice.
/edit:
My remove method looks like this:
public void remove (int itemid) {
for (ContactItem i : items)
if (i.ID == itemid)
items.remove(i);
}
(and it hangs in the end at the last pharantesis)
Pass a weak reference to the activity in your other thread, so you don't leak the context.
WeakReference<Activity> mRef_Activity = new WeakReference<Activity>(activity);
From that thread, run the notify update on the ui thread of the main activity.
final Activity activity = mRef_Activity.get();
if(activity != null)
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
ArrayAdapter<String> adapter = activity.getAdapter();
adapter.notifyDataSetChanged();
}
});
Note if you are running async task, instead of a callable or runnable, you can just put the notify update in the postExecute of the async, as it is a callback from the main UI thread.
Using static variable reference means that you should be carefully in the thread. As far as i know, the adapter updates the data only in the UI thread.
One more thing you should know is you should implement the getCount() function to return correct number.
I had a problem when my list view does not update my data. even i trigger the onDatasetChange(). I forgot the getCount() function

Calling activity from class that doesnt extend Activity

Is there any way to call an activity from something that doesnt extend Activity? without running it on UI-tread. I want toast-messages in my game, like in candy crush. But I have no idea how to call the toast-activity in an efficient way during real-time.
I know you can use context and get activity from that but it does not run very smoothly. Does anyone have experience with how to do this?
Toasts as well as other components that manipulates the user interface must not be used outside of the UI thread.
What you could do in order to solve your issue, is to design a messaging system between the thread managing you game, and your UI thread. In order to do so, you can use a Handler and its messaging facilities (sendMessage, post, postDelayed ...) to send messages or even Runnables to be run on your main thread.
If you create a Handler in the main thread, it will automatically associate itself with your main thread's event loop, thus making every work sent to it be executed in the main thread.
A basic example of what you could do would be :
class MainActivity extends Activity {
// The handler is associated with your Activity's thread
private Handler _handler = new Handler();
// ...
private Thread _worker = new Thread() {
#Override
public void run() {
_handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Some text", Toast.LENGTH_SHORT).show();
}
});
}
};
};
As a shorthand, you could also use Activity.runOnUiThread(Runnable r) that executes the given runnable in the UI thread by default.

Creating additional threads inside an AsyncTask in android and updating the UI using an ExecutorService, am I doing it wrong?

I'm working on an Android app and I've got an Activity on which I am displaying two TableLayout's on top of each other. The first TableLayout holds a scoreboard for a race (type 1) and the second the scoreboard for another race (type 2).
I inflate both of these TableLayout's from the same XML layout file. I populate them with data from a doInBackground() (AsyncTask). However, the population part is pretty heavy, so I have put this into a custom synchronized method (inside the AsyncTask class) and pass the inflated TableLayout objects as an argument to this method.
I call said method from the doInBackground() method by using an ExecutorService object. This ExecutorService object is created with Executors.newSingleThreadExecutor(). I put the first method call in one Runnable and the second method call in another Runnable and fire off the ExecutorService.execute() on these runnables.
In the method I am accessing UI elements by calling findViewById() on the passed in TableLayout object parameter and calling methods like TextView.setText() on these views. This works fine most of the time, but I am starting to see a some exceptions like
ERROR/AndroidRuntime(409): FATAL EXCEPTION: pool-9-thread-1
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
and
ERROR/AndroidRuntime(378): FATAL EXCEPTION: pool-2-thread-1
java.lang.NullPointerException
which lets me to belive that I must be doing this incredibly backwards. I'll include a simplified code snippet below to help describe what I am doing currently:
class AddRacesTask extends AsyncTask<Void, View, Void> {
synchronized void populateTable(ArrayList<Race> data, TableLayout table) {
TextView headerText = table.findViewById(R.id...);
headerText.setText("Header");
for(Race race : data) {
TableRow row = getLayoutInflater().inflate(R.layout....);
TextView pos = row.findViewById(R.id....);
pos.setText(race.getPos());
TextView person = row.findViewById(R.id....);
person.setText(race.getPerson());
// etc.
// etc..
// etc...
table.addView(row);
}
publishProgress(table);
}
#Override
private Void doInBackground(Void... voids) {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
TableLayout raceType1Layout = getLayoutInflater().inflate(R.layout...);
populateTable(data, raceType1Layout);
}
});
executor.execute(new Runnable() {
#Override
public void run() {
TableLayout raceType2Layout = getLayoutInflater().inflate(R.layout...);
populateTable(data, raceType1Layout);
}
});
}
#Override
protected void onProgressUpdate(View... values) {
if(values[0] instanceof TableLayout) {
container.addView(values[0]);
}
}
}
I welcome any feedback at all. Also if you need more to go on, I'd be happy to post my actual source code but I figured a simplified and clean version would be easier.
You need implement a custom Adapter that populate your custom view with Race object. You receive your data in background with custom thread or AsyncTask and pass to the UI thread via Handler or onPostExecute method respectively. Then, you can call Adapter.clear method and after
for(Race r: ArrayList<Race>)
Adapter.add(r)
It would probably be recommended to update your UI via a Handler after you have completed your work in the AsyncTask. This is the standard for how messages between an AsyncTask/Thread should communicate with the UI Thread.
The reason for this is because you are not allowed to access UI
elements in a thread that is NOT the main UI Thread.
Another method you could utilize is the runOnUiThread Method that is available from the Activity class. However; I'd recommend using a handler.

Android Thread modify EditText

I am having a problem with modifying EditText in another function started by the thread:
Thread thRead = new Thread( new Runnable(){
public void run(){
EditText _txtArea = (EditText) findViewById(R.id.txtArea);
startReading(_txtArea);
}
});
my function is as follows:
public void startReading(EditText _txtArea){
_txtArea.setText("Changed");
}
It always force closes while trying to modify the edittext. Does someone know why?
UI views should not be modified from non-UI thread. The only thread that can touch UI views is the "main" or "UI" thread, the one that calls onCreate(), onStop() and other similar component lifecycle function.
So, whenever your application tries to modify UI Views from non-UI thread, Android throws an early exception to warn you that this is not allowed. That's because UI is not thread-safe, and such an early warning is actually a great feature.
UPDATE:
You can use Activity.runOnUiThread() to update UI. Or use AsyncTask. But since in your case you need to continuously read data from Bluetooth, AsyncTask should not be used.
Here is an example for runOnUiThread():
runOnUiThread(new Runnable() {
#Override
public void run() {
//this will run on UI thread, so its safe to modify UI views.
_txtArea.setText("Changed");
}
});
First of all take a look at your log, it usually contains a stack trace when an app shuts down.
You shouldn't run the thread like you normally do, instead use runOnUiThread:
Runnable thRead = new Runnable(){
public void run() {
EditText _txtArea = (EditText) findViewById(R.id.txtArea);
startReading(_txtArea);
}
};
runOnUiThread(thRead);
The explaination: Only the UI thread is allowed to change the state of UI components.
This article may help you.
http://android-developers.blogspot.com/2009/05/painless-threading.html
There are few options:
1. run it on UI thread Activity.runOnUiThread(Runnable)
2. use AsyncTask
Except runOnUiThread() (which works), there is also another way, which I know of:
Define a handler in your UI (Activity) class:
public class MainActivity extends Activity {
.....
Handler uiThreadHandler = new Handler() {
public void handleMessage(Message msg) {
Object o = msg.obj;
if (o==null) o = "";
TextView textIn = (TextView)findViewById(R.id.textin);
textIn.setText(o.toString());
}
};
}
and from inside some thread you can call it:
Message msg = uiThreadHandler.obtainMessage();
msg.obj = "Text for EditView";
uiThreadHandler.sendMessage(msg);
By default, the main thread is the UI thread. All code that modifies the appearance of the application needs to be run in this thread. If you want to have multiple threads in your application that can modify the UI I would suggest using the AsyncTask class.
public someMethod(){
new ChangeTextTask().execute();
}
private class ChangeTextTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
startReading(_txtArea);
return null;
}
}
However, you need to take steps to prevent multiple threads from accessing the EditText object at once. Otherwise you'll wind up getting a CurrentModificationException error.
http://developer.android.com/reference/android/os/AsyncTask.html

Categories