컨텍스트를 얻는 다양한 방법의 차이점은 무엇입니까?


390

내가 본 안드로이드 코드의 다양한 비트에서 :

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

그러나 어느 것이 바람직한 지, 어떤 상황에서 사용 해야하는지에 대한 적절한 설명을 찾을 수 없습니다.

이것에 관한 문서를 가리키는 포인터와 잘못된 것을 선택하면 깨질 수있는 것에 대한 지침은 대단히 감사하겠습니다.


2
이 링크가 도움이 될 수 있습니다. 이것을 통해 ..
Aju

답변:


305

Android의 컨텍스트와 관련하여 문서가 드물다는 데 동의하지만 다양한 소스에서 몇 가지 사실을 함께 모을 수 있습니다.

공식 Google Android 개발자 블로그 의이 블로그 게시물 은 주로 메모리 누수를 해결하기 위해 작성되었지만 컨텍스트에 대한 유용한 정보도 제공합니다.

일반 Android 애플리케이션에는 일반적으로 활동 및 애플리케이션의 두 가지 컨텍스트가 있습니다.

기사를 조금 더 읽으면 둘 사이의 차이점과 Activity.getApplicationContext()Activity Context 대신 Context ( ) 응용 프로그램을 사용하려고 할 때의 차이점에 대해 알 수 있습니다 this. 기본적으로 응용 프로그램 컨텍스트는 응용 프로그램과 연결되어 있으며 앱의 수명주기 내내 항상 동일합니다. 여기서 활동 컨텍스트는 활동과 연관되어 있으며 화면 방향 변경 중 활동이 파괴되어 여러 번 파괴 될 수 있습니다. 이러한.

Android SDK에서 작업하는 Google 엔지니어 중 한 명인 Dianne Hackborn의 게시물 이외의 getBaseContext ()를 사용하는시기에 대해서는 실제로 찾을 수 없었습니다.

getBaseContext ()를 사용하지 말고 가지고있는 Context 만 사용하십시오.

그것은 안드로이드 개발자 뉴스 그룹 의 게시물에서 가져온 것입니다. 안드로이드에서 일하는 소수의 사람들이 실제로 뉴스 그룹을 모니터링하고 질문에 대답하기 때문에 질문을하는 것도 고려할 수 있습니다.

따라서 전체적으로 가능한 경우 전역 응용 프로그램 컨텍스트를 사용하는 것이 좋습니다.


13
활동 B를 시작할 수있는 활동 A가있을 때 CLEAR_TOP 플래그를 사용하여 A를 다시 시작할 수 있으며 (이 사이클을 여러 번 반복 할 수 있음) 참조 된 문맥? Diana는 getBaseContext 대신 'this'를 사용한다고 말하지만 대부분 A가 재사용되지만 A에 대한 새 객체가 생성되고 이전 A가 누출되는 상황이 있습니다. 따라서 getBaseContext가 대부분의 경우 가장 적합한 선택 인 것 같습니다. 그렇다면 왜 그런지 명확하지 않습니다 Don't use getBaseContext(). 누군가 이것을 명확히 할 수 있습니까?
JBM

2
Activity를 확장하지 않는 클래스 내부의 컨텍스트 객체에 어떻게 액세스합니까?
Cole

1
@Cole, 클래스를 만들 수 있습니다. 여기에서 "ExampleClass"라고합니다. 생성자는 Context 객체를 가져 와서 클래스 인스턴스 변수 "appContext"를 인스턴스화합니다. 그런 다음 Activity 클래스 (또는 그 문제에 대한 다른 클래스)는 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);
}

삼) . 리스너 및 기타 유형의 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). 토스트의 경우 일반적으로을 사용 getApplicationContext()하지만 가능한 경우 활동, 서비스 등에서 전달 된 컨텍스트를 사용하십시오.

응용 프로그램의 컨텍스트를 사용하십시오.

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 대신 사용할 수 있습니다 OuterClass.this; stackoverflow.com/questions/9605459/…에있는
Paul Verest

3
도움이되는 답변에 +1하세요! 수락 된 답변이 수락 된 답변만큼 괜찮다는 데 동의합니다. 그러나 거룩한 molly이 답변은 매우 유익했습니다! 이러한 모든 예제에 대해 감사합니다. 컨텍스트 사용 전체를 더 잘 이해하는 데 도움이되었습니다. 나는 당신의 대답을 내 컴퓨터의 텍스트 파일에 참조로 복사했습니다.
Ryan

13

나는 며칠 전에이 스레드를 읽었으며 같은 질문을했습니다. 이것을 읽은 후의 결정은 간단했습니다. 항상 applicationContext를 사용하십시오.

그러나이 문제가 발생하여 몇 시간 동안 발견하고 몇 초 동안 해결했습니다 ... (단어 변경)

Spinner가 포함 된 뷰를 팽창시키기 위해 LayoutInflater를 사용하고 있습니다.

두 가지 가능성이 있습니다.

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을 인스턴스화 한 경우 활동에서 스피너를 클릭하면 달빅 가상 머신 (코드가 아닌)에서 발생하는 잡히지 않는 예외가 발생합니다. 내 실수가 어디 있었는지 알 수있는 시간 ...).

baseContext를 사용하면 괜찮습니다. 컨텍스트 메뉴가 열리고 선택 사항 중에서 선택할 수 있습니다.

