Etkinlikler arasında veri paylaşmanın en iyi yolu nedir?


239

Uygulamada kullanılan ana etkinlik olan bir etkinliğim var ve bir dizi değişkeni var. İlk aktivitedeki verileri kullanabilmek için iki aktivitem daha var. Şimdi böyle bir şey yapabileceğimi biliyorum:

GlobalState gs = (GlobalState) getApplication();
String s = gs.getTestMe();

Ancak çok sayıda değişkeni paylaşmak istiyorum ve bazıları oldukça büyük olabilir, bu yüzden yukarıdaki gibi kopyalarını oluşturmak istemiyorum.

Get ve set yöntemlerini kullanmadan değişkenleri doğrudan almanın ve değiştirmenin bir yolu var mı? Google geliştirici sitesindeki bir makaleyi okuduğumu hatırlıyorum, bunun Android'deki performans için önerilmez.


2
Android 2.3 (Gingerbread) itibariyle, get / set optimizasyonu Dalvik tarafından otomatik olarak gerçekleştirilir; bu yalnızca Android'in eski sürümlerini hedefliyorsanız geçerlidir.
StellarVortex

Örnekte dize verilerinin kopyalanmadığını unutmayın. Bunun yerine, aynı dize nesnesine bir başvuru oluşturur.
Code-Apprentice

İnanmak zor, neden başka bir aktiviteden bir aktivite başlatmak ve herhangi bir karmaşık nesneyi birinden diğerine geçirmek için bir olasılık yok? Serileştirme olmadan, nesneyi ve tüm bu çabayı kaydedin. Bu bir güvenlik açığı mı yoksa her iki etkinlik de aynı uygulamada ise nesne referansını geçmeye karşı başka bir neden var mı? (Farklı uygulamalarda olmalarının farklı olduğunu anlıyorum)
Droidum


LiveData en iyi, en yeni çözümdür. Cevabımı aşağıdan kontrol edin.
Amir Uval

Yanıtlar:


476

İşte bunu başarmanın en yaygın yollarının bir derlemesi :

  • Amaç içinde veri gönderme
  • Statik alanlar
  • HashHap WeakReferences
  • Kalıcı nesneler (sqlite, paylaşım tercihleri, dosya vb.)

TL; DR : veri paylaşmanın iki yolu vardır: verileri niyetin ekstralarına aktarmak veya başka bir yere kaydetmek. Veriler temel öğeler, Dizeler veya kullanıcı tanımlı nesneler ise: bunu amaç ekstralarının bir parçası olarak gönderin (kullanıcı tanımlı nesneler uygulanmalıdır Parcelable). Karmaşık nesneler geçiyorsa bir örneği başka bir yerde tek birtona kaydedin ve başlatılan etkinlikten bunlara erişin.

Her bir yaklaşımın nasıl ve neden uygulanacağına dair bazı örnekler:

Verileri amaçların içine gönderme

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("some_key", value);
intent.putExtra("some_other_key", "a value");
startActivity(intent);

İkinci etkinlikte:

Bundle bundle = getIntent().getExtras();
int value = bundle.getInt("some_key");
String value2 = bundle.getString("some_other_key");

İlkel veri veya Dizeler geçiriyorsanız bu yöntemi kullanın . Ayrıca, uygulayan nesneleri de geçirebilirsiniz Serializable.

Cazip olmasına rağmen, kullanmadan önce iki kez düşünmelisiniz Serializable: hata eğilimli ve korkunç derecede yavaş. Genel olarak: mümkünse uzak durunSerializable . Karmaşık kullanıcı tanımlı nesneler geçmek istiyorsanız, bakmak Parcelablearayüzüne . Uygulanması daha zor, ancak karşılaştırıldığında önemli ölçüde hız kazancı var Serializable.

Diske devam etmeden veri paylaşma

Çoğu durumda, her iki faaliyetin de aynı süreçte yürütüldüğü göz önünde bulundurularak, veriler arasında etkinlikler arasında veri paylaşmak mümkündür.

Not: bazen, kullanıcı etkinliğinizden ayrıldığında (istifa etmeden), Android uygulamanızı öldürmeye karar verebilir. Böyle bir senaryoda, android, uygulama öldürülmeden önce sağlanan amacı kullanarak son etkinliği başlatmaya çalıştığı vakaları yaşadım. Bu durumlarda, tek birtonda (sizinkini veya Application) depolanan veriler kaybolacak ve kötü şeyler meydana gelebilir. Bu gibi durumlardan kaçınmak için, nesneleri diske saklarsınız veya geçerli olduğundan emin olmak için kullanmadan önce verileri kontrol edersiniz.

