2012-12-01 30 views
23

Tôi đã cố gắng hết sức để làm cho tất cả dữ liệu cho trò chơi Android của tôi phù hợp với gói savedInstanceState. Có rất nhiều dữ liệu hoàn toàn, bao gồm nhiều đối tượng Parcelable. Điều này đảm bảo rằng khi ứng dụng bị tạm dừng hoặc thay đổi hướng, sẽ không có dữ liệu nào bị mất bởi Hoạt động đang được tạo lại.Lưu gói vào SharedPreferences

Tuy nhiên, gần đây tôi đã phát hiện ra rằng một gói savedInstanceState dường như KHÔNG thích hợp để lưu trữ lâu dài. Vì vậy, tôi đang tìm cách để tôi có thể điều chỉnh phương pháp lưu hiện có của mình để hoạt động như một giải pháp lâu dài, để trạng thái trò chơi luôn có thể được khôi phục.

Tôi đã nghe nói về 2 giải pháp cho đến nay:

1) Sử dụng các bó savedInstanceState cho thay đổi định hướng, mà còn kết hợp SharedPrefs cho khi ứng dụng cần phải được tắt hoàn toàn.

Điều này dường như vô cùng hiệu quả, vì nó sử dụng 2 phương pháp hoàn toàn khác nhau để làm cơ bản giống nhau. Ngoài ra, vì gói savedInstanceState của tôi sử dụng các đối tượng Parcelable, tôi sẽ phải cung cấp cho từng đối tượng một phương thức khác để cho phép chúng được ghi vào SharedPrefs. Về cơ bản LOTS mã trùng lặp và khó quản lý.

2) Tuần tự hóa gói savedInstanceState và ghi trực tiếp vào tệp.

Tôi mở cửa cho điều này, nhưng tôi thực sự không biết cách thực hiện. Tuy nhiên, tôi vẫn giữ hy vọng rằng có thể có một giải pháp tốt hơn, như tôi đã nghe nói rằng serialization trong Android là "comically/unusably slow".

Tôi sẽ rất biết ơn nếu ai đó có thể cung cấp cho tôi giải pháp cho điều này.

+0

Để tuần tự chỉ tìm kiếm một lớp tuần tự hóa, nó không phải là rất khó tìm. Tôi đã không nhận thấy bất kỳ sự chậm trễ excruciating trong khi sử dụng nó – mango

+0

Các thông tin duy nhất tôi có thể tìm thấy cho tôi biết tôi cần phải thực hiện Serializable - nhưng Bundle không thực hiện giao diện này. – Dan

+0

Tôi khuyên bạn nên sử dụng thư viện https://github.com/iamironz/binaryprefs, Nó cho phép lưu dữ liệu như java chuẩn thông qua triển khai Giao diện bền vững (Giao diện bên ngoài trong jdk) –

Trả lời

3

Tôi hiện đã đưa ra giải pháp của riêng mình cho vấn đề này, đây là phương tiện bán tự động để lưu Gói vào SharedPreferences. Tôi nói bán tự động bởi vì, mặc dù việc lưu Gói chỉ yêu cầu một phương pháp, truy xuất dữ liệu một lần nữa và chuyển nó trở lại thành một Gói có một số công việc.

Đây là đoạn mã để lưu Bundle:

SharedPreferences save = getSharedPreferences(SAVE, MODE_PRIVATE); 
Editor ed = save.edit(); 
saveBundle(ed, "", gameState); 

/** 
* Manually save a Bundle object to SharedPreferences. 
* @param ed 
* @param header 
* @param gameState 
*/ 
private void saveBundle(Editor ed, String header, Bundle gameState) { 
    Set<String> keySet = gameState.keySet(); 
    Iterator<String> it = keySet.iterator(); 

    while (it.hasNext()){ 
     key = it.next(); 
     o = gameState.get(key); 
     if (o == null){ 
      ed.remove(header + key); 
     } else if (o instanceof Integer){ 
      ed.putInt(header + key, (Integer) o); 
     } else if (o instanceof Long){ 
      ed.putLong(header + key, (Long) o); 
     } else if (o instanceof Boolean){ 
      ed.putBoolean(header + key, (Boolean) o); 
     } else if (o instanceof CharSequence){ 
      ed.putString(header + key, ((CharSequence) o).toString()); 
     } else if (o instanceof Bundle){ 
      saveBundle(header + key, ((Bundle) o)); 
     } 
    } 

    ed.commit(); 
} 

