วิธีส่งวัตถุผ่านบันเดิล


119

ฉันต้องส่งการอ้างอิงไปยังคลาสที่ประมวลผลส่วนใหญ่ผ่านบันเดิล

ปัญหาคือมันไม่เกี่ยวข้องกับเจตนาหรือบริบทและมีวัตถุที่ไม่ใช่ดั้งเดิมจำนวนมาก ฉันจะบรรจุคลาสเป็นแบบพัสดุ / ซีเรียลได้และส่งต่อไปยัง a ได้startActivityForResultอย่างไร


2
"ฉันจำเป็นต้องส่งต่อการอ้างอิงไปยังคลาสที่ประมวลผลส่วนใหญ่ผ่านบันเดิล" - เพราะเหตุใด
CommonsWare

1
ฉันมีออบเจ็กต์ (DataManager) มันจัดการเซิร์ฟเวอร์และเรียกใช้แบ็กเอนด์สองสามตัวสำหรับ GUI บางตัว เมื่อมีการสร้างการเชื่อมต่อใหม่ฉันต้องการให้ผู้ใช้สามารถเริ่มกิจกรรมใหม่ที่แสดงรายการใน ListView การเชื่อมต่อที่ใช้งานอยู่ทั้งหมดและให้ผู้ใช้เลือก จากนั้นข้อมูลที่ได้จะเชื่อมโยงกับ GUI ใหม่ นี่เป็นเพียงตัวเลือกสกินสำหรับส่วนหลังเท่านั้น
ahodder

3
หากคุณกำลังติดต่อกับอินสแตนซ์เดียวกันของวัตถุมากกว่าหลายกิจกรรมที่คุณอาจต้องการที่จะต้องพิจารณารูปแบบเดี่ยว มีการสอนที่ดีเป็นที่นี่
sotrh

คำตอบ:


55

การคิดว่าจะใช้เส้นทางใดไม่เพียง แต่ต้องตอบคำถามสำคัญของ CommonsWare ที่ว่า "ทำไม" แต่ยังต้องตอบคำถาม "ถึงอะไร" ด้วย คุณผ่านมันไปหรือเปล่า

ความจริงก็คือสิ่งเดียวที่สามารถผ่านบันเดิลได้คือข้อมูลธรรมดาทุกอย่างจะขึ้นอยู่กับการตีความว่าข้อมูลนั้นหมายถึงหรือชี้ไปที่อะไร คุณไม่สามารถผ่านวัตถุได้อย่างแท้จริง แต่สิ่งที่คุณทำได้คือหนึ่งในสามสิ่ง:

1) คุณสามารถแยกออบเจ็กต์ออกเป็นข้อมูลที่สร้างขึ้นและหากสิ่งที่อยู่ในอีกด้านหนึ่งมีความรู้เกี่ยวกับอ็อบเจ็กต์ประเภทเดียวกันก็สามารถรวบรวมโคลนจากข้อมูลที่ต่อเนื่องกันได้ นั่นคือวิธีที่ประเภททั่วไปส่วนใหญ่ส่งผ่านบันเดิล

2) คุณสามารถส่งที่จับทึบแสงได้ หากคุณกำลังส่งผ่านภายในบริบทเดียวกัน (แม้ว่าจะมีคนถามว่าทำไมต้องรำคาญ) นั่นจะเป็นจุดจับที่คุณสามารถเรียกหรือหักล้างได้ แต่ถ้าคุณส่งผ่าน Binder ไปยังบริบทอื่นค่าตามตัวอักษรจะเป็นตัวเลขที่กำหนดเอง (อันที่จริงแล้วตัวเลขเหล่านี้จะนับตามลำดับจากการเริ่มต้น) คุณไม่สามารถทำอะไรได้นอกจากติดตามมันจนกว่าคุณจะส่งกลับไปยังบริบทเดิมซึ่งจะทำให้ Binder เปลี่ยนกลับเป็นแฮนเดิลเดิมทำให้มีประโยชน์อีกครั้ง