Tek bir sınıf kullanın

Verileri tutacak bir sınıfa sahip olun:

public class DataHolder {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}

  private static final DataHolder holder = new DataHolder();
  public static DataHolder getInstance() {return holder;}
}

Başlatılan aktiviteden:

String data = DataHolder.getInstance().getData();

Uygulama singletonunu kullan

Uygulama singletonu, android.app.Applicationuygulama başlatıldığında oluşturulan bir örneğidir . Aşağıdakileri genişleterek özel bir tane sağlayabilirsiniz Application:

import android.app.Application;
public class MyApplication extends Application {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}
}

Etkinliği başlatmadan önce:

MyApplication app = (MyApplication) getApplicationContext();
app.setData(someData);

Ardından, başlatılan etkinlikten:

MyApplication app = (MyApplication) getApplicationContext();
String data = app.getData();

Statik alanlar

Fikir temel olarak singleton ile aynıdır, ancak bu durumda verilere statik erişim sağlarsınız:

public class DataHolder {
  private static String data;
  public static String getData() {return data;}
  public static void setData(String data) {DataHolder.data = data;}
}

Başlatılan aktiviteden:

String data = DataHolder.getData();

HashHap WeakReferences

Aynı fikir, ancak çöp toplayıcısının referanslandırılmamış nesneleri kaldırmasına izin vermek (örneğin, kullanıcı etkinlikten ayrıldığında):

public class DataHolder {
  Map<String, WeakReference<Object>> data = new HashMap<String, WeakReference<Object>>();

  void save(String id, Object object) {
    data.put(id, new WeakReference<Object>(object));
  }

  Object retrieve(String id) {
    WeakReference<Object> objectWeakReference = data.get(id);
    return objectWeakReference.get();
  }
}

Etkinliği başlatmadan önce:

DataHolder.getInstance().save(someId, someObject);

Başlatılan aktiviteden:

DataHolder.getInstance().retrieve(someId);

Niyetin ekstralarını kullanarak nesne kimliğini iletmeniz gerekebilir veya gerekmeyebilir. Her şey sizin özel probleminize bağlıdır.

Nesneleri diske devam ettirme

Amaç, diğer etkinliği başlatmadan önce verileri diske kaydetmek.

Avantajları: Etkinliği diğer yerlerden başlatabilirsiniz ve veriler zaten devam ediyorsa, iyi çalışmalıdır.

Dezavantajları: hantaldır ve uygulanması daha fazla zaman alır. Daha fazla kod gerektirir ve bu nedenle hataları ortaya çıkarma şansı daha fazladır. Ayrıca çok daha yavaş olacaktır.

Nesneleri sürdürmenin bazı yolları şunlardır:


11
Bunun daha büyük / daha karmaşık veriler için "normal" yol olmadığını iddia ediyorum. Statik bir singleton veya Application nesnesi kullanmak çok daha kolay ve harika çalışıyor. Artık OP'nin örnekte bir Dize kullandığını söyleyen, bunun için Niyet mükemmel ve tercih edilir.
Charlie Collins

10
Serializable, Android işlem modelinde ciddi performans sorunları olduğunu buldu. Bu yüzden Parcelable'ı tanıttılar. Yukarıdaki yanıtta Serializable yerine Parcelable'ı okuyun .
Subin Sebastian

3
Bu setResultyöntemle yapılır . Ayrıca, bu durumda, ikincil faaliyet startActivityForResultyöntem kullanılarak çağrılmalıdır .
Cristian

2
Harika özet! Singletons sorun yok ediliyor ilgili olarak, orada birçok faaliyetleri ve nesnelerle uygulamalar için basit bir çözümdür: Kullanım bir alt sınıf Activityboyunca ve bunun içinde onCreate()uygulamayı başlatırken dolduracak tekiz yeri kontrolünden statik alanı. Bu alan boşsa, diğer etkinlikleri öldürmek için FLAG_ACTIVITY_CLEAR_TASKveya a tuşunu kullanarak başlangıç ​​etkinliğine geri dönün BroadcastReceiver.
Janosch

1
Uygulama sınıfında veri kaydetmeyi önermem. Daha fazla
bilgiyi

22

