刚毕业那会儿,一心想朝着java web的方向进军,却岂料实习的时候阴差阳错地踏入了Android的大门,自此人生跌宕起伏、坎坎坷坷,在一家外企参与了几个需要越过GFW才能使用的有关于体育赛事的项目,之后跳了个槽跟另外一个哥们做了好几个没见到阳光的充斥着浓浓果味儿的App。无数的努力付之东流会让一个人厌倦某一件事情,所以我要开始写写能让自己看懂的文章,借此找点乐子当然也是为了记录记录自己的学习历程。

探究activity的各回调方法之前,首先插入一张官方的生命周期图,然后用适合自己的语言记录下简化的生命周期中各回调方法的涵义。

onCreate()

一般人认为的activity的入口(然而不是),当activity第一次created之后会回调这个方法。如果把activity比作房子的话,回调这个方法之前activity还只是一个毛坯房,我们要在这个方法里边对它进行装修,这样它随后显示的效果就跟我们所预想的一样了。

onstart() :

这个时候我们可以看到activity了。

onResume() :

这个时候我们可以跟activity进行交互了。

onPause() :

这个时候我们还是能看到activity,但是不能进行交互了。举个栗子,假设我们当前的activity为A,有一个启动activity B的意图,这个时候A会回调onPause(),当A的onPause()回调完成之后,B开始onCreate()->onStart()->onResume()...完了之后B就处于可见可交互的状态了。那么A呢?如果此时A看不见了,A就会回调onStop()方法;另一种情况是此时A还是部分可见的(比如Activity B的主题是@android:style/Theme.Dialog),A就不会回调onStop();  从上面的分析可以知道,onPause()方法里面不允许做耗时的操作,不然B等了半天都启动不了。

有一点需要注意的是,不是说A处于部分可见但是不可交互的状态就一定会回调onPause()的,不信你show一个Dialog,show一个DialogFragment,或者show一个设置焦点为true的PopupWindow试试看。

onStop():

这个时候activity已经一丁点儿都看不到了。

onDestroy():

activity的临终遗言就在这里面写了,因为回调完它就被摧毁了。activity被摧毁有两种情况,一是someone调用了finish()方法,二是系统要节省内存空间而临时干掉它. 如何区分呢?官方文档里面说的是通过isFinishing()这个方法来判断。 如果是someone调用了finish()方法,isFinishing()毫无疑问是return true的。如果是系统为了节省空间,isFinishing()=false?

onRestart():

activity准备重新出来见人了。典型的栗子是启动一个能完全遮挡住前一个activity的新的activity之后,再按back键返回到前一个actvity,这样前一个activity就会onRestart()->onStart()->onResume();另一个栗子是按下电源键,熄灭屏幕,再打开,点亮屏幕的时候;还有一个是按下home键,再从最近任务栏或者点击应用图标重新进去的时候....


到此为止,简化的activity生命周期就大致掌握了,但是将近七千行的activity源码可不止这几个回调方法。当然,我们不必要去追究里面每个方法每个变量的意义与作用,我觉得那样是一种浪费时间的表现,还不如去多看几个用得上的api或者研究下目前一些流行的开源框架怎么使用(好吧,实际上是老子看不懂那一堆fucking source code~~),话虽如此,一些有用或者有意思的回调方法我们还是需要了解了解的,要不然怎么提升自己的编程逼格呢!!!!!!!!!!

onApplyThemeResource():

一般来说在AndroidManifest.xml的application标签下会全局设置一个theme属性,或者单独为每个activity设置也可以,这样onApplyThemeResource()方法会先于onCreate()调用,当然你若是不在AndroidManifest.xml设置,硬是单单在onCreate()里面调用setTheme()方法也是可以的,这样onApplyThemeResource()就会在onCreate()后调用了。

这个方法顾名思义就是activity应用主题资源的,当然并不是说activity直接就调用onApplyThemeResource()了,我们可以稍微追踪下它的调用路线。

我们知道要启动一个activity,会调用ActivityThread的performLaunchActivity()方法来创建这个activity,下面我们点进去找到关于设置主题的几行代码。

private Activity performLaunchActivity(......) {
......
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
......
activity.attach(appContext, this,......);
......
int theme = r.activityInfo.getThemeResource();
//如果在AndroidManifest.xml里面设置了有效的theme属性,则调用setTheme()
if (theme != 0) {
activity.setTheme(theme);
}
}
}   

