Android 의 Context 추상 류 에 대한 자세 한 설명

18203 단어 AndroidContext
Context 에 대해 우 리 는 먼저 알 아야 한다.
(1)응용 프로그램 환경의 정보,즉 문맥 을 묘사 합 니 다.
(2)이 종 류 는 추상(abstract class)류 로 Android 는 이 추상 류 의 구체 적 인 실현 류 를 제공 합 니 다(다음 에 우 리 는 ContextIml 류 라 고 말 할 것 입 니 다).
(3)이 를 통 해 우 리 는 응용 프로그램의 자원 과 종 류 를 얻 을 수 있 고 응용 단계 의 조작 도 포함한다.예 를 들 어 Activity 를 시작 하고 방송 을 보 내 며 Intent 정 보 를 받 아들 이 는 등 이다.
따라서 우 리 는 이 Context 대상 을 이용 하여 응용 단계 작업(application-level operations)을 구축 할 수 있 습 니 다.
1.Context 관련 유형의 계승 관계
2016322150958744.gif (639×450)
관련 클래스 소개:
Context 클래스 경로:/frameworks/base/core/java/android/content/context.java
설명:추상 류,통용 되 는 API 를 제공 합 니 다.소스 코드(부분)는 다음 과 같 습 니 다.   

public abstract class Context { 
   ... 
   public abstract Object getSystemService(String name); //        
   public abstract void startActivity(Intent intent);   //    Intent  Activity 
   public abstract ComponentName startService(Intent service); //  Service 
   //       SharedPreferences   
   public abstract SharedPreferences getSharedPreferences(String name,int mode); 
   ... 
} 
 
ContextIml.java 클래스  경로:/frameworks/base/core/java/android/app/contextImpl.java
설명:이 Context 류 의 실현 류 는 ContextIml 이 고 이 류 는 Context 류 의 기능 을 실현 했다.이 함수 의 대부분 기능 은 속성 mPackage Info 를 직접 호출 하여 완성 하 는 것 입 니 다.이 점 은 우리 가 나중에 말 할 것 입 니 다.    소스 코드(부분)는 다음 과 같 습 니 다.

/** 
 * Common implementation of Context API, which provides the base 
 * context object for Activity and other application components. 
 */ 
class ContextImpl extends Context{ 
  //  Application      mPackageInfo   
  /*package*/ ActivityThread.PackageInfo mPackageInfo; 
   
  @Override 
  public Object getSystemService(String name){ 
    ... 
    else if (ACTIVITY_SERVICE.equals(name)) { 
      return getActivityManager(); 
    }  
    else if (INPUT_METHOD_SERVICE.equals(name)) { 
      return InputMethodManager.getInstance(this); 
    } 
  }  
  @Override 
  public void startActivity(Intent intent) { 
    ... 
    //      Activity 
    mMainThread.getInstrumentation().execStartActivity( 
      getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1); 
  } 
} 
ContextWrapper 클래스 경로:\\frameworks\base\core\\java\\android\\content\ContextWrapper.java
설명:그 명칭 과 같이 이 종 류 는 Context 류 에 대한 포장 일 뿐 이 며,이러한 구조 함 수 는 진정한 Context 인용,즉 ContextIml 대상 을 포함한다.소스 코드(부분)는 다음 과 같 습 니 다.

public class ContextWrapper extends Context { 
  Context mBase; //       ContextIml  ,     Application、Service、Activity    
   
  //  Application、Service、Activity,       mBase     
  protected void attachBaseContext(Context base) { 
    if (mBase != null) { 
      throw new IllegalStateException("Base context already set"); 
    } 
    mBase = base; 
  } 
  @Override 
  public void startActivity(Intent intent) { 
    mBase.startActivity(intent); //  mBase     
  } 
} 
ContextThemeWrapper 클래스 경로:/frameworks/base/core/java/android/view/contextThemeWrapper.java
설명:이 클래스 내부 에는 테마(Theme)와 관련 된 인터페이스,즉 android:theme 속성 이 지정 한 것 이 포함 되 어 있 습 니 다.Activity 만 테마 가 필요 하고 Service 는 테마 가 필요 하지 않 기 때문에 Service 는 ContextWrapper 류 에 직접 계승 합 니 다.
소스 코드(부분)는 다음 과 같 습 니 다.

public class ContextThemeWrapper extends ContextWrapper { 
   //       ContextIml  ,     Application、Service、Activity    
    
   private Context mBase; 
  //mBase            
   public ContextThemeWrapper(Context base, int themeres) { 
      super(base); 
      mBase = base; 
      mThemeResource = themeres; 
   } 
 
