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

Posted

技术标签:

【中文标题】我应该如何实现我的 AsyncTask 类?【英文标题】:How should I implement my AsyncTask class? 【发布时间】:2021-03-24 14:21:22 【问题描述】:

我正在制作一个天气应用程序,我使用 AsyncTask 从 API 获取响应,然后设置 UI。经过简单处理后,我的代码如下所示:

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


它有效,但我收到警告:

这个 AsyncTask 类应该是静态的,否则可能会发生泄漏

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

【问题讨论】:

既然你已经在使用 Kotlin,你应该迁移到包括远程接口在内的协程,因为 Retrofit 也支持协程。 【参考方案1】:

这个 AsyncTask 类应该是静态的,否则可能会发生泄漏

MainActivity 中,有2 个AsyncTask 类带有inner 修饰符,这意味着内部类将保持对外部类的强引用。警告告诉您,当AsyncTask 在后台执行其工作时,如果用户离开当前活动(按返回键或调用finish() 方法),那么活动实例将被泄露,因为AsyncTask 仍然保留强烈引用它。

解决方案

使用WeakReference 让AsyncTask 保持对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()
                
            
        )
    

从活动中使用

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

【讨论】:

为了让它工作,我需要公开“setUI()”方法。公开这些方法是一种好习惯吗? 您可以控制活动并知道在哪里以正确的方式调用公共方法,因此可以这样做。【参考方案2】:

以下是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;
    

【讨论】:

以上是关于我应该如何实现我的 AsyncTask 类?的主要内容,如果未能解决你的问题,请参考以下文章

我如何从AsyncTask中的数据库类中获取对象?

处理 AsyncTask 警告类应该是静态的,否则可能会发生泄漏

Android:如何将参数传递给AsyncTask的onPreExecute()?

如何从 Android AsyncTask 更改活动 UI?

如何在 AsyncTask 类中添加更多方法?

如何使用 asynctask 执行数据库操作来实现进度条?