Nesneleri paket aracılığıyla gönderme


119

İşlememin çoğunu bir paket aracılığıyla gerçekleştiren sınıfa bir başvuru iletmem gerekiyor.

Sorun, amaçlarla veya bağlamlarla ilgisi olmaması ve çok sayıda ilkel olmayan nesneye sahip olmasıdır. Nasıl parcelable / seri hale halinde sınıfı paketleyip bir iletecek mı startActivityForResult?


2
"İşlememin çoğunu bir paket aracılığıyla gerçekleştiren sınıfa bir başvuru iletmem gerekiyor" - neden?
CommonsWare

1
Bir nesnem var (DataManager), bir sunucuyu yönetiyor ve bazı GUI'ler için birkaç arka uç çalıştırıyor. Yeni bir bağlantı kurulduğunda, kullanıcının tüm aktif bağlantıları ListView'da listeleyen ve kullanıcının birini seçmesini sağlayan yeni bir aktivite başlatabilmesini istiyorum. Ortaya çıkan veriler daha sonra yeni bir GUI'ye bağlanacaktır. Bu gerçekten arka uç için bir görünüm seçicidir.
eski

3
Bir nesnenin aynı örneğini birden çok aktivite üzerinde ele alıyorsanız , tekli modeli düşünmek isteyebilirsiniz . Burada iyi bir öğretici var .
sotrh

Yanıtlar:


55

Hangi yolu seçeceğinizi bulmak, yalnızca CommonsWare'in "neden" temel sorusunu değil, aynı zamanda "neye?" Sorusunu da yanıtlamayı gerektirir. geçiyor musun

Gerçek şu ki, paketlerden geçebilecek tek şey düz verilerdir - geri kalan her şey, bu verilerin ne anlama geldiğinin veya ne anlama geldiğinin yorumlanmasına dayanır. Kelimenin tam anlamıyla bir nesneyi geçemezsiniz, ancak yapabileceğiniz şu üç şeyden biridir:

1) Nesneyi temel verisine ayırabilirsiniz ve diğer uçta aynı türden bir nesne hakkında bilgi varsa, serileştirilmiş verilerden bir klon oluşturabilir. Yaygın türlerin çoğu bu şekilde paketlerden geçer.

2) Opak bir tutacağı geçirebilirsiniz. Eğer onu aynı bağlamda geçiriyorsanız (yine de biri neden uğraştığınız sorulabilir), bu, başvurabileceğiniz veya başvurmayı kaldırabileceğiniz bir tutamaç olacaktır. Ancak bunu Binder'den farklı bir bağlama geçirirseniz, gerçek değeri rastgele bir sayı olacaktır (aslında, bu rastgele sayılar başlangıçtan itibaren sırayla sayılır). Hiçbir şey yapamazsınız, ancak orijinal bağlama geri dönene kadar takip edin, bu da Binder'ın onu orijinal tutamaca geri dönüştürmesine ve yeniden kullanışlı hale getirmesine neden olur.

3) Dosya tanımlayıcı veya belirli işletim sistemi / platform nesnelerine referans gibi sihirli bir tutamaç geçirebilirsiniz ve doğru bayrakları ayarlarsanız Binder, alıcı için aynı kaynağa işaret eden bir klon oluşturacaktır, bu da aslında diğer Son. Ancak bu yalnızca birkaç tür nesne için işe yarar.

Büyük olasılıkla, ya sınıfınızı sırf diğer ucun onu takip edebilmesi ve daha sonra size geri verebilmesi için geçiriyorsunuz ya da serileştirilmiş kurucu verilerden bir klonun oluşturulabileceği bir bağlama aktarıyorsunuz ... ya da başka işe yaramayacak bir şey yapmaya çalışıyorsunuz ve tüm yaklaşımı yeniden düşünmeniz gerekiyor.


1
Tur cevabınız için teşekkürler. Hakkınız, tek yapmam gereken, yeni aktiviteme bir nesne listesi referansını iletmek. Yeni etkinlik listeden bazı verileri alacak ve seçilebilir bir ListView gösterecektir. onSelect, etkinlik ana bilgisayar etkinliğine bir sonuç (tıklama nesnesiyle ilgili bazı veriler) döndürecektir. Doğru anlarsam, 2. seçeneğinizin bunu en uygun şekilde ele aldığına inanıyorum; bu opak tutamacı nasıl elde ederim?
eski

