异步任务和上下文

19

所以我正在使用Android的AsyncTask类来编写我的第一个多线程应用程序。我试图在第二个线程中使用它来触发一个Geocoder,然后使用onPostExecute更新UI,但是我一直遇到适当的Context问题。

我有点费劲地在主线程上使用Contexts,但我不太确定Context是什么以及如何在后台线程上使用它,而且我没有找到任何好的例子。有帮助吗?这是我尝试做的事情的摘录:

public class GeoCode extends AsyncTask<GeoThread, Void, GeoThread> {
  @Override
  protected GeoThread doInBackground(GeoThread... i) {
    List<Address> addresses = null;
    Geocoder geoCode = null; 
    geoCode = new Geocoder(null); //Expects at minimum Geocoder(Context context);
    addresses = geoCode.getFromLocation(GoldenHour.lat, GoldenHour.lng, 1);
  }
}

第六行一直在失败,因为Context不正确。

5个回答

18

@Eugene van der Merwe

下面的代码对我有效:)-->

public class ApplicationLauncher extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.applicationlauncher);

    LoadApplication loadApplication = new LoadApplication(this);
    loadApplication.execute(null);
}

private class LoadApplication extends AsyncTask {

    Context context;
    ProgressDialog waitSpinner;
    ConfigurationContainer configuration = ConfigurationContainer.getInstance();

    public LoadApplication(Context context) {
        this.context = context;
        waitSpinner = new ProgressDialog(this.context);
    }

    @Override
    protected Object doInBackground(Object... args) {
        publishProgress(null);
        //Parsing some stuff - not relevant
        configuration.initialize(context);
        return null;
    }

    @Override
    protected void onProgressUpdate(Object... values) {
        super.onProgressUpdate(values);
        // Only purpose of this method is to show our wait spinner, we dont
        // (and can't) show detailed progress updates
        waitSpinner = ProgressDialog.show(context, "Please Wait ...", "Initializing the application ...", true);
    }

    @Override
    protected void onPostExecute(Object result) {
        super.onPostExecute(result);
        waitSpinner.cancel();
    }
}
}

Cheers,

Ready4Android


29
这段代码存在上下文泄漏问题。你将Activity保存为AsyncTask中的Context,这是可以的,但当用户旋转设备时,会重新创建Activity。AsyncTask继续运行,引用旧的Activity副本,现在内存中有两个Activity副本。 - Christopher Perry
@ChristopherPerry:在AsyncTask中将Context保持为WeakReference是否可以解决泄漏问题?还是有更好的方法可以安全地调整上述解决方案? - gcl1
还是只传递应用程序上下文就足够安全了? - gcl1
1
@gcl1,使用WeakReference可以解决内存泄漏问题。不幸的是,旋转屏幕会导致您失去引用,因为它现在可以进行垃圾回收,而Android非常激进的垃圾回收例程将期望您的应用程序呈现撤销凯撒所得的内容。我建议避免像瘟疫一样将任何上下文传递给AsyncTask。相反,您可以在Activity中的AsyncTask执行调用之前启动进度对话框,然后在Asynctask返回时关闭它。在这种情况下,更好的解决方案是使用[Otto](http://square.github.io/otto/)。 - Christopher Perry
@ChristopherPerry:谢谢,我没听说过Otto-会去了解一下!在我的情况下,我不需要上下文显示对话框,而是需要发送广播,以便将已经下载的照片写入设备图库(参见此页面)。我想我只需要小心使用弱引用即可。 - gcl1
1
无论@ChristopherPerry的评论如何,此代码存在内存泄漏问题:由于AsyncTask是一个内部类(非静态),它对封闭的Activity有隐式引用。即使您根本不传递Context,这也会导致内存泄漏。因此,WeakReference无法解决此问题! - Vasiliy

4
上下文是一个对象,它提供了对应用程序运行时环境的访问。在大多数情况下,当您需要从Android环境中获取对象(例如资源、视图、基础设施类等)时,您需要手头上有上下文。
在Activity类中获取Context实例非常简单-- Activity本身是Context的子类,所以您所需要做的就是使用“this”关键字来指向您当前的上下文。
无论何时创建可能需要Context的代码-- 您都应该注意从父Activity传递Context对象。在您的示例中,您可以添加一个明确的构造函数,接受Context作为输入参数。

2

我进行了更多的研究,有人建议将它传递给线程(不确定为什么我没有想到)。我通过一个参数将其传递给Geocoder线程,就这样,它可以工作了。


4
请问您能否提供一个您所做过的示例,并更新正确答案以回答这个问题? - Eugene van der Merwe
@Eugene van der Merwe 看看我的回答,我认为这是他最终所做的事情的一个例子。 - Michael Peterson

1

使用AsyncTask更新UI的问题在于需要获取当前活动上下文。但是每次方向更改时,上下文都会被销毁和重建。

这里有一个很好的答案来回答你的问题:Android AsyncTask context behavior


0

看起来你好像没有使用params参数。你可以用它来传递上下文。

public class GeoCode extends AsyncTask<Context, Void, GeoThread> {
  @Override
  protected GeoThread doInBackground(Context... params) {
    List<Address> addresses = null;
    Geocoder geoCode = null; 
    geoCode = new Geocoder(params[0]); //Expects at minimum Geocoder(Context context);
    addresses = geoCode.getFromLocation(GoldenHour.lat, GoldenHour.lng, 1);
  }
}

然后在您的活动中:

GeoCode myGeoCode = new GeoCode();
myGeoCode.execute(this);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接