2012-06-12 29 views
12

hàng ngày có rất nhiều câu hỏi của type sau trên SO:Tại sao getters không được ưu tiên khi truy cập các biến giữa các hoạt động?

Làm thế nào để có được một biến từ Activity khác?

Câu trả lời thường khuyên bạn nên sử dụng SharedPreferences hoặc Intent.putExtra().

Với tôi, phương pháp getter là cách tiếp cận để truy cập biến từ lớp khác. Xét cho cùng, số Activity đang được xem xét là một lớp và các biến của nó là các thành viên của lớp.

Tại sao các phương thức getter không được ưa thích hơn các phương pháp như SharedPreferences hoặc tính năng bổ sung Intent?

tôi đang nói về những tình huống đơn giản mà đòi hỏi phải truy cập vào một biến giữa các hoạt động, ví dụ này:

class OneClass extends Activity { 
    int a; 

    .. 
    // some changes to a 
    .. 
} 

Và sau đó trong lớp khác (Activity):

class SomeOtherClass extends Activity { 
    .. 
    // trying to access a here 
    .. 
} 

Là một getter phương pháp một cách tiếp cận đúng ở đây, hay không?

Một lần nữa - Tôi không nói về các tình huống mà những thứ này thực sự là cách đi đúng. SharedPreferences để lưu trữ liên tục một lượng nhỏ dữ liệu, extras làm tài liệu nói: Điều này có thể được sử dụng để cung cấp thông tin mở rộng cho thành phần. Ví dụ, nếu chúng ta có một hành động để gửi một tin nhắn e-mail, chúng ta có thể cũng bao gồm các phần thêm dữ liệu vào đây để cung cấp cho một chủ đề, cơ thể vv


Theo một số các câu trả lời đã có khả năng chỉ ra rằng có những tình huống nhất định như không bảo đảm của Activity khác đang hoạt động, tôi đoán có nhiều lý do có thể xảy ra và chính xác hơn là tại sao mọi người đề xuất ý định và sở thích chung.

Trả lời

12

Câu trả lời cho câu hỏi của bạn là hai lần:

  • Đối với khía cạnh meta, mà thuộc về meta SO dù sao, nhiều lập trình viên người mới thấy Android, muốn viết các ứng dụng, và hút ở Java.
  • Đối với quesiton khác, thường sử dụng getter và setter sẽ không hoạt động, bởi vì bạn không thể chuyển đối tượng giữa các Hoạt động theo cách đơn giản. Trong khi bạn về mặt kỹ thuật có thể làm điều này với một Parcelable, nó không được khuyến khích, và cách tốt hơn là sử dụng một ý định để truyền dữ liệu giữa các thành phần ứng dụng.
  • Một điểm nổi bật khác là ứng dụng Android phải giữ một số lượng tối thiểu trạng thái bên trong các thành phần. Tôi nghĩ đây là một thành công lớn của Android. Nếu bạn nhìn vào các ứng dụng trên mạng, trung bình có rất ít trạng thái toàn cầu hơn các chương trình điển hình được viết bằng java. Các chương trình cũng nhỏ hơn, được mong đợi, nhưng thực tế là một hoạt động nguyên tử có thể biểu diễn trạng thái của một màn hình, và thực tế là bất kỳ màn hình đơn nào cũng sẽ không bền bỉ nhiều trạng thái trên toàn bộ ứng dụng, để phân tách hợp lý giữa các thành phần ứng dụng.
+0

Nó không chỉ là bạn, ai đó là downvoting tất cả các câu trả lời. – Malcolm

+0

@KristopherMicinski cảm ơn câu trả lời, điều đó cũng trong một thời gian ngắn, và điều đó nữa khi câu hỏi bị tấn công bởi người điều hành và cử tri xuống. Xóa rất nhiều thứ cho tôi. Cảm ơn một lần nữa. :-) –

5

Câu trả lời đơn giản là vì vòng đời Activity được hệ điều hành Android kiểm soát. Các hoạt động không giống như các lớp bình thường được khởi tạo bởi mã người dùng và được đảm bảo có sẵn cho đến khi chúng không còn được tham chiếu nữa.

