Cách gửi các đối tượng qua gói


119

Tôi cần chuyển một tham chiếu đến lớp thực hiện phần lớn quá trình xử lý của tôi thông qua một gói.

Vấn đề là nó không liên quan gì đến ý định hoặc bối cảnh và có một lượng lớn các đối tượng không phải nguyên thủy. Làm cách nào để đóng gói lớp đó thành một lớp có thể thay đổi được / có thể nối tiếp và chuyển nó đến a startActivityForResult?


2
"Tôi cần chuyển một tham chiếu đến lớp thực hiện phần lớn quá trình xử lý của tôi thông qua một gói" - tại sao?
CommonsWare

1
Tôi có một đối tượng (DataManager), nó xử lý một máy chủ và chạy một vài phụ trợ cho một số GUI. Khi một kết nối mới được thiết lập, tôi muốn người dùng có thể bắt đầu một hoạt động mới liệt kê trong ListView tất cả các kết nối đang hoạt động và yêu cầu người dùng chọn một. Dữ liệu kết quả sau đó sẽ liên kết với một GUI mới. Đây thực sự chỉ là một công cụ làm đẹp da cho phần sau.
ahodder

3
Nếu bạn đang xử lý cùng một trường hợp của một đối tượng qua nhiều hoạt động, bạn có thể muốn xem xét mô hình singleton . Có một hướng dẫn tốt ở đây .
sotrh

Câu trả lời:


55

Tìm ra con đường để đi đòi hỏi bạn phải trả lời không chỉ câu hỏi quan trọng của CommonsWare là "tại sao" mà còn cả câu hỏi "để làm gì?" bạn đang vượt qua nó.

Thực tế là thứ duy nhất có thể đi qua các gói là dữ liệu thuần túy - mọi thứ khác đều dựa trên các diễn giải về ý nghĩa hoặc ý nghĩa của dữ liệu đó. Bạn không thể chuyển một đối tượng theo đúng nghĩa đen, nhưng những gì bạn có thể làm là một trong ba điều:

1) Bạn có thể chia nhỏ đối tượng thành dữ liệu cấu thành của nó và nếu đầu dây bên kia có kiến ​​thức về cùng một loại đối tượng, nó có thể tập hợp một bản sao từ dữ liệu được tuần tự hóa. Đó là cách hầu hết các loại thông thường chuyển qua các gói.

2) Bạn có thể vượt qua một tay cầm mờ đục. Nếu bạn đang chuyển nó trong cùng một ngữ cảnh (mặc dù người ta có thể hỏi tại sao lại bận tâm) thì đó sẽ là một xử lý mà bạn có thể gọi hoặc bỏ qua. Nhưng nếu bạn chuyển nó qua Binder đến một ngữ cảnh khác thì giá trị theo nghĩa đen của nó sẽ là một số tùy ý (trên thực tế, những số tùy ý này được đếm tuần tự từ khi khởi động). Bạn không thể làm gì khác ngoài việc theo dõi nó, cho đến khi bạn chuyển nó trở lại ngữ cảnh ban đầu, điều này sẽ khiến Binder biến đổi nó trở lại tay cầm ban đầu, khiến nó hữu dụng trở lại.

3) Bạn có thể chuyển một ma thuật xử lý, chẳng hạn như trình mô tả tệp hoặc tham chiếu đến các đối tượng hệ điều hành / nền tảng nhất định và nếu bạn đặt đúng cờ, Binder sẽ tạo một bản sao trỏ đến cùng một tài nguyên cho người nhận, tài nguyên này thực sự có thể được sử dụng trên kết cục khác. Nhưng điều này chỉ hoạt động với một số rất ít loại đối tượng.

Rất có thể, bạn đang chuyển lớp của mình chỉ để đầu bên kia có thể theo dõi và trả lại cho bạn sau, hoặc bạn đang chuyển nó đến một ngữ cảnh nơi bản sao có thể được tạo từ dữ liệu cấu thành được tuần tự hóa ... hoặc khác bạn đang cố gắng làm điều gì đó mà không hiệu quả và bạn cần phải suy nghĩ lại toàn bộ cách tiếp cận.


