首页 文章

在Android中获取'Context'的静态方法?

提问于
浏览
849

有没有办法在静态方法中获取当前的 Context 实例?

我正在寻找那种方式,因为我讨厌每次更改时保存“Context”实例 .

20 回答

  • 50

    Kotlin way

    表现:

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

    然后,您可以通过MyApplication.instance访问该属性

  • 36

    我认为你需要一个 getAppContext() 方法的主体:

    public static Context getAppContext()
       return MyApplication.context;
    
  • 65

    大多数需要方便的方法来获取应用程序上下文的应用程序创建自己的类,扩展android.app.Application .

    GUIDE

    您可以通过首先在项目中创建类来完成此操作,如下所示:

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

    然后,在AndroidManifest中,您应该在AndroidManifest.xml的标记中指定您的类的名称:

    <application 
        ...
        android:name="com.example.App" >
        ...
    </application>
    

    然后,您可以使用以下方法在任何静态方法中检索应用程序上下文:

    public static void someMethod() {
        Context context = App.getContext();
    }
    

    WARNING

    在将类似上述内容添加到项目之前,您应该考虑文档说的内容:

    通常不需要子类Application . 在大多数情况下,静态单例可以以更模块化的方式提供相同的功能 . 如果你的单例需要一个全局上下文(例如注册广播接收器),那么检索它的函数可以给一个Context,它在第一次构造单例时在内部使用Context.getApplicationContext() .


    REFLECTION

    还有另一种使用反射来获取应用程序上下文的方法 . 在Android中经常会忽略反思,我个人认为这不应该用于 生产环境 .

    要检索应用程序上下文,我们必须调用自API 1以来可用的隐藏类(ActivityThread)上的方法:

    public static Application getApplicationUsingReflection() throws Exception {
        return (Application) Class.forName("android.app.ActivityThread")
                .getMethod("currentApplication").invoke(null, (Object[]) null);
    }
    

    还有一个隐藏类(AppGlobals),它提供了一种以静态方式获取应用程序上下文的方法 . 它使用 ActivityThread 获取上下文,因此以下方法和上面发布的方法之间确实没有区别:

    public static Application getApplicationUsingReflection() throws Exception {
        return (Application) Class.forName("android.app.AppGlobals")
                .getMethod("getInitialApplication").invoke(null, (Object[]) null);
    }
    

    快乐的编码!

  • 1

    In Kotlin

    open class MyApp : Application() {
        override fun onCreate() {
            super.onCreate()
            mInstance = this
        }
    
        companion object {
            lateinit var mInstance: MyApp
            fun getContext(): Context? {
                return mInstance.applicationContext
            }
        }
    }
    

    并得到像上下文

    MyApp.mInstance
    

    要么

    MyApp.getContext()
    
  • 2

    我在某个时候用过这个:

    ActivityThread at = ActivityThread.systemMain();
    Context context = at.getSystemContext();
    

    这是我用于获取系统服务和工作的有效上下文 .

    但是,我只在框架/基础修改中使用它,并没有在Android应用程序中尝试它 .

    您必须知道的 warning :在使用此上下文注册广播接收器时,它将无法工作,您将获得:

    java.lang.SecurityException:给定调用程序包android没有在进程ProcessRecord中运行

  • 2

    如果由于某种原因,您希望在任何类中使用Application上下文,而不仅仅是那些扩展应用程序/活动,可能是某些工厂或帮助程序类 . 您可以将以下单身添加到您的应用中 .

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

    然后在你的应用程序类的onCreate中初始化它

    GlobalAppContextSingleton.getInstance().initialize(this);
    

    通过电话随处使用它

    GlobalAppContextSingleton.getInstance().getApplicationContext()
    

    但是我不建议将这种方法应用于任何应用程序上下文 . 因为它可能导致内存泄漏 .

  • 33

    不,我不会't think there is. Unfortunately, you'从 ActivityContext 的其他子类之一调用 getApplicationContext() . 此外,this问题有些相关 .

  • 0

    这是从UI线程中的任何位置获取Application(这是一个上下文)的 undocumented 方法 . 它依赖于隐藏的静态方法 ActivityThread.currentApplication() . 它至少应该在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
    }
    

    请注意,此方法可能返回null,例如当你在UI线程之外调用方法,或者应用程序没有绑定到线程时 .

    如果您可以更改应用程序代码,最好使用@RohitGhatol的解决方案 .

  • 3

    您可以使用以下内容:

    MainActivity.this.getApplicationContext();
    

    MainActivity.java:

    ...
    public class MainActivity ... {
        static MainActivity ma;
    ...
        public void onCreate(Bundle b) {
             super...
             ma=this;
             ...
    

    任何其他类:

    public ...
        public ANY_METHOD... {
             Context c = MainActivity.ma.getApplicationContext();
    
  • 1

    如果您不想修改清单文件,可以在初始活动中将上下文手动存储在静态变量中:

    public class App {
        private static Context context;
    
        public static void setContext(Context cntxt) {
            context = cntxt;
        }
    
        public static Context getContext() {
            return context;
        }
    }
    

    只需在您的活动(或活动)开始时设置上下文:

    // MainActivity
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        // Set Context
        App.setContext(getApplicationContext());
    
        // Other stuff
    }
    

    Note: 与所有其他答案一样,这是潜在的内存泄漏 .

  • 1

    据我所知,Rohit 's answer seems correct. However, be aware that AndroidStudio' s "Instant Run"依赖于代码中没有 static Context 属性 .

  • 11

    我已经测试了所有这里描述的建议,可以说:

    如果我使用以下代码:

    public class App extends Application {
        private static Context context;
        public void onCreate() {
            super.onCreate();
            context = getApplicationContext();
        }
    
        public static Context getAppContext() {
            return context;
        }
    }
    

    我在Studio 3中收到一条错误消息:

    Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run) .

    但如果我使用以下代码:

    public class App extends Application {
        private static List<Context> context = new ArrayList<>();
    
        @Override
        public void onCreate() {
            super.onCreate();
            context.add(0, getApplicationContext());
        }
    
        public static Context getAppContext() {
            return context.get(0);
        }       
    }
    

    它工作正常没有任何错误 .

  • 1180

    做这个:

    在Android Manifest文件中,声明以下内容 .

    <application android:name="com.xyz.MyApplication">
    
    </application>
    

    然后写课:

    public class MyApplication extends Application {
    
        private static Context context;
    
        public void onCreate() {
            super.onCreate();
            MyApplication.context = getApplicationContext();
        }
    
        public static Context getAppContext() {
            return MyApplication.context;
        }
    }
    

    现在到处都调用 MyApplication.getAppContext() 来静态获取应用程序上下文 .

  • 32

    如果您愿意使用RoboGuice,则可以将上下文注入到您想要的任何类中 . 以下是RoboGuice 2.0(在撰写本文时为测试版4)的一小部分示例

    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) {
            }
        }
    }
    
  • 8

    假设我们通常因为你想要初始化一个帮助器或者获得一个资源而导致's no guarantee that the context retrieved in such a way will always be non-null. At the time you need it, it' s,你不能及时延迟;处理null case对你没有帮助 . 所以我明白我是基本上与Android架构作斗争,如docs所述

    注意:通常不需要子类Application . 在大多数情况下,静态单例可以以更模块化的方式提供相同的功能 . 如果您的单例需要全局上下文(例如注册广播接收器),则在调用单例的getInstance()方法时将Context.getApplicationContext()包含为Context参数 .

    并由Dianne Hackborn解释

    应用程序存在的唯一原因是因为在1.0之前的开发过程中,我们的一个应用程序开发人员不断地烦恼我需要拥有一个他们可以从中获得的顶级应用程序对象,这样他们就可以拥有更多“正常“对他们的应用模式,我最终放弃了 . 我将永远后悔放弃那个 . :)

    她也建议解决这个问题:

    如果您想要的是可以在应用的不同部分共享的全局状态,请使用单例 . [...]这更自然地导致你应该如何管理这些东西 - 按需初始化它们 .

    所以我做的是摆脱扩展Application,并将上下文直接传递给singleton帮助器的getInstance(),同时在私有构造函数中保存对应用程序上下文的引用:

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

    然后调用者将本地上下文传递给帮助者:

    Helper.getInstance(myCtx).doSomething();
    

    因此,要正确回答这个问题:有一些方法可以静态访问应用程序上下文,但是所有这些都应该是不鼓励的,你应该更喜欢将本地上下文传递给单例的getInstance() .


    对于任何有兴趣的人,您可以在fwd blog阅读更详细的版本

  • 4

    这取决于您使用上下文的内容 . 我可以想到该方法至少有一个缺点:

    如果您尝试使用 AlertDialog.Builder 创建 AlertDialog ,则 Application 上下文将不起作用 . 我相信你需要当前的背景 Activity ...

  • 1

    我刚刚发布了一个名为Vapor API的面向Android的jQuery框架,旨在简化应用程序开发 .

    中央$ facade class维护WeakReference(链接到关于Ethan Nicholas的关于此的精彩Java博文)到当前的 Activity 上下文,您可以通过调用来检索:

    $.act()
    

    WeakReference 维护引用而不会阻止垃圾回收回收原始对象,因此您不应该遇到内存泄漏问题 .

    当然,缺点是你冒着 $.act() 可能返回null的风险 . 我还没有遇到这种情况,所以它可能只是一个最小的风险,值得一提 .

    如果您没有使用VaporActivity作为 Activity 类,也可以手动设置上下文:

    $.act(Activity);
    

    此外,Vapor API框架的大部分内容都使用这种存储的上下文,这可能意味着如果您决定使用框架,则根本不需要自己存储它 . 查看site以获取更多信息和样本 .

    我希望有帮助:)

  • 0

    根据this source,您可以通过扩展ContextWrapper来获取自己的Context

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

    JavaDoc for ContextWrapper

    代理Context的实现,简单地将其所有调用委托给另一个Context . 可以进行子类化以修改行为而无需更改原始上下文 .

  • 0

    所以我修改了接受的答案,因为它导致内存泄漏,这就是我提出的......

    AndroidManifest.xml

    <application android:name="com.xyz.MyApplication">
    ...
    
        </application>
    

    MyApplication.java

    public class MyBakingAppContext extends Application {
        private static Object mContext;
    
        public void onCreate() {
            super.onCreate();
            mContext = getApplicationContext();
        }
    
        public static Context getAppContext() {
            return (Context)mContext;
        }
    }
    

    我实际做的是,为对象分配上下文并将对象作为上下文返回(将其转换为上下文) . 希望它有所帮助 .

  • 3

    我使用Singleton设计模式的变体来帮助我 .

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

    然后我在 activity.onCreate() 中调用 ApplicationContextSingleton.setContext( this ); ,在 onDestroy() 中调用 ApplicationContextSingleton.setContext( null ); ;

相关问题