3) คุณสามารถส่งที่จับวิเศษเช่นตัวอธิบายไฟล์หรือการอ้างอิงไปยังออบเจ็กต์ระบบปฏิบัติการ / แพลตฟอร์มบางอย่างและหากคุณตั้งค่าแฟล็กที่ถูกต้อง Binder จะสร้างโคลนที่ชี้ไปยังทรัพยากรเดียวกันสำหรับผู้รับซึ่งสามารถใช้งานได้จริง อีกด้านหนึ่ง แต่สิ่งนี้ใช้ได้กับวัตถุบางประเภทเท่านั้น

เป็นไปได้มากว่าคุณกำลังส่งผ่านชั้นเรียนของคุณเพื่อให้ปลายอีกด้านสามารถติดตามและส่งคืนให้คุณในภายหลังหรือคุณกำลังส่งต่อไปยังบริบทที่สามารถสร้างโคลนจากข้อมูลที่เป็นส่วนประกอบแบบอนุกรม ... หรืออื่น ๆ คุณกำลังพยายามทำบางสิ่งที่ไม่ได้ผลและคุณต้องคิดใหม่ทั้งแนวทาง


1
ขอบคุณสำหรับการตอบกลับทัวร์ ถูกต้องสิ่งที่ฉันต้องทำมีเพียงส่งการอ้างอิงรายการวัตถุไปยังกิจกรรมใหม่ของฉัน กิจกรรมใหม่จะนำข้อมูลบางส่วนจากรายการและแสดง ListView ที่เลือกได้ onSelect กิจกรรมจะส่งคืนผลลัพธ์ (ข้อมูลบางส่วนที่เกี่ยวข้องกับออบเจ็กต์คลิก) ไปยังกิจกรรมโฮสต์ ถ้าฉันเข้าใจถูกต้องฉันเชื่อว่าตัวเลือกที่ 2 ของคุณจัดการสิ่งนี้ได้อย่างเหมาะสมที่สุด จะเอาด้ามทึบนี้ได้อย่างไร
ahodder

กิจกรรมอื่น ๆ ของคุณไม่สามารถดึงข้อมูลจากวัตถุทึบแสงมาแสดงได้ สิ่งที่คุณอาจต้องการทำคือสร้างและส่งผ่านวัตถุตัวแทนบางประเภทที่รองรับซึ่งมีสำเนาของข้อมูลที่จะแสดง
Chris Stratton

158

คุณยังสามารถใช้ Gson เพื่อแปลงวัตถุเป็น JSONObject และส่งต่อไปยังบันเดิล สำหรับฉันเป็นวิธีที่สง่างามที่สุดที่ฉันพบในการทำเช่นนี้ ฉันยังไม่ได้ทดสอบว่ามันมีผลต่อประสิทธิภาพอย่างไร

ในกิจกรรมเริ่มต้น

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

ในกิจกรรมถัดไป

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

3
เนื่องจากเป็นเรื่องของการส่งผ่านสิ่งต่างๆระหว่างกิจกรรมจึงไม่ได้เกิดขึ้นบ่อยพอที่จะส่งผลกระทบอย่างมากต่อประสิทธิภาพโดยรวมของแอป ดังที่กล่าวว่าฉันสงสัยว่ามันจะทำงานเพื่อทำให้เป็นอนุกรม DataManager ของโพสต์ต้นฉบับเนื่องจากดูเหมือนว่ามีการเชื่อมต่อซ็อกเก็ตและคลาสอื่น ๆ ที่คล้ายกัน
britzl

4
นอกจากนี้ Google ยังแนะนำวิธีแก้ปัญหานี้แทน Serialize: ดูส่วนสุดท้าย "ทางเลือกที่แนะนำ" ของหน้าเอกสารนี้
TechNyquist

3
เช่นเดียวกับคำเตือนฉันใช้เทคนิคนี้มาระยะหนึ่งแล้ว แต่มีข้อ จำกัด ของหน่วยความจำสำหรับสิ่งที่คุณสามารถส่งผ่านเป็น String ได้ดังนั้นอย่าลืมว่าข้อมูลของคุณไม่ใหญ่เกินไป
jiduvah

