Android之Activity和Intent


Android四大组件之一

主要用于与用户进行交互,在一个App中可能存在零个或多个Activity

1.1    Activity的创建

Activity创建:

创建的步骤:

1)  定义一个类,继承自Activity

2)  在清单文件中进行注册(先注册,后使用)

Android的四大组件都必须要在清单文件中进行注册

思考:

1)  当在AndroidManifest.xml清单文件中,如果不设置默认启动页,会报错吗?

答:不会报错,但是App就没有启动页面

2)  当在AndroidManifest.xml清单文件中,设置多个页面都是默认启动页,会报错吗?如果不报错,到底是执行哪个页面呢?

答:不会报错,如果设置了多个启动页面,那么在程序图标的列表中就会生成多个启动图标,     点击不同的启动图标都可以开启应用程序,进入对应的页面。

1.2    Activity的启动和关闭

Acitivity的启动

需要使用到Intent类,Intent不是Android四大组件之一,Intent,意图,主要用于Android三大组件之间的通讯,是三大组件的桥梁。

PS:这里的三大组件是Activity、Service、BroadcastReceiver

Activity的关闭

1)  finnish方法

2)点击返回按钮(返回按钮的点击事件默认就是关闭当前的Activity,这个点击事件是可以修改的。

1.3    Intent意图

1.3.1  概念

Context.startActivity(Intent)

一个Android应用程序可以包含零或多个Activity,当应用程序具有多个Activity时,就可能需要从一个Activity跳转到另一个Activity。在Android中,Activity之间的导航是通过Intent(意图)来完成

Intent并不是Andorid应用的组件,但它是各组件之间通信的载体

Intent不仅仅可以用于同一个App内来传递“意图”,也可以跨App

==Context,上下文环境(在这里就是指应用程序环境,封装了当前应用程序的一些相关信息)

1.3.2  显示意图

显示意图:需要明确指定需要启动的组件名称

new Intent(Context, Class)

intent.setClass(Context, Class)

intent.setClassName(Context, StringclassName)

intent.setClassName(String packageName,String className)

intent.setComponent(ComponentNamecomponent)

应用场景:开启自己应用程序内部的界面,效率较高

intent.setClass(this,SecondActivity.class);

startActivity(intent);

1.3.3  显示意图示例

// 显示意图的演示,需要明确指定开启的组件的类名

publicvoidopen03(View v) {

// 如果使用的是显示意图,那么启动组件的时候就不会去使用意图过滤器过滤

Intent intent =newIntent();

// 设置组件的信息

// 这里要注意:设置组件信息的api有很多,其实都是大同小异的

// intent.setClass(this, SecondActivity.class);

// intent.setClassName(this,"com.example.day06.activitybasic.SecondActivity");

// intent.setClassName(this.getPackageName(),"com.example.day06.activitybasic.SecondActivity");

ComponentName component =newComponentName(this, SecondActivity.class);

intent.setComponent(component);

startActivity(intent);

}

1.3.4  隐式意图

隐式意图不需要明确指定需要启动的组件的名称,只需要给定启动的组件的相关信息

所谓的相关系,比如:打电话、上网、发邮件

Intent.setAction(String)// action是一个任意的字符串,但是一般以包名.intent.行为命名,比如com.qianfeng.demo.intent.hit

Intent.setData(Uri)

Intent.addCategory(String)// 自定义的一般都是default类型,default类型可以不单独设置(默认)

intent.setAction(String action)  //

new Intent(String action)

new Intent(String action, Uri uri)

Intent intent = new Intent()

intent.setAction(String action)

intent.setData(Uriuri)

intent.setAction(String action)

intent.setData(Uri uri)

意图过滤器:

action

Action代表该Intent所要完成的一个抽象“动作”

一个Intent对象最多包含一个action

一个Activity可以定义多个action

Android本身提供了很多action供开发者使用

Action代表该Intent所要完成的一个抽象“动作”

一个Intent对象最多包含一个action

一个Activity可以定义多个action

Android本身提供了很多action供开发者使用

category

为Activity增加额外的附加类别信息

为Activity增加额外的附加类别信息

data

声明数据的类型(mimeType)、约束(scheme)、主机名(host)、端口(port)、路径(path)等

声明数据的类型(mimeType)、约束(scheme)、主机名(host)、端口(port)、路径(path)等

应用场景:开启别的应用程序中的界面,系统需要查询匹配的Activity,效率较低

1.3.5  隐式意图示例

示例1:打开拨号界面

publicvoidopen01(View v) {

Intentintent =newIntent();

//如果使用的是隐式意图,那么在意图中就保存相关的信息(就是想做什么事情)

//打篮球(动作+数据)

//动作

intent.setAction(Intent.ACTION_DIAL);

//url:统一资源定位符http://www.baidu.com

//uri:统一资源标识符tel://110 person://zhangsan

intent.setData(Uri.parse("tel://110"));

//开启一个界面,完成指定的操作

startActivity(intent);

}

示例二:自定义隐式视图及过滤器



android:name="com.example.day06.activitybasic.SecondActivity"

android:label="界面2">


那么系统就会找到系统中的各个组件对用的意图过滤器,

然后进行过滤,如果过滤通过就会开启这个组件 -->


<actionandroid:name="com.example.day06.activitybasic.inetnt.hit"/>


<categoryandroid:name="android.intent.category.DEFAULT"/>


<dataandroid:scheme="person"/>


android:name="com.example.day06.activitybasic.ThirdActivity"

android:label="界面3">

"com.example.day06.activitybasic.inetnt.hit"/>

"android.intent.category.DEFAULT"/>

"person"/>

注意:两个Activity设置了两个同样的过滤器

//开启自己的隐式意图,不需要指定Activity的类名

publicvoidopen02(View v){

Intent intent =newIntent();

//设置action

intent.setAction("com.example.day06activity01.inetnt.hit");

//设置类型(如果是默认的,可以不设置)

//intent.addCategory(Intent.CATEGORY_DEFAULT);

//设置Date

intent.setData(Uri.parse("person://zhangsan"));

startActivity(intent);

}

效果图:

1.3.6  显示意图和隐式意图的区别

1、显示意图:必须指定要激活的组件的完整包名和类名

(应用程序之间耦合在一起,一般是在激活自己应用中的组件时使用显示意图。

效率较高)

2、隐式意图:只需要指定执行的动作和数据就可以

(好处:应用程序之间没有耦合。一般在激活其他应用中的组件时使用隐式意图。

缺点:效率较低)

程序设计原则:高内聚、低耦合。

什么时候使用这两个Intent?

一般情况下,在开启同一个应用程序内部的组件会使用显示意图,相反,如果要开启其他应用程序中的组件,一般使用隐式意图。

1.4    通过intent传递数据

1、Intent.setData(Uri)——> intent.getData():传递简单的文本数据,// 传递Uri对象(实际上就是一个字符串)

2、putExtra(name ,value)——>  getXXXExtra()//传递额外数据

通过该方法可以传递以下数据类型:

八种基本数据类型

数组

字符串

序列化对象

(在Android中有两个序列化接口:

ASerializable接口: JDK自带的接口,使用方式很简单,只需要让自定义类实现Serializable接口即可。

b)Parcelable接口,Android SDK提供的接口

相对于Serializable接口,序列化和反序列化的效率更高,是Android推荐使用的,但是序列化和反序列的部分操作需要程序员自己完成

Bundle对象,就是一个容器,操作和Map集合类似,是以键值对的方式存储

3、putExtras(Bundle extras)——> getExtras():可以传递一组数据(Map集合)

4、putXXXArrayListExtra传递ArrayList对象

Intent intent = getIntent();

1.5    获取Activity的返回值

快捷键:ctrl+shift+o      优化导包

开启Activity并获取返回值

实现步骤:

1)Activity01中调用startActivityForResult(inetnt,requestCode)方法,开启一个指定的Activity