4

Tôi tin rằng lý do Hoạt động không có getters và setter có liên quan đến vòng đời của một Hoạt động. Bạn thực sự không nên đảm bảo rằng các Hoạt động khác vẫn còn hoạt động vì nếu chúng không có trên màn hình, hệ thống có thể làm sạch chúng bất cứ lúc nào.

Tuy nhiên, để theo dõi mẫu của bạn, bạn mở rộng Ứng dụng và sử dụng getters và setters cho điều đó. How to declare global variables in Android?

+1

Có lẽ, nhưng việc sử dụng các biến toàn cầu là một ý tưởng xấu (và không được hiểu rõ) trong Android nói chung, và hy sinh phong cách Android cho một sở thích của phong cách java có vẻ khá giả tạo. –

+0

Ồ tôi đồng ý. Bạn biết rằng globals là xấu trong một lớp lập trình giới thiệu. Tôi chỉ cố gắng chỉ cho anh ta cách người ta sử dụng mẫu đó. Im kinh nghiệm của tôi mở rộng Ứng dụng tất cả mọi thứ thường kết thúc trong đó và nó trở nên rất lộn xộn khá nhanh chóng. Nhận xét tốt. Cảm ơn. –

2

Bạn nói đúng khi nói đến cấu trúc Lớp cơ bản. Tuy nhiên nếu, bạn xem xét vòng đời hoạt động và quản lý bộ nhớ giữ toàn bộ hoạt động còn sống để truy cập lượng dữ liệu nhỏ không hợp lý.

2

Tình huống hơi phức tạp hơn bạn đề xuất. Nếu bạn chỉ đơn giản là viết các lớp tồn tại bên trong vòng đời của một hoạt động, và bạn muốn họ truy cập một số thành viên của hoạt động, hơn bạn có thể dễ dàng sử dụng getters và mô hình điển hình java.

Điều đó đang được nói, Android không chứa một số cơ chế tự nhiên để truy cập vào phiên bản Hoạt động khác. Điều này có lẽ rất có chủ ý. Các hoạt động có nghĩa là hoạt động theo một cách khác biệt. Sự so sánh gần nhất là mỗi Activity có nghĩa là giống như một trang trên một trang web (vì vậy việc tham chiếu một thể hiện khác của một trang sẽ không có ý nghĩa nhiều).

2

Tôi không nghĩ rằng điều này là bằng cách nào đó duy nhất cho Android. Bất kỳ khung công tác dựa trên Java tương đối phức tạp nào đều có 'quy tắc' cấp cao hơn như thế này.

  • Swing hoặc AWT của Java hạn chế bạn gọi các phương thức nhất định từ các chủ đề nhất định.
  • Java ME hoạt động rất giống với Android theo khía cạnh này.
  • Trong Java EE - hãy quên chia sẻ mọi thứ trên Servlets hoặc EJB với static thành viên. Họ thậm chí không có trên cùng một cỗ máy.

Để trả lời trực tiếp câu hỏi của bạn: bạn không thể truy cập các đối tượng "tự do" giống như cách bạn có thể làm trong một chương trình Java đơn giản vì một số giả định phụ thuộc vào trong cùng một ClassLoader.

2

Điều này sẽ hoạt động hoàn hảo nếu chương trình có thể kiểm soát khi các hoạt động được tạo và hủy. Nhưng vấn đề là chúng được quản lý bởi hệ điều hành.

Bạn có thể lưu trữ tham chiếu đến hoạt động khác. Chỉ những gì sẽ xảy ra nếu nó bị phá hủy hoặc tái tạo? Bạn sẽ có một tham chiếu đến cá thể lớp có liên quan lâu hơn không có phương tiện đáng tin cậy để phát hiện trường hợp này.

Tùy chọn và mục đích được chia sẻ được sử dụng trong các trường hợp này vì chúng là độc lập nhà nước. Bất kể điều gì xảy ra với bất kỳ hoạt động nào, sở thích sẽ luôn luôn có sẵn trong trạng thái của chúng. Ý định cũng là một đối tượng tồn tại một mình và sẽ không bị cũ.

