我应该如何实现我的 AsyncTask class?

How should I implement my AsyncTask class?

我正在制作一个天气应用程序,我在其中使用 AsyncTask 从 API 获取响应,然后设置 UI。在 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()
                }
            })
        }
    }
}

有效,但我收到警告:

This AsyncTask class should be static or leaks might occur

我想以正确的方式制作它。我试图在 MainActivity class 之外实现它,将 Context 作为参数传递,但是 setUI 函数呢?我想让它成为 public 是个坏主意。

以下是制作方法 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","test@ns.com");

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

AsyncTaskResult 在哪里

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

This AsyncTask class should be static or leaks might occur

MainActivity中,有2个AsyncTask class带有inner修饰符,也就是说里面的class会保持强引用外部 class。该警告告诉您,当 AsyncTask 在后台执行其工作时,如果用户离开当前 activity(按返回键或调用 finish() 方法),则 activity 实例将被泄露,因为 AsyncTask 仍然保持对它的强引用。

解决方案

使用 WeakReferenceAsyncTask 保持对 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()
                }
            }
        })
    }
}

使用来自 activity

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