然后去activity的setTheme()里面看看,看之前先了解下activity的继承关系,如下图。activity是直接继承自ContextThemeWrapper类的,所以才具有了变换主题的能力,实际上activity的setTheme()方法即是ContextThemeWrapper的setTheme()方法。

点进去ContextThemeWrapper的setTheme()方法:

public void setTheme(int resid) {
if (mThemeResource != resid) {
mThemeResource = resid;
initializeTheme();
}
}

然后initializeTheme():

private void initializeTheme() {
final boolean first = mTheme == null;
if (first) {
mTheme = getResources().newTheme();
Resources.Theme theme = getBaseContext().getTheme();
if (theme != null) {
mTheme.setTo(theme);
}
}
//找到目标
onApplyThemeResource(mTheme, mThemeResource, first);
}

以上用语言来表示就是,activity在创建的时候,如果有在AndroidManifest.xml里面设置有效的主题资源id,就会在onCreate()之前调用setTheme()方法,然后调用initializeTheme()方法,进而回调onApplyThemeResource()方法;其实前面看ActivityThread的performLaunchActivity()方法的时候,除了关于设置主题的代码外,上面还多了几行代码,为什么要把它提出来呢,因为initializeTheme()方法有个getBaseContext()的方法,那多出来的几行代码就是为了此刻来说明下的。看看Contextwrapper类的源码,发现它有一个名为mBase的Context类型的成员变量,这个mBase变量委托Contextwrapper类来调用自己的所有方法,大概就是下面这样:

public class ContextWrapper extends Context {
Context mBase;
......
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
} public Context getBaseContext() {
return mBase;
} @Override
public AssetManager getAssets() {
return mBase.getAssets();
} @Override
public Resources getResources()
{
return mBase.getResources();
}
......

所以initializeTheme()方法调用的getBaseContext()方法取的就是这个名为mBase的Context类型的成员变量,但是实际上Context类是一个抽象类,里面根本没有任何实现,这个时候就要重新回到上面的performLaunchActivity(),看看那多出来的两行代码到底干了什么。

首先是createBaseContextForActivity()方法,只贴重要的代码,其它的不想看:

private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
......
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
......
return baseContext;
}

然后是Activity的attch()方法,就不贴代码了,点进去发现调用了attachBaseContext()方法,对了,实则调用的就是上面ContextWrapper类的attachBaseContext(),由此,分析得知,activity在创建的时候通过createBaseContextForActivity()得到一个Context的实现类ContextImpl类的实例,然后在attch()方法里面通过调用attachBaseContext()方法让ContextWrapper类的mBase成员变量指向这个实例.

综上所述,前面initializeTheme()方法里面的getBaseContext()就说得通了,它实际得到的是一个ContextImpl类的实例,Activity第一次设置主题的时候,ContextImpl类的getTheme()方法会根据你的targetSdkVersion版本返回一个对应的默认的主题对象。以后有时间再去看看ContextImpl类,现在先跳过这个实际开发没什么用的onApplyThemeResource()方法。

onContentChanged():

当屏幕的内容视图发生改变时会调用,官方文档上的这句话反正我是看不大明白,只能看看源码了。这里以AppCompatActivity为例,发现AppCompatActivity的setContentView()或者addContentView()方法都是通过AppCompatDelegate来委托调用的,于是我们找到它的实现类AppCompatDelegateImplV7,然后把这两个方法贴出来如下:

......
final Window.Callback mOriginalWindowCallback;
......
@Override
public void setContentView(View v) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v);
mOriginalWindowCallback.onContentChanged();
} @Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
......

所以,Activity在onCreate()里边调用setContentView()方法或者addContentView()方法的时候,都会紧接着回调onContentChanged()方法,上面代码mOriginalWindowCallback变量指向的就是委托的Activity对象。根据上述分析,onContentChanged()方法什么时候会回调呢?就是当Activity的根视图中id为android.R.id.content的ViewGroup的子View发生改变时,这种改变指的是子View的替换。这样来看的话,我们在onCreate()里边就不用写个initView()的方法来findViewById()了,直接把这些操作丢在onContentChanged()方法里边,感觉吊吊的。遗憾的是,有了butterknife、AndroidAnnotations、Dagger亦或是其它的注解框架,谁还会用findViewById()?

当然,也可以直接看看Activity里边的setContentView()方法,然后看看PhoneWindow类里边的setContentView()方法,原理是一样的。那么这个id为android.R.id.content的ViewGroup到底是什么呢?以后有时间的话要单独记录下有关DecorView的知识