Diğer etkinliğiniz, görüntülenmek üzere opak bir nesneden herhangi bir veri çıkaramaz. Muhtemelen yapmak isteyeceğiniz şey, görüntülenecek bilgilerin kopyalarını içeren desteklenen türde bazı vekil nesneler oluşturmak ve bunlardan geçmek.
Chris Stratton

158

Ayrıca bir nesneyi JSONObject'e dönüştürmek ve onu pakete aktarmak için Gson'ı kullanabilirsiniz. Benim için bunu yapmanın en zarif yolu buydu. Performansı nasıl etkilediğini test etmedim.

İlk Aktivite

Intent activity = new Intent(MyActivity.this,NextActivity.class);
activity.putExtra("myObject", new Gson().toJson(myobject));
startActivity(activity);

Sonraki Aktivitede

String jsonMyObject;
Bundle extras = getIntent().getExtras();
if (extras != null) {
   jsonMyObject = extras.getString("myObject");
}
MyObject myObject = new Gson().fromJson(jsonMyObject, MyObject.class);

3
Bu, aktiviteler arasında geçiş yapma meselesi olduğundan, uygulamanın genel performansı üzerinde büyük bir etkiye sahip olacak kadar sık ​​gerçekleşmez. Soket bağlantıları ve diğer benzer sınıflar varmış gibi göründüğü için orijinal gönderinin DataManager'ını serileştirmenin işe yarayacağından şüpheliyim.
britzl

4
Ayrıca Google, Seri hale getirmek yerine bu çözümü önermektedir: bu belge sayfasının
TechNyquist

3
sadece bir uyarı olarak, bu tekniği bir süre takip ettim ama bir String olarak geçirebilecekleriniz için bir bellek sınırı var, bu yüzden verilerinizin çok büyük olmadığından emin olun.
jiduvah

Projenize Gson desteğini nasıl ekleyeceğinizi öğrenmek için blog.madadipouya.com/2015/09/21/… sayfasına bakın .
geekQ

Akıllı çözüm, size şapka çıkartın!
Rohit Mandiwal

20

Parcelable arabirimi bir niyet olan bir nesne geçirmek için iyi bir yoldur.

Özel nesnelerimi nasıl Parcelable yapabilirim? Parcelable'ın nasıl kullanılacağına dair oldukça iyi bir cevap

Resmi google dokümanları ayrıca bir örnek içerir


1
ya da seri hale getirilebilirler.
Jeffrey Blattman

1
Ancak performansı 10x önemli ölçüde azaltır !! Bu karşılaştırmaya göz atın: developerphil.com/parcelable-vs-serializable
saiyancoder

2
+1 @ Mati'nin yorumu, ancak bunu tek bir nesneye uygulandığında 10x bağlama yerleştirmek 1 ms'ye eşdeğerdir. Yani belki de göründüğü kadar kötü değil.
pinoyyid

1
Katılıyorum. Sorun, bir Rest API'den kaynak alıyorsanız çok yaygın bir kullanım örneği olan koleksiyonlarla uğraşırken ortaya çıkar. Ancak tek bir nesne için kötü şöhretli bir şey olmamalı. Her neyse, tüm standart kodlar yolunuza çıkan bir şeyse , hepsini sizin için üreten bu kitaplığı deneyebilirsiniz : github.com/johncarl81/parceler . Gerçekten güzel bir yaklaşım!
saiyancoder

Bozuk bağlantı: 404 (bulunamadı)
Gallal

14

Global uygulamayı kullanabilirsiniz durumunu .

Güncelleme:

Özelleştirin ve ardından bunu AndroidManifest.xml dosyanıza ekleyin:

<application android:label="@string/app_name" android:debuggable="true" android:name=".CustomApplication"

Ve sonra projenizde şöyle bir ders alın:

package com.example;

import android.app.Application;

public class CustomApplication extends Application {
    public int someVariable = -1;
}

Ve " herhangi bir Etkinlik veya Hizmetten getApplication () aracılığıyla erişilebilir " olduğundan, bunu şu şekilde kullanırsınız:

CustomApplication application = (CustomApplication)getApplication();
application.someVariable = 123; 

Umarım yardımcı olur.


