获取上下文的各种方法之间有什么区别?


390

在各种Android代码中,我已经看到:

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

但是,我找不到哪种比较合适的解释,在哪种情况下应该使用哪种解释。

我们将不胜感激有关此文档的指针以及有关如果选择了错误的内容可能会破坏的指南。


2
此链接可能会对您有所帮助。通过这个 ..
Aju 2012年

Answers:


305

我同意,关于Android中的Contexts的文档很少,但是您可以汇总来自各种来源的一些事实。

这篇 Google Android开发者官方博客上的博客文章主要用于帮助解决内存泄漏,但同时也提供了一些有关上下文的良好信息:

在常规的Android应用程序中,通常有两种上下文,活动和应用程序。

稍微阅读一下文章可以进一步了解两者之间的区别,以及何时需要考虑使用应用程序Context(Activity.getApplicationContext())而不是Activity上下文this。基本上,应用程序上下文与应用程序相关联,并且在您的应用程序的整个生命周期中始终是相同的,其中,活动上下文与活动相关联,并且在屏幕方向更改期间活动被破坏时,活动上下文可能会被破坏多次。这样。

除了Dianne Hackborn的帖子外,我什么都找不到关于何时使用getBaseContext()的任何信息,Dianne Hackborn是从事Android SDK的一位Google工程师:

不要使用getBaseContext(),而要使用您拥有的Context。

那是来自android-developers新闻组上的帖子,您可能也想在那里问您的问题,因为少数从事Android实际工作的人员会监视该新闻组并回答问题。

因此,总的来说,似乎尽可能使用全局应用程序上下文。


13
当我有一个可以启动活动B的活动A时,活动B可以使用CLEAR_TOP标志重新启动A(并可能重复此循环很多次)-在这种情况下,我应该使用哪种上下文以避免积累大量的引用的上下文?Diana说使用'this'而不是getBaseContext,但是然后...大多数时候A将被重用,但是在某些情况下将创建A的新对象,然后旧的A泄漏。因此,在大多数情况下,getBaseContext似乎是最合适的选择。然后不清楚为什么Don't use getBaseContext()。有人可以澄清一下吗?
JBM

2
如何访问不扩展Activity的类内部的上下文对象?
科尔,

1
@Cole,您可以创建一个类,在此我们将其称为“ ExampleClass”,其构造函数使用Context对象并实例化类实例变量“ appContext”。然后,您的Activity类(或与此有关的任何其他Class)可以调用一个使用ExampleClass的“ appContext”实例变量的ExampleClass方法。
Archie1986

54

这是我发现的有关使用context

1)。Activity自身内部,this用于放大布局和菜单,注册上下文菜单,实例化窗口小部件,启动其他活动,在中创建新Intent内容Activity,实例化首选项或可用的其他方法Activity

充气布局:

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

充气菜单:

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

注册上下文菜单:

this.registerForContextMenu(myView);

实例化小部件:

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

开始一个Activity

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

实例化首选项:

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

2)。对于应用程序范围的类,getApplicationContext()在应用程序的生命周期中使用此上下文作为上下文。

检索当前Android软件包的名称:

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

绑定应用程序范围的类:

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

3)。对于侦听器和其他类型的Android类(例如ContentObserver),请使用Context替换,例如:

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

其中this或者context是一个类(活动等)的情况下。

Activity 上下文替换:

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

侦听器上下文替换:

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

ContentObserver 上下文替换:

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

4)。对于BroadcastReceiver(包括嵌入式/嵌入式接收器),请使用接收器自身的上下文。

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

内联/嵌入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)。对于服务,请使用服务自己的上下文。

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)。对于Toast,通常使用getApplicationContext(),但在可能的情况下,请使用从Activity,Service等传递的上下文。

使用应用程序的上下文:

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

使用从源传递的上下文:

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

最后,请勿getBaseContext()按照Android框架开发人员的建议使用。

更新:添加Context用法示例。


1
可以使用mContext代替mContext OuterClass.this; 请参阅stackoverflow.com/questions/9605459/…中的
Paul Verest

3
+1这样有用的答案!我同意接受的答案和接受的答案一样好,但是圣洁的莫莉,这个答案非常有用!感谢您提供所有这些示例,它们帮助我更好地理解了整个上下文的使用。我什至将您的答案复制到计算机上的文本文件中作为参考。
瑞安(Ryan)

13

我几天前读了这个话题,问自己同样的问题。阅读本文后,我的决定很简单:始终使用applicationContext。

但是,我遇到了一个问题,花了几个小时才找到它,花了几秒钟来解决它...(更改一个单词...)

我正在使用LayoutInflater膨胀包含Spinner的视图。

因此,这里有两种可能性:

1)

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

2)

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

然后,我正在做这样的事情:

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

我注意到的是:如果使用applicationContext实例化linearLayout,那么当您在活动中单击微调器时,将有一个未捕获的异常,该异常来自dalvik虚拟机(而不是您的代码),这就是为什么我花了很多钱的原因时间来找出我的错误在哪里...)。

