学习Android有一段时间了,但可能是因为没有及时地进行归纳总结,很多东西掌握得并不是很好,遇到问题时往往花费的时间比较多。希望自己以后能够逐渐培养写博客的习惯,归纳所得,总结问题,方便自己学习的同时帮助到需要的人。最近结合官方文档总结学习了Android四大组件方面的内容,分享所得。
Android应用有4大组件,他们分别是Activity(活动)、Service(服务)、Content Provider(内容提供者)、Broadcast Receiver(广播接收器)。
应用组件是Android应用的基本构建部分,每一个组件都是一个点,系统可以通过它进入你的应用。对用户来说并不是所有组件都一定是入口点,有些组件相互依赖,但是每个组件都以独立实体形式存在并有着不同的作用,每个组件都是一个可以有助于定义应用总体行为的特定部分。
首先介绍的是Activity。
一、 Activity概述
二、 Activity使用
1.基本使用
2.传递数据
三、 Activity生命周期
四、 Activity启动模式
五、 Activity总结
一、Activity概述
Android四大组件中哦我们最熟悉而且最常用的应该就是Activity里了,下面这张图是官方对于Activity的介绍:
一个Activity是一个应用组件,可以提供用户交互的屏幕,用来打电话,拍照,发邮件,浏览地图等。每个Activity都有一个可以绘制用户界面的窗口,这个窗口一般全屏,但是也可以小于屏幕并浮动在其它窗口之上。
二、Activity使用
上面简单地介绍了一下Activity,下面介绍Activity的使用方法。
基本使用
-
新建Activity。要使用Activity,先新建一个子类继承Activity,在子类里我们需要根据Activity的生命周期重写系统调用的回调方法,比如当activity创建的时候,停止的时候,恢复的时候,销毁的时候。其中最重要的回调方法是onCreate()和onPause()。
例如:
-
创建一个Activity完成后,我们如果要使用它,必须在清单文件里进行注册。注册的时候如果像下图这样设置了intent-filter,那么当应用启动时,第一个看到的Activity就是这个了。
启动Activity。从一个Activity跳转到另一个Activity可以使用startActivity方法,使用时传入一个Intent对象,如果要携带数据可以在Intent上附加数据。
传递数据
当需要在不同Activity间传递数据时,我们可以在Intent里附加数据。下面这张图是Intent里的常用方法:
不难看到,我们可以用键值对的形式将数据放入Intent中,在目的Activity中通过** getIntent() **获取到Intent后得到携带的数据。
例如,现在我们要把Activity1中的String类型的数据"JackalTsc"以及int类型的数据88传递到Activity2中,那么可以如下进行:
- Activity1跳转前将数据放入到Intent中
Intent intent = new Intent(Activity1.this, Activity2.class);
intent.putExtra("datastring", "JackalTsc");
intent.putExtra("dataint", 22);
startActivity(intent);
- Activity2中获取到Intent后提取数据
String dataStr = getIntent().getStringExtra("datastring");
int dataInt = getIntent().getIntExtra("dataint", -1);
Log.e("ssssss", "获取到的数据为 " + dataStr + " " + dataInt);
上面是在Activity间传递简单的数据,传递一个对象的步骤如下。
1.类序列化 主要实现Parcelable接口
public class User implements Parcelable {
private String userName;
private int userAge;
public User(String userName, int userAge) {
this.userName = userName;
this.userAge = userAge;
}
protected User(Parcel in) {
userName = in.readString();
userAge = in.readInt();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(userName);
dest.writeInt(userAge);
}
public String getUserName() {
return userName;
}
public int getUserAge() {
return userAge;
}
}
2.将可序列化的对象放入Intent中
User user = new User("XiaoMing", 22);
Intent intent = new Intent(Activity1.this, Activity2.class);
intent.putExtra("dataobject", user);
startActivity(intent);
3.在目的Activity中获取数据
User mUser = getIntent().getParcelableExtra("dataobject");
Log.e("ssssss", "获取到的对象数据为 " +
mUser.getUserName() + " " +
mUser.getUserAge());
上面在Activity间传递对象时主要涉及到类的序列化,Android中实现序列化有两个选择,一是实现Serializable接口(是JavaSE本身就支持的),二是实现Parcelable接口(是Android特有功能,效率比实现Serializable接口高效,可用于Intent数据传递,也可以用于进程间通信(IPC))。实现Serializable接口非常简单,声明一下就可以了,而实现Parcelable接口稍微复杂一些,但效率更高,推荐用这种方法提高性能。
三、Activity的生命周期
Activity的生命周期这部分很重要,我们需要在Activity在不同的状态的时候做不同的事情。
从图中我们可以看出,Activity生命周期主要有7种阶段:
- onCreate() 创建,当Activity初次创建时调用
- onStart() 开始,Activity对用户可见时调用
- onResume() 恢复,Activity获取用户焦点,正常交互
- onPause() 暂停,Activity失去用户焦点但是仍然可见
- onStop() 停止,Activity不可见时调用
- onRestart() 重新开始,Activity从不可见变为可见时调用
- onDestroy() 销毁,Activity销毁
小结:
entire lifetime 完整生命周期,调用onCreate()方法到onDestroy()方法之间是一个完整的生命周期. 在onCreate方法里进行一些“全局”设置,比如定义布局,在onDestroy方法里释放所有持有的资源。
visible lifetime 可见生命周期,调用onStart()方法到调用onStop()方法之间是可见的生命周期. 在这个周期间,用户可以看见屏幕上的activity并可以与之交互。例如, 当一个新的activity启动并且当前activity不可见时,onStop()方法被调用。 在这两个方法之间,你可以保持一些需要展示给用户的资源。
foreground lifetime 焦点生命周期,主要是在调用onResume()方法和调用onPause()方法之间.在此期间,activity在其它所有activity之前并且拥有用户焦点。一个activity可能经常进行焦点生命周期的切换, 比如当设备休眠或者一个对话框出现时方法onPause()就会被调用。因为此状态可能经常切换,所以为了避免用户等待,在这两个方法里的代码要尽可能轻量级。
问题:当一个名为A的Activity启动B的时候,执行顺序是怎样的?
- A执行onPause()
- B按顺序执行onCreate(), onStart(), and onResume()
- 之后如果A已经不可见,那么A继续执行onStop(),否则就不执行。
Activity的状态保存
首先我们看下面的代码。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//这里设置布局
setContentView(R.layout.layout_activity_handler);
}
新建Activity时重写onCreate()方法做一些初始化操作,这段代码我们应该再熟悉不过了,但是我们可以看到有个Bundle类型的形参传进来,参数名是savedInstanceState,字面翻译为“保存的实例状态”,这个形参的作用是什么呢?通过阅读官方文档,可以看到这方面的介绍。
当一个Activity暂停(pause)或者停止(stop)时,它的状态是保留的。这是因为当暂停或者停止的时候,Activity对象仍然存在于内存中,它的所有成员信息以及当前状态都是存在的,所以用户在Activity里做的改变都将保持,这样当Activity恢复到前台时,这些变化依然在那。
当系统为了恢复内存而销毁Activity的时候,Activity对象同时被销毁了,所以系统无法简单恢复这个Activity的状态。当用户返回到这个Activity时,系统其实是重新创建了它。但是用户自己并不知道系统把Activity销毁了并重新建了Activity一个对象,用户希望Activity还是原来的样子,此时,我们就需要做一些工作,来保存Activity的一些重要信息,一般是重写回调方法** onSaveInstanceState() **。
在Activity销毁之前,系统调用onSaveInstanceState()方法,使用时一般是把数据放在Bundle对象里,然后给此方法传入这个对象进行操作。之后,当系统重新创建这个Activity时,会传递这个Bundle给onCreate()方法和onRestoreInstanceState()方法。使用两个中的任何一个,都可以获取到Bundle中保存的状态信息,如果没有状态信息恢复,Bundle对象就是null。
下面这张图很清楚,
官方文档上还说明了一点,即使你没有重写onSaveInstanceState()方法,Activity自己也会调用默认的onSaveInstanceState()方法来保存一些状态,当然它保存的可能是些简单的信息,比如布局里的View的一些状态,所以我们最好还是自己重写来保存一些需要保存的信息。
还有个要注意的点就是,当设备的一些配置发生变化时,比如横竖屏切换,Activity其实是有变化的,系统先销毁当前的Activity然后立马创建它,所以有必要的话最好做一些状态保存的相关工作。
五、Activity的启动模式
在我们的一个应用中可能存在很多的Activity,分别有着不同的作用,Android对于这么多的Activity是采用Task,以栈的形式来管理的。在Activity注册时,我们可以通过launchMode标签来指定Activity的启动模式,下面是4种启动模式的详细介绍。
standard模式
标准模式,这是系统默认的启动模式,每次通过这种模式启动Activity时,系统会在当前的任务栈内为目标Activity创建新的实例,并且不会创建新的Task.singleTop模式
栈顶复用模式,如果要启动的Activity的实例已经存在当前Task栈顶时,系统会调用onNewIntent()方法将intent指向它,而不是创建一个新的实例。如果要启动的Activity没有位于栈顶,那么系统会重新创建实例,并将它加载到Task栈顶。singleTask模式
栈内复用模式,首先检测整个Activity栈中是否存在要启动的Activity,如果要启动的Activity存在但不是位于栈顶,则会先将该Activity以上的其它Activity销毁,再将要启动的Activity置顶,如果位于已经栈顶,那么和singleTop模式一样,如果如果要启动的Activity不存在,则会创建Activity的实例并加入栈顶。singleInstance模式
全局单例模式,这种启动模式下,系统保证无论从哪个Task中启动目标Activity,都只会创建一个实例,而且该实例会单独存在一个Task中,在这个Task中有且仅有这一个Activity。
启动模式这里,standard模式和singleTop模式很好理解,singleTask和singleInstance比较特殊需要重点理解。
六、Activity总结
Activity暂时总结的就这么多,其中生命周期和启动模式是重点。