3

Chủ yếu là vì toàn bộ quá trình gửi mục đích không đơn giản như vậy. Một ý định có thể đi qua hệ thống, giữa các tiến trình vv .. trong ngắn hạn đối tượng bạn đã tạo không phải là đối tượng được nhận ở cuối (điều này có thể được chứng minh nếu cố gắng mở rộng một lớp ý định, gửi nó đến một hoạt động khác và cố gắng đưa nó trở lại lớp mở rộng của bạn ở đầu bên kia, nó đơn giản không phải là cùng một đối tượng).

Bây giờ tôi cũng thực sự ghét này, đó là lý do tại sao tôi đã tạo ra một số lớp cơ sở đó giúp tôi làm việc với ý định (tôi gọi họ BundleWrappers) mà sẽ làm việc một cái gì đó như thế này:

bạn tạo một POJO với getters/setters , bạn điền vào đối tượng đó và sử dụng nó theo bất kỳ cách nào bạn muốn,

sau đó khi thời gian chỉ được sắp xếp thành một con thỏ và deserialize nó vào cùng một đối tượng ở đầu kia.

thì bạn sẽ có cùng một đối tượng với getters và setters trong hoạt động khác.

Lý do chính là do bạn phải tìm cách theo dõi tất cả các khóa cho phần bổ sung và triển khai bổ sung để tuần tự hóa gói.

thậm chí với phương pháp của tôi, nó không dễ sử dụng, nhưng nó là tốt nhất tôi đã tìm thấy cho đến nay về hiệu suất và tổ chức đối tượng.

public abstract class BundleWrapper implements Parcelable { 

    protected static final String KEY_PARCELABLE = "key_parcelable"; 

    public static final String TAG = BundleWrapper.class.getSimpleName(); 

    public BundleWrapper() { 
     super(); 
    } 

    abstract Parcelable getParcelable(); 

    public Bundle toBundle(){ 
     final Bundle bundle = new Bundle(); 
     Parcelable parcelable = getParcelable(); 
     if (parcelable != null) { 
      bundle.setClassLoader(parcelable.getClass().getClassLoader()); 
      bundle.putParcelable(KEY_PARCELABLE, parcelable); 
     } 
     return bundle; 
    } 

    public static Object fromBundle(final Intent intent) { 
     return fromBundle(intent.getExtras()); 
    } 

    public static Object fromBundle(final Bundle bundle) { 
     if (bundle != null && bundle.containsKey(KEY_PARCELABLE)) { 
      bundle.setClassLoader(BundleWrapper.class.getClassLoader()); 
      return bundle.getParcelable(KEY_PARCELABLE); 
     } 
     return null; 
    } 

} 

Đây là lớp cơ sở của tôi, cho việc sử dụng nó, bạn chỉ đơn giản là mở rộng nó và thực hiện parcelable (phần chậm phát triển của quá trình :):

public class WebViewFragmentBundle extends BundleWrapper implements Parcelable { 

    public static final String TAG = WebViewFragmentBundle.class.getSimpleName(); 

    private String url; 
    public WebViewFragmentBundle() { 
     super(); 
    } 

    public WebViewFragmentBundle(Parcel source) { 
     this.url = source.readString(); 
    } 

    public String getUrl() { 
     return url; 
    } 


    public void setUrl(String url) { 
     this.url = url; 
    } 

    @Override 
    Parcelable getParcelable() { 
     return this; 
    } 

    @Override 
    public int describeContents() { 
     return 0; 
    } 

    @Override 
    public void writeToParcel(Parcel dest, int flags) { 
     dest.writeString(url); 
    } 

    public static final Parcelable.Creator<WebViewFragmentBundle> CREATOR = new Parcelable.Creator<WebViewFragmentBundle>() { 
     @Override 
     public WebViewFragmentBundle createFromParcel(Parcel source) { 
      return new WebViewFragmentBundle(source); 
     } 

     @Override 
     public WebViewFragmentBundle[] newArray(int size) { 
      return new WebViewFragmentBundle[size]; 
     } 
    }; 


} 

