Androidew1267
Androidew1267

Reputation: 613

How should I implement my AsyncTask class?

I am making a weather app, where I use AsyncTask for getting response from API and after that setting up the UI. Here how looks like my code now after simplyfing:

class MainActivity : AppCompatActivity() {


    /*
    SOME INSIGNIFICANT CODE HERE
    */

    private fun setUI(currentWeather: Root){
        tv_city.text = "${currentWeather.name}, ${currentWeather.sys.country}"
        /*
        ...
        */
    }

    inner class WeatherByNameTask: AsyncTask<String, Unit, Unit>(){

        override fun doInBackground(vararg p0: String?) {
            val city: String? = p0[0]
            val call = weatherApi.getCurrentWeatherByCityName(city!!, API_KEY, "metric")
            call.enqueue(object: Callback<Root>{
                override fun onResponse(call: Call<Root>, response: Response<Root>) {
                    if (!response.isSuccessful){
                        Toast.makeText(this@MainActivity, "Code: ${response.code()}", Toast.LENGTH_LONG).show()
                    } else {
                        val currentWeather = response.body()
                        setUI(currentWeather!!)
                    }
                }

                override fun onFailure(call: Call<Root>, t: Throwable) {
                    Toast.makeText(this@MainActivity, "Code: ${t.message}", Toast.LENGTH_LONG).show()
                }
            })
        }
    }

    inner class WeatherByCoordTask: AsyncTask<Location, Unit, Unit>(){

        override fun doInBackground(vararg p0: Location?) {
            val lat: String = p0[0]?.latitude.toString()
            val lon: String = p0[0]?.longitude.toString()
            val call = weatherApi.getCurrentWeatherByCoordinates(lat, lon, API_KEY, "metric")
            call.enqueue(object: Callback<Root>{
                @SuppressLint("SetTextI18n")
                override fun onResponse(call: Call<Root>, response: Response<Root>) {
                    if (!response.isSuccessful){
                        Toast.makeText(this@MainActivity, "Code: ${response.code()}", Toast.LENGTH_LONG).show()
                    } else {
                        val currentWeather = response.body()
                        setUI(currentWeather!!)
                    }
                }

                override fun onFailure(call: Call<Root>, t: Throwable) {
                    Toast.makeText(this@MainActivity, "Code: ${t.message}", Toast.LENGTH_LONG).show()
                }
            })
        }
    }
}

It works, but I'm getting a warning:

This AsyncTask class should be static or leaks might occur

I want to make it in correct way. I tried to implement it outside the MainActivity class, passing the Context as a parameter, but what's about setUI function? I guess making it public is bad idea.

Upvotes: 0

Views: 52

Answers (2)

Son Truong
Son Truong

Reputation: 14173

This AsyncTask class should be static or leaks might occur

In the MainActivity, there are 2 AsyncTask class with inner modifier, this means the inner class will keep a strong reference to the outer class. The warning tells you while the AsyncTask is doing its job in the background, if the user leaves the current activity (press Back key or calling finish() method), then the activity instance will be leaked because the AsyncTask still keeps a strong reference to it.

Solution

Using WeakReference to let the AsyncTask keeps a weak reference to the MainActivity.

class WeatherByNameTask (activity: MainActivity): AsyncTask<String, Unit, Unit>(){
    private val activityRef = WeakReference<MainActivity>(activity)

    override fun doInBackground(vararg p0: String?) {
        val city: String? = p0[0]
        val call = weatherApi.getCurrentWeatherByCityName(city!!, API_KEY, "metric")
        call.enqueue(object: Callback<Root>{
            override fun onResponse(call: Call<Root>, response: Response<Root>) {
                if (!response.isSuccessful){
                    activityRef.get()?.let {
                        Toast.makeText(it, "Code: ${response.code()}", Toast.LENGTH_LONG).show()
                    }
                } else {
                    val currentWeather = response.body()
                    activityRef.get()?.setUI(currentWeather!!)
                }
            }

            override fun onFailure(call: Call<Root>, t: Throwable) {
                activityRef.get().let {
                    Toast.makeText(it, "Code: ${t.message}", Toast.LENGTH_LONG).show()
                }
            }
        })
    }
}

class WeatherByCoordTask (activity: MainActivity): AsyncTask<Location, Unit, Unit>(){
    private val activityRef = WeakReference<MainActivity>(activity)

    override fun doInBackground(vararg p0: Location?) {
        val lat: String = p0[0]?.latitude.toString()
        val lon: String = p0[0]?.longitude.toString()
        val call = weatherApi.getCurrentWeatherByCoordinates(lat, lon, API_KEY, "metric")
        call.enqueue(object: Callback<Root>{
            @SuppressLint("SetTextI18n")
            override fun onResponse(call: Call<Root>, response: Response<Root>) {
                if (!response.isSuccessful){
                    activityRef.get()?.let {
                        Toast.makeText(it, "Code: ${response.code()}", Toast.LENGTH_LONG).show()
                    }
                } else {
                    val currentWeather = response.body()
                    activityRef.get()?.setUI(currentWeather!!)
                }
            }

            override fun onFailure(call: Call<Root>, t: Throwable) {
                activityRef.get().let {
                    Toast.makeText(it, "Code: ${t.message}", Toast.LENGTH_LONG).show()
                }
            }
        })
    }
}

Use from the activity

val weatherByNameTask = WeatherByNameTask(this)
val weatherByCoordTask = WeatherByCoordTask(this)

Upvotes: 1

Amin
Amin

Reputation: 1

Following is the way how to make AsyncTask :

private class AsyncTaskGetPlaces extends AsyncTask<Void, Void, AsyncTaskResult<Object>>
    {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected AsyncTaskResult<Object> doInBackground(Void... params)
        {
            try
            {
                LibHttp libHttp = new LibHttp();

                String res = libHttp.listBusiness("21","[email protected]");

                return new AsyncTaskResult<Object>(res);
            }
            catch (Exception e)
            {
                e.printStackTrace();

                return new AsyncTaskResult<Object>(e);
            }
        }

        @Override
        protected void onPostExecute(AsyncTaskResult<Object> result)
        {

            if(result.getError()!= null)
            {
                showOKAlertMsg("App",getResources().getString(R.string.txt_data_not_found), false);
            }
            else
            {
                String res = result.getResult().toString();

                try {
                    JSONObject resObj = new JSONObject(res);

                    if(resObj.getString("status_code").equals("1")){
                        //parse
                        // Do your task here

                    }

                } catch (JSONException e) {
                    e.printStackTrace();
                    showOKAlertMsg("",getResources().getString(R.string.txt_internal_server_error), false);
                }
            }
        }
    }

Where AsyncTaskResult is

  public class AsyncTaskResult<T> 
{
    private T result;

    private Exception error;

    public T getResult() 
    {
        return result;
    }

    public Exception getError()
    {
        return error;
    }

    public AsyncTaskResult(T result) 
    {
        this.result = result;
    }

    public AsyncTaskResult(Exception error) 
    {
        this.error = error;
    }
}

Upvotes: 0

Related Questions