intent:意图对象,用于开启指定的Activity

requestCode:请求码,用于区分被开启的Activity

2)Activity02中通过setResult(resultCode,intent)方法设置返回结果

resultCode:返回码,用于表示返回值的状态(操作成功:Activity.RESULT_OK)

intent:意图对象,用于封装返回结果

设置完结果之后关闭Activity02,当Ativity02被关闭后会自动将结果返回给开启他的组件(Activty01)

3)Activity01中改写onActivityResult(intrequestCode, int resultCode, Intent data)方法,用于处理返回结果

requestCode,请求码

resultCode,返回码(结果码)

data,Intent对象,封装了返回的数据

注意:在处理结果的时候通常都需要先对请求码和返回码进行判断

if(requestCode== REQUEST_CAL_WEIGHT && resultCode == RESULT_OK) {

}

概念:当新的Activity结束或返回上一个Activity时,需要返回一些结果,此时就需要设置并处理Activity的返回值。

实现步骤

1)startActivityForResult(Intentintent) // 开启ActivityB

2)getIntent()// 在ActivityB中获取来自ActivityA的Intent对象

3)setResult(intresultCode, Intent data) // 设置返回值(返回码 + 数据)

4)finish() // 关闭ActivityB并将结果返回给ActivityA

5)onActivityResult(intrequstCode, int resultCode, Intent data) // 在ActivityA中处理来自ActivityB的返回值

