Sự khác biệt giữa các phương pháp khác nhau để có được một bối cảnh là gì?


390

Trong các bit khác nhau của mã Android tôi đã thấy:

 public class MyActivity extends Activity {
    public void method() {
       mContext = this;    // since Activity extends Context
       mContext = getApplicationContext();
       mContext = getBaseContext();
    }
 }

Tuy nhiên tôi không thể tìm thấy bất kỳ lời giải thích hợp lý nào trong số đó là thích hợp hơn, và trong những trường hợp nào nên được sử dụng.

Con trỏ đến tài liệu về điều này, và hướng dẫn về những gì có thể phá vỡ nếu chọn sai, sẽ được đánh giá cao.


2
Liên kết này có thể giúp bạn. Đi qua đây ..
Aju

Câu trả lời:


305

Tôi đồng ý rằng tài liệu này rất ít khi nói đến Ngữ cảnh trong Android, nhưng bạn có thể ghép một vài sự kiện từ nhiều nguồn khác nhau.

Bài đăng blog này trên blog chính thức của nhà phát triển Google Android được viết chủ yếu để giúp giải quyết rò rỉ bộ nhớ, nhưng cũng cung cấp một số thông tin tốt về bối cảnh:

Trong một ứng dụng Android thông thường, bạn thường có hai loại Bối cảnh, Hoạt động và Ứng dụng.

Đọc bài viết thêm một chút sẽ nói về sự khác biệt giữa hai và khi bạn có thể muốn xem xét sử dụng Bối cảnh ứng dụng ( Activity.getApplicationContext()) thay vì sử dụng bối cảnh Hoạt động this). Về cơ bản, bối cảnh Ứng dụng được liên kết với Ứng dụng và sẽ luôn giống nhau trong suốt vòng đời của ứng dụng của bạn, khi bối cảnh Hoạt động được liên kết với hoạt động và có thể bị phá hủy nhiều lần khi hoạt động bị phá hủy trong khi thay đổi hướng màn hình và như là.

Tôi không thể tìm thấy bất cứ điều gì về thời điểm sử dụng getBaseContext () ngoài bài đăng từ Dianne Hackborn, một trong những kỹ sư của Google làm việc trên SDK Android:

Đừng sử dụng getBaseContext (), chỉ sử dụng Ngữ cảnh bạn có.

Đó là từ một bài đăng trên nhóm tin tức dành cho nhà phát triển Android , bạn cũng có thể muốn xem xét việc đặt câu hỏi của mình ở đó, bởi vì một số ít người làm việc trên Android theo dõi thực tế nhóm đó và trả lời câu hỏi.

Vì vậy, nhìn chung có vẻ thích hợp hơn để sử dụng bối cảnh ứng dụng toàn cầu khi có thể.


13
Khi tôi có một hoạt động A có thể bắt đầu hoạt động B, đến lượt nó, có thể khởi động lại A bằng cờ CLEAR_TOP (và có thể lặp lại chu trình này nhiều lần) - tôi nên sử dụng bối cảnh nào trong trường hợp này để tránh xây dựng một vệt lớn bối cảnh tham chiếu? Diana nói rằng sử dụng 'this' chứ không phải getBaseContext, nhưng sau đó ... hầu hết các lần A sẽ được sử dụng lại nhưng có những tình huống khi một đối tượng mới cho A sẽ được tạo ra và sau đó A bị rò rỉ. Vì vậy, có vẻ như getBaseContext là lựa chọn phù hợp nhất cho hầu hết các trường hợp. Vậy thì không rõ tại sao Don't use getBaseContext(). Ai đó có thể làm rõ điều này?
JBM

2
làm thế nào một người có thể truy cập vào đối tượng bối cảnh bên trong một lớp không mở rộng Hoạt động?
Cole

1
@Cole, bạn có thể tạo một lớp, chúng ta sẽ gọi "exampleClass" ở đây, hàm tạo của nó lấy một đối tượng Ngữ cảnh và khởi tạo một biến thể hiện của lớp, "appContext". Sau đó, lớp Activity của bạn (hoặc bất kỳ Lớp nào khác cho vấn đề đó) có thể gọi một phương thức exampleClass sử dụng biến đối tượng "appContext" của exampleClass.
Archie1986

