I am attempting to update a few UI elements in my app, after a separate class has been updated. The separate class is neither an activity nor a fragment. Could anyone point me in the right direction? Would a handler work well here, if so could you point me toward a acceptable example of handlers?
you can use this in a separe class.
public void setView(Activity activity, View view) {
activity.runOnUiThread(new Runnable() {
public void run()
{
/update your view here
}
});
}
You can get the view from your activity variable, instead of pass a view, if you prefere.
You cannot update any UI elements from a different thread than the main thread. If you are using the main thread, you can pass the View to the method in the other class and make some changes with that.
You need to link the two classes together by perhaps implementing a callback mechanism that will be processed using a handler into your main application thread, assuming that your external class can register a callback with the Activity class.
Or more simply you could use context by allowing a reference to be passed from the Activity to your other class, but you want to make sure you don't leak context.
Related
I am trying to keep my app clean by using good design principles. I want to keep the view, the logic and the database layers separate.
On the app I am working on now, I have a main activity, a singleton, a logic class and a I use room for the database access. I would like the activity to tell the logic to fetch data from the database and then load it in the singleton. Once it is done I need the main activity to show the data loaded in the singleton.
Room use LiveData. How can I use LiveData and then somehow return a result to the logic layer then load it into the singleton and let the main activity now that it is ready?
More generally, how can I tell a caller, from the callee, that the data is ready when it is asynchronous? For example if the main activity calls a logic class in another thread to load data, how can the logic class then let the main activity know it is done?
You can use a Handler to update the main activity on the UI thread:
LogicClass.functionThatAccessesRoom() {
//get data from Room
//...
Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(#NonNull Message msg) {
//update the UI/views here
}
};
handler.obtainMessage().sendToTarget();
}
Is it okay a presenter having Android Handler?
I know presenters should not have any Android related objects,
but I really don't have any clear answer.
Here's the thing,
this presenter runs a disk IO task on another thread,
meanwhile, the activity has to change its view.
These jobs are supposed to be done concurrently.
So I decided to pass the activity's handler as an argument
and let presenter send message to activity like this:
class FooPresenter: FooContract.Presenter {
…
private fun doDiskIOTask(handler: Handler) {
handler.sendEmptyMessage(0)
do_something_on_new_thread_and_join()
handler.sendEmptyMessage(1)
}
…
}
The activity has to know when the task is started and finished both and change view.
Could you tell me if I'm doing it any wrong or the better way?
Passing a Handler to the presenter doesn't sound like a good idea, you wouldn't be able to unit test it. I think a better approach would be to just call a method on the view and then run on UI thread from there. If your view interface is an Activity you can use the very convenient runOnUiThread method, e.g.
class MyActivity : AppCompatActivity, FooContract.View {
override fun emptyMessage0Method(){
runOnUiThread {
// manipulate views here
}
}
}
So, this is probably a very basic design question, but Im just not sure how to go about it.
Normally, when I use event listeners, I define it in whatever class, then I override the necessary methods in my Activity and instantiate the class and the listener as needed.
However, in this case, I have my MainActivity class (that also implements the listener), a class (called testClass) that implements the listener, and a broadcast receiver class. The broadcast receiver class instantiates the calls the testClass. Now, what I am trying to do is to update a TextView in MainActivity when a given function is called in the testClass.
Not sure how to go about this.
Hope this wobbly issue description makes sense.
This is a problem I've tackled in the past when using a background Service to update data that is displayed on screen. The general pattern I use is to add a member variable to your processing class (in this case, I think it's your TestClass) that is a Map (named something like mCallbackMap) with android.os.Handler as the key and your listener object as the value (normally this will be an interface that you define). The Handler, which is created in the Activity and thus associated with the main thread, is needed because you can't change the UI of an Activity from outside the main Thread; you'll use the Handler to post a runnable to the main thread instead of manipulating it directly.
When your activity gets going, probably in onCreate, onStart, or onResume, you'll register it as a callback with your TestClass by using the mCallbackMap's put() method. Simply instantiate a Handler, which you'll also store as a member variable of your activity, and use it as the key and your Activity as the value. You'll need to remove the callback in onPause or onStop so you don't leak the activity after it's out of view.
Then, once TestClass finishes handling whatever the broadcast gives it, you'll iterate through your mCallbackMap (maybe you have more than one callback, maybe you don't) and call Handler.post(Runnable). In the Runnable's run() method, you call the callback's methods as appropriate.
I have typical AsyncTask that I want to properly decouple from my Activity classes and UI logic.
I think of doing it this way:
Make listener interface
public interface MyTaskListener {
void onTaskProgress(...);
void onTaskDone(...);
}
Make my Activity implement MyTaskListener and pass this reference on task creation.
Call listener methods inonPreExecute(), onProgressUpdate() and onPostExecute().
But may be I'm reinvetning the wheel and Android framework has something better already implemented? I think of something like EventBus pattern where all interested parties can register for particular event types that I can fire from my AsyncTask.
The interface is most clean way of doing this, because your task class is 'sealed', knows nothing about surrounding environment (does not access any stuff from outer class which quite tempting when you use inner classes) and communicates back with generic channel.
I have one main activity that handles all the UI stuff. In the activity i have a ListView which is in a SlidingDrawer, above that are 4 TextViews.
I want to change the text of the 4 TextViews once i click on an item from the ListView.
I get the text from an online database, so i put the code for that in an extra class.
I created a reference to the workerclass:
final OneTopic ot = new OneTopic();
In my onClickListener I call the method from the workerclass
ot.postData(position);
At the beginning of the workerclass i have a reference to the main class
Main main = new Main();
when the worker class is finished, it calls the method that updates the UI in the Activity.
main.displayText(body, title, date, poster);
The method its calling looks like this:
public void displayText(String body, String title, String date, String poster) {
tx_body.setText(Html.fromHtml(body));
tx_title.setText(title);
tx_date.setText(date);
tx_poster.setText(poster);
}
If I do this my app force closes once it tries to set the text.
What am I doing wrong?
At the beginning of the workerclass i have a reference to the main
class
Main main = new Main();
That's actually creating a new instance of your Main class, not getting a reference to the current one. If you want a reference to your activity from your worker thread, then you need to pass in your activity as a parameter.
ot.postData(Main.this, position);
Without seeing your entire code, I see two possibilities:
Your WorkerThread is not running in the UI Thread and you can only update UI objects from within the UI Thread. Check out the Worker Threads topic in the Android dev guide. Basically the code for displayText() is what needs to go in the Runnable in the example I just provided you.
I am slightly troubled at this statement:
At the beginning of the workerclass i have a reference to the main class Main main = new Main();
If Main is your activity, then you should not be constructing a new one. You need a reference to the Activity instance that started the worker thread.
Hope this helps. I see you're new to StackOverflow. If this answers your question, please mark it as accepted. Good luck!