Cách tốt nhất để chia sẻ dữ liệu giữa các hoạt động là gì?


239

Tôi có một hoạt động là hoạt động chính được sử dụng trong toàn bộ ứng dụng và nó có một số biến. Tôi có hai hoạt động khác mà tôi muốn có thể sử dụng dữ liệu từ hoạt động đầu tiên. Bây giờ tôi biết tôi có thể làm một cái gì đó như thế này:

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

Tuy nhiên tôi muốn chia sẻ nhiều biến số và một số biến có thể khá lớn nên tôi không muốn tạo các bản sao của chúng như trên.

Có cách nào để trực tiếp nhận và thay đổi các biến mà không cần sử dụng các phương thức get và set không? Tôi nhớ đã đọc một bài viết trên trang web dev của Google nói rằng điều này không được khuyến nghị cho hiệu suất trên Android.


2
Kể từ Android 2.3 (Gingerbread), việc tối ưu hóa get / set được thực hiện tự động bởi Dalvik; điều này chỉ có liên quan nếu bạn đang nhắm mục tiêu các phiên bản Android cũ hơn.
StellarVortex

Lưu ý rằng ví dụ này không sao chép dữ liệu chuỗi. Thay vào đó, nó tạo ra một tham chiếu đến cùng một đối tượng chuỗi.
Code-Apprentice

Thật khó tin, tại sao không có khả năng bắt đầu một hoạt động từ một hoạt động khác và vượt qua bất kỳ đối tượng phức tạp nào từ lần đầu tiên đến lần thứ hai? Không có tuần tự hóa, lưu đối tượng và tất cả những nỗ lực đó. Đây có phải là một lỗ hổng bảo mật hay lý do nào khác chống lại việc chuyển tham chiếu đối tượng nếu cả hai hoạt động trong cùng một ứng dụng? (Tôi hiểu nó khác nếu chúng ở trong các ứng dụng khác nhau)
Droidum


LiveData là giải pháp tốt nhất, gần đây nhất. Kiểm tra câu trả lời của tôi dưới đây.
Amir Uval

Câu trả lời:


475

Dưới đây là tổng hợp các cách phổ biến nhất để đạt được điều này :

  • Gửi dữ liệu trong ý định
  • Các trường tĩnh
  • HashMap của WeakReferences
  • Các đối tượng bền bỉ (sqlite, chia sẻ tùy chọn, tệp, v.v.)

TL; DR : có hai cách chia sẻ dữ liệu: truyền dữ liệu trong phần bổ sung của mục đích hoặc lưu nó ở nơi khác. Nếu dữ liệu là nguyên thủy, Chuỗi hoặc đối tượng do người dùng xác định: gửi dữ liệu đó như một phần của phần bổ sung ý định (đối tượng do người dùng xác định phải triển khai Parcelable). Nếu vượt qua các đối tượng phức tạp, hãy lưu một thể hiện trong một singleton ở một nơi khác và truy cập chúng từ hoạt động đã khởi chạy.

Một số ví dụ về cách thức và lý do thực hiện từng phương pháp:

Gửi dữ liệu bên trong ý định

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

Về hoạt động thứ hai:

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

Sử dụng phương pháp này nếu bạn đang truyền dữ liệu nguyên thủy hoặc Chuỗi . Bạn cũng có thể vượt qua các đối tượng thực hiện Serializable.

Mặc dù hấp dẫn, bạn nên suy nghĩ kỹ trước khi sử dụng Serializable: nó dễ bị lỗi và chậm kinh khủng. Vì vậy, nói chung: tránh xaSerializable nếu có thể. Nếu bạn muốn vượt qua các đối tượng phức tạp do người dùng xác định, hãy xem Parcelablegiao diện . Khó thực hiện hơn, nhưng nó có tốc độ tăng đáng kể so với Serializable.

Chia sẻ dữ liệu mà không cần lưu vào đĩa

Có thể chia sẻ dữ liệu giữa các hoạt động bằng cách lưu nó vào bộ nhớ do trong hầu hết các trường hợp, cả hai hoạt động đều chạy trong cùng một quy trình.

Lưu ý: đôi khi, khi người dùng rời khỏi hoạt động của bạn (mà không thoát khỏi nó), Android có thể quyết định hủy ứng dụng của bạn. Trong kịch bản như vậy, tôi đã gặp phải trường hợp Android cố gắng khởi chạy hoạt động cuối cùng bằng cách sử dụng ý định được cung cấp trước khi ứng dụng bị giết. Trong trường hợp này, dữ liệu được lưu trữ trong một singleton (của bạn hoặc Application) sẽ biến mất và điều tồi tệ có thể xảy ra. Để tránh những trường hợp như vậy, bạn hoặc duy trì các đối tượng vào đĩa hoặc kiểm tra dữ liệu trước khi sử dụng để đảm bảo nó hợp lệ.