请求码和返回码

请求码:用于区分开启的Activity

返回码:用于标记结果返回的状态

Activity.RESULT_CANCELED

Activity.RESULT_OK

Activity.RESULT_FIRST_USER

请求码:用于区分开启的Activity

返回码:用于标记结果返回的状态

Activity.RESULT_CANCELED

Activity.RESULT_OK

Activity.RESULT_FIRST_USER

1.5.1  示例:获取Acitivity的返回值

publicvoidopenClick(View v) {

//开启计算体重的界面

Intentintent =newIntent(this, CalWeightActivity.class);

//开启一个Activity,开启之后就没有关联了

// startActivity(intent);

//开启一个Activity,并且获取Activity的返回值

//参数:requestCode,请求码,用于区分被开启的Activity

startActivityForResult(intent,REQUEST_CAL_WEIGHT);

}

/**

* 当被开启的Activity返回结果时调用

* 参数1:requestCode,请求码

* 参数2:resultCode,返回码(结果码)

* 参数3:data,Intent对象,封装了返回的数据

*/

@Override

protectedvoidonActivityResult(intrequestCode,intresultCode, Intent data) {

if(requestCode ==REQUEST_CAL_WEIGHT&& resultCode ==RESULT_OK) {

if(data !=null) {

doubleweight = data.getDoubleExtra("weight", -1);

tv_show.setText(weight +"");

}

}

}

/*

* 设置返回结果

* 注意:这个方法只是用于设置返回值,当调用完以后不会直接将结果返回,

* 而是要等到当前的Activity被关闭之后才会自动将结果返回

*

* 参数1:resultCode,返回码,用于表示返回值的状态

* 参数2:Intent,意图对象,在这里的Intent只是用于Activity之间的数据传递,

*                               不是用于开启某个组件,所以不需要包含任何组件的信息

*/

Intent intent =newIntent();

intent.putExtra("weight", weiht);

setResult(Activity.RESULT_OK,intent);

finish();//关闭当前的Acitivity,,将结果返回给

1.6    Activity的生命周期

Activity生命周期的7个相关方法

onCreate(): 当Activity第一次被创建时被调用

onStart(): 当Activity可见时被调用

onResume(): 当Activity获取焦点时被调用

onPause():当Activity失去焦点时被调用

onStop(): 当Activity不可见时被调用

onDestroy(): 当Activity被销毁时被调用

onRestart(): 当Activity重新可见时被调用

各生命周期回调方法的使用场景

onCreate()、onDestroy——>  界面退出之前的数据保存,如:短信草稿

onStart()、onStop()——>  更新UI的操作,如:视频播放、暂停,在Activity可见和不可见时调用

onResume()、onPause()——>  取消界面上的焦点,如:暂停游戏,在Activity是在获取和失去焦点时调用

onRestart(): 当Activity重新可见时被调用,从Activity不可见重新到可见时调用

Activity生命周期的三个状态

Resumed: 运行状态,位于前台,可与用户进行交互

Paused: 另一个Avtivity位于前台,本Activity还可见,但是不可交互

Stoped: 另一个Activity位于前台,完全遮挡本Activity

Activity的三种生命周期

完整生命周期:onCreate—> onStart —>onResume —> onPause —> onStop —> onDestroy

可视生命周期: onStart —> onResume —> onPause —> onStop

前台生命周期: onResume —> onPause

思考题:

1、  当前有一个Activity在前台,当点击返回()

返回:onPause():当Activity失去焦点时被调用------onStop():不可见--------onDestroy()销毁

Home:onPause() :当Activity失去焦点时被调用------onStop():不可见

2、前台ActivityA,开启ActivityB?

A::onPause():当Activity失去焦点时被调用------onStop():不可见

B: onCreate —> onStart可见 —> onResume获取焦点

==Android系统内部有一个内存管理机制,进程是分等级的,当高等级的进程需要使用内存又不够用的时候,系统会自动杀死优先级较低的进程,将释放出来的内存分配给高优先级的进程,当系统中内存又够用的时候,系统会再自动将先前因内存不足被自动杀死的继承重新开启。

1.7    Activity的状态恢复

【Activity状态保存

在某些情况下,系统可能会对Activity进行重构(重新构建)

