executing runOnUiThread on a separate class [duplicate] - java

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Android how to runOnUiThread in other class?
My Asyn Classes are a separate class file.
public class AdamTask extends AsyncTask<String, Void, String>{
public void showToast(final String toast)
{
runOnUiThread(new Runnable() {
public void run()
{
Toast.makeText(context, toast, Toast.LENGTH_SHORT).show();
}
});
}
}
How would I execute this method in my AsyncTask Class? I am getting an error The method runOnUiThread(new Runnable(){}) is undefined for the type AdamTask
new AdamTask(Eve.this, How to pass the eve activity here).execute();

Just typecast the context to Activity class
((Activity)context).runOnUiThread(new Runnable()
{
public void run()
{
Toast.makeText(context, toast, Toast.LENGTH_SHORT).show();
}
});

You need to have the Activity's reference (lets name it activity) and pass it to your AsyncTask class. Then you can call runOnUiThread like this:
activity.runOnUiThread
The runOnUiThread is a method defined in Activity class.
Just add a contsructor to your AsyncTask. Your AsyncTask will look like this:
public class AdamTask extends AsyncTask<String, Void, String> {
private Activity activity; //activity is defined as a global variable in your AsyncTask
public AdamTask(Activity activity) {
this.activity = activity;
}
public void showToast(final String toast)
{
activity.runOnUiThread(new Runnable() {
public void run()
{
Toast.makeText(activity, toast, Toast.LENGTH_SHORT).show();
}
});
}
...
}
Then to call the AsyncTask you need something like this:
AdamTask adamTask = new AdamTask(Eve.this);
adamTask.excecute(yourParams);
UPDATE As Sam mentioned in the comments, AsyncTasks may result in context leaking when configuration changes occur (one example is when screen rotates and the Activity is recreated). A way to deal with this is the headless fragment technique.
Another way, more efficient, is using an event bus. See here for more information (link provided by Sam in the comments).

You need your Activity object to do this. Pass your Activity's this reference through the constructor and use it in your AsyncTask.
public class AdamTask extends AsyncTask<String, Void, String>{
public void showToast(final String toast)
{
activityObj.runOnUiThread(new Runnable() {
public void run()
{
Toast.makeText(context, toast, Toast.LENGTH_SHORT).show();
}
});
}
}
Since runOnUiThread is a public method in Activity class, you cannot use it in some other Custom class or class that extends other than Activity itself.
Look here, runonUi.
If you are not clear, please check this answer to learn how to send Activity Object through constructor

Related

How to interact with UI from a different class

I would like to update my UI from a different class. I am familiar with runOnUiThread() method, but don't know how to implement it in this scenario?
public class UploadAct extends MainActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload);
}
//my code and functions would go here
}
Then, my UploadData class
public class UploadData extends UploadAct {
public void doSomethig(){
printThis("I want to print this message to the UI");
}
public void printThis(String messsage) {
final String mess = message;
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),mess,Toast.LENGTH_LONG).show();
// I want this to display on the main thread
txt_upload.setText(mess);// and this also
}
});
}
}
Use BroadcastReceiver
// define a Broadcast Intent Action in String resources
<string name="broadcast_id">MY_BROADCAST_ID</string>
// register receiver in constructor/onCreate()
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter myIntentFilter = new IntentFilter();
myIntentFilter.addAction(context.getString(R.string.broadcast_id));
context.registerReceiver(myBroadcastReceiver, myIntentFilter);
// place your BroadcastReceiver in MainActivity, your UploadData class
public class MyBroadcastReceiver extends BroadcastReceiver {
public MyBroadcastReceiver(){
super();
}
#Override public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Broadcast received");
if(intent.getAction() != null && intent.getAction().equals(context.getString(R.string.broadcast_id)) ){
// do something
}
}
}
// send Broadcasts from where you want to act, your UploadAct class.
Intent intent = new Intent();
intent.setAction(context.getString(R.string.broadcast_id));
context.sendBroadcast(intent);
Log.d(TAG, "Broadcast sent.");
// you can unregister this receiver in onDestroy() method
context.unregisterReceiver(myBroadcastReceiver);
You can also use an interface to update your UI as a listener.
First, Create an interface
public interface UpdateTextListener {
void updateText(String data);
}
Then, Call its method in your UploadData class
public class UploadData extends UploadAct {
UpdateTextListener listener;
public void doSomethig(){
listener.updateText("data to be loaded");
}
}
Then, Update your UploadAct by listening to this method
public class UploadAct extends MainActivity implements UpdateTextListener {
#Override
public void updateText(String data) {
textview.setText(data);
}
}
First of all - there is no such thing like UI of some class. There are activities that can have handles to UI widgets (ex TextView). If you want to make some changes to UI from your UploadData class you have to pass somehow reference to this class. Possibly by constructor:
public class UploadData extends UploadAct{
private TextView txt_upload;
public UploadData(TextView tv)
{
txt_upload = tv;
}
public void doSomethig(){
printThis("I want to print this message to the UI")
}
public void printThis(String messsage) {
final String mess = message;
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),mess,Toast.LENGTH_LONG).show();// I want this to display on the main thread
txt_upload.setText(mess);// and this also
}
});
}
}
I assume that you create DataUpload in your MainActivity.
Everyone use so much library to be trendy as they forget built in functions in Android :)
For sure isn't any hard thing to use AsyncTask, beside it provides the doInBackground function it has the https://developer.android.com/reference/android/os/AsyncTask.html#publishProgress(Progress...) function too, what you have asked for.
Just create a class (UploadTask) which extends AsyncTask and override 1-2 function.