Sử dụng một lớp đơn

Có một lớp để giữ dữ liệu:

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

Từ hoạt động ra mắt:

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

Sử dụng ứng dụng đơn

Ứng dụng singleton là một phiên bản android.app.Applicationđược tạo khi ứng dụng được khởi chạy. Bạn có thể cung cấp một tùy chỉnh bằng cách mở rộng 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;}
}

Trước khi ra mắt hoạt động:

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

Sau đó, từ hoạt động ra mắt:

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

Các trường tĩnh

Ý tưởng về cơ bản giống như singleton, nhưng trong trường hợp này bạn cung cấp quyền truy cập tĩnh vào dữ liệu:

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

Từ hoạt động ra mắt:

String data = DataHolder.getData();

HashMap của WeakReferences

Cùng một ý tưởng, nhưng cho phép trình thu gom rác loại bỏ các đối tượng không được ước tính (ví dụ: khi người dùng thoát khỏi hoạt động):

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

Trước khi ra mắt hoạt động:

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

Từ hoạt động ra mắt:

DataHolder.getInstance().retrieve(someId);

Bạn có thể hoặc không phải vượt qua id đối tượng bằng cách sử dụng các tính năng bổ sung của ý định. Tất cả phụ thuộc vào vấn đề cụ thể của bạn.

Kiên trì các đối tượng vào đĩa

Ý tưởng là lưu dữ liệu trong đĩa trước khi khởi chạy hoạt động khác.

Ưu điểm: bạn có thể khởi chạy hoạt động từ những nơi khác và, nếu dữ liệu đã được duy trì, nó sẽ hoạt động tốt.

Nhược điểm: nó cồng kềnh và mất nhiều thời gian hơn để thực hiện. Yêu cầu nhiều mã hơn và do đó có nhiều cơ hội giới thiệu lỗi hơn. Nó cũng sẽ chậm hơn nhiều.

Một số cách để duy trì các đối tượng bao gồm:


11
Tôi cho rằng đó không phải là cách "thông thường" đối với dữ liệu lớn hơn / phức tạp hơn. Việc sử dụng một singleton tĩnh hoặc đối tượng Ứng dụng dễ dàng hơn nhiều và nó hoạt động rất tốt. Bây giờ nói rằng OP đã sử dụng Chuỗi trong ví dụ, vì vậy, Intent là hoàn hảo và được ưa thích.
Charlie Collins

10
Serializable đã phát hiện có vấn đề nghiêm trọng về hiệu năng trên mô hình quy trình Android. Đó là lý do tại sao họ giới thiệu Parcelable. Đọc Parcelable thay vì serializable trong câu trả lời ở trên.
Subin Sebastian

3
Điều đó được thực hiện thông qua setResultphương pháp. Ngoài ra, trong trường hợp đó, hoạt động thứ cấp phải được gọi bằng startActivityForResultphương thức.
Cristian

2
Tóm tắt tuyệt vời! Liên quan đến vấn đề singletons bị phá hủy, có một giải pháp đơn giản cho các ứng dụng có nhiều hoạt động và đối tượng: Sử dụng một lớp con trong Activitysuốt và onCreate()kiểm tra bất kỳ trường tĩnh nào của singleton mà bạn điền khi khởi động ứng dụng. Nếu trường đó trống, quay lại hoạt động bắt đầu bằng cách sử dụng FLAG_ACTIVITY_CLEAR_TASKhoặc BroadcastReceiverđể giết các hoạt động khác.
Janosch

1
Tôi không khuyên bạn nên lưu dữ liệu trong lớp ứng dụng. Đọc thêm tại đây: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan

22

Những gì bạn có thể sử dụng:

  1. truyền dữ liệu giữa các hoạt động (như Cristian nói)
  2. sử dụng một lớp có nhiều biến tĩnh (vì vậy bạn có thể gọi chúng mà không cần phiên bản của lớp và không sử dụng getter / setter)
  3. Sử dụng cơ sở dữ liệu
  4. Tùy chọn chia sẻ

Những gì bạn chọn phụ thuộc vào nhu cầu của bạn. Có lẽ bạn sẽ sử dụng nhiều hơn một cách khi bạn có "rất nhiều"


1
Xin lưu ý rằng các số liệu thống kê được xóa trong quá trình chết
EpicPandaForce

