Context context understood Android

Context

1.Context inheritance and source code analysis

Here Insert Picture DescriptionHere Insert Picture Description

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */

These are the official google api-28 Notes on context, probably translated as follows:
1. a description of the application context;
2. is an abstract class;
3, through which we can access resources and classes of applications, including some applications level operations.

Context class which has achieved concrete two subclasses: ContextImpl and ContextWrapper. ContextWrapper class which, as its name suggests, this is just a wrapper it, ContextWrapper constructor must contain a genuine Context reference, while ContextWrapper provided attachBaseContext () is used to specify the real object ContextWrapper Context object, call the ContextWrapper the method will be a real turning Context object it contains. ContextThemeWrapper class, its interior contains relevant to the subject (Theme) interface, it requires only Activity theme, the theme of the Service is not required, because there is no interface Service backstage scenes, so the direct successor to the Service ContextWrapper, Application empathy. The class is truly ContextImpl various methods Context class of all functions, applications in the Context of the call, its implementation comes from the class.

A, Context Class inheritance
source code (part) as follows:

public abstract class Context {  
         ...  
         //获得系统级服务  
         public abstract Object getSystemService(String name);  
          //通过一个Intent启动Activity  
         public abstract void startActivity(Intent intent);    
         //启动Service 
         public abstract ComponentName startService(Intent service);   
         //根据文件名得到SharedPreferences对象  
         public abstract SharedPreferences getSharedPreferences(String name,int mode); //Activity token  弹框中有用到
          public IBinder getActivityToken();
         //
         public ApplicationInfo getApplicationInfo();
         ...  
    }

ContextImpl.java class that implements the functions Context class. Note that most of the functions of the function is called directly to its properties mPackageInfo completed
source code (part) as follows:

class ContextImpl extends Context{  
        //所有Application程序公用一个mPackageInfo对象  
       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 a package Context class is the class constructor contains a true Context reference, i.e. ContextIml object.

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类包含了主题(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;  
         }  
    }

应用程序创建Context实例的:

  • 创建Application对象时,而且整个App共一个Application对象
  • 创建Service对象时
  • 创建Activity对象时

所以App共有的Context数目公式为:
总Context实例个数 = Service个数 + Activity个数 + 1(Application对应的Context实例)

1、创建Application对象
  每个应用程序在第一次启动时,都会首先创建Application对象。如果对应用程序启动一个Activity(startActivity)流程比较清楚的话,创建Application的时机在创建handleBindApplication()方法中,该函数位于 ActivityThread.java类中 ,如下:
    //创建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()或startActivityForResult()请求启动一个Activity时,如果系统检测需要新建一个Activity对象时,就会回调handleLaunchActivity()方法,该方法继而调用performLaunchActivity()方法,去创建一个Activity实例,并且回调onCreate(),onStart()方法等,函数都位于 ActivityThread.java类 ,如下:
    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实例,就会回调handleCreateService()方法完成相关数据操作。handleCreateService()函数位于 ActivityThread.java类,如下:
    //创建一个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);  
        ...  
    }

通常我们想要获取Context对象,主要有以下四种方法

  1. View.getContext,返回当前View对象的Context对象,通常是当前正在展示的Activity对象。
  2. Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。
  3. ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context,可以使用这个方法,这个方法在实际开发中使用并不多,也不建议使用。
  4. Activity.this 返回当前的Activity实例,如果是UI控件需要使用Activity作为Context对象,但是默认的Toast实际上使ApplicationContext也可以。
    区别:getApplication()和getApplicationContext()

Context引起的一些问题(网络上的例子)
错误的单例模式

public class Singleton {    
    private static Singleton instance;    
    private Context mContext;  
      
    private Singleton(Context context) {        
        this.mContext = context;
    }    
        
    public static Singleton getInstance(Context context) {       
        if (instance == null) {
 	 instance = new Singleton(context);
        }       
        return instance;
    }
}

这是一个非线程安全的单例模式,instance作为静态对象,其生命周期要长于普通的对象,其中也包含Activity,假如Activity A去getInstance获得instance对象,传入this,常驻内存的Singleton保存了你传入的Activity A对象,并一直持有,即使Activity被销毁掉,但因为它的引用还存在于一个Singleton中,就不可能被GC掉,这样就导致了内存泄漏。

View持有Activity引用
public class MainActivity extends Activity {
    private static Drawable mDrawable; 
   
    @Override
    protected void onCreate(Bundle saveInstanceState) {        
        super.onCreate(saveInstanceState);
        setContentView(R.layout.activity_main);        
        ImageView iv = new ImageView(this);
        mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
        iv.setImageDrawable(mDrawable);
    }
}

有一个静态的Drawable对象当ImageView设置这个Drawable时,ImageView保存了mDrawable的引用,而ImageView传入的this是MainActivity的mContext,因为被static修饰的mDrawable是常驻内存的,MainActivity是它的间接引用,MainActivity被销毁时,也不能被GC掉,所以造成内存泄漏。
正确使用Context
一般Context造成的内存泄漏,几乎都是当Context销毁的时候,却因为被引用导致销毁失败,而Application的Context对象可以理解为随着进程存在的,所以我们总结出使用Context的正确姿势:
1:当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
自己的经验:
1: If we use ApplicationContext to start a LaunchMode of Activity for the standard time will complain android.util.AndroidRuntimeException:. Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag Is this really what you want this is because the non-Activity type? the Context and no so-called task stack, so to be activated Activity can not find the stack. The solution to this problem is to specify FLAG_ACTIVITY_NEW_TASK flag for the Activity to be started, so when he started to create a new task stack for it, but this time Activity singleTask mode is activated. This all started with the Application Activity manner not recommended, Service with the Application.
2: Application and Service go layout inflate is legal, but will use the system default theme style, if you customize some styles may not be used. Therefore, this method is not recommended.
Summary: all with the UI-related, should be used as a Context Activity to handle; a number of other operations, Service, Activity, Application and other examples are, of course, pay attention to hold Context referenced to prevent memory leaks.

Guess you like

Origin blog.csdn.net/JiKaTogether/article/details/90949141