Ne kullanabilirsiniz:

  1. faaliyetler arasında veri aktarımı (Cristian'in dediği gibi)
  2. çok fazla statik değişkeni olan bir sınıf kullanma (böylece sınıfın bir örneği olmadan ve getter / setter kullanmadan bunları çağırabilirsiniz)
  3. Veritabanı kullanma
  4. Paylaşılan Tercihler

Seçtiğiniz, ihtiyaçlarınıza bağlıdır. Muhtemelen "çok" olduğunda birden fazla yol kullanacaksınız.


1
Statik işlem ölümüyle ilgili olarak silindi
EpicPandaForce

@EpicPandaForce Elbette ve ayrıca cihaz kapatıldığında.
WarrenFaith

1
Ancak cihaz kapalıysa, uygulama MAINişlemden yeniden başlar . İşlem ölümünden sonra, en son açık olan etkinlikte yeniden başlarsınız; bu, uygulamanın derinliklerinde bir ayrıntı sayfası olabilir.
EpicPandaForce

@EpicPandaForce, Cpt Obvious olsa bile ironi özledim gibi görünüyor.
WarrenFaith

16

Google'ın size ne komut verdiğini yapın! burada: http://developer.android.com/resources/faq/framework.html#3

  • İlkel Veri Türleri
  • Kalıcı Olmayan Nesneler
  • Singleton sınıfı - benim favorim: D
  • Genel statik alan / yöntem
  • Nesnelere Zayıf Referansların Karma Haritası
  • Kalıcı Nesneler (Uygulama Tercihleri, Dosyalar, contentProviders, SQLite DB)

1
Kalıcı Nesneler için Google Bağlantısı: developer.android.com/guide/topics/data/data-storage.html
NicoMinsk

Sadece antipatterns, Singleton sınıfı ilk tasarım modelidir, statik alan / yöntemli bir sınıf teklitonlar gibi çok fazla davranır ve busniss nesnelerini ısrar etmek için veritabanı yapmak bazen iyi bir şey değildir, elbette sizin hatanız değil ve listeliyorsunuz başarmak için yollar, ama merak ediyorum neden Google böyle çok aptalca bir şey, performans sorunları ya da ne karmaşık ?? ya da ben Android yolu anlamıyorum ?? !!!!
La VloZ Merrill

bağlantı koptu :(
Shayan_Aryan

14

"Ancak bir çok değişkeni paylaşmak istiyorum ve bazıları oldukça büyük olabilir, bu yüzden yukarıdaki gibi bunların kopyalarını oluşturmak istemiyorum."

Bu bir kopya yapmaz (özellikle String ile , ancak nesneler bile nesnenin kendisi değil, referansın değerine göre geçer ve kullanımı gayet iyi olur - muhtemelen diğer araçlardan daha iyidir çünkü ortak ve İyice anlaşıldı). Alıcıları ve ayarlayıcıları kullanmamak gibi eski "performans mitleri" nin hala bir değeri vardır, ancak dokümanlarda da güncellenmiştir .

Ancak bunu yapmak istemiyorsanız, değişkenleri genel hale getirebilir veya GlobalState'te korunabilir ve doğrudan erişebilirsiniz. JavaDoc Uygulama nesnesinin belirttiği gibi statik bir tekton oluşturabilirsiniz :

Normalde Uygulama alt sınıfına gerek yoktur. Çoğu durumda, statik tek tonlar aynı işlevselliği daha modüler bir şekilde sağlayabilir. Singletonunuzun genel bir bağlama ihtiyacı varsa (örneğin yayın alıcılarını kaydetmek için), onu alma işlevine, singleton'u ilk oluştururken dahili olarak Context.getApplicationContext () kullanan bir Bağlam verilebilir.

Kullanılması Niyet diğeri burada cevapları not olarak, veri veri aktarmak için başka bir yoludur, ama genellikle daha küçük veri ve basit türleri için kullanılır. Daha büyük / daha karmaşık veriler iletebilirsiniz, ancak bu yalnızca statik bir singleon kullanmaktan daha karmaşıktır. Uygulama nesne hala (bu bir Android uygulamasındaki iyi tanımlanmış bir yaşam döngüsü vardır çünkü) gerçi Android uygulama bileşenleri arasındaki büyük / daha karmaşık olmayan kalıcı veri paylaşımı için benim kişisel favorim.

Ayrıca, diğerlerinin de belirttiği gibi, veriler çok karmaşık hale gelirse ve kalıcı olması gerekiyorsa, SQLite veya dosya sistemini de kullanabilirsiniz.


1
Aslında, son zamanlarda dokümanlar üzerinde bunu tökezledim: developer.android.com/guide/appendix/faq/framework.html#3 . "Kalıcı olmayan karmaşık nesneler" için, statik bir tekton oluşturmak ve yıkmak için Application sınıfının kullanılmasını önerir! Böylece iyi tanımlanmış yaşam döngüsü Uygulama sağlar ve statik bir singleton kullanım kolaylığı.
Charlie Collins

2
SSS bu bölümü şimdi kaldırılmış gibi görünüyor (sadece "kalıcı olmayan nesneler" görüyorum ve uygulama sınıfı söz yok). Her neyse ayrıntılandırabilir misin?
Tony Chan

1
Şu anda developer.android.com/guide/faq/framework.html#3 "Tek bir uygulamada Etkinlikler / Hizmetler arasında veriyi nasıl aktarırım?" Ve Uygulama sınıfından bahsetmiyorum.
Jerry101

Bir uygulama nesnesi kullanmaktan hoşlanıyorum. Ancak pek çok potansiyel işveren, bunu kod zorluklarında gördüklerinde hoşlanmıyor. Yanılıyor olabilirler, ama ağırlıklarını atıyorlar. BTW: bu 'framework.html # 3 bağlantısı artık çalışmıyor.
Matt J.

7

Application sınıfını ve etiketini orada istediğiniz herhangi bir nesneye genişletebilirsiniz, daha sonra bunlar uygulamanızın herhangi bir yerinde kullanılabilir


Uygulama sınıfında veri kaydetmeyi önermem. Daha fazla
bilgiyi

4

Etkinlikler arasında veri paylaşmanın yeni ve daha iyi bir yolu vardır ve LiveData'dır . Android geliştiricisinin sayfasındaki bu alıntıya özellikle dikkat edin:

LiveData nesnelerinin yaşam döngüsüne duyarlı olması, bunları birden fazla etkinlik, parça ve hizmet arasında paylaşabileceğiniz anlamına gelir. Örneği basit tutmak için, LiveData sınıfını tek birton olarak uygulayabilirsiniz

Bunun anlamı çok büyüktür - herhangi bir model verisi bir LiveDatasarıcı içinde ortak bir singleton sınıfında paylaşılabilir . ViewModelTest edilebilirlik uğruna faaliyetlerinden kendi faaliyetlerine enjekte edilebilir. Artık bellek sızıntılarını önlemek için zayıf referanslar hakkında endişelenmenize gerek yok.


2

Yukarıda açıklanan ve http://developer.android.com/guide/faq/framework.html adresindeki zayıf referans yaklaşımının hashini kullanmak benim için sorunlu görünüyor. Yalnızca harita değeri değil, tüm girdiler nasıl geri kazanılır? Hangi kapsamda ayırıyorsunuz? Çerçeve, Faaliyet yaşam döngüsünün kontrolünde olduğundan, katılan Faaliyetlerden birine sahip olmak, sahibi müşterileri önceden imha edildiğinde çalışma zamanı hataları riski taşır. Uygulamaya sahipse, bazı Faaliyetlerin hashutun geçerli bir anahtar ve potansiyel olarak hatalı toplanmış zayıf referansı olan girdilere tutunmasını önlemek için girdiyi açıkça kaldırması gerekir. Ayrıca, bir anahtar için döndürülen değer null olduğunda istemci ne yapmalıdır?

Bana öyle geliyor ki, Uygulamaya ait veya bir singleton içindeki WeakHashMap daha iyi bir seçimdir. Haritadaki bir değere anahtar bir nesne üzerinden erişilir ve anahtara güçlü referanslar olmadığında (yani tüm Etkinlikler anahtarla ve onunla eşleştiği şeylerle yapıldığında), GC harita girişini geri alabilir.


2

Etkinlikler arasında veri paylaşmanın çeşitli yolları vardır

1: Niyet kullanan etkinlikler arasında veri aktarımı

Intent intent=new Intent(this, desirableActivity.class);
intent.putExtra("KEY", "Value");
startActivity(intent)

2: Statik anahtar kelimeyi kullanarak değişkeni genel statik olarak tanımlayın ve projenin herhangi bir yerinde kullanın

      public static int sInitialValue=0;

classname.variableName kullanarak projede herhangi bir yerde kullanın;

3: Veritabanını Kullanma

ama biraz uzun süreç, veri eklemek için sorgu kullanmak ve gerektiğinde imleci kullanarak veri yinelemek gerekir. Ancak önbelleği temizlemeden veri kaybetme şansı yoktur.

4: Paylaşılan Tercihleri ​​Kullanma

veritabanından çok daha kolay. ancak ArrayList, List ve custome nesnelerini kaydedemeyeceğiniz bazı sınırlamalar vardır.

5: Aplication sınıfında alıcı ayarlayıcı oluşturun ve projenin herhangi bir yerine erişin.

      private String data;
      public String getData() {
          return data;
      }

      public void setData(String data) {
          this.data = data;
      }

burada ayarlayın ve etkinliklerden yararlanın

         ((YourApplicationClass)getApplicationContext()).setData("abc"); 

         String data=((YourApplicationClass)getApplicationContext()).getData();  

1

Birkaç fikrim var, ama aradığın şey olup olmadığını bilmiyorum.

Tüm verileri tutan bir hizmeti kullanabilir ve daha sonra veri kurtarma için etkinliklerinizi hizmete bağlayabilirsiniz.

Veya verilerinizi serileştirilebilir veya ayrıştırılabilir bir pakette paketleyin ve bir pakete ekleyin ve paketi etkinlikler arasında geçirin.

Bu aradığınız şey olmayabilir, ancak bir SharedPreferences veya genel olarak bir tercih kullanmayı da deneyebilirsiniz.

Her iki şekilde de neye karar verdiğini bana bildir.



1

Niyetiniz mevcut Faaliyetten diğer Faaliyetleri çağırmaksa, Amaçlar'ı kullanmalısınız . Odak noktanız, kalıcı veriler üzerinde gerektiğinde paylaşmaya göre daha az olabilir.

Ancak, bu değerleri gerçekten sürdürmeniz gerekiyorsa, bunları yerel depolamadaki bir tür yapılandırılmış metin dosyasında veya veritabanında devam ettirebilirsiniz. Bir özellikler dosyası, XML dosyası veya JSON dosyası verilerinizi depolayabilir ve etkinlik oluşturma sırasında kolayca ayrıştırılabilir. Ayrıca tüm Android cihazlarda SQLite bulunduğunu unutmayın, böylece bunları bir veritabanı tablosunda saklayabilirsiniz. Ayrıca, anahtar / değer çiftlerini saklamak ve haritayı yerel depolamaya serileştirmek için bir Harita kullanabilirsiniz, ancak bu, basit veri yapıları için yararlı olmak için çok hantal olabilir.


1

Yukarıda belirtilen cevapların hepsi harika ... Ben sadece hiç kimse henüz faaliyetleri yoluyla veri kalıcı hakkında bahsetmedi ekledi ve bu ilgili veri devam etmek için dahili android SQLite veritabanı kullanmaktır ... Aslında sizin databaseHelper uygulama durumunda ve etkinleştirir gerekirse gerektiği gibi çağırmak .. Ya da sadece bir yardımcı sınıf yapmak ve gerektiğinde DB çağrıları yapmak ... Sadece dikkate başka bir katman eklemek ... Ama diğer tüm cevaplar yeterli olacaktır yanı sıra .. Gerçekten sadece tercih


1

Giriş yaptıktan sonra e-posta gönderme örneği etkinlikleri arasında veri paylaşımı

"e-posta", istenen etkinlikteki değere başvurmak için kullanılabilecek addır

1 Giriş sayfasındaki kod

Intent openLoginActivity = new Intent(getBaseContext(), Home.class);
    openLoginActivity.putExtra("email", getEmail);

Ana sayfada 2 kod

Bundle extras = getIntent().getExtras();
    accountEmail = extras.getString("email");

1

Ve veri nesnesi ile çalışmak istiyorsanız, bu ikisi çok önemlidir:

Serileştirilebilir ve Parcelable

  • Serializable, kullanıcının verileri ihtiyaçlarına göre marshal edemeyeceğini gösteren bir işaretleyici arabirimidir. Böylece nesne uygulandığında Serializable Java bunu otomatik olarak serileştirir.
  • Parcelable android kendi serileştirme protokolüdür. Parcelable'da geliştiriciler, marshaling ve unmarshaling için özel kod yazarlar. Bu, Serileştirmeye kıyasla daha az çöp nesnesi oluşturur
  • Parcelable'ın performansı, özel uygulaması nedeniyle Serializable ile karşılaştırıldığında çok yüksektir.Android'de nesneleri serileştirirken Parcelable implantasyonunun kullanılması şiddetle önerilir.

public class User implements Parcelable

buradan daha fazla kontrol edin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.