Android change event definition from other class - java

I have a main class
public class MainActivity extends Activity {
downloadTask downloadTask;
....
void updateProgress(int... progress) {
}
}
and an AsyncTask
public class downloadTask extends AsyncTask<Void, double[], Void> {
....
protected void onProgressUpdate(int... progress) {
}
}
Is there a way that I can set the onProgressUpdate & override it so that it uses updateProgress instead, from the MainActivity?
I'm aware I can create a new class that extends downloadTask and use #Override there. I'm just wondering if there's another way. For instance in javascript I could do something like
downloadTask.onProgressUpdate = updateProgress
Is something like this possible in java?

You can do something like this:
AsyncTask<Void, double[], Void> task = new AsyncTask<Void, double[], Void>(activity);
{
private WeakReference<Activity> activityReference;
public AsyncTask(Activity activity)
{
this.activityReference = new WeakReference<Activity>(activity);
}
protected void onProgressUpdate(int... progress)
{
Activity activity = activityReference.get();
if(activity != null)
{
activity.updateProgres(...);
}
}
}
Note that I use WeakReference inside the AsyncTask, this is very important to prevent memory leaks.

Related

Static field with leak context

My code is
// Asynctask class to handle load data from database in background
private class SyncGetLocations extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
mPrgLoading.setVisibility(View.VISIBLE);
mScrollView.setVisibility(View.GONE);
mToolbarView.setVisibility(View.GONE);
}
#Override
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Get data from database
getLocationDataFromDatabase(mSelectedId);
return null;
}
… etc
Android studio report that this Asynctask class should be static else leaks might occur.
When change to static get a lots of errors like non-static methods and fields cannot be referenced from a static context.
I added:
MyActivity myactivity = new MyActivity();
myactivity.mPrgLoading.setVisibility(View.VISIBLE);
…
all look nice until I run application and get error because I try to access void fields.
Advices?
The problem is that when you make the inner class static you no longer have access to the Activity and its properties. One workaround would be to have a weak reference to your Activity inside the AsynkTask:
private static class YourTask extends AsyncTask<Void, Void, Void> {
private WeakReference<YourActivity> activityReference;
YourTask(YourActivity context) {
activityReference = new WeakReference<>(context);
}
#Override
protected String doInBackground(Void... params) {
return "task finished";
}
#Override
protected void onPostExecute(String result) {
YourActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
activity.myVariable = "example";
TextView textView = activity.findViewById(R.id.textview);
textView.setText(result);
}
}
Anyway I think that creating the AsyncTask as a top level class instead of as an inner class would also solve your problem (as an inner class you have the risk of the class being alive more time than it needs to because of the AsyncTask).

Using Toast in another class file without Activity extends Android

I've 2 files: Core (extends Activity) and DwCore. I'm using AsyncTask in a Core subclass and I want to use Toast in DwCore subclass but I can't get properly the Core Context.
Core
class DwFiles extends AsyncTask<Void, Void, Long> {
protected Long doInBackground(Void... parms) {
long totalSize = 0;
dwCore.mainCounter(Core.this);
return totalSize;
}
}
DwCore subclass
public void mainCounter(Context c){
Integer count = 0;
for(int i=0;i<count;i++){
Toast.makeText(c, count.toString(), Toast.LENGTH_SHORT).show();
}
}
You can't do anything that affects the UI from worker threads - that includes showing toasts.
class DwFiles extends AsyncTask<Void, Void, Long> {
protected Long doInBackground(Void... parms) {
long totalSize = 0;
publishProgress(check_point);
return totalSize;
}
}
protected void onProgressUpdate(Integer integers) {
dwCore.mainCounter(Core.this);
}
public void mainCounter(Context c){
Integer count = 0;
for(int i=0;i<count;i++){
Toast.makeText(getActivity(), count.toString(), Toast.LENGTH_SHORT).show();
}
}
You can use getActivity() function or this.getActivity().
getActivity() can be used in a Fragment for getting the parent Activity of the Fragment (basically the Activity that displayed the Fragment).
can't be done as doInBackGround runs on a background thread which cant access the ui thread.
What you can do is ,Create an Custom interface implement it on your activity then push the ui related update from the onProgressUpdate().
Interface ProgUpdate{
void uiUpdate(int update)
}
BackGround theread :
ProgUpdate listener;
onProgressUpdate(Int i){
listener.uiUpdate(i)
}

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
}
}

Understanding AsyncTask with web server accessing

New programmer. Questions at the bottom.
I have an array of fleets that corresponds to vehicles. I would like to make successive calls to the server with each slot in my fleet array.
I wish to do this with an AsyncTask.
private class refreshTruckInformation extends AsyncTask<URL, Void, Void> {
#Override
protected void doInBackground(URL... urls) {
}
#Override
protected void onProgressUpdate(Integer... progress) {
}
#Override
protected void onPostExecute(Void... voids) {
}
}
**How do I pass in my array of fleets so that I can use them in my doInBackground?
I also want to have a progress bar that goes based on the percentage of fleets it has gone through. What is a good way to do this?**
Thanks!
**How do I pass in my array of fleets so that I can use them in my doInBackground?
Remember that refreshTruckInformation is still a class. So you can use any constructor or settier methods to pass your array.
I also want to have a progress bar that goes based on the percentage
of fleets it has gone through. What is a good way to do this?**
From your doInBackground method you can publis progress using publishProgress method. This progress parameter will be catched in onProgressUpdate method
For example
private class RefreshTruckInformation extends AsyncTask<URL, Void, Void> {
private int[] b;
public RefreshTruckInformation (int[] a){
// use array
b = a;
}
public void setArray(int[] a){
// use array
b = a;
}
#Override
protected void doInBackground(URL... urls) {
}
#Override
protected void onProgressUpdate(Integer... progress) {
}
#Override
protected void onPostExecute(Void... voids) {
}
}
And call using
RefreshTruckInformation r = new RefreshTruckInformation (yourArray);
or call method like
r.setArray(yourArray);
and execute like
r.execute();

executing runOnUiThread on a separate class [duplicate]

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

Categories

Resources