1
Cảm ơn đã trả lời tour. Quyền của bạn, tất cả những gì tôi cần làm chỉ là chuyển một tham chiếu của danh sách các đối tượng vào hoạt động mới của tôi. Hoạt động mới sẽ lấy một số dữ liệu từ danh sách và hiển thị một ListView có thể chọn. onSelect, hoạt động sẽ trả về một kết quả (một số dữ liệu liên quan đến đối tượng nhấp chuột) cho hoạt động máy chủ. Nếu tôi hiểu đúng, tôi tin rằng tùy chọn 2 của bạn xử lý điều này một cách hợp lý nhất; làm cách nào để lấy cái tay cầm mờ đục này?
ahodder

Hoạt động khác của bạn không thể trích xuất bất kỳ dữ liệu nào từ một đối tượng mờ đục để hiển thị. Những gì bạn có thể muốn làm là tạo và chuyển một số đối tượng thay thế thuộc loại được hỗ trợ có chứa các bản sao của thông tin sẽ được hiển thị.
Chris Stratton

158

Bạn cũng có thể sử dụng Gson để chuyển đổi một đối tượng thành JSONObject và chuyển nó vào gói. Đối với tôi, đó là cách thanh lịch nhất mà tôi tìm thấy để làm điều này. Tôi chưa thử nghiệm xem nó ảnh hưởng như thế nào đến hiệu suất.

Trong hoạt động ban đầu

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

Trong hoạt động tiếp theo

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

3
Vì vấn đề chuyển nội dung giữa các hoạt động nên không thường xuyên xảy ra để có tác động lớn đến hiệu suất tổng thể của ứng dụng. Điều đó đang được nói, tôi nghi ngờ nó sẽ hoạt động để tuần tự hóa DataManager của bài đăng gốc vì có vẻ như nó có kết nối socket và các lớp tương tự khác.
britzl

4
Ngoài ra Google gợi ý giải pháp này thay vì Serialize: kiểm tra phần cuối "Đề xuất thay thế" của trang doc này
TechNyquist

3
Như một lời cảnh báo, tôi đã làm theo kỹ thuật này một thời gian nhưng có giới hạn bộ nhớ đối với những gì bạn có thể truyền dưới dạng Chuỗi, vì vậy hãy đảm bảo dữ liệu của bạn không quá lớn.
jiduvah

Xem blog.madadipouya.com/2015/09/21/… để tìm hiểu cách thêm hỗ trợ Gson vào dự án của bạn.
geekQ

Giải pháp thông minh, dành cho bạn!
Rohit Mandiwal,

20

Các Parcelable giao diện là một cách tốt để vượt qua một đối tượng với Intent.

Làm cách nào tôi có thể làm cho các đối tượng tùy chỉnh của mình có thể thành Parcelable? là một câu trả lời khá hay về cách sử dụng Parcelable

Tài liệu chính thức của google cũng bao gồm một ví dụ


1
hoặc chúng cũng có thể được nối tiếp hóa.
Jeffrey Blattman

1
Nhưng làm giảm đáng kể hiệu suất 10x !! Kiểm tra điểm chuẩn này: developerphil.com/parcelable-vs-serializable
saiyancoder

2
Nhận xét của +1 @ Mati, tuy nhiên, để đặt nó vào ngữ cảnh thì 10x khi áp dụng cho một đối tượng tương đương với 1 ms. Vì vậy, có lẽ không tệ như nó âm thanh.
pinoyyid

1
Đồng ý. Vấn đề là khi bạn xử lý các bộ sưu tập, đây là trường hợp sử dụng rất phổ biến nếu bạn đang nhận tài nguyên từ API Rest. Nhưng đối với một đối tượng duy nhất, không nên là một cái gì đó quá nổi tiếng. Dù sao đi nữa, nếu tất cả mã soạn sẵn là thứ gì đó cản trở bạn, bạn có thể thử lib này tạo ra tất cả cho bạn: github.com/johncarl81/parceler . Một cách tiếp cận thực sự tốt đẹp!
saiyancoder

