提问



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


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

最佳参考


做这个:


在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()来静态获取应用程序上下文。

其它参考1


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


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





警告


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



  通常不需要子类Application。在大多数情况下,
  静态单例可以在更模块化的情况下提供相同的功能
  办法。如果您的单身人士需要全局上下文(例如注册
  广播接收器),可以给出一个检索它的功能
  内部使用Context.getApplicationContext()的Context
  首先构建单身人士。






REFLECTION


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


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


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


还有一个隐藏类(AppGlobals)提供了一种以静态方式获取应用程序上下文的方法。它使用ActivityThread得到上下文,所以下面的方法和上面发布的方法确实没有区别:[58]


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





快乐的编码!

其它参考2


不,我不认为有。不幸的是,你不再从ActivityContext的其他子类中调用getApplicationContext()。此外,这个问题有些相关。

其它参考3


这是从UI线程中的任何位置获取应用程序(这是一个上下文)的未记录的方法。它依赖于隐藏的静态方法ActivityThread.currentApplication()。它至少应该在Android 4.x上运行。[60]


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的解决方案。

其它参考4


假设我们正在谈论获取应用程序上下文,我按照@Rohit Ghatol扩展应用程序的建议实现了它。那时发生的事情是,不能保证以这种方式检索的上下文总是非null在你需要的时候,通常是因为你想要初始化一个帮助者,或者获得一个你不能及时拖延的资源;处理null case对你没有帮助。
所以我理解我基本上是在与Android架构作斗争,正如文档中所述[62]



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



并由Dianne Hackborn解释[63]



  应用程序存在的唯一原因是因为在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博客[64]上阅读更详细的版本

其它参考5


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


如果你试图用AlertDialog.Builder创建AlertDialogApplication上下文不会起作用。我相信你需要当前Activity的上下文......

其它参考6


如果您对使用RoboGuice持开放态度,可以将上下文注入到您想要的任何类中。这里有一个关于如何使用RoboGuice 2.0(在撰写本文时为beta 4)的小样本[65]


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

其它参考7


我在某个时候使用过这个:


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


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


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


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



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


其它参考8


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


public static Context getAppContext()
   return MyApplication.context; 

其它参考9


您可以使用以下内容:


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

其它参考10


我使用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 );;

其它参考11


根据这个源代码,你可以通过扩展ContextWrapper [66]来获得你自己的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();
    }
}


ContextWrapper的JavaDoc [67]



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


其它参考12


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


中央$门面类维护WeakReference(链接到关于Ethan Nicholas的关于此的精彩Java博客文章)到当前Activity上下文,您可以通过调用来检索:[69] [70]


$.act()


WeakReference维护引用而不阻止垃圾回收原始对象,所以你不应该有内存泄漏的问题。


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


如果不使用VaporActivity作为Activity类,也可以手动设置上下文:[71]


$.act(Activity);


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


我希望有帮助:)

其它参考13


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


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


除了应用程序上下文之外,我不建议使用这种方法。因为它可能导致内存泄漏。

其它参考14


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



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


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

其它参考15


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


public class App {
    private static Context context;

    public static void setContext(Context cntxt) {
        context = cntxt;
    }

    public static Context getContext() {
        return context;
    }
}


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


// MainActivity

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Set Context
    App.setContext(getApplicationContext());

    // Other stuff
}





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

其它参考16


Kotlin方式:


表现:


<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访问该属性

其它参考17


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


如果我使用以下代码:


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


它工作得很好,没有任何错误。

其它参考18


Rohit的回答似乎是正确的。但是,请注意,AndroidStudio的即时运行取决于代码中没有static Context属性,据我所知。