1、 系统资源不够用时

2、 切换系统语言

3、 横竖屏切换

。。。。。。。。。。。。。

在Activity重构的时候,会导致数据丢失,Android系统提供了状态恢复机制用于保存Activity的状态。

1、onSaveInstanceState(BundleoutState)中保存数据

2、onCreate(BundlesavedInstanceState)恢复数据

或者在onRestoreInstanceState(BundlesavedInstanceState)中恢复数据

如果在横竖屏切换的时候不需要重构Activity,那么我们可以在清单文件中进行位置,配置方式如下:

android:configChanges="orientation|screenSize|keyboardHidden"

配置完属性后,就可以放置在横竖屏切换时重构Activity

ps:android:configChanges的作用就是 配置的属性值如果发生改变就不会导致Activity的重构,取而代之的是会调用onConfigurationChanged()方法1、在onSaveInstanceState(BundleoutState)中对数据进行保存,将数据保存在outState对象中

2、从Bundle对象中读取数据,读取数据后重新更新到界面

a)在onCreate(BundlesavedInstanceState)方法中进行恢复

注意:需要判断savedInstanceState是否为空

b)在onRestoreInstanceState(BundlesavedInstanceState)方法中进行恢复

注意:不需要判断savedInstanceState是否为空

1.7.1  实例代码

public class MainActivity extends Activity{

privateTextView tv_num;

privateint num = 0;

@Override

protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Log.i("mtag","onCreate");

tv_num= (TextView) findViewById(R.id.tv_num);

/*

* 恢复数据

* 这里要注意的是,当Activity第一次其中时,是不存在数据恢复的情况,

* 所以,Bundle的引用是null

*/

//               if(savedInstanceState!= null) {

//                         intnum = savedInstanceState.getInt("num");

//                         tv_num.setText(num+"");

//               }

}

//当配置发生改变时调用

@Override

publicvoid onConfigurationChanged(Configuration newConfig) {

super.onConfigurationChanged(newConfig);

Log.i("mtag","onConfigurationChanged");

if(newConfig.orientation== Configuration.ORIENTATION_LANDSCAPE) {

Toast.makeText(this,"横屏", 0).show();

}else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

Toast.makeText(this,"竖屏", 0).show();

}

}

/**

* 当Activity自动重构时调用,主要用于恢复Activity中的数据

*/

@Override

protectedvoid onRestoreInstanceState(Bundle savedInstanceState) {

super.onRestoreInstanceState(savedInstanceState);

Log.i("mtag","onRestoreInstanceState");

intnum = savedInstanceState.getInt("num");

tv_num.setText(num+"");

}

/**

* 当Activity自动重构时调用,主要用于保存当前Activity的状态(内部的数据)

* 参数:outState,Bundle对象,是框架传入的一个对象,主要用户保存Activity中的数据

*/

@Override

protectedvoid onSaveInstanceState(Bundle outState) {

super.onSaveInstanceState(outState);

Log.i("mtag","onSaveInstanceState");

//将数据保存在Bundle对象中

outState.putInt("num",num);

}

@Override

protectedvoid onDestroy() {

super.onDestroy();

Log.i("mtag","onDestroy");

}

publicvoid add(View v) {

//每次点击按钮之后都会加1

tv_num.setText((++num)+"");

}

}】

1.8    任务和后退栈(Task and Back Stack)

概念

任务(Task)

一个应用程序一般都是由多个Activity组成的,task就是多个 Activity的集合

后退栈(Back Stack)

就是一个保存和管理应用程序中所有Activity的容器

用户在进行操作时将与task中的Activity进行交互,这些Activity会按照启动顺序排队存入后退栈

Android为什么要引入任务和后退栈的概念?

在Windows下可以同时开启多个程序,并且可以在多个程序之间进行切换,在Andorid下也可以开启多个程序,可以通过长摁home键来查看开启的App,要实现在多个Activity之间进行切换,那么就要将已经打开的Activity保存在内存中,而Android系统是通过后退栈来存储已经开启的Activity,并记录他们开启的先后顺序。

1.9    Activity的启动模式

android:launchMode=""在清单文件中配置即可

standard: 默认的启动模式,每次启动Activity都会创建一个新的Activity实例

singleTop:如果Activity位于栈顶,则不再生成实例,而是调用onNewIntent()方法

singleTask:如果存在实例,则带至栈顶并销毁它上面所有的Activity

singleInstance:无论当前Activity是否是同一个App的,都只用一个实例

推荐阅读更多精彩内容