Context相关
context常被称为“上下文”,也可称为“环境”。Activity是一个context,Service是一个context,Application也是一个context。context在加载资源、启动Activity、获取系统服务、创建View等操作都要参与。Android把“场景”抽象成Context类。一个应用程序可以认为是一个工作环境,用户在这个环境中会切换到不同的场景,不管是Activity页面还是各种Seivice都是一个场景,页面用户交互也好,打电话,发短信也好都是一个场景。
Activity,Service,Broadcast Receiver,Content Provider这四大组件必须工作在Context环境下,Button,TextView,LinearLayout这些控件也必须工作在Context环境下,Button mButton=new Button(Context)是可以的。
- 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.
以上为官方源码注释,谷歌翻译为:
有关应用程序环境的全局信息的接口。 这是一个抽象类,其实现由Android系统提供。 它允许访问应用程序特定的资源和类,以及用于应用程序级操作(例如启动活动,广播和接收意图等)的呼叫。
就是说,它描述一个应用程序环境的信息(即上下文);是一个抽象类,Android提供了该抽象类的具体实现类;通过它我们可以获取应用程序的资源和类(包括应用级别操作,如启动Activity,发广播,接受Intent等)。
Context是一个abstract类,它有两个实现类,ContextImpl和ContextWrapper。
ContextImpl类:
ContextImpl是上下文功能的实现类。
ContextWrapper类:
Proxying implementation of Context that simply delegates all of its calls to another Context. Can be subclassed to modify behavior without changing the original Context.
以上为官方源码注释,谷歌翻译为:
代理Context的实现,只需将其所有调用委托给另一个Context即可。 可以被子类化以修改行为而不改变原始上下文。
构造函数:
public ContextWrapper(Context base) {mBase = base; }
/**
设置此ContextWrapper的基本上下文。 然后将所有呼叫委派给基本上下文。 如果已经设置了基本上文,则抛出IllegalStateException。
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。attachBaseContext()方法其实是由系统来调用的,它会把ContextImpl对象作为参数传递到attachBaseContext()方法当中,从而赋值给mBase对象,之后ContextWrapper中的所有方法其实都是通过这种委托的机制交由ContextImpl去具体实现的,所以说ContextImpl是上下文功能的实现类。
ContextThemeWrapper类:
ContextThemeWarpper在ContextWarpper的基础上增加了Theme的封装,其内部包含了与主题(Theme)相关的接口,Theme指在AndroidManifest.xml中通过android:theme为Application元素或者Activity元素指定的主题。只有Activity才需要主题,Service是不需要主题的,因为Service是没有界面的后台场景,所以Service直接继承于ContextWrapper,Application同理。
Context的两个子类分工明确,其中ContextImpl是Context的具体实现类,ContextWrapper是Context的包装类。Activity,Application,Service虽都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们初始化的过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法。
Context作用域:
|Context作用域| Application | Activity | Service |
| ------------- |-------------| -----|
|Show a Dialog | NO | Yes | No
| Start an Activity| 不推荐| Yes | 不推荐
| Layout Inflation | 不推荐| Yes | 不推荐
| Start a Service | Yes | Yes | Yes
| Send a Broadcast | Yes | Yes | Yes
| Register Broadcast Receiver| Yes | Yes | Yes
| Load Resource Value| Yes | Yes | Yes
用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错:
android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
因为非Activity类型的Context并没有所谓的任务栈,所以待启动的Activity就找不到栈了。解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以singleTask模式启动的。所有这种用Application启动Activity的方式不推荐使用,Service同Application。
在Application和Service中去layout inflate也是合法的,但是会使用系统默认的主题样式,单是你自定义的某些样式就无法使用。所以这种方式也不推荐使用。
凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏。
获取Context:
- View.getContext,返回当前View对象的Context对象,一般是当前正在展示的Activity对象。
- Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象。
- Activity.this 返回当前的Activity实例。
- ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context,实际开发使用不多,不建议使用。
Context内存泄漏:
单例引起的内存泄露
public class AppSettings {
private Context mAppContext;
private static AppSettings sInstance;
private AppSetting(Context context){
mAppContext = context;
}
public static AppSetting getInstance(Context context){
if(sInstance==null){
sInstance = new AppSetting(context);
}
return sInstance;
}
}
sInstance作为静态对象,其生命周期要长于普通的对象,其中也包含Activity,假如Activity A去getInstance获得sInstance对象,传入this,常驻内存的AppSettings保存了你传入的Activity A对象,并一直持有,即使Activity被销毁掉,但因为它的引用还存在于一个AppSettings中,就不可能被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销毁的时候,却因为被引用导致销毁失败。
- Application的Context对象随进程而存在,当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
- 不要让生命周期长于Activity的对象持有到Activity的引用。慎重的使用“static”关键字。
- 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
- Context里如果有线程,一定要在onDestroy()里及时停掉。
Activity相关
Activity是android四大组件中最常见的组件了,可以用来显示view。Activity类将负责创建一个窗口,您可以在其中放置用户界面,与用户进行交互。Activity继承自ContextThemeWrapper,它是一个Content,可以说它是一个“场景”一个提供用户与应用程序交互的“场景”。
了解Activity最先的了解它的生命周期,它的生命周期可以用一张经典图来概括:
在了解Android生命周期之前,我们先得了解Activity栈:
Android针对Activity的管理使用的是栈,Activity栈保存了已经启动并且还没有终止的所有的Activity,栈是“先进后出”原则,Activity栈同理。某一个时刻只有一个Activity处在栈顶,当这个Activity被销毁后,下面的Activity才有可能浮到栈顶,或者有一个新的Activity被创建出来,则旧的Activity就被压栈沉下去了,程序的每一个显示屏幕就是一个Activity。正在运行的Activity处在栈的最顶端,它是运行状态的。也就是说Activity 的状态与其在Activity栈的位置有着密切的关系。
Activity的生命周期指Activity从启动到销毁的过程,Activity有4种状态:
(1)活动(Active)状态:这时候Activity处于栈顶,且是可见的,有焦点的,能够接收用户输入前 景Activity。Runtime将试图不惜一切代价保持它活着,甚至杀死其他Activity以确保它有它所需的资源。当另一个Activity变成Active时,当前 的将变成Paused状态。
(2)暂停(Paused)状态:在 某些情况下,你的Activity是可见的,但没有焦 点,在这时候,Actvity处于Paused状态。例如,如果有一个透明或非全屏幕上的Activity在你的Actvity上面,你的 Activity处于Paused状态,该Actvity仍被认为是Active的,但是它不接受用户输入事件。在极端情况下,Runtime将 杀死Paused Activity,以进一步回收资源。当一个Actvity完全被遮住时,它将进入Stopped状态。
(3)停止(Stopped)状态:当Activity是不可见的时,Activity处于Stopped状态。Activity将继续保留在内存中保持当前的所有状态和成员信息,假 设系统别的地方需要内存的话,这时它是被回收对象的主要候选。当Activity处于Stopped状态时,一定要保存当前数据和当前的UI状态,否则一 旦Activity退出或关闭时,当前的数据和UI状态就丢失了。
(4)非活动(Inactive)状态:Activity被杀掉以后或者被启动以前,处于Inactive状态。这时Activity已被移除从Activity堆栈中,需要重新启动才可以显示和使用。
Activity生命周期
public class MyActivity extends Activity {
/**
* onCreate()方法是每一个activity首先调用的方法,用于Activity的创建,你可以在其中初始化用户界面,
* 分配引用类变量,绑定数据控件,并创建服务和线程。如果你需要呈现用户界面,你必须调用
* setContentView(View view)方法。如果你的Activity需要创建一些对象的话,最好在onCreate方法中创建,
* 因为它在一个Activity的完整生命周期中只被调用一次。
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/**
* 调用在onCreate()或者onRestart()方法之后,其处于“用户可见不可交互”的状态。其处于第一次启动或者重新启动,
* 根据这点,可做相应操作,可以检测一些系统特征是否有效果。
*/
@Override
protected void onStart() {
super.onStart();
}
/**
* 该方法调用时,变成和用户可交互的状态,将当前Activity放在栈的最上端。但是,onResume()不是Activity对
* 用户可见的最佳指标,诸如键盘保护的系统窗口可能在前面。 使用{@link #onWindowFocusChanged}
* 确定您的活动对用户可见(例如,恢复游戏)。
*/
@Override
protected void onResume() {
super.onResume();
}
/**
* Activity调用该方法时是可见但不可交互的,如你在其上覆盖一个透明的Activity,其将一直处于onPause状态。在
* 系统即将开始其他Activity时调用,此方法通常用于将保存未更改的数据,停止动画以及可能耗用CPU的其他事项等,
* 但必须快速,不能做耗时操作。之后,该Activity暂停,在系统需要更多内存的情况下,可能会杀死已暂停的进程以回收资源。
* 在调用onPause之前,onSaveInstanceState会被调用。这个方法提供了一个机会保存当前的UI状态到Bundle当中。
* Bundle信息将会被传递到OnCreate和onRestoreInstanceState方法。
*/
@Override
protected void onPause() {
super.onPause();
}
/**
* Activity调用该方法时变得不可见,被下一个activity覆盖。在内存不足的情况下,系统没有足够的内存可以在
* 其onPause()调用后保持Activity运行,这种方法可能永远不会被调用。
*/
@Override
protected void onStop() {
super.onStop();
}
/**
* 当Activity被重新显示给用户(用户已经返回到它)时,在onStop()之后调用。 之后是onStart()。
*/
@Override
protected void onRestart() {
super.onRestart();
}
/**
* 当Activity(用户调用finish()或系统由于内存不足)被系统销毁杀掉时系统调用,(整个生命周期只调用1次)
* 用来释放onCreate ()方法中创建的资源,释放那些有可能导致内存泄漏的资源,如结束线程等
*/
@Override
protected void onDestroy() {
super.onDestroy();
}
}
其他情况说明:
- 如果在 onCreate() 里调用 finish() 方法会直接调用 onDestory() 方法。
- 启动Activity:onCreate()->onStart()->onResume()->Activityis running 。
- 按back键返回:onPause()->onStop()->onDestroy() 再次启动时:onCreate()->onStart()->onResume()->Activityis running 。
- 按home键返回:onPause()->onStop() 再次启动时:onRestart()->onStart()->onResume()->Activity isrunning 。
- 切换到别的Activity(当前Activity不finish),或者采用这个,启动程序进入Activity,按Home键进入桌面,然后长按Home建点击该应用重新进入Activity:onPause()->onStop() 再次启动时:onRestart()->onStart()->onResume()->Activityis running 。
- 切换到别的Activity(当前Activity finish):onPause()->onStop()->onDestroy() 再次启动时:onCreate()->onStart()->onResume()->Activity isrunning 。
- 锁屏:onPause()->onResume()。
- 切换到其他Activity然后返回原来的Activity:onPause()->onResume()。
- 横竖屏切换时候activity的生命周期:①不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次。②设置Activity的
android:configChanges="orientation"
时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次。③设置Activity的android:configChanges="orientation|keyboardHidden"
时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法。
Activity启动模式
standard(默认模式):
调用startActivity()方法都就会产生一个新的实例。并将该Activity增加到当前Task栈中——这种模式不会启动新的Task,新的Activity将被增加到原有的Task栈中,并处于栈顶的位置。系统不会在乎该Activity是否已经在Task栈中存在,每次启动都会创建该Activity的一个新的实例。
singleTop:
singleTop也是发送新的实例,但不同standard的一点是,在请求的Activity正好位于栈顶时,不会构造新的实例,否则调用Activity的newInstance()方法产生一个新实例。这种模式不会启动新的Task。
singleTask:
Activity在整个应用程序的上下文中只存在一个实例,每次启动该Activity时系统首先会在返回栈中检查是否存在该Activity的实例,如果目标Activity已经存在,但没有位于Task栈顶,系统将会把位于该Activity上面的所有Activity移出Task栈,从而使得目标Activity转入栈顶。如果没有发现就会创建一个新的Activity实例。当Activity已经位于Task栈顶,此时与singleTop模式的行为相同。
singleInstance:
指定为 singleInstance模式的活动会启用一个新的返回栈来管理这个Activity,加载该Activity时如果没有实例化,他会创建新的Task后,实例化入栈,如果已经存在,直接调用 onNewIntent(),该Activity的Task中不允许启动其它的Activity,任何从该Activity启动的其他Activity都将被 放到其他task中,先检查是否有本应用的task,没有的话就创建。