Reputation: 613
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
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
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