Có cách nào để lấy Context
cá thể hiện tại bên trong một phương thức tĩnh không?
Tôi đang tìm kiếm theo cách đó bởi vì tôi ghét lưu phiên bản 'Bối cảnh' mỗi khi nó thay đổi.
Context
, thì có thể có một cách tốt hơn để thiết kế mã.
Có cách nào để lấy Context
cá thể hiện tại bên trong một phương thức tĩnh không?
Tôi đang tìm kiếm theo cách đó bởi vì tôi ghét lưu phiên bản 'Bối cảnh' mỗi khi nó thay đổi.
Context
, thì có thể có một cách tốt hơn để thiết kế mã.
Câu trả lời:
Làm cái này:
Trong tệp Bản kê khai Android, khai báo như sau.
<application android:name="com.xyz.MyApplication">
</application>
Sau đó viết lớp:
public class MyApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyApplication.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApplication.context;
}
}
Bây giờ mọi nơi gọi MyApplication.getAppContext()
để có được bối cảnh ứng dụng của bạn tĩnh.
static context
biến này là volatile
?
Phần lớn các ứng dụng muốn có một phương thức thuận tiện để lấy bối cảnh ứng dụng tạo lớp riêng của chúng mở rộng android.app.Application
.
HƯỚNG DẪN
Bạn có thể thực hiện điều này bằng cách trước tiên tạo một lớp trong dự án của bạn như sau:
import android.app.Application;
import android.content.Context;
public class App extends Application {
private static Application sApplication;
public static Application getApplication() {
return sApplication;
}
public static Context getContext() {
return getApplication().getApplicationContext();
}
@Override
public void onCreate() {
super.onCreate();
sApplication = this;
}
}
Sau đó, trong AndroidManifest của bạn, bạn nên chỉ định tên của lớp trong thẻ của AndroidManifest.xml:
<application
...
android:name="com.example.App" >
...
</application>
Sau đó, bạn có thể truy xuất bối cảnh ứng dụng trong bất kỳ phương thức tĩnh nào bằng cách sử dụng như sau:
public static void someMethod() {
Context context = App.getContext();
}
CẢNH BÁO
Trước khi thêm một cái gì đó như trên vào dự án của bạn, bạn nên xem xét tài liệu nói gì:
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 chức năng tương tự 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ỬA CHỮA
Ngoài ra còn có một cách khác để có được bối cảnh ứng dụng bằng cách sử dụng sự phản chiếu. Sự phản chiếu thường bị xem thường trong Android và cá nhân tôi nghĩ rằng điều này không nên được sử dụng trong sản xuất.
Để lấy lại bối cảnh ứng dụng, chúng ta phải gọi một phương thức trên một lớp ẩn ( ActivityThread ) đã có sẵn từ API 1:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.ActivityThread")
.getMethod("currentApplication").invoke(null, (Object[]) null);
}
Có thêm một lớp ẩn ( AppGlobals ) cung cấp một cách để có được bối cảnh ứng dụng theo cách tĩnh. Nó được sử dụng bối cảnh ActivityThread
để thực sự không có sự khác biệt giữa phương pháp sau và phương pháp được đăng ở trên:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.AppGlobals")
.getMethod("getInitialApplication").invoke(null, (Object[]) null);
}
Chúc mừng mã hóa!
Giả sử chúng ta đang nói về việc lấy Bối cảnh ứng dụng, tôi đã triển khai nó theo đề xuất của @Rohit Ghatol Ứng dụng mở rộng. Điều gì đã xảy ra sau đó, không có gì đảm bảo rằng bối cảnh được truy xuất theo cách như vậy sẽ luôn là không có giá trị. Tại thời điểm bạn cần, thường là vì bạn muốn khởi tạo một người trợ giúp hoặc nhận tài nguyên mà bạn không thể trì hoãn kịp thời; xử lý các trường hợp null sẽ không giúp bạn. Vì vậy, tôi hiểu rằng về cơ bản tôi đã chiến đấu chống lại kiến trúc Android, như đã nêu trong các tài liệu
Lưu ý: 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á), hãy bao gồm Context.getApplicationContext () làm đối số Ngữ cảnh khi gọi phương thức getInstance () của singleton.
và được giải thích bởi Dianne Hackborn
Lý do duy nhất khiến Ứng dụng tồn tại như một thứ bạn có thể rút ra là vì trong quá trình phát triển trước 1.0, một trong những nhà phát triển ứng dụng của chúng tôi đã liên tục làm phiền tôi về việc cần phải có một đối tượng ứng dụng cấp cao nhất mà họ có thể xuất phát để họ có thể có "bình thường hơn" "Đối với họ mô hình ứng dụng, và cuối cùng tôi đã nhượng bộ. Tôi sẽ hối hận mãi mãi về cái đó. :)
Cô cũng đang đề xuất giải pháp cho vấn đề này:
Nếu những gì bạn muốn là một số trạng thái toàn cầu có thể được chia sẻ trên các phần khác nhau của ứng dụng của bạn, hãy sử dụng một singleton. [...] Và điều này dẫn đến cách bạn nên quản lý những thứ này một cách tự nhiên hơn - khởi tạo chúng theo yêu cầu.
Vì vậy, những gì tôi đã làm là thoát khỏi việc mở rộng Ứng dụng và chuyển trực tiếp ngữ cảnh đến getInstance () của trình trợ giúp đơn lẻ, đồng thời lưu tham chiếu đến ngữ cảnh ứng dụng trong hàm tạo riêng:
private static MyHelper instance;
private final Context mContext;
private MyHelper(@NonNull Context context) {
mContext = context.getApplicationContext();
}
public static MyHelper getInstance(@NonNull Context context) {
synchronized(MyHelper.class) {
if (instance == null) {
instance = new MyHelper(context);
}
return instance;
}
}
người gọi sau đó sẽ chuyển một bối cảnh cục bộ cho người trợ giúp:
Helper.getInstance(myCtx).doSomething();
Vì vậy, để trả lời đúng câu hỏi này: có nhiều cách để truy cập Bối cảnh ứng dụng một cách tĩnh, nhưng tất cả chúng đều không được khuyến khích và bạn nên chuyển ngữ cảnh cục bộ sang getInstance () của singleton.
Đối với bất kỳ ai quan tâm, bạn có thể đọc phiên bản chi tiết hơn tại blog fwd
getInstance(ctx)
. Bạn có một gốc instance
loại GC MyHelper
, có một trường mContext
loại riêng Context
, tham chiếu đến bối cảnh ứng dụng được thu thập thông qua bối cảnh được truyền tới getInstance()
. instance
không bao giờ được đặt lần thứ hai, cũng không bị xóa, do đó, GC sẽ không bao giờ bắt được appcontext được tham chiếu bởi instance
. Bạn không rò rỉ bất kỳ hoạt động nào vì vậy IMO chi phí thấp.
this
ở Application.onCreate()
, mà làm cho câu trả lời được chấp nhận hơn.
Không, tôi không nghĩ là có. Thật không may, bạn bị kẹt khi gọi getApplicationContext()
từ Activity
hoặc một trong các lớp con khác của Context
. Ngoài ra, này câu hỏi có phần liên quan.
Đây là một cách không có giấy tờ để nhận Ứng dụng (là Ngữ cảnh) từ bất kỳ đâu trong luồng UI. Nó dựa vào phương thức tĩnh ẩn ActivityThread.currentApplication()
. Nó nên hoạt động ít nhất trên Android 4.x.
try {
final Class<?> activityThreadClass =
Class.forName("android.app.ActivityThread");
final Method method = activityThreadClass.getMethod("currentApplication");
return (Application) method.invoke(null, (Object[]) null);
} catch (final ClassNotFoundException e) {
// handle exception
} catch (final NoSuchMethodException e) {
// handle exception
} catch (final IllegalArgumentException e) {
// handle exception
} catch (final IllegalAccessException e) {
// handle exception
} catch (final InvocationTargetException e) {
// handle exception
}
Lưu ý rằng phương thức này có thể trả về null, ví dụ như khi bạn gọi phương thức bên ngoài luồng UI hoặc ứng dụng không bị ràng buộc với luồng.
Vẫn tốt hơn khi sử dụng giải pháp của @RohitGhatol nếu bạn có thể thay đổi mã Ứng dụng.
Nó phụ thuộc vào những gì bạn đang sử dụng bối cảnh cho. Tôi có thể nghĩ về ít nhất một nhược điểm của phương pháp đó:
Nếu bạn đang cố gắng tạo một AlertDialog
với AlertDialog.Builder
, Application
bối cảnh sẽ không hoạt động. Tôi tin rằng bạn cần bối cảnh cho hiện tại Activity
...
Cách của Kotlin :
Rõ ràng:
<application android:name="MyApplication">
</application>
MyApplication.kt
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: MyApplication
private set
}
}
Sau đó bạn có thể truy cập vào tài sản thông qua MyApplication.instance
Nếu bạn mở để sử dụng RoboGuice , bạn có thể đưa ngữ cảnh vào bất kỳ lớp nào bạn muốn. Đây là một ví dụ nhỏ về cách thực hiện với RoboGuice 2.0 (bản beta 4 tại thời điểm viết bài này)
import android.content.Context;
import android.os.Build;
import roboguice.inject.ContextSingleton;
import javax.inject.Inject;
@ContextSingleton
public class DataManager {
@Inject
public DataManager(Context context) {
Properties properties = new Properties();
properties.load(context.getResources().getAssets().open("data.properties"));
} catch (IOException e) {
}
}
}
Tôi đã sử dụng điều này tại một số điểm:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
Đây là một bối cảnh hợp lệ tôi đã sử dụng để nhận các dịch vụ hệ thống và làm việc.
Nhưng, tôi chỉ sử dụng nó trong các sửa đổi khung / cơ sở và không thử nó trong các ứng dụng Android.
Một cảnh báo mà bạn phải biết: Khi đăng ký máy thu phát với bối cảnh này, nó sẽ không hoạt động và bạn sẽ nhận được:
java.lang.SecurityException: Đưa ra gói người gọi android không chạy trong tiến trình ProcessRecord
open class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: MyApp
fun getContext(): Context? {
return mInstance.applicationContext
}
}
}
và có được bối cảnh như
MyApp.mInstance
hoặc là
MyApp.getContext()
Bạn có thể sử dụng như sau:
MainActivity.this.getApplicationContext();
MainActivity.java:
...
public class MainActivity ... {
static MainActivity ma;
...
public void onCreate(Bundle b) {
super...
ma=this;
...
Bất kỳ lớp nào khác:
public ...
public ANY_METHOD... {
Context c = MainActivity.ma.getApplicationContext();
Nếu bạn không muốn sửa đổi tệp kê khai, bạn có thể lưu trữ ngữ cảnh theo cách thủ công trong một biến tĩnh trong hoạt động ban đầu của mình:
public class App {
private static Context context;
public static void setContext(Context cntxt) {
context = cntxt;
}
public static Context getContext() {
return context;
}
}
Và chỉ đặt bối cảnh khi hoạt động (hoặc hoạt động) của bạn bắt đầu:
// MainActivity
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set Context
App.setContext(getApplicationContext());
// Other stuff
}
Lưu ý: Giống như tất cả các câu trả lời khác, đây là một rò rỉ bộ nhớ tiềm năng.
Theo nguồn này, bạn có thể có được Ngữ cảnh của riêng mình bằng cách mở rộng ContextWrapper
public class SomeClass extends ContextWrapper {
public SomeClass(Context base) {
super(base);
}
public void someMethod() {
// notice how I can use "this" for Context
// this works because this class has it's own Context just like an Activity or Service
startActivity(this, SomeRealActivity.class);
//would require context too
File cacheDir = getCacheDir();
}
}
Việc triển khai ủy quyền của Ngữ cảnh chỉ đơn giản là ủy thác tất cả các cuộc gọi của nó đến một Ngữ cảnh khác. Có thể được phân lớp để sửa đổi hành vi mà không thay đổi Ngữ cảnh gốc.
Nếu bạn vì một lý do nào đó muốn bối cảnh Ứng dụng trong bất kỳ lớp nào, không chỉ những ứng dụng / hoạt động mở rộng, có thể cho một số lớp nhà máy hoặc người trợ giúp. Bạn có thể thêm các singleton sau vào ứng dụng của bạn.
public class GlobalAppContextSingleton {
private static GlobalAppContextSingleton mInstance;
private Context context;
public static GlobalAppContextSingleton getInstance() {
if (mInstance == null) mInstance = getSync();
return mInstance;
}
private static synchronized GlobalAppContextSingleton getSync() {
if (mInstance == null) mInstance =
new GlobalAppContextSingleton();
return mInstance;
}
public void initialize(Context context) {
this.context = context;
}
public Context getApplicationContext() {
return context;
}
}
sau đó khởi tạo nó trong onCreate của lớp ứng dụng của bạn với
GlobalAppContextSingleton.getInstance().initialize(this);
sử dụng nó bất cứ nơi nào bằng cách gọi
GlobalAppContextSingleton.getInstance().getApplicationContext()
Tôi không đề xuất cách tiếp cận này cho bất cứ điều gì trừ bối cảnh ứng dụng. Vì nó có thể gây rò rỉ bộ nhớ.
Tôi sử dụng một biến thể của mẫu thiết kế Singleton để giúp tôi điều này.
import android.app.Activity;
import android.content.Context;
public class ApplicationContextSingleton {
private static Activity gContext;
public static void setContext( Activity activity) {
gContext = activity;
}
public static Activity getActivity() {
return gContext;
}
public static Context getContext() {
return gContext;
}
}
Sau đó tôi gọi hoạt độngApplicationContextSingleton.setContext( this );
của mình.onCreate () và trong onDestroy () ;ApplicationContextSingleton.setContext( null );
Tôi vừa phát hành một khung lấy cảm hứng từ jQuery cho Android có tên Vapor API nhằm mục đích làm cho việc phát triển ứng dụng trở nên đơn giản hơn.
Lớp $
mặt tiền trung tâm duy trì một WeakReference
(liên kết đến bài đăng blog Java tuyệt vời về điều này của Ethan Nicholas) đến Activity
bối cảnh hiện tại mà bạn có thể truy xuất bằng cách gọi:
$.act()
Một WeakReference
duy trì một tài liệu tham khảo mà không ngăn chặn bộ sưu tập rác lấy lại đối tượng ban đầu, vì vậy bạn không nên có vấn đề với rò rỉ bộ nhớ.
Nhược điểm của khóa học là bạn gặp rủi ro $.act()
có thể trả về null. Mặc dù vậy, tôi chưa bắt gặp kịch bản này, vì vậy có lẽ đó chỉ là một rủi ro tối thiểu, đáng nói.
Bạn cũng có thể đặt bối cảnh theo cách thủ công nếu bạn không sử dụng VaporActivity
làm Activity
lớp của mình :
$.act(Activity);
Ngoài ra, phần lớn khung API Vapor sử dụng bối cảnh được lưu trữ này vốn có nghĩa là bạn hoàn toàn không cần phải lưu trữ nó nếu bạn quyết định sử dụng khung. Kiểm tra trang web để biết thêm thông tin và mẫu.
Tôi hy vọng điều đó sẽ giúp :)
Câu trả lời của Rohit có vẻ đúng. Tuy nhiên, lưu ý rằng "Chạy ngay lập tức" của AndroidStudio phụ thuộc vào việc không có static Context
thuộc tính trong mã của bạn, theo như tôi biết.
trong Kotlin, việc đặt Bối cảnh / Bối cảnh ứng dụng vào đối tượng đồng hành vẫn tạo ra cảnh báo Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
hoặc nếu bạn sử dụng một cái gì đó như thế này:
companion object {
lateinit var instance: MyApp
}
Nó chỉ đơn giản là đánh lừa việc không phát hiện ra rò rỉ bộ nhớ, đối tượng Ứng dụng vẫn có thể tạo ra rò rỉ bộ nhớ, vì lớp Ứng dụng và hậu duệ của nó là một Ngữ cảnh.
Ngoài ra, bạn có thể sử dụng giao diện chức năng hoặc thuộc tính Chức năng để giúp bạn có được bối cảnh ứng dụng của mình.
Đơn giản chỉ cần tạo một lớp đối tượng:
object CoreHelper {
lateinit var contextGetter: () -> Context
}
hoặc bạn có thể sử dụng nó an toàn hơn bằng cách sử dụng loại nullable:
object CoreHelper {
var contextGetter: (() -> Context)? = null
}
và trong lớp Ứng dụng của bạn thêm dòng này:
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
CoreHelper.contextGetter = {
this
}
}
}
và trong bảng kê khai của bạn, hãy khai báo tên ứng dụng . MyApp
<application
android:name=".MyApp"
Khi bạn muốn có được bối cảnh chỉ cần gọi:
CoreHelper.contextGetter()
// or if you use the nullable version
CoreHelper.contextGetter?.invoke()
Hy vọng nó sẽ giúp.
Hãy thử một cái gì đó như thế này
import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.os.Bundle; public class MainActivity extends AppCompatActivity { private static Context context; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = getApplicationContext(); } public static void getContext(View view){ Toast.makeText(context, "Got my context!", Toast.LENGTH_LONG).show(); } }