如果您使用baseContext,那么就可以了,上下文菜单将打开,您可以在选择中进行选择。

所以这是我的结论:我想(我没有进一步测试它)比在Activity中处理contextMenu时需要的baseContext要好。

该测试已使用API​​ 8进行了编码,并在HTC Desire(Android 2.3.3)上进行了测试。

希望到目前为止,我的评论让您感到无聊,并祝您一切顺利。快乐的编码;-)


在活动中创建视图时,我总是使用“ this”。在重新启动活动的基础上,将重新创建视图,并且可能会使用新的上下文来再次创建视图。在开发人员博客中发布的缺点是,当ImageView被破坏时,使用的可绘制/位图可能会挂在该上下文上。尽管如此,这就是我目前的工作。关于应用程序中其他地方的代码(普通类),我只是使用应用程序上下文,因为它不是特定于任何活动或UI元素的。
乔恩·威利斯(JonWillis)2012年

6

首先,我同意我们应该尽可能使用appcontext。然后在活动中“ this”。我从来没有需要basecontext。

在我的测试中,大多数情况下它们可以互换。在大多数情况下,要保留上下文的原因是访问文件,首选项,数据库等。这些数据最终会作为文件反映在应用程序的私有数据文件夹(/ data / data /)中。无论您使用哪种上下文,它们都将映射到相同的文件夹/文件,因此您可以。

这就是我观察到的。也许在某些情况下您应该区分它们。


我需要basecontext来在启动时全局设置应用程序语言(当它与电话默认语言不匹配时)。
蒂娜

3

在某些情况下,在线程中运行某些内容时,可能会在应用程序上下文上使用活动上下文。当线程完成执行并且您需要将结果返回给调用者活动时,您需要带有处理程序的上下文。

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

2

简单来说

getApplicationContext()就像方法名称所建议的那样,它将使您的应用知道应用程序范围的详细信息,您可以从应用程序中的任何位置进行访问。因此,您可以在服务绑定中利用此功能,Application context直到应用程序退出之前,广播注册等将一直存在。

getActivity()this使您的应用知道当前屏幕,该屏幕也会显示,同时显示application context。因此,您想要了解的有关当前屏幕的任何信息Window ActionBar Fragementmanger都可以在此上下文中使用。基本上和Activity扩展Context。该上下文将一直存在,直到当前组件(活动)有效为止


1

混淆源于以下事实:访问上下文的方法很多,(从表面上看)没有明显的差异。以下是您可以在活动中访问上下文的四种最常用的方法。

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

什么是上下文? 我个人喜欢将Context视为任何给定时间的应用程序状态。应用程序上下文表示应用程序的全局或基础配置,活动或服务可以在其上构建,并表示应用程序的配置实例或其传递状态。

如果查看android.content.Context的源代码,则会看到Context是一个抽象类,对该类的注释如下:

与有关应用程序环境的全局信息的接口。这是一个抽象类,其实现由Android系统提供。它允许访问application-specific资源和类,并且可以进行application-level诸如启动活动,广播和接收意图之类的操作的调用。资源。应用程序级资源可能正在访问诸如String资源[getResources()]或资产之类的[getAssets()]东西,而系统级资源则是您可以访问的任何东西Context.getSystemService().

实际上,请看一下有关方法的注释,它们似乎加强了这一概念:

getSystemService()system-level按名称将句柄返回给服务。返回的对象的类因请求的名称而异。 getResources():为您的应用程序的包返回一个Resources实例。 getAssets():为您的应用程序的包返回一个Resources实例。值得指出的是,在Context抽象类中,上述所有方法都是抽象的!getSystemService(Class)的只有一个实例具有实现,并且该实现调用抽象方法。这意味着,这些的实现应主要由实现类提供,这些实现类包括:

ContextWrapper
Application
Activity
Service
IntentService

查看API文档,类的层次结构如下所示:

语境

| — ContextWrapper

| — —应用

| — — ContextThemeWrapper

| — — — —活动

| — —服务

| — — — IntentService

由于我们知道Context本身并不能提供任何见解,因此我们沿着树往下看,看到ContextWrapper并意识到那里也没有太多。自从Application扩展以来ContextWrapper,那里没什么可看的,因为它不会覆盖ContextWrapper。这意味着Context的实现是由OS提供的,并且对于而言是隐藏的API。您可以通过查看ContextImpl类的源代码来查看Context的具体实现。


0

我仅getBaseContext在从onClick(非常绿色的菜鸟到Java和android)烤面包时使用过。当我的答题器直接位于活动中并且必须getBaseContext在匿名内部答题器中使用时,才使用此选项。我猜想这几乎是技巧getBaseContext,它可能是返回隐藏内部类的活动的上下文。


1
这是错误的,它返回了活动本身的基本上下文。要从匿名内部类获取活动(您要用作上下文的活动),请使用MyActivity.this。使用您描述的基本上下文可能不会引起问题,但这是错误的。
nickmartens1980 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.