2012-03-10 40 views
0

我有一個活動,它查找用戶位置(MyLocation類),然後使用地理點或沒有它,它運行一個AsyncTask連接到服務器並從我的服務器獲取城市列表。列表準備就緒後,將它們保存在ArrayList城市中。一旦城市ArrayList被填充,我希望它被保存爲好(證明配置更改)。 CityItem實現Parcelable。我將它們保存在onSaveInstanceState中並在onCreate上檢索它們。配置更改後丟失對變量的引用

現在,一切工作正常,如果任務已完成,並填寫了城市列表。然後我來回旋轉我的設備和Log.i(「StartActivity」,「城市列表下載:」+ cities.toString());被調用。

,如果我旋轉設備被發現前的GeoPoint(或任務完成 - 很難說,因爲它發生快),然後

public void gotCities(ArrayList<CityItem> _cities){ 

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

被調用(和城市是完全沒有在日誌中)但是當我再次旋轉它ArrayList城市似乎再次爲空

看來,如果配置發生變化,並且savedInstanceState.cities爲空,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; 

} 

gotCities()方法是從的AsyncTask onPostExecute稱爲:

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

回答

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

如果您有任何問題,請分享您對此解決方案的看法。我會修改問題,以便更好地解決問題。

0

當取向cnange命中,你的活動已停止,破壞並重新創建的,只保證被調用的回調函數是onPause()。當屏幕鎖定啓動時會發生同樣的情況 - 它會在縱向模式下強制您的活動。

我建議您閱讀有關android活動生命週期的內容。經驗法則是:

  • 的onCreate()是initalizing接口對象和服務
  • onResume()被調用時,你的活動都在上面,即將呈現給用戶
  • 的onPause()的時候它失去了重點,不再提供使用。

由於獲取位置需要很長時間,因此最好將其從活動移動到後臺服務(如果需要,從onCreate()開始),並將其生命週期與活動分離。服務可以通過廣播消息或Java方法調用將結果傳遞給活動

並查看onSaveInstanceState() - 首先調用super.onSaveInstanceState(),然後修改包以包含數據。這樣他們永遠不會被保存。

+0

但我從onSaveInstanceState回調,所以它被調用。位置獲取被移動到一個TimerTask(我的位置負責這個並將位置發送到locationResult)。我在第二個小時前找到了解決方案,但我想知道它是否正常。 – 2012-03-10 13:06:56

+1

靜態字段沒問題。它的壽命與您的應用程序JVM一樣長,並且不受活動生命週期的影響。 – 2012-03-10 13:54:47

+0

謝謝。但我仍然覺得我錯過了一些東西。我擔心城市變量只是被泄露。我想知道我在哪裏讓它發生。 – 2012-03-10 13:56:17