AsyncTask task go to a endless loop

i have problem with some kind a endless loop with AsyncTask..
this is my method with AsyncTask in a fragment class
public AsyncTask<Void, Void, Void> refreshTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
Looper.prepare();
System.out.println("UŠAO SAM U DO IN BACKGRASD");
Ponuda.deleteAll();
ArrayList<Ponuda> novaLista= new ArrayList<Ponuda>();
novaLista= (ArrayList<Ponuda>) Ponuda.getAll();
System.out.println("PONUDE: "+ novaLista.size());
((MainActivity)getActivity()).loadData();
Looper.loop();
System.out.println("asdasd"+ Looper.myLooper());
return null;
}
#Override
protected void onPostExecute(Void Void) {
System.out.println("UŠAO SAM U DO IN Post");
mSwipeRefreshLayout.setRefreshing(false);
ArrayList<Ponuda> novaLista= new ArrayList<Ponuda>();
novaLista= (ArrayList<Ponuda>) Ponuda.getAll();
System.out.println("PONUDE: "+ novaLista.size());
RVAdapter adapter = new RVAdapter(novaLista,getContext());
rv.setAdapter(adapter);
super.onPostExecute(Void);
}
};
#Override
public void onRefresh() {
System.out.println("Refreshana je stranica");
refreshTask.execute();
}`
and this is the method that i call from fragment
public void loadData(){
System.out.println("Poziva se funkcija za dohvat podataka");
DataLoader dataLoader;
dataLoader = new WebServiceDataLoader();
if(Ponuda.getAll().isEmpty() || Grad.getAll().isEmpty()){
System.out.println("Dohvaćamo web podatke");
Toast.makeText(this, "Dohvaćamo podatke s weba", Toast.LENGTH_LONG).show();
dataLoader = new WebServiceDataLoader();
} else {
System.out.println("Dohvaćamo lokalne podatke");
Toast.makeText(this, "Dohvaćamo podatke lokalno", Toast.LENGTH_LONG).show();
dataLoader = new DatabaseDataLoader();
}
dataLoader.loadData(this);
System.out.println("asdasdasd");
}
When i debug, the program runs this function and go to fragment inicialization and then go to a endless loop. First it go to ActivityThread class, then to Handler class, then to Looper class it repeat thoose classes again and again. Can somebody please help me?
The problem is the use of the Looper within the AsyncTask's doInBackground() method. AsyncTask is not a generic threading mechanism: it is intended to do short-lived things on a background worker thread which is managed by the system. Your doInBackground() method should not block indefinitely, loop forever, etc. The Looper class is normally used by threads which are going to use a message queue in Android, which allow you to attach Handler objects to them.
This article will help clarify how AsyncTask works: http://po.st/Cei3m2

How do I get a string from a thread or return a String form a Thread?

Im using a webservice that get a data and stores in a String. I need to use this String but I cant take it. Global variables don't work in Threads. I'm using a traditional Thread new Thread() { public void run() {.
Example of AsyncTask:
public class Task extends AsyncTask<Params, Progress, String> {
// are you know how to use generic types?
protected String doInBackground(Params[] params){
// this code will run in seperate thread
String resultString;
return resultString;
}
protected void onPostExecute(String resultString){
// this code will call on main thread (UI Thread) in this thread you can update UI e.g. textView.setText(resultString);
}
}
Use LocalBroadcastManager to send and receive data.
This way you can avoid memory leak issues.
Here is code for activity
public class YourActivity extends Activity {
private void signal(){
LocalBroadcastManager.getInstance(YourActivity.this).registerReceiver(receiver, new IntentFilter("Your action name"));
Intent yourAction = new Intent(YourActivity.this, YourIntentService.class);
String string = "someData";
yourAction .putExtra("KEY_WITH_URL", string);
startService(yourAction);
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String string = intent.getStringExtra("KEY_WITH_ANSWER");
//Do your code
}
};
}
Here code for thread which download String or whatever
public class YourIntentService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
// Download here
Intent complete = new Intent ("Your action name");
complete.putExtra("KEY_WITH_ANSWER", stringToReturn);
LocalBroadcastManager.getInstance(YourIntentService.this).sendBroadcast(complete);
}
}
You can use Thread instead of IntentService.
use a Handler created from the main thread. Then pass your data throuh it
use a weak reference of your activity in your thread; this way you can call directly the main thread - Activity.runOnUiThread(Runnable)
...
Activity activity = activityWeakReference.get();
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
activity.runOnUiThread(new Runnable() {
#Override
public void run()
{
// you are in main thread, pass your data
}
});
}
You can use Async task:
private class Whatever extends AsyncTask<Void, Void, String> {
protected String doInBackground(Void... void) {
// do your webservice processing
return your_string;
}
protected void onPostExecute(String result) {
// Retrieves the string in the UI thread
}
}

Android acessing MainActivity from AsyncTask

My application crashes after click the button, but the code executes properly.
public void makeLead(View v) throws Exception {
try {
RegisterTimer rt = new RegisterTimer();
rt.ma = this;
rt.execute(null);
} catch (Exception e) {
e.printStackTrace();
}
}
public void log(String msg)
{
final TextView tv = (TextView)findViewById(R.id.editText);
tv.append(msg);
}
private class RegisterTimer extends AsyncTask {
public MainActivity ma;
#Override
protected Object doInBackground(Object[] objects) {
ma.log("ausd");
return null;
}
}
makeLead is onClick event. Method ma.log generates an error but works properly (msg added to textEdit). When I delete ma.log, app doesn't crash. I have no logs in my AndroidStudio so I can't see error message. What's wrong ?
You can not touch the Views in a non UI Thread.
and you are appending text to TextView in a background Thread which is not allowed.
and I hope there is no problem with the initialization of MainActivity inside RegisterTimer as you are not creating the instance of Activity manually. You are in correct way with the initialization rt.ma = this. and why do you need AsyncTask just for changing the text of a TextView?
You cannot update ui from a doInbackground. Initializing ma is not required
Make AsyncTask an inner class of Activity and update ui in onPostExecute
or use interface as a callback to the activity
Edit:
To make it clear
Make asynctaks an inner class of activity. Declare textview as a instance variable. Return result in doInbackground
In Activity
TextView tv;
#Override
public void onCreate(Bundle savedInstancestate)
super.onCreate(savedInstancestate);
setContentView(R.layout.yourlayout);
tv = (TextView)findViewById(R.id.editText);
Then
#Override
protected String doInBackground(Void objects) {
// some background computation
return "ausd";
}
In onpostExecute
#Override
protected void onPostExecute(String result)
{
super.onPostExecute();
tv.append(result);
}
Also you need
private class RegisterTimer extends AsyncTask<Void,Void,String> { // args missing
As described by #Raghunandan you have not initialized ma.
next is you cannot access view in background thread.
if your thread class is inside of MainActivity class then you can use
runOnUiThread(new Runnable() {
#Override
public void run() {
ma.log("ausd");
}
});
inside doInBackground method to update view.
Your method log is public, you don't need to make an object of the MainActivity class to access it, instead you can call it directly. Also you need to add some template after your ASYNC task, if you want to pass some input to your background process, you are using ASYNC task in a wrong way.

Is it possible to send an event from a thread to an activity?

If I want to send an event, e.g. OnClick, to an activity from a thread? Thanks.
The expected work flow is below:
public class HelloAndroid extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Crate threadA
Start threadA
}
public void OnSomeEvent() {
do something that changes the views in this activity;
}
private class ThreadA extends Thread {
public void run() {
do something ...
Send Some Event to Activity HelloAndroid.
}
}
You can always send a message from a thread to the activity, like that:
//this should be in your Activity class
private Handler SomeHandler = new Handler() {
public void handleMessage(Message msg) {
ReactOnMessage();
}
};
private class SomeThread implements Runnable {
public void run() {
doSomething();
SomeHandler.sendEmptyMessage(0);
}
}
You can also create message, which will contain some values.
You will have to use Handlers to update UI.
All UI related event have to executed from UI Thread.
http://developer.android.com/guide/appendix/faq/commontasks.html#threading
If I understand correctly, you want to call the method OnSomeEvent() of HelloAndroid from your inner ThreadA class, right?
If this is the case you could right:
private class ThreadA extends Thread {
public void run() {
HelloAndroid.this.OnSomeEvent();
}
}
or even simpler, just call OnSomeEvent() method directly.

Categories

Resources