探究Activity的各回调方法的更多相关文章

  1. Android Fragment用法之给Activity创建事件回调

    在某些案例中,可能需要Fragment与Activity共享事件.在Fragment内部定义一个回调接口是一个好方法,并且规定由持有它的Activity实现这个回调方法.当Activity通过接口接受 ...

  2. fragment Activity之间传值的方法 之------------接口回调

    首先  定义一个  回调接口 public interface FragmentCallBack { public void callbackFun1(Bundle arg); public void ...

  3. Android事件侦听器回调方法浅谈

    http://developer.51cto.com/art/201001/180846.htm Android事件侦听器作为视图View类的接口,其中包含有不少回调方法,比如:onClick():o ...

  4. Android中的广播基本实现及回调方法的理解

    在Android中broadcast这一节的内容其实不算多主要是牵扯到一个broadcastreceiver类,这个类是一个抽象类,下面有一个抽象方法onreceiver(),可以再我们收到网络状态变 ...

  5. 微信授权登录,关于调不起授权页面,无法响应回调方法,获取不到code 详解

    前期准备工作:申请AppId,下载资源包jar.文档等. 微信授权登录步骤: 1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据c ...

  6. Java回调方法详解

    回调在维基百科中定义为: 在计算机程序设计中,回调函数,是指通过函数参数传递到其他代码的,某一块可执行代码的引用. 其目的是允许底层代码调用在高层定义的子程序. 举个例子可能更明白一些:以Androi ...

  7. 给jquery-validation插件添加控件的验证回调方法

    jquery-validation.js在前端验证中使用起来非常方便,提供的功能基本上能满足大部分验证需求,例如:1.内置了很多常用的验证方法:2.可以自定义错误显示信息:3.可以自定义错误显示位置: ...

  8. javascript 利用匿名函数对象给你异步回调方法传参数

    先来创建一个匿名函数对象: /*** * 匿名函数 */ var callChangeBtn=new function(bugBtn){ this.chage=function(json){ bugB ...

  9. 【C#】回调方法不通过object参数获得委托实例

    回调方法中几乎都会存在获取委托实例的需求,进而通过委托实例调用EndInvoke以得到异步执行的返回值.在我看过的相关文章中,获取委托实例的方法几乎都是同一个,就是向BeginInvoke的最后一个参 ...

随机推荐

  1. JavaScript----Performance Tool and Process

    1. Syntax-------------JSLint, JSHint, 2. Combine------------YUI 3. Minify---------------JSMin

  2. 使用NetUserAdd API函数创建远程用户

    http://apps.hi.baidu.com/share/detail/33407620 使用NetUserAdd编程创建远程用户Windows API NetUserAdd()可以创建Windo ...

  3. MySQL表损坏预防与修复

    1.       表损坏的原因分析 以下原因是导致mysql 表毁坏的常见原因: 1. 服务器突然断电导致数据文件损坏. 2. 强制关机,没有先关闭mysql 服务. 3. mysqld 进程在写表时 ...

  4. id有空格获取不到元素

  5. MongoDB主从配置

    master的配置 # cat mongod.conf dbpath = /app/sinova/mongodata/db            #指定数据库目录 logpath = /app/sin ...

  6. 【搜索】【并查集】Codeforces 691D Swaps in Permutation

    题目链接: http://codeforces.com/problemset/problem/691/D 题目大意: 给一个1到N的排列,M个操作(1<=N,M<=106),每个操作可以交 ...

  7. kafka中对一个topic增加replicas

    是指手动写扩充replicas的配置文件,然后使用工具进行操作. 参考官网site:http://kafka.apache.org/documentation.html#basic_ops_autom ...

  8. [OSX] 取消开机启动

    以Pulse Secure为例 参考:https://kb.pulsesecure.net/articles/Pulse_Secure_Article/KB26679 输入指令: launchctl ...

  9. [Locked] Number of Connected Components in an Undirected Graph

    Number of Connected Components in an Undirected Graph Given n nodes labeled from 0 to n - 1 and a li ...

  10. 关于 NoSQL 数据库你应该了解的 10 件事

    四分之一个世纪以来,关系型数据库(RDBMS)一直是主流数据库模型.但是现在非关系型数据库,“云”或者“NoSQL”数据库,正在作为一种替代数据库模型获得越来越多的占有率.本文中我们将关注非关系型 N ...