54

Đây là những gì tôi đã tìm thấy về việc sử dụng context:

1). Trong Activitychính nó, sử dụng thisđể tăng bố cục và menu, đăng ký menu ngữ cảnh, tiện ích khởi tạo, bắt đầu các hoạt động khác, tạo mới Intenttrong mộtActivity , ưu tiên khởi tạo hoặc các phương thức khác có sẵn trong một Activity.

Bố trí thổi phồng:

View mView = this.getLayoutInflater().inflate(R.layout.myLayout, myViewGroup);

Menu thổi phồng:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    this.getMenuInflater().inflate(R.menu.mymenu, menu);
    return true;
}

Đăng ký menu ngữ cảnh:

this.registerForContextMenu(myView);

Tiện ích khởi tạo:

TextView myTextView = (TextView) this.findViewById(R.id.myTextView);

Bắt đầu một Activity:

Intent mIntent = new Intent(this, MyActivity.class);
this.startActivity(mIntent);

Ưu tiên khởi tạo:

SharedPreferences mSharedPreferences = this.getPreferenceManager().getSharedPreferences();

2).Đối với lớp toàn ứng dụng, hãy sử dụng getApplicationContext()làm bối cảnh này cho tuổi thọ của ứng dụng.

Lấy tên của gói Android hiện tại:

public class MyApplication extends Application {    
    public static String getPackageName() {
        String packageName = null;
        try {
            PackageInfo mPackageInfo = getApplicationContext().getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), 0);
            packageName = mPackageInfo.packageName;
        } catch (NameNotFoundException e) {
            // Log error here.
        }
        return packageName;
    }
}

Ràng buộc một lớp toàn ứng dụng:

Intent mIntent = new Intent(this, MyPersistent.class);
MyServiceConnection mServiceConnection = new MyServiceConnection();
if (mServiceConnection != null) {
    getApplicationContext().bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}

3). Đối với Người nghe và các loại lớp Android khác (ví dụ ContentObserver), hãy sử dụng thay thế Ngữ cảnh như:

mContext = this;    // Example 1
mContext = context; // Example 2

ở đâu thishaycontext là bối cảnh của một lớp (Hoạt động, v.v.).

Activity thay thế bối cảnh:

public class MyActivity extends Activity {
    private Context mContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        mContext = this;
    }
}

Thay thế ngữ cảnh người nghe:

public class MyLocationListener implements LocationListener {
    private Context mContext;
    public MyLocationListener(Context context) {
        mContext = context;
    }
}

ContentObserver thay thế bối cảnh:

public class MyContentObserver extends ContentObserver {
    private Context mContext;
    public MyContentObserver(Handler handler, Context context) {
        super(handler);
        mContext = context;
    }
}

4) . Dành choBroadcastReceiver (bao gồm cả phần thu được nhúng / nhúng), hãy sử dụng bối cảnh riêng của người nhận.

Bên ngoài BroadcastReceiver:

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (action.equals(Intent.ACTION_SCREEN_OFF)) {
            sendReceiverAction(context, true);
        }
        private static void sendReceiverAction(Context context, boolean state) {
            Intent mIntent = new Intent(context.getClass().getName() + "." + context.getString(R.string.receiver_action));
            mIntent.putExtra("extra", state);
            context.sendBroadcast(mIntent, null);
        }
    }
}

Inline / Embedded BroadcastReceiver:

public class MyActivity extends Activity {
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final boolean connected = intent.getBooleanExtra(context.getString(R.string.connected), false);
            if (connected) {
                // Do something.
            }
        }
    };
}

5). Đối với Dịch vụ, hãy sử dụng bối cảnh riêng của dịch vụ.