và trong một trường hợp sử dụng:

public static void launchAugmentedRealityActivityForResult(final Activity context, WebViewFragmentBundle wrapper) { 
     final Intent intent = new Intent(context, Augmented.class); 
     intent.putExtras(wrapper.toBundle()); 
     intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); 
     context.startActivityForResult(intent, AUGMENTED_RESULT_CODE); 
    } 

và truyền ở đầu bên kia như:

(WebViewFragmentBundle)BundleWrapper.fromBundle(getIntent()); 
2

Để sử dụng ví dụ của bạn, vấn đề đầu tiên bạn gặp phải là cách chuyển một tham chiếu đến một phiên bản OneClass tới SomeOtherClass. SomeOtherClass cần tham chiếu đến phiên bản OneClass để gọi oneClass.getVariable(). Không có cách nào dễ dàng để làm điều này bởi vì khi một Hoạt động bắt đầu một Hoạt động khác, hãy thực hiện bằng cách gọi số startActivity() và chuyển một Mục đích. Đó là cơ chế mà bạn có sẵn để truyền các tham số cho một Activity khi bạn khởi động nó, đó là lý do tại sao bạn nên sử dụng nó.

Theo ví dụ của bạn, một lựa chọn khác là sử dụng biến tĩnh (lớp) để giữ dữ liệu bạn muốn chuyển giữa các hoạt động. Vì vậy, bạn có thể làm một cái gì đó như thế này:

class OneClass extends Activity { 
    private static int a; 

    public static int getA() { 
     return a; 
    } 

    .. 
    // some changes to a 
    .. 
} 

class SomeOtherClass extends Activity { 
    .. 
    // trying to access a here 
    int myA = OneClass.getA(); 
} 

Tuy nhiên, điều này khá nhiều giả định rằng sẽ chỉ có một ví dụ của OneClass và đó là nơi tất cả phá vỡ xuống IMHO. Trong Android, các hoạt động được tạo và phá hủy khắp nơi. Có thể có một vài trường hợp hoạt động tại bất kỳ thời điểm nào và bạn không bao giờ biết số lượng bạn có hoặc hoạt động nào hiện đang hoạt động. Điều đó làm cho các biến tĩnh bên trong các lớp Activity khó có được quyền. Để truyền dữ liệu giữa các hoạt động, tôi sẽ sử dụng Extras in Intent, vì rõ ràng là bạn đang truyền dữ liệu khi bắt đầu một Activity. Đó là tự tài liệu.

Mặt khác, nếu bạn có dữ liệu thực sự toàn cầu cho toàn bộ ứng dụng của mình, thì tôi sẽ chỉ sử dụng biến tĩnh (lớp) trong một số lớp có thể truy cập trực tiếp từ tất cả các lớp. Một số người phân lớp Application để làm điều này, nhưng tài liệu chỉ ra rằng bạn không phải và nói chung bạn không phải làm như vậy. Bạn chỉ có thể làm điều gì đó như thế này:

public class Globals { 
    public static int a; // Publicly available, don't need getter 
} 

Sau đó, bất kỳ lớp chỉ có thể lưu trữ một cái gì đó trong Globals.a hay truy cập nó. Bạn không cần phải phân lớp Application cho việc này.

+0

Cảm ơn đoạn mã. Giải thích rất nhiều. Upvoted nó. Nhưng xin lỗi không thể chấp nhận nó. Cảm ơn một lần nữa. :-) –

0

Giải pháp: Hoạt động là một thành phần ứng dụng, nó có vòng đời và ngăn xếp ngược lại không giống như các lớp. Mặc dù chúng ta có thể vượt qua các đối tượng thông qua parceable và serialization nhưng nó không được khuyến khích. Chúng ta có thể truyền đối tượng thông qua gói trong đối tượng intent hoặc sử dụng các tùy chọn chia sẻ để truy cập đối tượng. Sử dụng getter sẽ không phải là một ý tưởng tốt. Hoặc bạn có thể tạo một Lớp không đổi riêng biệt và xác định varaible tĩnh ở đó và sau đó có thể truy cập nó.

Các vấn đề liên quan