ดูblog.madadipouya.com/2015/09/21/…เพื่อเรียนรู้วิธีเพิ่มการสนับสนุน Gson ในโครงการของคุณ
geekQ

โซลูชันที่ชาญฉลาดตอบสนองคุณ!
Rohit Mandiwal

20

Parcelableอินเตอร์เฟซที่เป็นวิธีที่ดีที่จะผ่านวัตถุที่มีเจตนา

ฉันจะทำให้ออบเจ็กต์แบบกำหนดเองของฉันเป็นพัสดุได้อย่างไร เป็นคำตอบที่ดีในการใช้งานParcelable

นอกจากนี้Google เอกสารอย่างเป็นทางการยังมีตัวอย่าง


1
หรือยังสามารถต่ออนุกรมได้อีกด้วย
Jeffrey Blattman

1
แต่ประสิทธิภาพลดลงมาก 10x !! ตรวจสอบเกณฑ์มาตรฐานนี้: developerphil.com/parcelable-vs-serializable
saiyancoder

2
ความคิดเห็นของ +1 @ Mati อย่างไรก็ตามการใส่ไว้ในบริบท 10x เมื่อนำไปใช้กับออบเจ็กต์เดียวจะเทียบเท่ากับ 1 ms ดังนั้นอาจจะไม่เลวร้ายอย่างที่คิด
pinoyyid

1
ตกลง. ปัญหาคือเมื่อคุณจัดการกับคอลเล็กชันซึ่งเป็นกรณีการใช้งานที่พบบ่อยมากหากคุณได้รับทรัพยากรจาก Rest API แต่สำหรับวัตถุชิ้นเดียวไม่ควรเป็นเรื่องฉาวโฉ่ อย่างไรก็ตามถ้าทุกรหัสสำเร็จรูปเป็นสิ่งที่ได้รับในทางของคุณคุณสามารถลอง lib นี้ที่สร้างว่าทั้งหมดสำหรับคุณ: github.com/johncarl81/parceler แนวทางที่ดีจริงๆ!
saiyancoder

ลิงก์เสีย: 404 (ไม่พบ)
Gallal

14

คุณสามารถใช้สถานะแอปพลิเคชันส่วนกลาง

ปรับปรุง:

ปรับแต่งแล้วเพิ่มสิ่งนี้ใน AndroidManifest.xml ของคุณ:

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

จากนั้นมีชั้นเรียนในโครงการของคุณดังนี้:

package com.example;

import android.app.Application;

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

และเนื่องจาก " สามารถเข้าถึงได้ผ่าน getApplication () จากกิจกรรมหรือบริการใด ๆ " คุณจึงใช้สิ่งนี้:

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

หวังว่าจะช่วยได้


1
ขอบคุณสำหรับคำตอบ แต่อย่างไร
ahodder

ฉันเชื่อว่าคุณเป็นแค่แอปพลิเคชันคลาสย่อยและสามารถจัดเก็บสิ่งที่คุณต้องการได้ การเปลี่ยนแปลง xml ที่คุณต้องการมีระบุไว้ในลิงค์ด้านบน
Mark Storer

9
ในฐานะหลักการออกแบบทั่วไปคุณควรหลีกเลี่ยง globals เว้นแต่คุณจะต้องการจริงๆ ในกรณีนี้มีทางเลือกที่ดี
dhaag23

โอเคฉันคิดว่าฉันเข้าใจในสิ่งที่คุณกำลังพูด เพียงแค่ขยายแอปพลิเคชันและโยนตัวแปรที่ถือวัตถุที่ต้องการส่งผ่าน ฉันตรวจสอบหน้าอ้างอิงแล้วและไม่เห็นการเปลี่ยนแปลง xml ที่จำเป็น
ahodder