Lưu ý rằng tôi đã chỉ trường hợp bằng văn bản cho các loại tôi cần, nhưng điều này sẽ dễ dàng thích nghi nếu bạn có Gói đó cũng bao gồm các loại khác.

Phương pháp này sẽ đệ quy lưu các đối tượng nhóm khác được lưu trữ bên trong Gói nhất định. Tuy nhiên, nó sẽ không hoạt động cho các đối tượng Parcelable, vì vậy tôi phải thay đổi các đối tượng Parcelable của mình để làm cho chúng tự lưu trữ chúng thành một Bundle thay thế. Vì Parcels and Bundles khá giống nhau, điều này không quá khó. Tôi nghĩ rằng các gói cũng có thể hơi chậm hơn so với Parcels, thật không may.

Sau đó tôi đã viết các hàm tạo trong tất cả các đối tượng trước đây có thể kiện của mình để cho phép chúng tự nhóm lại từ dữ liệu được lưu trữ SharedPreferences. Thật dễ dàng để tạo lại các khóa cho dữ liệu bạn cần.Giả sử bạn có cấu trúc dữ liệu sau đây:

Bundle b { 
    KEY_X -> int x; 
    KEY_Y -> Bundle y { 
       KEY_Z -> int z; 
      } 
} 

Những điều này sẽ được lưu vào SharedPreferences như sau:

KEY_X -> x 
KEY_YKEY_Z -> z 

Nó có thể không phải là phương pháp đẹp nhất trên thế giới, nhưng nó hoạt động, và chi phí cho tôi ít hơn nhiều so với thay thế, vì bây giờ phương thức onSaveInstanceState của tôi và các phương thức onPause của tôi sử dụng cùng một kỹ thuật.

+0

làm thế nào chúng ta có thể getBundle trong tình huống này? Cảm ơn –

+1

Tôi không chắc chắn ý của bạn là chính xác ... Khi gói đã được lưu vào SharedPrefs, gói có thể được truy xuất giống như bất kỳ gói nào khác. – Dan

+0

khóa và o là gì. Biểu mẫu nơi bạn vượt qua các đề xuất này – ekjyot

18

Hài hước, tuần này, số 47 của Android Weekly giải phóng thư viện này: android complex preferences.

Nó phải phù hợp với bạn.

+0

Điều này có vẻ rất hứa hẹn, nhưng than ôi, tôi không thể làm cho nó hoạt động bất cứ điều gì. Tôi đã thử với các đối tượng Bundle khác nhau, bao gồm các đối tượng rỗng, và một số đối tượng đơn giản hơn như Points, nhưng vẫn không có may mắn. Nó hoặc phàn nàn về một "tham chiếu vòng tròn" khi lưu, hoặc "đối tượng được lưu trữ tại khóa ___ là thể hiện của một trò chơi khác" khi tải. Điều này khiến tôi phát điên ... – Dan

+0

Vui lòng đăng câu hỏi này dưới dạng câu hỏi riêng. Tôi sẽ quan tâm nếu bạn thêm một bình luận vào địa chỉ của tôi để tôi có thể theo dõi nó. – Snicolas

+1

Thực ra nó chỉ tiết kiệm mọi thứ khi JSon sử dụng GSon ... Dù sao thì, cảm giác của tôi là dữ liệu của bạn có thể là lớp bên trong của một thứ gì đó. Điều này sẽ cung cấp cho bạn chu kỳ rất dễ dàng. Các lớp học riêng biệt của bạn có phải là POJO không? – Snicolas

1

Tôi đã mở rộng câu trả lời từ Dan bằng chức năng tự động tạo lại Gói và làm cho các tên ít có khả năng xung đột hơn.

private static final String SAVED_PREFS_BUNDLE_KEY_SEPARATOR = "§§"; 