Liên kết bị hỏng: 404 (không tìm thấy)
Gallal

14

Bạn có thể sử dụng trạng thái ứng dụng toàn cầu .

Cập nhật:

Tùy chỉnh và sau đó thêm nó vào AndroidManifest.xml của bạn:

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

Và sau đó có một lớp trong dự án của bạn như thế này:

package com.example;

import android.app.Application;

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

Và bởi vì " Nó có thể được truy cập thông qua getApplication () từ bất kỳ Hoạt động hoặc Dịch vụ nào ", bạn sử dụng nó như sau:

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

Hy vọng rằng sẽ giúp.


1
Cảm ơn vì đã trả lời, nhưng làm thế nào?
ahodder

Tôi tin rằng bạn chỉ cần phân lớp Ứng dụng và sau đó có thể lưu trữ bất cứ thứ gì bạn thích. Các thay đổi xml bạn cần được đề cập trong liên kết ở trên.
Mark Storer

9
Là một hiệu trưởng về thiết kế chung, bạn nên tránh những hình cầu trừ khi bạn thực sự cần chúng. Trong trường hợp này có những lựa chọn thay thế tốt.
dhaag23

Ok, tôi nghĩ tôi hiểu những gì bạn đang nói. Chỉ cần mở rộng Ứng dụng và ném vào một biến chứa đối tượng cần được truyền; Tôi đã xem lại trang tham chiếu và không thấy các thay đổi xml cần thiết.
ahodder

Tôi cũng muốn viết điều này như một câu trả lời. Đây chắc chắn là một trong những cách để làm điều đó. Nhưng hãy nhớ rằng các đối tượng này vẫn ở trong bộ nhớ trừ khi bạn hủy cung cấp lại chúng (hoặc ngữ cảnh Ứng dụng bị phá hủy) và có thể chiếm không gian khi bạn không cần đến chúng.
Igor Čordaš

12

Bạn cũng có thể làm cho các đối tượng của mình có thể Serializable và sử dụng các phương thức getSerializableputSerializable của Bundle .


1
Tôi đã thử điều đó và nhanh chóng nhận ra nó sẽ không thực tế. Tôi không nghĩ rằng hầu hết các đối tượng được lưu trữ trong lớp đã truyền (luồng) có thể tuần tự hóa. :) cảm ơn mặc dù.
ahodder

10

Giải pháp khả thi:

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

Class CustomObject:

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

Đối tượng tùy chỉnh:

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }

7

Một cách khác để gửi các đối tượng thông qua gói là sử dụng bundle.putByteArray
Mã mẫu

public class DataBean implements Serializable {
private Date currentTime;

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

public Date getCurrentTime() {
    return currentTime;
 }
}

đưa Đối tượng của DataBean vào Gói:

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

Chuyển đổi các đối tượng thành mảng byte

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

Lấy lại đối tượng từ Gói:

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

Phương pháp lấy đối tượng từ mảng byte:

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

Hy vọng điều này sẽ giúp ích cho những người bạn khác.


mã này trông mượt mà và dễ nhìn. Nhưng tôi cảm thấy có điều gì đó khác về lý do tại sao SDK không cung cấp một cái gì đó như thế này để chuyển các đối tượng. Bạn có thể cho tôi biết thêm về giải pháp này không?
Mario Lenci

3
Không cần chút nào cho tất cả mã đó! Sử dụng bundle.putSerializable (objectImplementingSerializable) - điều này bên dưới những gì bạn đang được triển khai lại đây một lần nữa ...
Risadinha

3

1.Một ví dụ rất trực tiếp và dễ sử dụng, làm cho đối tượng được thông qua thực hiện Serializable.

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

2. bỏ qua đối tượng trong gói

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

3. chuyển đối tượng được chuyển từ gói dưới dạng Có thể hóa nối tiếp sau đó truyền đến Đối tượng.

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

0