   @Override 
   protected void attachBaseContext(Context newBase) { 
      super.attachBaseContext(newBase); 
      mBase = newBase; 
   } 
} 
Activity 류,Service 류,Application 류 는 본질 적 으로 Context 서브 클래스 이 므 로 더 많은 정 보 는 소스 코드 를 참고 하여 이해 할 수 있 습 니 다.
 
2.Context 인 스 턴 스 를 언제 만 듭 니까? 
 
Context 의 계승 관 계 를 숙지 한 후에 우 리 는 응용 프로그램 이 어떤 상황 에서 Context 대상 을 만들어 야 하 는 지 분석 할 것 이다.응용 프로그램 이 Context 인 스 턴 스 를 만 드 는 경 우 는 다음 과 같은 몇 가지 상황 이 있 습 니 다.
(1)애플 리 케 이 션 대상 을 만 들 때 전체 애플 리 케 이 션 은 하나의 애플 리 케 이 션 대상 이다.
(2)서비스 대상 을 만 들 때
(3)Activity 대상 을 만 들 때
 
따라서 응용 프로그램 앱 이 공유 하 는 Context 수량 공식 은 다음 과 같다.
 
총 Context 실례 개수=Service 개수+Activity 개수+1(Application 에 대응 하 는 Context 실례)
 
Context 를 만 들 시기:
 
1.애플 리 케 이 션 대상 을 만 들 시기
 
모든 프로그램 은 처음 시작 할 때 애플 리 케 이 션 대상 을 먼저 만 듭 니 다.응용 프로그램 이 Activity(startActivity)프로 세 스 를 시작 하 는 것 에 대해 잘 알 고 있다 면 응용 프로그램 을 만 들 때 handleBindApplication()방법 에서 이 함 수 는 Activity Thread.자바 류 에 있 습 니 다.다음 과 같 습 니 다.

//  Application      ContextIml   
private final void handleBindApplication(AppBindData data){ 
  ... 
  ///  Application   
  Application app = data.info.makeApplication(data.restrictedBackupMode, null); 
  ... 
} 
 
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { 
  ... 
  try { 
    java.lang.ClassLoader cl = getClassLoader(); 
    ContextImpl appContext = new ContextImpl();  //    ContextImpl     
    appContext.init(this, null, mActivityThread); //    ContextIml        
    ///    Application    
    app = mActivityThread.mInstrumentation.newApplication( 
        cl, appClass, appContext); 
    appContext.setOuterContext(app); //  Application      ContextImpl        
  }  
  ... 
} 
2.Activity 대상 을 만 들 시기 
startActivity()나 startActivity ForResult()를 통 해 Activity 를 시작 할 것 을 요청 할 때 시스템 에서 새로운 Activity 대상 이 필요 할 때 handle LaunchActivity()방법 을 되 돌려 줍 니 다.이 방법 은 performLaunchActivity()방법 을 호출 하여 Activity 인 스 턴 스 를 만 들 고 onCreate(),onStart()방법 등 을 되 돌려 줍 니 다.함 수 는 모두 Activity Thread.자바 류 에 있 습 니 다.다음 과 같 습 니 다.

//    Activity       ContextIml   
private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) { 
  ... 
  Activity a = performLaunchActivity(r, customIntent); //    Activity 
} 
private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent) { 
  ... 
  Activity activity = null; 
  try { 
    //    Activity     
    java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); 
    activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); 
  } 
  if (activity != null) { 
    ContextImpl appContext = new ContextImpl();   //    Activity   
    appContext.init(r.packageInfo, r.token, this);  //    ContextIml        
    appContext.setOuterContext(activity);      //  Activity      ContextImpl   
    ... 
  } 
  ...   
} 
3.Service 대상 을 만 들 시기
 startService 나 bindService 를 통 해 시스템 이 Service 인 스 턴 스 를 새로 만들어 야 한 다 는 것 을 감지 하면 handle CreateService()방법 을 되 돌려 관련 데이터 작업 을 완성 합 니 다.handle CreateService()함 수 는 Activity Thread.자바 류 에 있 습 니 다.다음 과 같 습 니 다.