ฉันอยากจะเขียนสิ่งนี้เป็นคำตอบด้วย นี่เป็นหนึ่งในวิธีที่ทำได้อย่างแน่นอน แต่โปรดทราบว่าวัตถุเหล่านี้จะอยู่ในหน่วยความจำเว้นแต่คุณจะยกเลิกการอ้างอิง (หรือบริบทของแอปถูกทำลาย) และสามารถครอบครองพื้นที่ได้เมื่อคุณไม่ต้องการ
Igor Čordaš

12

นอกจากนี้คุณยังสามารถทำให้วัตถุของคุณSerializableและใช้ Bundle ของgetSerializableและputSerializableวิธี


1
ฉันลองแล้วและตระหนักได้อย่างรวดเร็วว่ามันทำไม่ได้ ฉันไม่คิดว่าออบเจ็กต์ส่วนใหญ่ที่จัดเก็บในคลาสที่ผ่าน (เธรด) สามารถต่ออนุกรมกันได้ :) ขอบคุณแม้ว่า
ahodder

10

แนวทางแก้ไขที่เป็นไปได้:

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

คลาส CustomObject:

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

วัตถุที่กำหนดเองย่อย:

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }

7

อีกวิธีหนึ่งในการส่งวัตถุผ่านบันเดิลคือการใช้bundle.putByteArray
โค้ดตัวอย่าง

public class DataBean implements Serializable {
private Date currentTime;

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

public Date getCurrentTime() {
    return currentTime;
 }
}

ใส่ Object ของ DataBean ไว้ใน Bundle:

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

การแปลงวัตถุเป็นไบต์อาร์เรย์