public class MyService extends Service {
    private BroadcastReceiver mBroadcastReceiver;
    @Override
    public void onCreate() {
        super.onCreate();
        registerReceiver();
    }
    private void registerReceiver() {
        IntentFilter mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        this.mBroadcastReceiver = new MyBroadcastReceiver();
        this.registerReceiver(this.mBroadcastReceiver, mIntentFilter);
    } 
}

6). Đối với bánh mì nướng, thường sử dụnggetApplicationContext() , nhưng nếu có thể, hãy sử dụng bối cảnh được truyền từ Hoạt động, Dịch vụ, v.v.

Sử dụng bối cảnh của ứng dụng:

Toast mToast = Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG);
mToast.show();

Sử dụng bối cảnh được truyền từ một nguồn:

public static void showLongToast(Context context, String message) {
    if (context != null && message != null) {
        Toast mToast = Toast.makeText(context, message, Toast.LENGTH_LONG);
        mToast.show();
    }
}

Và cuối cùng, không sử dụng getBaseContext() theo lời khuyên của các nhà phát triển khung của Android.

CẬP NHẬT: Thêm ví dụ về Contextviệc sử dụng.


1
Thay vì mContext người ta có thể sử dụng OuterClass.this; xem bình luận trong stackoverflow.com/questions/9605459/ khăn
Paul Verest

3
+1 cho câu trả lời hữu ích như vậy! Tôi đồng ý rằng câu trả lời được chấp nhận là tốt như câu trả lời được chấp nhận, nhưng thánh molly câu trả lời này là siêu thông tin! Cảm ơn bạn vì tất cả những ví dụ đó, họ đã giúp tôi hiểu rõ hơn về việc sử dụng bối cảnh nói chung. Tôi thậm chí đã sao chép câu trả lời của bạn vào một tệp văn bản trên máy của tôi làm tài liệu tham khảo.
Ryan

13

Tôi đọc chủ đề này vài ngày trước, tự hỏi mình câu hỏi tương tự. Quyết định của tôi sau khi đọc điều này rất đơn giản: luôn luôn sử dụng applicationContext.

Tuy nhiên, tôi đã gặp phải một vấn đề với điều này, tôi đã dành một vài giờ để tìm thấy nó và một vài giây để giải quyết nó ... (thay đổi một từ ...)

Tôi đang sử dụng LayoutInflater để tăng cường chế độ xem có chứa Spinner.

Vì vậy, đây là hai khả năng:

1)

    LayoutInflater layoutInflater = LayoutInflater.from(this.getApplicationContext());

2)

    LayoutInflater layoutInflater = LayoutInflater.from(this.getBaseContext());

Sau đó, tôi đang làm một cái gì đó như thế này:

    // managing views part
    View view = ContactViewer.mLayoutInflater.inflate(R.layout.aViewContainingASpinner, theParentView, false);
    Spinner spinner = (Spinner) view.findViewById(R.id.theSpinnerId);
    String[] myStringArray = new String[] {"sweet","love"};

    // managing adapter part
    // The context used here don't have any importance -- both work.
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this.getApplicationContext(), myStringArray, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);

    theParentView.addView(view);

Điều tôi nhận thấy: Nếu bạn khởi tạo linearLayout của mình bằng applicationContext, thì khi bạn nhấp vào công cụ quay vòng trong hoạt động của mình, bạn sẽ có một ngoại lệ chưa được phát hiện, đến từ máy ảo dalvik (không phải từ mã của bạn, đó là lý do tôi đã chi tiêu rất nhiều thời gian để tìm đâu là sai lầm của tôi ...).

Nếu bạn sử dụng baseContext, thì không sao, menu ngữ cảnh sẽ mở và bạn sẽ có thể chọn trong số các lựa chọn của mình.

Vì vậy, đây là kết luận của tôi: Tôi cho rằng (tôi chưa thử nghiệm thêm) so với baseContext được yêu cầu khi xử lý bối cảnh trong Hoạt động của bạn ...

Thử nghiệm đã được thực hiện mã hóa với API 8 và được thử nghiệm trên HTC Desire, android 2.3.3.

Tôi hy vọng bình luận của tôi đã không làm bạn chán cho đến nay, và chúc bạn mọi điều tốt đẹp nhất. Chúc mừng mã hóa ;-)