//    Service       ContextIml   
private final void handleCreateService(CreateServiceData data){ 
  ... 
  //    Service   
  Service service = null; 
  try { 
    java.lang.ClassLoader cl = packageInfo.getClassLoader(); 
    service = (Service) cl.loadClass(data.info.name).newInstance(); 
  } catch (Exception e) { 
  } 
  ... 
  ContextImpl context = new ContextImpl(); //    ContextImpl     
  context.init(packageInfo, null, this);  //    ContextIml        
  //         Application     
  Application app = packageInfo.makeApplication(false, mInstrumentation); 
  //  Service      ContextImpl   
  context.setOuterContext(service); 
  ... 
} 

 
또한 강조해 야 할 것 은 ContextImp 에 대한 분석 을 통 해 알 수 있 듯 이 그 방법의 대부분 조작 은 그 속성 mPackageInfo(이 속성 류)를 직접 호출 하 는 것 이다.
Package Info)와 관련 된 방법 으로 왔 습 니 다.이 는 ContextImp 는 경량급 류 이 고 Package Info 가 진정한 중량급 류 라 는 것 을 의미한다.하나의 앱 에 있 는...
모든 ContextIml 인 스 턴 스 는 같은 package Info 대상 에 대응 합 니 다.
           
3.Context 를 이용 하여 Shared Preferences 류 가 져 오기
Context 를 이용 하여 Shared Preferences 류 를 얻 는 방법 을 분석 합 니 다.Shared Preferences 류 는 모두 가 사용 한 적 이 있 을 것 입 니 다.일반 획득 자 입 니 다.
법 은 getShared Preferences()방법 을 호출 하여 관련 정보 에 따라 Shared Preferences 대상 을 얻 는 것 이다.구체 적 인 절 차 는 다음 과 같다.
1.호출  getShared Preferences()에서 해당 하 는 파일 을 가 져 옵 니 다.이 함 수 는 다음 과 같은 기능 을 수행 합 니 다.

//Context       ,            xml            
private static final HashMap<File, SharedPreferencesImpl> sSharedPrefs =  
    new HashMap<File, SharedPreferencesImpl>();  
 
@Override 
public SharedPreferences getSharedPreferences(String name, int mode){ 
   //     SharedPreferencesImpl   ,      HashMap                 
   SharedPreferencesImpl sp;  
   File f = getSharedPrefsFile(name); //            ,         
   synchronized (sSharedPrefs) {    //          ,       SharedPreferences   
     sp = sSharedPrefs.get(f); 
     if (sp != null && !sp.hasFileChanged()) { 
       //Log.i(TAG, "Returning existing prefs " + name + ": " + sp); 
       return sp; 
     } 
   } 
   //       xml  ,       map       
   Map map = null; 
   if (f.exists() && f.canRead()) { 
     try { 
       str = new FileInputStream(f); 
       map = XmlUtils.readMapXml(str); 
       str.close(); 
     }  
     ... 
   } 
    
   synchronized (sSharedPrefs) { 
     if (sp != null) { 
       //Log.i(TAG, "Updating existing prefs " + name + " " + sp + ": " + map); 
       sp.replace(map);  //       
     } else { 
       sp = sSharedPrefs.get(f); 
       if (sp == null) {  
         //    SharedPreferencesImpl  ,          
         sp = new SharedPreferencesImpl(f, mode, map);  
         sSharedPrefs.put(f, sp); 
       } 
     } 
     return sp; 
   } 
} 
 
2.Shared Preferences 는 인터페이스 에 불과 합 니 다.xml 파일 을 조작 하 는 방법 을 정 의 했 습 니 다.이 종 류 는 Shared Preferences Impl 입 니 다.이 종 류 는 ContextIml 의 내부 클래스 입 니 다.이 종 류 는 다음 과 같 습 니 다.

//soga,         Context ContextIml      
//SharedPreferences      ,       SharedPreferencesImpl  
private static final class SharedPreferencesImpl implements SharedPreferences{ 
   private Map mMap; //               ,       
    
   //  key      value  
   public String getString(String key, String defValue) { 
     synchronized (this) { 
       String v = (String)mMap.get(key); 
       return v != null ? v : defValue; 
     } 
   } 
   ... 
   //   SharedPreferencesImpl     Edito ,        
   public final class EditorImpl implements Editor { 
     private final Map<String, Object> mModified = Maps.newHashMap(); //            
   } 
} 
 
기본적으로 Shared Preferences 대상 을 가 져 오 는 것 이 이렇게 왔 습 니 다.
4.임의의 위치 에서 응용 프로그램 Context 가 져 오기
1.임의의 위치 에서 프로그램 Context 가 져 오기
Android 프로그램 에서 자원 에 접근 할 때 Context 를 제공 해 야 합 니 다.일반적으로 각종 component(Activity,Provider 등)에서 만 api 를 편리 하 게 사용 하여 Context 를 가 져 올 수 있 습 니 다.프로 그래 밍 을 좋아 하 는 사람들 은 도구 류 를 작성 하면 코드 재 활용 을 효과적으로 실현 할 수 있다 는 것 을 잘 알 고 있 습 니 다.안 드 로 이 드 에서 일부 도구 류 의 작성 은 당 혹 스 럽 습 니 다.예 를 들 어 도구 류 에서 Shared Preferences 를 얻 으 려 면 Context 의 지원 이 필요 합 니 다.
Context 로 인 한 번 거 로 움 을 해결 하기 위해 서 는 애플 리 케 이 션 류 를 사용자 정의 하여 이 기능 을 실현 할 수 있 습 니 다.