/** 
* Save a Bundle object to SharedPreferences. 
* 
* NOTE: The editor must be writable, and this function does not commit. 
* 
* @param editor SharedPreferences Editor 
* @param key SharedPreferences key under which to store the bundle data. Note this key must 
*   not contain '§§' as it's used as a delimiter 
* @param preferences Bundled preferences 
*/ 
public static void savePreferencesBundle(SharedPreferences.Editor editor, String key, Bundle preferences) { 
    Set<String> keySet = preferences.keySet(); 
    Iterator<String> it = keySet.iterator(); 
    String prefKeyPrefix = key + SAVED_PREFS_BUNDLE_KEY_SEPARATOR; 

    while (it.hasNext()){ 
     String bundleKey = it.next(); 
     Object o = preferences.get(bundleKey); 
     if (o == null){ 
      editor.remove(prefKeyPrefix + bundleKey); 
     } else if (o instanceof Integer){ 
      editor.putInt(prefKeyPrefix + bundleKey, (Integer) o); 
     } else if (o instanceof Long){ 
      editor.putLong(prefKeyPrefix + bundleKey, (Long) o); 
     } else if (o instanceof Boolean){ 
      editor.putBoolean(prefKeyPrefix + bundleKey, (Boolean) o); 
     } else if (o instanceof CharSequence){ 
      editor.putString(prefKeyPrefix + bundleKey, ((CharSequence) o).toString()); 
     } else if (o instanceof Bundle){ 
      savePreferencesBundle(editor, prefKeyPrefix + bundleKey, ((Bundle) o)); 
     } 
    } 
} 

/** 
* Load a Bundle object from SharedPreferences. 
* (that was previously stored using savePreferencesBundle()) 
* 
* NOTE: The editor must be writable, and this function does not commit. 
* 
* @param sharedPreferences SharedPreferences 
* @param key SharedPreferences key under which to store the bundle data. Note this key must 
*   not contain '§§' as it's used as a delimiter 
* 
* @return bundle loaded from SharedPreferences 
*/ 
public static Bundle loadPreferencesBundle(SharedPreferences sharedPreferences, String key) { 
    Bundle bundle = new Bundle(); 
    Map<String, ?> all = sharedPreferences.getAll(); 
    Iterator<String> it = all.keySet().iterator(); 
    String prefKeyPrefix = key + SAVED_PREFS_BUNDLE_KEY_SEPARATOR; 
    Set<String> subBundleKeys = new HashSet<String>(); 

    while (it.hasNext()) { 

     String prefKey = it.next(); 

     if (prefKey.startsWith(prefKeyPrefix)) { 
      String bundleKey = StringUtils.removeStart(prefKey, prefKeyPrefix); 

      if (!bundleKey.contains(SAVED_PREFS_BUNDLE_KEY_SEPARATOR)) { 

       Object o = all.get(prefKey); 
       if (o == null) { 
        // Ignore null keys 
       } else if (o instanceof Integer) { 
        bundle.putInt(bundleKey, (Integer) o); 
       } else if (o instanceof Long) { 
        bundle.putLong(bundleKey, (Long) o); 
       } else if (o instanceof Boolean) { 
        bundle.putBoolean(bundleKey, (Boolean) o); 
       } else if (o instanceof CharSequence) { 
        bundle.putString(bundleKey, ((CharSequence) o).toString()); 
       } 
      } 
      else { 
       // Key is for a sub bundle 
       String subBundleKey = StringUtils.substringBefore(bundleKey, SAVED_PREFS_BUNDLE_KEY_SEPARATOR); 
       subBundleKeys.add(subBundleKey); 
      } 
     } 
     else { 
      // Key is not related to this bundle. 
     } 
    } 

    // Recursively process the sub-bundles 
    for (String subBundleKey : subBundleKeys) { 
     Bundle subBundle = loadPreferencesBundle(sharedPreferences, prefKeyPrefix + subBundleKey); 
     bundle.putBundle(subBundleKey, subBundle); 
    } 


    return bundle; 
} 
+0

https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/src-html/org/ apache/commons/lang/StringUtils.html để có được những gì StringUtils funcs đang làm (không có trong android) –

Các vấn đề liên quan