그래서 여기에 내 결론이 있습니다 : 귀하의 활동에서 contextMenu를 처리 할 때 baseContext가 필요하다고 생각합니다 (더 이상 테스트하지는 않았습니다) ...

테스트는 API 8로 코딩되었으며 HTC Desire, Android 2.3.3에서 테스트되었습니다.

내 의견이 지금까지 지루해지지 않았기를 바랍니다. 행복한 코딩 ;-)


활동에서보기를 작성할 때 항상 "this"를 사용했습니다. 활동이 다시 시작되면보기가 다시 작성되고보기를 다시 작성하는 데 사용할 새로운 컨텍스트가있을 수 있습니다. 개발자 블로그에 게시 된 단점은 ImageView가 destoryed되고 사용 된 drawable / bitmap이 해당 컨텍스트에 매달릴 수 있다는 것입니다. 그럼에도 불구하고 그것은 지금 내가하는 일입니다. 앱의 다른 곳에서 코드 (일반 클래스)에 관해서는 응용 프로그램 컨텍스트를 활동이나 UI 요소에 국한되지 않는 것으로 사용합니다.
JonWillis

6

먼저, 가능할 때마다 appcontext를 사용해야한다는 데 동의합니다. 활동에서 "이". 기본 컨텍스트가 필요하지 않았습니다.

내 테스트에서 대부분의 경우 서로 교환 할 수 있습니다. 대부분의 경우 컨텍스트를 유지하려는 이유는 파일, 환경 설정, 데이터베이스 등에 액세스하기위한 것입니다. 이러한 데이터는 결국 앱의 개인 데이터 폴더 (/ data / data /)에 파일로 반영됩니다. 어떤 컨텍스트를 사용하든 동일한 폴더 / 파일에 매핑되므로 괜찮습니다.

그것이 내가 관찰 한 것입니다. 어쩌면 구별해야 할 경우가 있습니다.


시작할 때 (전화 기본 언어의 언어와 일치하지 않을 때) 앱 언어를 전역 적으로 설정하려면 기본 컨텍스트가 필요했습니다.
티나

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가 추상 클래스이며 클래스에 대한 주석은 다음과 같습니다.

응용 프로그램 환경에 대한 글로벌 정보에 대한 인터페이스입니다. 이것은 안드로이드 시스템에 의해 구현되는 추상 클래스입니다. application-specific리소스 및 클래스에 대한 액세스 뿐만 아니라 application-level활동 시작, 방송 및 수신 의도 등과 같은 작업에 대한 업 콜에 액세스 할 수 있습니다. 이에 서 벗어난 것은 컨텍스트가 시스템 수준뿐만 아니라 응용 프로그램 수준에 액세스하기위한 공통 구현을 제공한다는 것입니다 자원. 응용 프로그램 수준 리소스는 문자열 리소스 [getResources()]또는 자산 [getAssets()]과 같은 것에 액세스 할 수 있으며 시스템 수준 리소스는Context.getSystemService().

사실, 방법에 대한 의견을 살펴보면 이러한 개념을 강화하는 것 같습니다.

getSystemService(): system-level이름 을 기준으로 핸들을 서비스에 반환합니다 . 반환 된 객체의 클래스는 요청 된 이름에 따라 다릅니다. getResources(): 애플리케이션 패키지의 Resources 인스턴스를 반환합니다. getAssets(): 애플리케이션 패키지의 Resources 인스턴스를 반환합니다. Context abstract 클래스에서 위의 모든 메소드가 추상적이라는 것을 지적 할 가치가 있습니다! getSystemService (Class) 인스턴스는 하나만 구현되어 있으며 추상 메소드를 호출합니다. 즉, 이러한 구현은 주로 다음을 포함하는 구현 클래스에서 제공해야합니다.

ContextWrapper
Application
Activity
Service
IntentService

API 문서를 보면 클래스의 계층 구조는 다음과 같습니다.

문맥

| — ContextWrapper

| — — 응용

| — — ContextThemeWrapper

| — — — — 활동

| — — 서비스

| — — — IntentService

우리는 그 Context자체가 통찰력을 제공하지 않는다는 것을 알고 있기 때문에 나무를 내려 가서 살펴보고 ContextWrapper그 중 많은 것이 없다는 것을 깨닫습니다. Application이 확장되므로 ContextWrapper에서 제공하는 구현을 재정의하지 않기 때문에 살펴볼 필요가 없습니다 ContextWrapper. 이는 Context 구현이 OS에서 제공하며에서 숨겨져 있음을 의미합니다 API. ContextImpl 클래스의 소스를보고 컨텍스트의 구체적인 구현을 살펴볼 수 있습니다.


0

나는 이것을 사용 getBaseContext했으며 onClick(Java와 Android 모두에 매우 녹색 멍청한 놈 에서) 토스트 할 때 사용했습니다 . 내 리모콘이 활동에 직접 getBaseContext있고 익명의 내부 리모콘에서 사용해야 할 때 이것을 사용합니다 . 나는 getBaseContext그것이 거의 속임수라고 추측합니다. 아마도 내부 클래스가 숨어있는 활동의 컨텍스트를 반환합니다.


1
이것은 잘못되었습니다. 활동 자체의 기본 컨텍스트를 반환합니다. 익명의 내부 클래스에서 활동 (컨텍스트로 사용하려는 활동)을 얻으려면 다음과 같이 사용하십시오 MyActivity.this. 설명 할 때 기본 컨텍스트를 사용하면 문제가 발생하지 않지만 잘못되었습니다.
nickmartens1980
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.