Tôi đã luôn sử dụng "cái này" khi tạo chế độ xem trong một hoạt động. Trên cơ sở rằng nếu hoạt động khởi động lại, các khung nhìn được làm lại và có thể có một bối cảnh mới sẽ được sử dụng để tạo lại các lượt xem. Hạn chế như được đăng trong blog của nhà phát triển là trong khi ImageView bị phá hủy thì có thể vẽ / bitmap được sử dụng có thể treo trên bối cảnh đó. Tuy nhiên, đó là những gì tôi làm vào lúc này. Về mã ở nơi khác trong ứng dụng (các lớp thông thường) Tôi chỉ sử dụng bối cảnh ứng dụng vì nó không đặc trưng cho bất kỳ hoạt động hoặc thành phần UI nào.
JonWillis

6

Đầu tiên, tôi đồng ý rằng chúng ta nên sử dụng appcontext bất cứ khi nào có thể. sau đó "cái này" trong hoạt động. Tôi chưa bao giờ có nhu cầu về basecontext.

Trong các thử nghiệm của tôi, trong hầu hết các trường hợp, chúng có thể được hoán đổi cho nhau. Trong hầu hết các trường hợp, lý do bạn muốn nắm giữ ngữ cảnh là để truy cập các tệp, tùy chọn, cơ sở dữ liệu, v.v. Những dữ liệu này cuối cùng được phản ánh dưới dạng các tệp trong thư mục dữ liệu riêng tư của ứng dụng (/ data / data /). Cho dù bạn sử dụng bối cảnh nào, chúng sẽ được ánh xạ vào cùng thư mục / tệp để bạn ổn.

Đó là những gì tôi quan sát được. Có thể có những trường hợp bạn nên phân biệt chúng.


Tôi đã cần cơ sở để thiết lập ngôn ngữ ứng dụng trên toàn cầu khi khởi động (khi ngôn ngữ này không khớp với ngôn ngữ mặc định của điện thoại).
Tina

3

Trong một số trường hợp, bạn có thể sử dụng bối cảnh Hoạt động trên bối cảnh ứng dụng khi chạy một cái gì đó trong một luồng. Khi luồng hoàn thành thực thi và bạn cần trả kết quả trở lại hoạt động của người gọi, bạn cần bối cảnh đó với một trình xử lý.

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);

2

Nói một cách đơn giản

getApplicationContext()như tên phương thức đề xuất sẽ làm cho ứng dụng của bạn biết về các chi tiết rộng của ứng dụng mà bạn có thể truy cập từ bất kỳ đâu trong ứng dụng. Vì vậy, bạn có thể sử dụng điều này trong ràng buộc dịch vụ, đăng ký phát sóng, vv Application contextsẽ tồn tại cho đến khi ứng dụng thoát.

getActivity()hoặc thissẽ làm cho ứng dụng của bạn biết về màn hình hiện tại cũng hiển thị chi tiết cấp độ ứng dụng được cung cấp bởi application context. Vì vậy, bất cứ điều gì bạn muốn biết về màn hình hiện tại như thế nào Window ActionBar Fragementmangervà có sẵn với bối cảnh này. Về cơ bản và Activitymở rộng Context. Bối cảnh này sẽ tồn tại cho đến khi thành phần hiện tại (hoạt động) còn sống


1

Sự nhầm lẫn bắt nguồn từ thực tế là có rất nhiều cách để truy cập vào Bối cảnh, với (trên bề mặt) không có sự khác biệt rõ rệt. Dưới đây là bốn trong số những cách phổ biến nhất bạn có thể truy cập vào Ngữ cảnh trong một Hoạt động.

getContext()
getBaseContext()
getApplicationContext()
getActionBar().getThemedContext() //new