@EpicPandaForce tất nhiên và cả khi thiết bị đã tắt.
WarrenFaith

1
Nhưng nếu thiết bị bị tắt, ứng dụng sẽ khởi động lại từ MAINhành động. Sau quá trình chết, bạn khởi động lại bất kỳ hoạt động nào được mở cuối cùng, đó có thể là một trang chi tiết ở đâu đó sâu trong ứng dụng.
EpicPandaForce

@EpicPandaForce dường như bạn đã bỏ lỡ sự trớ trêu của tôi ngay cả khi bạn là Cpt Rõ ràng.
WarrenFaith

16

Làm những gì google yêu cầu bạn làm! tại đây: http://developer.android.com/resource/faq/framework.html#3

  • Kiểu dữ liệu nguyên thủy
  • Đối tượng không cố định
  • Lớp đơn - yêu thích của tôi: D
  • Một trường / phương thức tĩnh công khai
  • HashMap của WeakReferences cho các đối tượng
  • Các đối tượng liên tục (Tùy chọn ứng dụng, Tệp, Trình tạo nội dung, SQLite DB)

1
Liên kết Google cho các đối tượng liên tục: developer.android.com/guide/topics/data/data-st
Storage.html

Chỉ có antipotype, lớp Singleton là mẫu thiết kế đầu tiên, một lớp có trường / phương thức tĩnh hoạt động rất giống như singletons và tạo cơ sở dữ liệu cho các đối tượng busniss vẫn tồn tại đôi khi không phải là điều tốt, tất nhiên đó không phải là lỗi của bạn và bạn có thể liệt kê cách để đạt được nó, nhưng tôi tự hỏi tại sao Google lại phức tạp như một thứ rất ngu ngốc, vấn đề về hiệu suất hay sao ?? !!!! hoặc tôi không hiểu cách Android ?? !!!!
La VloZ Merrill

