**Android 基础知识点**

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销毁的时候,却因为被引用导致销毁失败。

  1. Application的Context对象随进程而存在,当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
  2. 不要让生命周期长于Activity的对象持有到Activity的引用。慎重的使用“static”关键字。
  3. 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
  4. Context里如果有线程,一定要在onDestroy()里及时停掉。

Activity相关

Activity是android四大组件中最常见的组件了,可以用来显示view。Activity类将负责创建一个窗口,您可以在其中放置用户界面,与用户进行交互。Activity继承自ContextThemeWrapper,它是一个Content,可以说它是一个“场景”一个提供用户与应用程序交互的“场景”。

了解Activity最先的了解它的生命周期,它的生命周期可以用一张经典图来概括:
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();
    }
}

其他情况说明:

  1. 如果在 onCreate() 里调用 finish() 方法会直接调用 onDestory() 方法。
  2. 启动Activity:onCreate()->onStart()->onResume()->Activityis running 。
  3. 按back键返回:onPause()->onStop()->onDestroy() 再次启动时:onCreate()->onStart()->onResume()->Activityis running 。
  4. 按home键返回:onPause()->onStop() 再次启动时:onRestart()->onStart()->onResume()->Activity isrunning 。
  5. 切换到别的Activity(当前Activity不finish),或者采用这个,启动程序进入Activity,按Home键进入桌面,然后长按Home建点击该应用重新进入Activity:onPause()->onStop() 再次启动时:onRestart()->onStart()->onResume()->Activityis running 。
  6. 切换到别的Activity(当前Activity finish):onPause()->onStop()->onDestroy() 再次启动时:onCreate()->onStart()->onResume()->Activity isrunning 。
  7. 锁屏:onPause()->onResume()。
  8. 切换到其他Activity然后返回原来的Activity:onPause()->onResume()。
  9. 横竖屏切换时候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,没有的话就创建。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值