Bối cảnh là gì? Cá nhân tôi thích nghĩ về Bối cảnh là trạng thái của ứng dụng của bạn tại bất kỳ thời điểm nào. Bối cảnh ứng dụng đại diện cho cấu hình toàn cầu hoặc cơ sở của ứng dụng của bạn và Hoạt động hoặc Dịch vụ có thể dựa trên ứng dụng đó và thể hiện một thể hiện cấu hình của Ứng dụng của bạn hoặc trạng thái chuyển tiếp cho ứng dụng đó.

Nếu bạn nhìn vào nguồn cho android.content.Context, bạn sẽ thấy rằng Ngữ cảnh là một lớp trừu tượng và các nhận xét về lớp như sau:

Giao diện thông tin toàn cầu về một môi trường ứng dụng. Đây là một lớp trừu tượng có triển khai được cung cấp bởi hệ thống Android. Nó cho phép truy cập vào application-specificcác tài nguyên và các lớp, cũng như các cuộc gọi lên cho các application-levelhoạt động như khởi chạy các hoạt động, phát sóng và nhận ý định, v.v. tài nguyên. Tài nguyên cấp ứng dụng có thể đang truy cập vào những thứ như Tài nguyên chuỗi [getResources()]hoặc tài sản [getAssets()]và tài nguyên cấp hệ thống là bất cứ thứ gì bạn truy cậpContext.getSystemService().

Như một vấn đề thực tế, hãy xem các ý kiến ​​về các phương pháp và chúng dường như củng cố quan niệm này:

getSystemService(): Trả lại tay cầm cho một system-leveldịch vụ theo tên. Lớp của đối tượng trả về thay đổi theo tên được yêu cầu. getResources(): Trả về một thể hiện Tài nguyên cho gói ứng dụng của bạn. getAssets(): Trả về một thể hiện Tài nguyên cho gói ứng dụng của bạn. Có thể chỉ ra rằng trong lớp trừu tượng Bối cảnh, tất cả các phương thức trên đều trừu tượng! Chỉ có một phiên bản của getSystemService (Class) có triển khai và gọi một phương thức trừu tượng. Điều này có nghĩa là, việc thực hiện cho những điều này nên được cung cấp chủ yếu bởi các lớp thực hiện, bao gồm:

ContextWrapper
Application
Activity
Service
IntentService

Nhìn vào tài liệu API, hệ thống phân cấp của các lớp trông như thế này:

Bối cảnh

| - Bối cảnh

| - - Ứng dụng

| - - ContextThemeWrapper

| - - - - Hoạt động

| - - Dịch vụ

| - - - Dịch vụ ý định

Vì chúng tôi biết rằng Contextbản thân nó không cung cấp bất kỳ cái nhìn sâu sắc nào, chúng tôi di chuyển xuống cây và nhìn vào ContextWrappervà nhận ra rằng cũng không có nhiều ở đó. Vì Ứng dụng mở rộng ContextWrapper, nên không có gì nhiều để xem xét cả vì nó không ghi đè lên việc triển khai được cung cấp bởi ContextWrapper. Điều này có nghĩa là việc triển khai cho Ngữ cảnh được cung cấp bởi HĐH và bị ẩn khỏi API. Bạn có thể xem triển khai cụ thể cho Ngữ cảnh bằng cách xem nguồn cho lớp ContextImpl.


0

Tôi chỉ sử dụng cái này và getBaseContextkhi nướng từ một onClick(rất xanh cho cả Java và Android). Tôi sử dụng điều này khi clicker của tôi trực tiếp trong hoạt động và phải sử dụng getBaseContexttrong một clicker ẩn danh bên trong. Tôi đoán đó là khá nhiều mánh khóe getBaseContext, có lẽ nó đang trả lại bối cảnh của hoạt động mà lớp bên trong đang che giấu.


1
Điều này là sai, nó đang trả về bối cảnh cơ bản của chính hoạt động. Để có được hoạt động (hoạt động bạn muốn sử dụng làm bối cảnh) từ một lớp bên trong ẩn danh, hãy sử dụng một cái gì đó như MyActivity.this. Sử dụng bối cảnh cơ sở như bạn mô tả có thể sẽ không gây ra vấn đề nhưng nó là sai.
nickmartens1980
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.