1
Cevabınız için teşekkürler ama nasıl?
eski

Ben sadece Uygulama alt sınıfına girdiğine ve daha sonra istediğin her şeyi saklayabileceğine inanıyorum. İhtiyacınız olan xml değişiklikleri yukarıdaki bağlantıda belirtilmiştir.
Mark Storer

9
Genel bir tasarım müdürü olarak, gerçekten ihtiyacınız olmadıkça globallerden kaçınmak iyi bir fikirdir. Bu durumda iyi alternatifler var.
dhaag23

Tamam, sanırım ne dediğini anladım. Sadece Uygulamayı genişletin ve geçirilmesi gereken nesneyi tutan bir değişkeni atın; Referans sayfasını inceledim ve gerekli xml değişikliklerini görmedim.
eski

Bunu da bir cevap olarak yazmak istedim. Bu kesinlikle bunu yapmanın yollarından biridir. Ancak bu nesnelerin, siz onları geri çevirmediğiniz sürece (veya Uygulama bağlamı yok edilmedikçe) hafızada kalacağını ve ihtiyacınız olmadığında yer kaplayabileceğini unutmayın.
Igor Čordaš

12

Ayrıca nesnelerin yapabilirsiniz Serializable ve Bundle kullanalım getSerializable ve putSerializable yöntemleri.


1
Bunu denedim ve pratik olmayacağını çabucak anladım. Geçirilen sınıfta (evreler) depolanan nesnelerin çoğunun serileştirilebilir olduğunu düşünmüyorum. :) yine de teşekkürler.
eski

10

Olası çözüm:

Bundle bundle = new Bundle();
bundle.putSerializable("key", new CustomObject());

Sınıf CustomObject:

class CustomObject implements Serializable{
 private SubCustomObject1 sc1;
 private SubCustomObject2 sc2;
}

Alt özel nesneler:

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }

7

Paket aracılığıyla nesne göndermenin bir başka yolu da Örnek kod kullanmaktırbundle.putByteArray

public class DataBean implements Serializable {
private Date currentTime;

public setDate() {
    currentTime = Calendar.getInstance().getTime();
 }

public Date getCurrentTime() {
    return currentTime;
 }
}

Object of DataBean'i Bundle'a koyun:

class FirstClass{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//When you want to start new Activity...
Intent dataIntent =new Intent(FirstClass.this, SecondClass.class);
            Bundle dataBundle=new Bundle();
            DataBean dataObj=new DataBean();
            dataObj.setDate();
            try {
                dataBundle.putByteArray("Obj_byte_array", object2Bytes(dataObj));

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();

            }

            dataIntent.putExtras(dataBundle);

            startActivity(dataIntent);
}

Nesneleri bayt dizilerine dönüştürme

/**
 * Converting objects to byte arrays
 */
static public byte[] object2Bytes( Object o ) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream( baos );
      oos.writeObject( o );
      return baos.toByteArray();
    }

Nesneyi Paketten geri alın:

