Static field with leak context - java

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).

Related

Should anonymous class object set to null onDestroy?

I heard that the Anonymous Classes can leak memory.
Similarly, Anonymous Classes will also maintain a reference to the class that they were declared inside. Therefore a leak can occur if you declare and instantiate an AsyncTask anonymously inside your Activity. If it continues to perform background work after the Activity has been destroyed, the reference to the Activity will persist and it won’t be garbage collected until after the background task completes.
Should anonymous class object set to null onDestroy to prevent memory leaks? Here are some pieces of my code.
public class RegisterActivity extends AppCompatActivity {
private ApiHandler registerHandler = null;
private static final int SERVICE_REQUEST_REGISTER = 243;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
init();
}
private void init() {
useApiService();
initApiHandler();
}
protected void useApiService() {
apiService = ApiClient.getClient(getApplicationContext()).create(ApiInterface.class);
}
private void initApiHandler() {
registerHandler = new ApiHandler(this, SERVICE_REQUEST_REGISTER) {
#Override
protected String successStatusCode() {
return "802";
}
#Override
protected String secretKey() {
return getDefaultKey();
}
#Override
protected boolean isExchangeSecretKey() {
return false;
}
};
}
#Override
protected void onDestroy() {
super.onDestroy();
registerHandler = null;
}
}
Actually garbage colector do it for you.
What the piece of text you pasted says is if the Anonymous class you are creating start a new AsyncTask, the main class where you created it will never be destroyed...
in other words while there is one Anonymous class running tasks onDestroy is never called on main class

How can I modify a variable declared in the UI Thread, from an other thread?

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.

Android change event definition from other class

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.

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();

How should I refactor my code in order to avoid the Network on Main Thread exception on Android?

In my MainActivity I have a class initialization like so:
Presentation presentation = new Presentation("link");
Presentation is a class which gets initialized using values from a .JSON file on a web server:
public Presentation(String URL) {
// Do stuff
doNetworking();
}
private doNetworking() {
// Network access here
// This throws the Network on Main Thread exception
}
In my MainActivity, I need all the values of Presentation to be there in the next step:
Presentation presentation = new Presentation();
// Do some stuff with it
Using the AsyncTask I am not sure how I should go about doing this, so far I have something like this:
public Presentation(String URL) {
// Do stuff
new InitializePresentation().execute(URL);
}
private class InitializePresentation extends AsyncTask<String, Void, Boolean> {
// Amongst other things
protected Boolean doInBackground(String... params) {
// Do the networking stuff here
}
}
What I need is to refactor this code so that it is asynchroneous but behaves like a synchroneous call. Any help is greatly appreciated.
edit
How does one refactor a code to accomplish this?
Bitmap b = new Bitmap();
Load bitmap from network;
Use bitmap in imageview;
Can this be used in this fashion? Or do I have to use it like
Async, doInBackground() {
Load bitmap from network
Use bitmap in imageview
Continue with application
}
Thank you!
You can show a progress dialog while doing the network stuff:
private ProgressDialog progressDialog;
private Context context;
public InitializePresentation (Context context) {
this.context = context;
}
#Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(context, "", "loading", true);
}
/*
* #see android.os.AsyncTask#doInBackground(Params[])
*/
#Override
protected String doInBackground(String... arg0) {
// Do the networking stuff here
}
#Override
protected void onPostExecute(final String result) {
progressDialog.dismiss();
}

Categories

Resources