/**
 * 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();
    }

รับ Object คืนจาก Bundle:

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

วิธีการรับวัตถุจากไบต์อาร์เรย์:

/**
 * 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;
    }

หวังว่านี่จะช่วยเพื่อนคนอื่น ๆ


ซึ่งดูเรียบง่ายและดูรหัส แต่ฉันรู้สึกว่ามีบางอย่างเพิ่มเติมเกี่ยวกับสาเหตุที่ SDK ไม่เสนอสิ่งนี้เพื่อส่งผ่านวัตถุ คุณช่วยบอกฉันเพิ่มเติมเกี่ยวกับโซลูชันนี้ได้ไหม
Mario Lenci

3
ไม่จำเป็นต้องมีรหัสทั้งหมด! ใช้ bundle.putSerializable (objectImplementingSerializable) - สิ่งนี้จะอยู่ภายใต้สิ่งที่คุณจะนำกลับมาใช้ที่นี่อีกครั้ง ...
Risadinha

3

1. ตัวอย่างที่ตรงและใช้งานง่ายมากทำให้วัตถุที่จะส่งผ่านไปใช้งาน Serial ได้

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

2. ส่งผ่านวัตถุในกลุ่ม

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

3. รับอ็อบเจกต์ที่ส่งจากบันเดิลเป็นอนุกรมได้จากนั้นส่งไปยังอ็อบเจ็กต์

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

0

นี่เป็นคำตอบที่ล่าช้ามากสำหรับคำถามของฉันเอง แต่ก็ยังคงได้รับความสนใจอยู่เสมอดังนั้นฉันจึงรู้สึกว่าต้องจัดการกับมัน คำตอบเหล่านี้ส่วนใหญ่ถูกต้องและจัดการกับงานได้อย่างสมบูรณ์แบบ อย่างไรก็ตามขึ้นอยู่กับความต้องการของแอปพลิเคชัน คำตอบนี้จะใช้เพื่ออธิบายวิธีแก้ปัญหานี้สองวิธี

ใบสมัคร

ประการแรกคือแอปพลิเคชันเนื่องจากมีการพูดถึงมากที่สุดเกี่ยวกับคำตอบที่นี่ แอปพลิเคชันเป็นวัตถุที่ดีในการวางเอนทิตีที่ต้องการการอ้างอิงถึงบริบท ไม่ต้องสงสัยว่า "ServerSocket" จะต้องมีบริบท (สำหรับไฟล์ I / o หรือการอัปเดต "ListAdapter" แบบธรรมดา) โดยส่วนตัวแล้วฉันชอบเส้นทางนี้มากกว่า ฉันชอบแอปพลิเคชันมันมีประโยชน์สำหรับการดึงบริบท (เพราะสามารถทำให้เป็นแบบคงที่และไม่น่าจะทำให้หน่วยความจำรั่วไหล) และมีวงจรชีวิตที่เรียบง่าย

บริการ

บริการ`เป็นครั้งที่สอง "บริการ" เป็นทางเลือกที่ดีกว่าสำหรับปัญหาของฉันเพราะนั่นคือสิ่งที่บริการออกแบบมาเพื่อทำ:
บริการเป็นส่วนประกอบของแอปพลิเคชันที่สามารถดำเนินการในระยะยาวได้
พื้นหลังและไม่มีส่วนติดต่อผู้ใช้
บริการมีความเรียบร้อยเนื่องจากมีวงจรชีวิตที่กำหนดไว้มากขึ้นซึ่งง่ายต่อการควบคุม นอกจากนี้หากจำเป็นบริการต่างๆสามารถเรียกใช้แอปพลิเคชันภายนอกได้ (เช่นในการบูต) สิ่งนี้อาจจำเป็นสำหรับบางแอพหรือเป็นเพียงคุณสมบัติที่เรียบร้อย

นี่ไม่ใช่คำอธิบายทั้งหมด แต่ฉันทิ้งลิงก์ไปยังเอกสารสำหรับผู้ที่ต้องการตรวจสอบเพิ่มเติม โดยรวมแล้วสิ่งServiceที่ดีกว่าสำหรับอินสแตนซ์ที่ฉันต้องการคือการเรียกใช้ ServerSocket ไปยังอุปกรณ์ SPP ของฉัน


0

ฉันเจอคำถามนี้เมื่อฉันกำลังมองหาวิธีส่งผ่านวัตถุ Date ในกรณีของฉันตามที่แนะนำในคำตอบฉันใช้ Bundle.putSerializable () แต่จะใช้ไม่ได้กับสิ่งที่ซับซ้อนตามที่ DataManager อธิบายไว้ในโพสต์ต้นฉบับ

ข้อเสนอแนะของฉันที่จะให้ผลลัพธ์ที่คล้ายกันมากกับการวาง DataManager ดังกล่าวในแอปพลิเคชันหรือทำให้เป็น Singleton คือการใช้ Dependency Injection และผูก DataManager กับขอบเขต Singleton และฉีด DataManager ทุกที่ที่ต้องการ ไม่เพียง แต่คุณจะได้รับประโยชน์จากความสามารถในการทดสอบที่เพิ่มขึ้น แต่คุณยังจะได้รับรหัสที่สะอาดขึ้นโดยไม่ต้องใช้รหัส "ส่งผ่านการอ้างอิงระหว่างชั้นเรียนและกิจกรรม" ทั้งหมด (Robo) Guiceนั้นใช้งานง่ายมากและเฟรมเวิร์กDaggerใหม่ก็มีแนวโน้มที่ดีเช่นกัน


1
เมื่อมีบางอย่างเช่นวันที่คุณสามารถส่งผ่านค่า long ได้ แต่ส่วนที่เหลือฟังดูดี ขอบคุณ
ahodder

0

อีกวิธีง่ายๆในการส่งผ่านวัตถุโดยใช้บันเดิล:

  • ในคลาสออบเจ็กต์สร้างรายการแบบคงที่หรือโครงสร้างข้อมูลอื่นด้วยคีย์
  • เมื่อคุณสร้างวัตถุให้วางไว้ในโครงสร้างรายการ / ข้อมูลด้วยคีย์ (เช่นการประทับเวลาแบบยาวเมื่อสร้างวัตถุ)
  • สร้างเมธอด getObject แบบคงที่ (คีย์แบบยาว) เพื่อรับอ็อบเจ็กต์จากรายการ
  • ในบันเดิลจะต้องผ่านคีย์เพื่อให้คุณสามารถรับวัตถุได้ในภายหลังจากจุดอื่นในโค้ด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.