class SecondClass{
DataBean dataBean;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//Get Info from Bundle...
    Bundle infoBundle=getIntent().getExtras();
    try {
        dataBean = (DataBean)bytes2Object(infoBundle.getByteArray("Obj_byte_array"));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Bayt dizilerinden nesne alma yöntemi:

/**
 * Converting byte arrays to objects
 */
static public Object bytes2Object( byte raw[] )
        throws IOException, ClassNotFoundException {
      ByteArrayInputStream bais = new ByteArrayInputStream( raw );
      ObjectInputStream ois = new ObjectInputStream( bais );
      Object o = ois.readObject();
      return o;
    }

Umarım bu diğer arkadaşlara yardımcı olur.


bu, koda bakıldığında sorunsuz ve kolay görünüyor. Ancak, SDK'nın nesneleri geçirmek için neden böyle bir şey sunmadığına dair daha fazlası olduğunu hissediyorum. Bana bu çözüm hakkında daha fazla şey söyleyebilir misiniz?
Mario Lenci

3
Tüm bu koda gerek yok! Bundle.putSerializable (objectImplementingSerializable) kullanın - bu, burada tekrar uyguladığınız şeyin altında
kalır

3

1. Çok doğrudan ve kullanımı kolay bir örnek, geçilecek nesneyi Seri hale getirilebilir hale getirin.

class Object implements Serializable{
    String firstName;
   String lastName;
}

2. paketteki nesneyi geçirin

Bundle bundle = new Bundle();
Object Object = new Object();
bundle.putSerializable("object", object);

3. paketten geçirilen nesneyi Serializable olarak alın ve ardından Object'e çevirin.

Object object = (Object) getArguments().getSerializable("object");

0

Bu kendi soruma çok geç bir cevap, ancak dikkat çekmeye devam ediyor, bu yüzden onu ele almam gerektiğini hissediyorum. Bu cevapların çoğu doğrudur ve işi mükemmel bir şekilde yerine getirir. Ancak uygulamanın ihtiyaçlarına göre değişir. Bu cevap, bu soruna iki çözümü tanımlamak için kullanılacaktır.

Uygulama

İlki , burada cevap hakkında en çok konuşulan uygulama olduğu için Uygulama . Uygulama, bir Bağlam referansına ihtiyaç duyan varlıkları yerleştirmek için iyi bir nesnedir. Bir "ServerSocket" şüphesiz bir bağlama ihtiyaç duyar (dosya G / Ç veya basit "ListAdapter" güncellemeleri için). Ben şahsen bu yolu tercih ediyorum. Uygulamalardan hoşlanıyorum, bağlam geri getirme için kullanışlıdırlar (çünkü statik hale getirilebilirler ve muhtemelen bir bellek sızıntısına neden olmazlar) ve basit bir yaşam döngüsüne sahiptirler.

Hizmet

hizmet` saniyedir. Bir "Hizmet" aslında benim sorunum için daha iyi bir seçimdir çünkü hizmetlerin amacı budur:
Hizmet, içinde uzun süre çalışan işlemleri gerçekleştirebilen bir uygulama bileşenidir.
arka planda olup bir kullanıcı arayüzü sağlamaz.
Hizmetler, kontrol edilmesi daha kolay olan daha tanımlı bir yaşam döngüsüne sahip oldukları için derli topludur. Ayrıca, gerekirse, hizmetler uygulamanın dışında (yani önyükleme sırasında) çalışabilir. Bu, bazı uygulamalar için veya yalnızca düzgün bir özellik için gerekli olabilir.

Bu ikisinin de tam açıklaması değildi, ancak daha fazlasını araştırmak isteyenler için dokümanların bağlantılarını bıraktım. Genel olarak Serviceihtiyacım olan örnek için daha iyi - SPP cihazıma bir ServerSocket çalıştırmak.


0

Bir Date nesnesini geçmenin bir yolunu ararken bu soruyla karşılaştım. Benim durumumda, cevaplar arasında önerildiği gibi, Bundle.putSerializable () kullandım, ancak bu, orijinal gönderide açıklanan DataManager gibi karmaşık bir şey için işe yaramazdı.

Söz konusu DataManager'ı Uygulamaya koymaya çok benzer bir sonuç verecek veya onu bir Singleton haline getirecek olan önerim, Bağımlılık Enjeksiyonunu kullanmak ve DataManager'ı bir Singleton kapsamına bağlamak ve DataManager'ı gereken her yere enjekte etmektir. Yalnızca artırılmış test edilebilirlikten faydalanmakla kalmaz, aynı zamanda tüm kazan plakası "sınıflar ve etkinlikler arasında bağımlılıkları geçirme" kodu olmadan daha temiz bir kod elde edersiniz. (Robo) Guice ile çalışmak çok kolay ve yeni Dagger çerçevesi de umut verici görünüyor.


1
Tarih gibi bir şeyle uzun değeri geçebilirsin. Ama geri kalanı kulağa hoş geliyor. Teşekkürler.
ahodder

0

bir paket kullanarak nesneyi geçirmenin başka bir basit yolu:

  • sınıf nesnesinde, bir anahtarla statik bir liste veya başka bir veri yapısı oluşturun
  • nesneyi oluşturduğunuzda, anahtarla liste / veri yapısına yerleştirin (örneğin, nesne oluşturulduğunda uzun zaman damgası)
  • Listeden nesneyi almak için statik getObject (uzun anahtar) yöntemini oluşturun
  • pakette anahtarı geçerek nesneyi daha sonra kodun başka bir noktasından alabilirsiniz.
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.