import android.app.Application;

public class ContextUtil extends Application {
  private static ContextUtil instance;

  public static ContextUtil getInstance() {
    return instance;
  }

  @Override
  public void onCreate() {
    // TODO Auto-generated method stub
    super.onCreate();
    instance = this;
  }
}

그리고 manifest 에에 Android:name="my package.contextUtil"을 추가 하면 우 리 는 모든 클래스 에서 Context 를 가 져 올 수 있 습 니 다.예 를 들 어 Context c=ContextUtil.getInstance();
2.context 주의사항:
안 드 로 이 드 에서 context 는 많은 작업 을 할 수 있 지만 가장 중요 한 기능 은 자원 을 불 러 오고 방문 하 는 것 입 니 다.안 드 로 이 드 에는 두 가지 context 가 있 는데 하 나 는 application context 이 고 하 나 는 activity context 입 니 다.보통 우 리 는 각 종류 와 방법 간 에 activity context 를 전달 합 니 다.
예 를 들 어 activity 의 onCreate:

protected void onCreate(Bundle state) {
    super.onCreate(state);

    TextView label = new TextView(this); //  context view control
    label.setText("Leaks are bad");

    setContentView(label);
}

activity context 를 view 에 전달 하 는 것 은 view 가 activity 를 가리 키 는 인용 을 가지 고 activity 가 차지 하 는 자원:view hierachy,resource 등 을 참조 하 는 것 을 의미한다.
이렇게 하면 context 에서 메모리 가 유출 되면 많은 메모리 가 유출 된다.
여기 서 유출 된 것 은 gc 가 activity 의 메모 리 를 회수 할 방법 이 없다 는 뜻 이다.
Leaking an entire activity 는 쉬 운 일이 다.
화면 이 회전 할 때 시스템 은 현재 activity 를 없 애고 상태 정 보 를 저장 하 며 새 것 을 만 듭 니 다.
예 를 들 어 우 리 는 응용 프로그램 을 썼 습 니 다.그것 은 큰 그림 을 불 러 와 야 합 니 다.우 리 는 화면 을 회전 할 때마다 이 그림 을 없 애고 다시 불 러 오 는 것 을 원 하지 않 습 니 다.이 요 구 를 실현 하 는 간단 한 아 이 디 어 는 정적 인 Drawable 을 정의 하 는 것 입 니 다.그러면 Activity 클래스 가 만들어 서 메모리 에 저장 합 니 다.
유사 성 구현:

public class myactivity extends Activity {
    private static Drawable sBackground;
    protected void onCreate(Bundle state) {
        super.onCreate(state);

        TextView label = new TextView(this);
        label.setText("Leaks are bad");

        if (sBackground == null) {
            sBackground = getDrawable(R.drawable.large_bitmap);
        }
    label.setBackgroundDrawable(sBackground);//drawable attached to a view

    setContentView(label);
    }
}

