关于android:获取Context的各种方法有什么区别?

What's the difference between the various methods to get a Context?

在各种各样的Android代码中,我看到了:

1
2
3
4
5
6
7
 public class MyActivity extends Activity {
    public void method() {
       mContext = this;    // since Activity extends Context
       mContext = getApplicationContext();
       mContext = getBaseContext();
    }
 }

但是我找不到任何合适的解释,最好是哪种解释,以及在什么情况下应该使用哪种解释。

如果选择了错误的一个,那么对于这方面的文档以及关于可能会破坏什么的指导将是非常感激的。


我同意,在Android环境中,文档很少,但您可以从各种来源拼凑出一些事实。

谷歌Android开发者官方博客上的这篇博客文章主要是为了帮助解决内存泄漏问题,但也提供了一些关于上下文的好信息:

In a regular Android application, you
usually have two kinds of Context,
Activity and Application.

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

除了Dianne Hackborn的帖子之外,我真的找不到任何关于何时使用getBaseContext()的信息,Dianne Hackborn是一位致力于Android SDK的谷歌工程师:

Don't use getBaseContext(), just use
the Context you have.

这是来自安卓开发者新闻组的一篇文章,你可能也要考虑在那里问你的问题,因为在安卓开发者新闻组工作的少数人会监控该新闻组并回答问题。

因此,总的来说,如果可能的话,最好使用全局应用程序上下文。


以下是我在使用context方面的发现:

1)。在Activity中,使用this对布局和菜单进行充气、注册上下文菜单、实例化小部件、启动其他活动、在Activity中创建新的Intent、实例化首选项或Activity中提供的其他方法。

充气布局:

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

充气菜单:

1
2
3
4
5
6
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    this.getMenuInflater().inflate(R.menu.mymenu, menu);
    return true;
}

注册上下文菜单:

1
this.registerForContextMenu(myView);

实例化小部件:

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

启动Activity

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

实例化首选项:

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
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;
    }
}

绑定应用程序范围类:

1
2
3
4
5
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),使用上下文替换,如:

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

其中thiscontext是类(活动等)的上下文。

Activity上下文替换:

1
2
3
4
5
6
7
8
public class MyActivity extends Activity {
    private Context mContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        mContext = this;
    }
}

侦听器上下文替换:

1
2
3
4
5
6
public class MyLocationListener implements LocationListener {
    private Context mContext;
    public MyLocationListener(Context context) {
        mContext = context;
    }
}

ContentObserver上下文替换:

1
2
3
4
5
6
7
public class MyContentObserver extends ContentObserver {
    private Context mContext;
    public MyContentObserver(Handler handler, Context context) {
        super(handler);
        mContext = context;
    }
}

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

外部BroadcastReceiver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
5
6
7
8
9
10
11
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)。对于服务,使用服务自己的上下文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)。对于祝酒词,通常使用getApplicationContext(),但在可能的情况下,使用从活动、服务等传递的上下文。

使用应用程序上下文:

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

使用从源传递的上下文:

1
2
3
4
5
6
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()

更新:添加context用法示例。


几天前我读了这篇文章,问自己同样的问题。阅读后我的决定很简单:总是使用applicationContext。

然而,我遇到了一个问题,我花了几个小时找到它,并用几秒钟来解决它…(更改一个词…)

我正在使用一个布局充气器来充气包含旋转器的视图。

所以有两种可能性:

1)

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

2)

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

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

1
2
3
4
5
6
7
8
9
10
11
12
    // 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实例化了线性布局,那么当您在活动中单击微调器时,您会有一个未捕获的异常,来自dalvik虚拟机(而不是来自您的代码,这就是为什么我花了很多时间来查找我的错误在哪里…)。

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

所以我的结论是:我想(我没有做过进一步的测试)在您的活动中处理contextmenu时需要basecontext。

该测试已经用api 8进行了编码,并在HTC要求的android 2.3.3上进行了测试。

我希望我的评论不会让你感到厌烦,并祝你一切顺利。快乐编码;-)


首先,我同意我们应该尽可能使用AppContext。然后在活动中"这个"。我从来就不需要basecontext。

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

这就是我观察到的。也许有些情况你应该加以区分。


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

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

用简单的话

getApplicationContext()作为方法名的建议将使您的应用程序了解应用程序范围内的细节,您可以从应用程序的任何位置访问这些细节。因此,您可以在服务绑定、广播注册等方面使用它。Application context将一直运行到应用程序退出。

getActivity()this将使您的应用程序了解当前屏幕,该屏幕也可以看到Application context提供的应用程序级别详细信息。所以你想知道的关于当前屏幕的任何信息,比如WindowActionBarFragementmanger,都可以在这个上下文中找到。基本上,Activity扩展context。在当前组件(活动)处于活动状态之前,此上下文将处于活动状态


我只使用这个和EDCOX1 2当从EDCOX1的3个词(非常绿色NOOB到JAVA和Android)烘焙。当我的点击器直接在活动中,并且必须在匿名内部点击器中使用getBaseContext时,我使用这个。我猜想这几乎就是getBaseContext的诀窍,它可能返回了内部类隐藏的活动的上下文。