我有一个查找用户位置(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);
}
}
答案 0 :(得分:0)
当方向cnange命中时,你的活动被停止,销毁并重新创建,并且唯一被保证被调用的回调是onPause()。当屏幕锁定启动时会发生同样的情况 - 它会以纵向模式强制您的活动。
我建议你阅读有关android活动生命周期的内容。经验法则是:
由于获取位置需要很长时间,因此最好将其从活动移至后台服务(如果需要,请在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);
}
如果您有任何问题,请分享您对此解决方案的看法。我会修改这个问题,以便更好地适应这个问题。