liên kết bị hỏng :(
Shayan_Aryan 2/11/2016

14

"Tuy nhiên, tôi muốn chia sẻ rất nhiều biến số và một số biến có thể khá lớn nên tôi không muốn tạo ra các bản sao của chúng như trên."

Điều đó không tạo ra một bản sao (đặc biệt là với String , nhưng ngay cả các đối tượng cũng được truyền theo giá trị của tham chiếu, chứ không phải chính đối tượng và getter giống như vậy là tốt để sử dụng - có thể sử dụng tốt hơn các phương tiện khác vì chúng phổ biến và hiểu rõ). Các "huyền thoại hiệu suất" cũ hơn, chẳng hạn như không sử dụng getters và setters, vẫn có một số giá trị, nhưng cũng đã được cập nhật trong các tài liệu .

Nhưng nếu bạn không muốn làm điều đó, bạn cũng có thể đặt các biến công khai hoặc được bảo vệ trong GlobalState và truy cập chúng trực tiếp. Và, bạn có thể tạo một singleton tĩnh như đối tượng Ứng dụng JavaDoc chỉ ra :

Thông thường không cần phải phân lớp Ứng dụng. Trong hầu hết các tình huống, singletons tĩnh có thể cung cấp cùng chức năng theo cách mô đun hơn. Nếu singleton của bạn cần một bối cảnh toàn cầu (ví dụ để đăng ký máy thu quảng bá), chức năng truy xuất nó có thể được cung cấp một Ngữ cảnh sử dụng Context.getApplicationContext () khi lần đầu tiên xây dựng singleton.

Sử dụng dữ liệu Ý định , như các câu trả lời khác ở đây lưu ý là một cách khác để truyền dữ liệu, nhưng nó thường được sử dụng cho các dữ liệu nhỏ hơn và các loại đơn giản. Bạn có thể truyền dữ liệu lớn hơn / phức tạp hơn, nhưng nó liên quan nhiều hơn là chỉ sử dụng một đơn tĩnh. các ứng dụng đối tượng vẫn là yêu thích cá nhân của tôi để chia sẻ lớn hơn / dữ liệu phức tạp hơn không dai dẳng giữa các thành phần ứng dụng Android mặc dù (vì nó có một vòng đời được xác định rõ trong một ứng dụng Android).

Ngoài ra, như những người khác đã lưu ý, nếu dữ liệu trở nên rất phức tạp và cần phải kiên trì thì bạn cũng có thể sử dụng SQLite hoặc hệ thống tập tin.


1
Trên thực tế, tôi chỉ vấp phải điều này trong các tài liệu gần đây: developer.android.com/guide/appcill/faq/framework.html#3 . Đối với "các đối tượng phức tạp không liên tục", nó khuyên bạn nên sử dụng lớp Ứng dụng để tạo và phá bỏ một singleton tĩnh! Bằng cách đó, bạn có được Ứng dụng vòng đời được xác định rõ ràng cung cấp và dễ dàng sử dụng một singleton tĩnh.
Charlie Collins

2
phần đó của faq dường như đã bị xóa ngay bây giờ (tôi chỉ thấy "các đối tượng không liên tục" và không đề cập đến lớp Ứng dụng). Dù sao bạn có thể xây dựng?
Tony Chan

1
Hiện tại nó đang ở developer.android.com/guide/faq/framework.html#3 "Làm cách nào để tôi chuyển dữ liệu giữa các Hoạt động / Dịch vụ trong một ứng dụng?" Và không đề cập đến lớp Ứng dụng.
Jerry101

Tôi thích sử dụng một đối tượng Ứng dụng, tuân theo tiền lệ bạn đã đặt trong "Android đang hoạt động". Nhưng rất nhiều nhà tuyển dụng tiềm năng không thích nó khi họ thấy điều này trong các thách thức về mã. Họ có thể sai, nhưng họ ném xung quanh trọng lượng của họ. BTW: liên kết 'framework.html # 3 không còn hoạt động nữa.
Matt J.


4

Có một cách mới và tốt hơn để chia sẻ dữ liệu giữa các hoạt động và đó là LiveData . Đặc biệt lưu ý trích dẫn này từ trang của nhà phát triển Android:

Việc các đối tượng LiveData nhận thức được vòng đời có nghĩa là bạn có thể chia sẻ chúng giữa nhiều hoạt động, các đoạn và dịch vụ. Để giữ cho ví dụ đơn giản, bạn có thể triển khai lớp LiveData dưới dạng một singleton

Hàm ý của điều này là rất lớn - bất kỳ dữ liệu mô hình nào cũng có thể được chia sẻ trong một lớp đơn chung trong một LiveDatatrình bao bọc. Nó có thể được tiêm từ các hoạt động vào tương ứng của chúng ViewModelvì mục đích kiểm tra. Và bạn không còn cần phải lo lắng về các tài liệu tham khảo yếu để ngăn chặn rò rỉ bộ nhớ.


2

Sử dụng hàm băm của phương pháp tham chiếu yếu, được mô tả ở trên và trong http://developer.android.com/guide/faq/framework.html có vẻ có vấn đề với tôi. Làm thế nào là toàn bộ các mục được khai hoang, không chỉ là giá trị bản đồ? Phạm vi nào bạn phân bổ nó trong? Vì khung kiểm soát vòng đời Hoạt động, có một trong các Hoạt động tham gia sở hữu nó có nguy cơ xảy ra lỗi thời gian chạy khi chủ sở hữu bị hủy trước các máy khách của nó. Nếu Ứng dụng sở hữu nó, một số Hoạt động phải loại bỏ mục nhập một cách rõ ràng để tránh hashmap giữ các mục nhập có khóa hợp lệ và tham chiếu yếu được thu thập có khả năng được thu thập. Hơn nữa, khách hàng nên làm gì khi giá trị được trả về cho khóa là null?

Dường như với tôi rằng WeakHashMap thuộc sở hữu của Ứng dụng hoặc trong một singleton là lựa chọn tốt hơn. Một giá trị trong bản đồ được truy cập thông qua một đối tượng chính và khi không có tham chiếu mạnh đến khóa tồn tại (tức là tất cả các Hoạt động được thực hiện với khóa và những gì nó ánh xạ tới), GC có thể lấy lại mục nhập bản đồ.


2

Có nhiều cách khác nhau để chia sẻ dữ liệu giữa các hoạt động

1: Truyền dữ liệu giữa các hoạt động bằng Intent

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

2: Sử dụng từ khóa tĩnh, xác định biến là tĩnh công khai và sử dụng bất kỳ vị trí nào trong dự án

      public static int sInitialValue=0;

sử dụng bất cứ nơi nào trong dự án bằng classname.variableName;

3: Sử dụng cơ sở dữ liệu

nhưng quá trình dài bit của nó, bạn phải sử dụng truy vấn để chèn dữ liệu và lặp lại dữ liệu bằng con trỏ khi cần. Nhưng không có cơ hội mất dữ liệu mà không làm sạch bộ nhớ cache.

4: Sử dụng tùy chọn chia sẻ

dễ dàng hơn nhiều so với cơ sở dữ liệu. nhưng có một số hạn chế bạn không thể lưu các đối tượng ArrayList, List và tùy chọn.

5: Tạo trình thiết lập getter trong lớp Ứng dụng và truy cập bất kỳ vị trí nào trong dự án.

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

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

ở đây thiết lập và nhận được từ các hoạt động

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

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

1

Vâng, tôi có một vài ý tưởng, nhưng tôi không biết liệu chúng có phải là thứ bạn đang tìm kiếm không.

Bạn có thể sử dụng một dịch vụ chứa tất cả dữ liệu và sau đó chỉ cần liên kết các hoạt động của bạn với dịch vụ để lấy lại dữ liệu.

Hoặc đóng gói dữ liệu của bạn thành một thứ tự hoặc có thể phân loại và gắn chúng vào một gói và chuyển gói giữa các hoạt động.

Điều này có thể không phải là tất cả những gì bạn đang tìm kiếm, nhưng bạn cũng có thể thử sử dụng SharedPreferences hoặc một sở thích nói chung.

Dù bằng cách nào hãy cho tôi biết những gì bạn quyết định.


1

Giả sử bạn đang gọi hoạt động hai từ hoạt động một bằng cách sử dụng Ý định.
Bạn có thể truyền dữ liệu với aim.putExtra (),

Lấy cái này để bạn tham khảo. Gửi mảng với Intent.putExtra

Hy vọng đó là những gì bạn muốn.


1

Nếu mục đích của bạn là để gọi hoạt động khác từ Hoạt động hiện tại, bạn nên sử dụng Intents . Sự tập trung của bạn có thể ít vào dữ liệu bền bỉ hơn là chia sẻ nó trên cơ sở khi cần thiết.

Tuy nhiên, nếu bạn thực sự cần phải duy trì các giá trị này thì bạn có thể duy trì chúng trong một số loại tệp văn bản hoặc cơ sở dữ liệu có cấu trúc trên bộ nhớ cục bộ. Tệp thuộc tính, tệp XML hoặc tệp JSON có thể lưu trữ dữ liệu của bạn và dễ dàng được phân tích cú pháp trong quá trình tạo hoạt động. Đừng quên rằng bạn có SQLite trên tất cả các thiết bị Android, vì vậy bạn có thể lưu trữ chúng trong bảng cơ sở dữ liệu. Bạn cũng có thể sử dụng Bản đồ để lưu trữ các cặp khóa-giá trị và tuần tự hóa bản đồ vào bộ nhớ cục bộ, nhưng điều này có thể quá phức tạp để không hữu ích cho các cấu trúc dữ liệu đơn giản.


1

Tất cả các câu trả lời đã nói ở trên đều tuyệt vời ... Tôi chỉ thêm một câu chưa ai đề cập đến về việc lưu giữ dữ liệu thông qua các hoạt động và đó là sử dụng cơ sở dữ liệu SQLite tích hợp trong Android để duy trì dữ liệu liên quan ... Thực tế bạn có thể đặt cơ sở dữ liệuHelper ở trạng thái ứng dụng và gọi nó là cần thiết trong suốt quá trình kích hoạt .. Hoặc chỉ cần tạo một lớp trợ giúp và thực hiện các cuộc gọi DB khi cần ... Chỉ cần thêm một lớp khác để bạn xem xét ... Nhưng tất cả các câu trả lời khác sẽ đủ cũng như .. thực sự chỉ là sở thích


1

Chia sẻ dữ liệu giữa các ví dụ kích hoạt gửi email sau khi đăng nhập

"email" là tên có thể được sử dụng để tham chiếu giá trị cho hoạt động được yêu cầu

1 mã trên trang đăng nhập

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

2 mã trên trang chủ

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

1

Và nếu bạn muốn làm việc với đối tượng dữ liệu, hai điều này thực hiện rất quan trọng:

Nối tiếp vs Parcelable

  • Nối tiếp là một giao diện đánh dấu, ngụ ý người dùng không thể sắp xếp dữ liệu theo yêu cầu của họ. Vì vậy, khi đối tượng triển khai Java tuần tự hóa sẽ tự động tuần tự hóa nó.
  • Parcelable là giao thức serialization riêng của Android. Trong Parcelable, các nhà phát triển viết mã tùy chỉnh cho việc sắp xếp và không theo thứ tự. Vì vậy, nó tạo ra các đối tượng rác ít hơn so với Nối tiếp
  • Hiệu năng của Parcelable rất cao khi so sánh với serializable vì việc triển khai tùy chỉnh của nó Rất khuyến khích sử dụng cấy ghép Parcelable khi tuần tự hóa các đối tượng trong Android.

public class User implements Parcelable

kiểm tra thêm tại đây

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.