配置更改后丢失对变量的引用

时间:2012-03-10 12:14:15

标签: android android-configchanges

我有一个查找用户位置(MyLocation类)的活动,然后使用geopoint或没有它运行AsyncTask连接到服务器并从我的服务器获取城市列表。列表准备就绪后,将它们保存在ArrayList城市中。一旦城市ArrayList被填充,我希望它能够保存好(配置更改证明)。 CityItem实现了Parcelable。我将它们保存在onSaveInstanceState中并在onCreate上检索它们。

现在,如果任务已完成且城市列表已填满,一切正常。然后我来回旋转我的设备和Log.i(“StartActivity”,“城市列表已下载:”+ cities.toString());被叫。

但是如果我在找到地址点之前将设备旋转(或任务已完成 - 因为它发生得很快就很难判断),那么

public void gotCities(ArrayList<CityItem> _cities){

    cities = _cities;
    Log.i("StartActivity", "gotCities("+cities.size()+"): "+cities.toString());
}

被调用(城市在日志中完全没问题)但是当我再次旋转它时 ArrayList城市似乎再次为空

如果配置发生变化且savedInstanceState.cities为null,则会再次以某种方式创建ArrayList城市,并且它与gotCities()函数中的ArrayList不同。

我很确定这很简单,但我几个小时都在寻找答案而且我根本做不到。

活动代码:

public class StartActivity extends Activity {

public static final String PREFS_NAME = "PrefsFile";

MyLocation myLocationObject = null;
LatLngPoint point = null;
ArrayList<CityItem> cities = null;

FindCityTask task = null;
Activity startActivity;

@Override
public void onCreate(Bundle savedInstanceState) {

if(savedInstanceState!=null) if(savedInstanceState.containsKey("cities")) cities = savedInstanceState.getParcelableArrayList("cities"); if(cities!=null) Log.i("Cities retrieved", cities.toString());

    super.onCreate(savedInstanceState);

    startActivity = this;

        setContentView(R.layout.start);

        //check if the configuration (orientation) has been changed
        NonConfigurationObject nco = (NonConfigurationObject)getLastNonConfigurationInstance();
        if(nco!=null) if(nco.myLocationObject!=null) myLocationObject = nco.myLocationObject;
        if(nco!=null) if(nco.task!=null) task = nco.task;

        if(cities==null){

            Log.i("StartActivity", "Cities list is empty - retrieve them.");

            if(myLocationObject==null){
                getGeopoint();
            }
        } else {
            Log.i("StartActivity", "Cities list downloaded:"+cities.toString());

    }

}

private void getGeopoint(){

    if(isOnline()){ //there is internet connection


        if(myLocationObject==null){

            myLocationObject = new MyLocation();
            //calls function to check user location (returns false if no providers are enabled
            if(!myLocationObject.getLocation(this, locationResult)){ /*TODO handle */Log.i("StartActivity", "Location providers disabled");}

        }


    } else { //not online - show msg

        Log.i("StartActivity", "No internet connection");

    }

}


//waits for user geopoint. then starts FindCityTask 
LocationResult locationResult = new LocationResult(){
    @Override
    public void gotLocation(final Location location){
        if(location!=null){

            // location found
            Log.i("StartActivity", "Received location: "+location.toString());
            point = new LatLngPoint((float)location.getLatitude(), (float)location.getLongitude());

        } else {

            // location not found   
            Log.i("StartActivity", "No location received after 20 seconds");
            point = null;

        }

        //RUN TASK to connect to server to get cities list (even if there's no geopoint)
        task = new FindCityTask(startActivity);
        task.execute(point);

    }
};

public void gotCities(ArrayList<CityItem> _cities){

    cities = _cities;
    Log.i("StartActivity", "gotCities("+cities.size()+"): "+cities.toString());
}

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    Log.i("onSaveInstanceState", "onSaveInstanceState");
    if(cities!=null) savedInstanceState.putParcelableArrayList("cities", cities);
}

@Override
public NonConfigurationObject onRetainNonConfigurationInstance() {

    NonConfigurationObject nco = new NonConfigurationObject();

    if(myLocationObject!=null){
        nco.myLocationObject = myLocationObject;
    }
    if(task!=null){
        nco.task = task;
    }

    return nco;

}

static class NonConfigurationObject{

    MyLocation myLocationObject;
    FindCityTask task;

}

从AsyncTask onPostExecute调用gotCities()方法:

@Override
protected void onPostExecute(Void result) {
    if(this.activity!=null){
        ((StartActivity) activity).gotCities(cities);   
    }
}

2 个答案:

答案 0 :(得分:0)

当方向cnange命中时,你的活动被停止,销毁并重新创建,并且唯一被保证被调用的回调是onPause()。当屏幕锁定启动时会发生同样的情况 - 它会以纵向模式强制您的活动。

我建议你阅读有关android活动生命周期的内容。经验法则是:

  • onCreate()用于初始化接口对象和服务
  • onResume()会在您的活动出现并且即将呈现给用户
  • 时被调用
  • onPause(),当它失去焦点并且不再使用时。

由于获取位置需要很长时间,因此最好将其从活动移至后台服务(如果需要,请在onCreate()中启动)并将其生命周期与活动分离。服务可以通过广播消息或java方法调用将结果传递给活动

查看你的onSaveInstanceState() - 首先调用super.onSaveInstanceState(),然后修改bundle以包含你的数据。这样他们就永远不会得救。

答案 1 :(得分:0)

我终于明白了。这是指向错误(配置更改之前的那个)活动的AsyncTask。诀窍是在任务中附加对活动的引用(并在适当的时刻进行)。

所以要做的第一件事就是将这些函数放在AsyncTask中:

void attach(Activity activity){
    this.activity = activity;
}

void detach(){
    this.activity = null;
}

首次调用任务时,我们应该将活动附加到该任务。 然后onRetainNonConfigurationInstance()分离它。

@Override    
public NonConfigurationObject onRetainNonConfigurationInstance() {

    //normally it would return only the task, but i have to return another object
    //hence the NonConfigurationObject which holds reference to both the AsyncTask and MyLocation
    //(as seen in the original question)

    NonConfigurationObject nco = new NonConfigurationObject();

    if(task!=null){
        task.detach();
        nco.task = task;
    }

    return nco;

}

当我们在onCreate()中调用getLastNonConfigurationInstance()时,最后再次附加它。

        //check if the configuration (orientation) has been changed
        NonConfigurationObject nco = (NonConfigurationObject)getLastNonConfigurationInstance();

        if(nco!=null){ //not created for the first time

            Log.i("StartActivity", "NCO: "+nco.toString());
            task = nco.task;
            if(task!=null){ //nco can be present but task still null
                task.attach(this);
            } else {
                task = new FindCityTask(this);
            }

        } else {
            Log.i("StartActivity", "NCO: null");
            task = new FindCityTask(this);
        }

如果您有任何问题,请分享您对此解决方案的看法。我会修改这个问题,以便更好地适应这个问题。

相关问题