获取 Android 上下文的各种方法有啥区别?
Posted
技术标签:
【中文标题】获取 Android 上下文的各种方法有啥区别?【英文标题】:What's the difference between the various methods to get an Android Context?获取 Android 上下文的各种方法有什么区别? 【发布时间】:2010-11-04 20:19:30 【问题描述】:在我见过的各种 android 代码中:
public class MyActivity extends Activity
public void method()
mContext = this; // since Activity extends Context
mContext = getApplicationContext();
mContext = getBaseContext();
但是我找不到任何合适的解释来说明哪个更可取,以及在什么情况下应该使用哪个。
非常感谢您提供有关这方面的文档的指针,以及有关如果选择了错误的文档可能会造成什么问题的指导。
【问题讨论】:
此链接可能对您有所帮助。通过this.. 【参考方案1】:我同意 Android 中有关上下文的文档很少,但您可以将来自不同来源的一些事实拼凑起来。
官方 Google Android 开发者博客上的This blog post 主要是为了帮助解决内存泄漏问题,但也提供了一些关于上下文的有用信息:
在常规的 Android 应用程序中,您 通常有两种Context, 活动和应用程序。
进一步阅读这篇文章可以了解两者之间的区别,以及何时您可能需要考虑使用应用程序上下文 (Activity.getApplicationContext()
) 而不是使用活动上下文 this
)。基本上,应用程序上下文与应用程序相关联,并且在应用程序的整个生命周期中始终是相同的,因为活动上下文与活动相关联,并且可能会被多次销毁,因为活动在屏幕方向更改期间被销毁,并且这样的。
我找不到任何关于何时使用 getBaseContext() 的信息,除了 Dianne Hackborn 的帖子,她是从事 Android SDK 工作的 Google 工程师之一:
不要使用 getBaseContext(),只需使用 您拥有的上下文。
这是来自android-developers newsgroup 上的帖子,您可能也想考虑在那里提出您的问题,因为少数 Android 工作人员实际监控该新闻组并回答问题。
所以总的来说,尽可能使用全局应用程序上下文似乎更可取。
【讨论】:
当我有一个可以启动活动 B 的活动 A 时,该活动 B 又可以使用 CLEAR_TOP 标志重新启动 A(并且可能重复此循环多次) - 在这种情况下我应该使用什么上下文顺序避免建立大量引用的上下文? Diana 说使用 'this' 而不是 getBaseContext,但是……大多数时候 A 将被重用,但在某些情况下,将为 A 创建一个新对象,然后旧 A 泄漏。因此,对于大多数情况,getBaseContext 似乎是最合适的选择。然后不清楚为什么Don't use getBaseContext()
。有人可以澄清一下吗?
如何访问不扩展 Activity 的类中的上下文对象?
@Cole,你可以创建一个类,我们在这里称之为“ExampleClass”,它的构造函数接受一个 Context 对象并实例化一个类实例变量“appContext”。然后,您的 Activity 类(或任何其他类)可以调用使用 ExampleClass 的“appContext”实例变量的 ExampleClass 方法。【参考方案2】:
这是我发现的关于context
的使用情况:
1) . 在Activity
本身中,使用this
来扩展布局和菜单、注册上下文菜单、实例化小部件、启动其他活动、在@987654326 中创建新的Intent
@、实例化首选项或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),请使用以下上下文替换:
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();
最后,不要按照 Android 框架开发人员的建议使用 getBaseContext()
。
更新:添加Context
用法示例。
【讨论】:
可以使用OuterClass.this
代替 mContext ;请参阅***.com/questions/9605459/…中的 cmets
+1 以获得如此有用的答案!我同意接受的答案可以作为接受的答案,但是神圣的莫莉,这个答案非常有用!感谢您提供所有这些示例,它们帮助我更好地理解整个上下文的使用。我什至把你的答案复制到我机器上的一个文本文件中作为参考。【参考方案3】:
几天前我读了这个帖子,问自己同样的问题。阅读后我的决定很简单:始终使用 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 元素。【参考方案4】:首先,我同意我们应该尽可能使用 appcontext。然后是活动中的“这个”。我从来不需要basecontext。
在我的测试中,大多数情况下它们可以互换。在大多数情况下,您想要获取上下文的原因是访问文件、首选项、数据库等。这些数据最终反映为应用程序私有数据文件夹 (/data/data/) 中的文件。无论您使用哪种上下文,它们都将被映射到相同的文件夹/文件,所以您没问题。
这就是我观察到的。也许有些情况你应该区分它们。
【讨论】:
我需要 basecontext 在启动时全局设置应用程序语言(当它与手机默认语言不匹配时)。【参考方案5】:在某些情况下,在线程中运行某些东西时,您可能会使用 Activity 上下文而不是应用程序上下文。当线程完成执行并且您需要将结果返回给调用者活动时,您需要带有处理程序的上下文。
((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
【讨论】:
【参考方案6】:简单来说
getApplicationContext()
正如方法名称所暗示的那样,将使您的应用程序了解应用程序范围的详细信息,您可以从应用程序的任何位置访问这些详细信息。因此,您可以在服务绑定、广播注册等中使用它。Application context
将一直存在,直到应用退出。
getActivity()
或this
将使您的应用程序知道当前屏幕,这也可见于application context
提供的应用程序级别详细信息。因此,无论您想了解有关当前屏幕的任何信息,例如 Window
ActionBar
Fragementmanger
等,都可以在此上下文中使用。基本上和Activity
扩展Context
。此上下文将一直存在,直到当前组件(活动)还活着
【讨论】:
【参考方案7】:混淆源于这样一个事实,即有很多方法可以 访问上下文,(表面上)没有明显的差异。 以下是您可以访问的四种最常见的方式 Activity 中的上下文。
getContext()
getBaseContext()
getApplicationContext()
getActionBar().getThemedContext() //new
什么是上下文? 我个人喜欢将上下文视为您的应用程序在任何给定时间的状态。应用程序上下文表示您的应用程序的全局或基本配置,Activity 或服务可以在其上构建,并表示您的应用程序的配置实例或它的传递状态。
如果查看android.content.Context的源码,会发现Context是一个抽象类,类上的cmets如下:
有关应用程序环境的全局信息的接口。这是一个抽象类,其实现由Android系统提供。它
允许访问application-specific
资源和类,以及向上调用application-level
操作,例如启动活动、广播和接收意图等。
我从中得到的是 Context 提供了一个通用的实现来访问应用程序级别以及系统级别的资源。应用程序级资源可能正在访问诸如字符串资源[getResources()]
或资产[getAssets()]
之类的东西,而系统级资源是您使用Context.getSystemService().
访问的任何东西
事实上,看看方法上的 cmets,它们似乎强化了这个概念:
getSystemService()
:按名称将句柄返回给system-level
服务。返回对象的类因请求的名称而异。
getResources()
:为您的应用程序包返回一个资源实例。
getAssets()
:为您的应用程序包返回一个资源实例。
可能值得指出的是,在 Context 抽象类中,以上所有方法都是抽象的!只有一个 getSystemService(Class) 实例具有实现并调用抽象方法。这意味着,这些的实现应该主要由实现类提供,其中包括:
ContextWrapper
Application
Activity
Service
IntentService
查看 API 文档,类的层次结构如下所示:
上下文
| — ContextWrapper
|— — 应用
| — — ContextThemeWrapper
|— — — — 活动
| — — 服务
|— — — IntentService
由于我们知道Context
本身没有提供任何洞察力,因此我们向下移动并查看ContextWrapper
并意识到那里也没有太多信息。由于 Application 扩展了 ContextWrapper
,因此那里也没什么可看的,因为它不会覆盖 ContextWrapper
提供的实现。这意味着 Context 的实现由操作系统提供,并且对 API
隐藏。您可以通过查看 ContextImpl 类的源代码来了解 Context 的具体实现。
【讨论】:
【参考方案8】:我只在从onClick
(Java 和 android 都非常绿色的菜鸟)敬酒时使用了这个和getBaseContext
。当我的答题器直接在活动中并且必须在匿名内部答题器中使用 getBaseContext
时,我会使用它。我猜这几乎是getBaseContext
的诀窍,它可能会返回隐藏内部类的活动的上下文。
【讨论】:
这是错误的,它正在返回活动本身的基本上下文。要从匿名内部类获取活动(您想用作上下文的活动),请使用MyActivity.this
之类的东西。使用您描述的基本上下文可能不会导致问题,但这是错误的。以上是关于获取 Android 上下文的各种方法有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章
Android中getResource()获取资源和利用R文件获取资源有啥区别
Android下Context,Activity,Application之间有啥区别
SpringMVC当中的Controller接口和Struts2当中的Action有啥区别