이 절 차 는 보기 에는 매우 간단 하지만 문제 가 매우 크다.화면 이 회전 할 때 leak(즉 gc 는 activity 를 없 앨 수 없습니다)가 있 습 니 다.
우리 가 방금 말 했 듯 이 화면 이 회전 할 때 시스템 은 현재 activity 를 소각 합 니 다.그러나 drawable 이 view 와 연 결 된 후에 drawable 은 view 의 reference,즉 sBackground 는 label 의 인용 을 저장 하고 label 은 activity 의 인용 을 저장 합 니 다.drawable 이 소각 할 수 없 는 이상 인용 과 간접 인용 은 모두 소각 할 수 없습니다.그러면 시스템 은 현재 activity 를 소각 할 방법 이 없어 서 메모리 가 유출 되 었 습 니 다.gc 는 이러한 유형의 메모리 유출 에 대해 무력 합 니 다.
이러한 메모리 유출 을 피 하 는 방법 은 activity 의 모든 대상 의 수명 주기 가 activity 보다 길 어 지지 않도록 하 는 것 입 니 다.대상 이 activity 에 대한 인용 으로 인해 activity 가 정상적으로 소각 되 지 않도록 하 는 것 입 니 다.애플 리 케 이 션 context 를 사용 할 수 있 습 니 다.applicationcontext 는 application 의 일생 을 수반 하여 activity 의 수명 주기 와 무관 합 니 다.applicationcontext 는 Context.getapplicationContext 또는 Activity.getApplication 방법 으로 얻 을 수 있 습 니 다.
context 와 관련 된 메모리 유출 을 피하 고 다음 과 같은 몇 가 지 를 기억 하 십시오.
(1)생명주기 가 긴 대상 이 activity context 를 인용 하지 않도록 합 니 다.즉,activity 를 인용 하 는 대상 은 activity 자체 의 생명주기 와 같 아야 합 니 다.
(2)수명 주기 가 긴 대상 에 대해 서 는 애플 리 케 이 션 context 를 사용 할 수 있 습 니 다.
(3)비 정적 인 내부 클래스 를 피하 고 정적 클래스 를 사용 하여 생명주기 문 제 를 피하 고 내부 클래스 가 외부 대상 에 대한 인용 으로 인 한 생명주기 변화 에 주의해 야 한다.
3.다른 가방 의 Context 가 져 오기
Android 에는 Context 라 는 개념 이 있 으 니 모두 가 알 고 있 을 것 이다.Context 는 많은 일 을 할 수 있 습 니 다.activity 를 열 고 방송 을 보 내 며 이 가방 의 폴 더 와 데이터 베 이 스 를 열 고 classLoader 를 얻 으 며 자원 을 얻 을 수 있 습 니 다.만약 우리 가 가방 의 Context 대상 을 얻 었 다 면,우 리 는 기본적으로 이 가방 이 자신 이 할 수 있 는 대부분의 일 을 할 수 있 을 것 이다.
Context 는 createPackage Context 방법 이 있 습 니 다.다른 가방 의 컨 텍스트 를 만 들 수 있 습 니 다.이 인 스 턴 스 는 그 자체 의 Context 인 스 턴 스 와 다 르 지만 기능 은 같 습 니 다.
이 방법 은 두 개의 인자 가 있다.
(1)packageName 패키지 이름,Context 패키지 이름 을 가 져 옵 니 다.
(2)flags 표지 위치,CONTEXTINCLUDE_CODE 와 CONTEXTIGNORE_SECURITY 두 가지 옵션.CONTEXT_INCLUDE_코드 를 포함 한 이 가방 안의 코드 를 실행 할 수 있다 는 뜻 이다.CONTEXT_IGNORE_SECURITY 는 보안 경 고 를 무시 하고 이 표 지 를 추가 하지 않 으 면 일부 기능 이 사용 되 지 않 아 안전 경고 가 발생 한 다 는 뜻 이다.
다음은 작은 예 를 들 어 다른 가방 안의 어떤 종류의 방법 을 실행 합 니 다.또 다른 가방 의 가방 이름 은 chroya.demo,클래스 이름 Main,방법 이름 print 입 니 다.코드 는 다음 과 같 습 니 다.

package chroya.demo; 
 
import android.app.Activity; 
import android.os.Bundle; 
import android.util.Log; 
 
class Main extends Activity { 
   
  @Override 
  public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
  } 
   
  public void print(String msg) { 
    Log.d("Main", "msg:"+ msg); 
  } 
} 

package chroya.demo; 
 
import android.app.Activity; 
import android.os.Bundle; 
import android.util.Log; 
 
class Main extends Activity { 
   
  @Override 
  public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
  } 
   
  public void print(String msg) { 
    Log.d("Main", "msg:"+ msg); 
  } 
} 

이 가방 의 Main print 방법 을 호출 하 는 코드 블록 은 다음 과 같 습 니 다.

Context c = createPackageContext("chroya.demo", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); 
//      
Class clazz = c.getClassLoader().loadClass("chroya.demo.Main"); 
//       
Object owner = clazz.newInstance(); 
//  print  ,        
Object obj = clazz.getMethod("print", String.class).invoke(owner,"Hello"); 

Context c = createPackageContext("chroya.demo", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); 
//      
Class clazz = c.getClassLoader().loadClass("chroya.demo.Main"); 
//       
Object owner = clazz.newInstance(); 
//  print  ,        
Object obj = clazz.getMethod("print", String.class).invoke(owner, "Hello"); 

ok,이렇게 해서 우 리 는 chroya.demo 가방 의 Main 류 print 방법 을 호출 하여 결 과 를 실행 하고 Hello 를 인쇄 했 습 니 다.

좋은 웹페이지 즐겨찾기