Android四大组件之Activity123
Activity是Android最重要也是最常用的组件
一、Activity基本知识
-
Activity的生命周期
显示状态:onCreate,onStart,onResume
隐藏状态:onPause,onStop
销毁状态:onDestroy
当启动应用时会有如下方法得到执行onCreate-->onStart-->onResume
onStart是可见但不可交互状态
onResume是可见且可交互状态
当点击Back键时会调用onPause-->onStop-->onDestroy(一个Activity从创建到销毁的全过程,onRestart另说)
onPause是不可交互但可见状态
onStop是不可见且不可交互状态
通过Intent启动另一个Activity观察生命周期
需要注意的是点击button后首先执行的是onPause方法,然后创建另一个Activity,当SecondActivity可见时再执行MainActivity的onStop方法。
点击Back键后同理先执行onPause方法,回到MainActivity,因为MainActivity已经有实例,所以执行onRestart方法替代onCreate,之后依然类似,onStart-->onResume,以及SecondActivity的onStop-->onDestroy。
为什么只是先暂停当前Activity,而不是跳转Activity后再执行onPause-->onStop,或者先onPause-->onStop,然后再创建跳转Activity?
1)如果当前的应用是音乐播放器,当有电话打来时,当前的Activity在接听页面显示之前都不会暂停,不符合交互逻辑。
2)同样的例子,电话页面未显示而当前Activity已经stop,则会出现不可见的黑屏状态,直到接听页面创建并显示完成。
Activity横竖屏切换的生命周期
切换横竖屏时会将当前Activity按正常流程Destroy,然后重新创建Activity
切换横竖屏时,如果有输入框之类的控件,则需要保存竖屏时的状态,比如说有输入框之类的控件而且已经有输入内容,此时切到横屏不应该丢失数据,因此需要调用onSaveInstanceState保存状态信息。在onCerate方法中进行判断参数savedInstanceState是否为空
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
Log.i("tag", "MainActivity" + savedInstanceState.getString("libo"));
}
Log.i("tag", "MainActivity onCreate");
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.i("tag", "MainActivity onSaveInstanceState");
outState.putString("libo", "abc");
}
Activity生命周期的应用——音乐播放
onDestroy方法常用作释放资源
private MediaPlayer mediaPlayer;
private int position;
//在onCreate方法中初始化
mediaPlayer = MediaPlayer.create(this, R.raw.ifyou);
mediaPlayer.start();
//在onPause方法中暂停MediaPlayer,并记录播放进度
@Override
protected void onPause() {
super.onPause();
Log.i("tag", "MainActivity onPause");
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
position = mediaPlayer.getCurrentPosition();
Log.i("tag", position+"");
}
}
//在onResume方法中读取进度播放
@Override
protected void onResume() {
super.onResume();
Log.i("tag", "MainActivity onResume");
if (position != 0) {
mediaPlayer.seekTo(position);
Log.i("tag", position+"");
mediaPlayer.start();
}
}
//在onDestroy方法中进行资源的释放
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("tag", "MainActivity onDestroy");
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
-
Activity之间的启动方式
显式启动
Intent是Android程序中各组件之间进行交互的一种重要方式,不仅可以指明当前组件想要执行的动作,还可以在不同的组件之间传递数据。一般可被用于启动Activity,启动Service,以及发送广播等。
通过构造Intent,指定需要跳转的Activity。
Intent intent = new Intent(ThirdActivity.this, FourthActivity.class);
startActivity(intent);
隐式启动
为将要启动的Activity添加一个Intent过滤器,并指定action标签,指明当前Activity可以响应"libo"这个action。category标签默认为 android.intent.category.DEFAULT,更精确的指明了当前Activity能够响应带有category的Intent。action和category标签内容同时匹配时,这个Activity才能响应。当 category为默认时,可以省略 intent.addCategory() 方法。
<activity android:name="com.example.activity.FourthActivity">
<intent-filter >
<action android:name="libo"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction("libo");
startActivity(intent);
隐式启动多用于启动没有源文件的Activity,比如系统的浏览器,拨号,短信等
//打开网页
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.parse("http://www.qq.com");
intent.setData(uri);
startActivity(intent);
//打开图库
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivity(intent);
//发送短信
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "hello csdn");
startActivity(intent);
//拨号
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.parse("tel:666666");
intent.setData(uri);
startActivity(intent);
以上系统应用可以被调用,网页通过setData方法指定Intent操作的数据。另外,还可以通过Intent-Filter配置Data标签,更精确的指定当前活动可以响应什么类型的操作。
Data标签可以配置如下内容:
1.android:scheme 用于指定数据的协议部分,例如http、geo地理位置、tel拨打电话
2.android:host 用于指定数据的主机名部分,例如www.qq.com
3.android:port 用于指定数据的断口部分,一般紧随主机名之后
4.android:path 用指定主机名和端口之后的部分
5.android:mimeType 用于指定可以处理的数据类型。
更多系统的action可以查阅官方文档
-
Activity之间的数据传递
向下一个活动传递数据
通过Intent启动另一个Activity时,可以利用Intent携带一定量的具体数据。
具体方法为putExrta。通过键值对的方式。可以传递常用的8种类型及数组,String,int,double等
Intent intent = new Intent(ThirdActivity.this, FourthActivity.class);
intent.putExtra("name", "libo");
intent.putExtra("age", 21);
startActivity(intent);
在被启动的Activity中需要先进行getintent,获取启动Intent,然后通过getStringExtra等的函数取出数据
Intent intent = getIntent();
if (intent != null) {
text.setText(intent.getStringExtra("name") + " " + intent.getIntExtra("age", 0));
}
也可以通过Bundle先进行封装,再通过Intent传递
Intent intent = new Intent(ThirdActivity.this, FourthActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "libo");
bundle.putInt("age", 21);
intent.putExtras(bundle);
startActivity(intent);
Bundle也可以用于封装对象实例进行传递,不过对象须实现Serializable接口进行序列化(序列化是Java IO中的知识)
public class Person implements Serializable {
private String name;
private int age;
private String address;
public Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "name: " + name + "age: " + age + "address: " + address;
}
}
Intent intent = new Intent(ThirdActivity.this, FourthActivity.class);
Person person = new Person("libo", 21, "xian");
Bundle bundle = new Bundle();
bundle.putSerializable("person", person);
intent.putExtras(bundle);
startActivity(intent);
Intent intent = getIntent();
if (intent != null) {
Person person = (Person) intent.getSerializableExtra("person");
text.setText(person.toString());
}
Bundle也可以用于传递图片,但大小不能过大,上限约为0.5M
Intent intent = new Intent(ThirdActivity.this, FourthActivity.class);
Bundle bundle = new Bundle();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
bundle.putParcelable("bitmap", bitmap);
intent.putExtras(bundle);
startActivity(intent);
img.setImageBitmap((Bitmap) intent.getParcelableExtra("bitmap"));
当Bundle携带数据量过大时会抛出如下错误
!!! FAILED BINDER TRANSACTION !!!
如果需要在另一个Activity中使用本Activity的一些比较大的数据,可以考虑SQLite,或者文件
返回数据给上一个活动
此时启动Activity的方法不是startActivity而是startActivityForResult(Intent intent, int requestCode),request只要保证是唯一值就可以。
Intent intent = new Intent(MainActivity.this, FirstActivity.class);
startActivityForResult(intent, 1);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String str = data.getStringExtra("first");
text.setText(str);
}
break;
default:
break;
}
}
Intent intent = new Intent();
intent.putExtra("first", "libo");
setResult(RESULT_OK, intent);
finish();
-
Activity的启动模式( launchMode)
standard
默认是模式,可以创建条件允许的任意多个
singleTop
如果再次创建的Activity在栈顶,那么就不会再重新创建新Activity,而是执行onNewIntent方法
如果不在栈顶,则跟默认模式相同,还是会再创建新的Activity
singleTask
MainActivity->Activity->MainActivity(MainActivity默认,Activity singleTask),如果此时再启动Activity则会执行第二个MainActivity的onDestroy方法,即在任务栈中进行出栈,直到Activity处于栈顶。
singleInstance
启动Activity时会处于不同的任务栈中
Main1->First->Main2->First->Main3(Main默认模式,First singleInstance),First被启动两次但是只执行一次onCreate方法并创建一个新的任务栈,其余均为onNewIntent方法。
当First启动Main2时,因为是默认模式,则会寻找Task1,并创建Activity,Main3同样,此时显示的是Main3,单击Back键时会返回到Main2,Main1,最后才是First。
Back会先把此栈中的Activity全部出栈,再把Task2中的Activity出栈。
-
启动活动的最佳写法
启动不是由自己开发的一个Activity,不清楚该Activity需要传递哪些数据,除了查看源码之外,完全可以在Activity创建一个静态方法来提示调用者该Activity的详细信息。
public static void activityStart(Context context, String data1, String data2){
Intent intent = new Intent(context, FirstActivity.class);
intent.putExtra("data1", data1);
intent.putExtra("data2", data2);
context.startActivity(intent);
}
//启动时,只需一句代码,并且简单完整的传入所需的各个参数
FirstActivity.activityStart(this, "abc", "def");
二、深入了解Activity