Đây là một câu trả lời rất muộn cho câu hỏi của chính tôi, nhưng nó vẫn tiếp tục được chú ý, vì vậy tôi cảm thấy mình phải giải quyết nó. Hầu hết các câu trả lời này đều đúng và xử lý công việc một cách hoàn hảo. Tuy nhiên, nó phụ thuộc vào nhu cầu của ứng dụng. Câu trả lời này sẽ được sử dụng để mô tả hai giải pháp cho vấn đề này.

Ứng dụng

Đầu tiên là Ứng dụng , vì nó đã được nói nhiều nhất về câu trả lời ở đây. Ứng dụng là một đối tượng tốt để đặt các thực thể cần tham chiếu đến một Ngữ cảnh. Một `ServerSocket` chắc chắn sẽ cần một ngữ cảnh (đối với I / o tệp hoặc các bản cập nhật` ListAdapter` đơn giản). Cá nhân tôi thích con đường này hơn. Tôi thích các ứng dụng, chúng hữu ích cho việc truy xuất ngữ cảnh (vì chúng có thể được tạo tĩnh và không có khả năng gây rò rỉ bộ nhớ) và có vòng đời đơn giản.

Dịch vụ

Dịch vụ` là thứ hai. `` Dịch vụ` thực sự là lựa chọn tốt hơn cho vấn đề của tôi vì đó là những gì dịch vụ được thiết kế để làm:
Dịch vụ là một thành phần ứng dụng có thể thực hiện các hoạt động lâu dài trong
nền và không cung cấp giao diện người dùng.
Các dịch vụ gọn gàng ở chỗ chúng có vòng đời xác định hơn và dễ kiểm soát hơn. Hơn nữa, nếu cần, các dịch vụ có thể chạy bên ngoài ứng dụng (tức là khi khởi động). Điều này có thể cần thiết đối với một số ứng dụng hoặc chỉ là một tính năng gọn gàng.

Đây không phải là mô tả đầy đủ về nó, nhưng tôi đã để lại các liên kết đến tài liệu cho những ai muốn điều tra thêm. Nhìn chung thì Servicecàng tốt cho trường hợp tôi cần - chạy ServerSocket cho thiết bị SPP của tôi.


0

Tôi đã gặp câu hỏi này khi tôi đang tìm cách chuyển đối tượng Date. Trong trường hợp của tôi, như đã được đề xuất trong số các câu trả lời, tôi đã sử dụng Bundle.putSerializable () nhưng điều đó sẽ không hoạt động đối với một thứ phức tạp như DataManager được mô tả trong bài đăng gốc.

Đề xuất của tôi sẽ cho kết quả rất giống với việc đặt DataManager đã nói trong Ứng dụng hoặc biến nó thành Singleton là sử dụng Dependency Injection và liên kết DataManager với phạm vi Singleton và đưa DataManager vào bất cứ nơi nào cần thiết. Bạn không chỉ nhận được lợi ích của khả năng kiểm tra tăng lên mà bạn còn nhận được mã rõ ràng hơn mà không cần tất cả mã "chuyển phụ thuộc xung quanh giữa các lớp và hoạt động". (Robo) Guice rất dễ làm việc và khuôn khổ Dagger mới cũng có vẻ hứa hẹn.


1
Chà, với một cái gì đó như Ngày, bạn chỉ có thể chuyển giá trị dài. Nhưng, phần còn lại nghe tốt. Cảm ơn.
ahodder

0

một cách đơn giản khác để chuyển đối tượng bằng cách sử dụng một gói:

  • trong đối tượng lớp, tạo một danh sách tĩnh hoặc một cấu trúc dữ liệu khác bằng khóa
  • khi bạn tạo đối tượng, hãy đặt đối tượng đó vào danh sách / cấu trúc dữ liệu bằng khóa (ví dụ: dấu thời gian dài khi đối tượng được tạo)
  • tạo phương thức tĩnh getObject (phím dài) để lấy đối tượng từ danh sách
  • trong gói, hãy truyền khóa, vì vậy bạn có thể lấy đối tượng sau đó từ một điểm khác trong mã
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.