一、活动是什么
安卓开发中,活动(activity)是四大组件之一。它是一种可以包含用户界面的组件,主要用于和用户进行交互,可以说是一个安卓app的门户了
二、活动的用法
一般而论,通过在Android studio新建 activity,自动生成的代码就完成了一个活动的基本创建。而拆分来一步步看主要包含的步骤如下:
1.新建类MainActivity继承AppCompatActivity,并重写父类的onCreate()方法
2.在AndroidManiFest文件中注册该activity,如果要设置作为app的启动界面则还要用intent-filter的两行代码来限定
3.一般来说,还要为activity加载一个视图文件来达到一个展示内容的目的,activity主要用来处理交互时的逻辑,所以一般都会加入一句setContentView(),括号内用R文件引用所想要加载的对应的布局文件的xml
小问题:
Q:为什么新建的activity要继承AppCompatActivity而不是Activity?
A:查了一下网上的资料,简单来说就是为了达到更好的向下兼容,继承这个AppCompatActivity可以对一些基础组件完成一个统一的样式风格,其他好像没有什么提到的区别。
三、intent的使用
一个丰富的app肯定不只有一个界面,因此当我们需要不同的功能模块切换的时候必然涉及到我们activity的跳转(实际中可能活动中嵌套多个fragment来减少activity数量),而不同的界面之间的跳转则需要借助到intent来实现了
intent大致分为显式intent和隐式intent,使用的方法有所不同:
1.显式intent
显式intent顾名思义也就主要是一个比较直接的意图,在实例化intent这个类的时候重载两个参数的构造方法,前一个参数是context类要求提供一个启动活动的上下文(个人理解为当前跳转意图的起点环境),此处一般为所在的activity,第二个参数是class类即想要启动的目标活动(对应理解为终点),然后再通过activity类中的startActivity()方法将实例化好的intent传入即可
2.隐式intent
相比显式intent,隐式intent并不直接指明跳转的目标活动,而是通过匹配活动在AndroidManifest文件中注册信息中<intent-filter>的action和category等信息,通过这些信息让系统来找出符合的活动
此处intent的构造函数只接受一个参数,就是字符串形式的内容,对应的是AndroidManifest文件中活动中的<intent-filter>中Action标签内Android name的内容,直接copy-paste二连进来即可。隐式intent可以匹配一个action和多个category,所以想要匹配的category可以在后面加addCategory(),如果匹配到多个就会弹框出来选择,匹配不到的话app会直接crash,个人感觉这个用法比较鸡肋没啥应用场景,要跳转就明确跳转哪一个activity就完了,再到manifest写一些信息还是挺麻烦的,可能就考虑到多人协作开发的时候可以用吧,就不再赘述了。
除了内部程序的活动,我们还可以通过隐式intent来调用外部程序的活动,比如当我们想要展示一个网页的时候,我们本身的app一般都是调用一个外部的浏览器app来展示,此时可以对intent的代码这样写:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
上面的代码很少,第一行就是重载构造函数的时候指定action为ACTION_VIEW,这是一个内置的常量值,然后通过Uri.parse()方法,将一个网站字符串解析成一个uri对象,再调用setData()把这个Uri对象传递进去,就完成了一个打开浏览器的代码块。
除了响应打开一个网站之外,还可以指定调用其他的操作,比如唤起手机的拨号,只需要将上面的Action改为Intent.ACTION_DIAL,然后将网站的字符串改为手机号的字符串"tel:10086"的值即可
3.通过Intent在活动之间传递数据
很多时候,在活动跳转的时候,我们需要携带一些前面的活动得到的临时的数据到后面的活动中来使用,比如登录成功后进入用户界面需要一个特定的数据(用户名或者后台专门生成的特殊码)来获取该用户的信息,通过这个特定的数据来向后台get数据,那么就需要在活动之间来传递信息,而我们可以通过intent来实现这一功能。
String data = new String("hello world");
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("my_data",data);
在跳转的intent实例化结束后,通过putExtra()方法将数据放入,该方法接收两个参数,第一个参数是所传数据的key,即标签,字符串形式,用来后续取这个数据,第二个参数则是所传数据,此处是上面定义的一个string对象。
Intent intent = getIntent();
String data = intent.getStringExtra("my_data");
而在我们需要拿到这个数据的时候,我们就在跳转到的那个活动内先通过getIntent()方法获得intent实例,然后在通过intent.getStringExtra()方法传入前面的key来获取数据就可以拿到了,如果传的是int数据就getIntExtra() 方法,以此类推其他数据类型都大同小异。
那可以通过给下一个活动传数据,自然也就可以给上一个活动传数据了。
Intent intent = new Intent("FirstActivity.this,SecondActivity.class");
startActivityForResult(intent,1)
在上面的代码中,我们在跳转前的activity构造一个intent实例,然后调用了一个startActivityForResult()方法,该方法接收两个参数,一个是intent,第二个参数是请求码,用来后面回调的时候判断数据来源,只要是一个唯一值就行了,此处输入一个int值——1。
Intent intent = new Intent();
intent.putExtra("data","this is secondActivity");
setResult(2,intent)
finish();
下一步我们所做的就是重写在SecondActivity的onBcakPressed()方法,在其中构造一个intent实例,然后携带一个数据,最后再常规性的将活动销毁掉,这里面的setResult()方法接收两个参数,第一个是一个结果码,用来向上一个活动返回处理结果,内置的有RESULT_OK和RESULT_CANCELED两个值可以用,这里自己写一个2也可以。
最后要回到Firstactivity,因为前面的startActivityForResult()方法启动的SecondActivity,SecondActivity被销毁后会回调OnActivityResult()方法,该方法接收三个参数,分别是前面的请求码,结果码和intent,通过请求码和结果码可以准确定位返回数据的activity从而收到该activity传回的数据,以上就完成了往上一个活动回传数据的逻辑了。
四、活动的生命周期
活动的生命周期可以说是深刻理解活动的前提了,通过掌握活动的生命周期的情况才能在各个生命周期做不同的逻辑处理来帮助我们开发app应用,自己也不敢说懂多少,此处也就还是简单罗列一下了。
首先,Activity的生命周期顾名思义就是Activity从创建到销毁的过程,而Activity在其生命周期中有四个状态:运行状态,暂停状态,停止状态,销毁状态。首先,要知道活动是由栈的数据结构来管理的,每当一个活动正在运行,那么他就会创建入栈到栈顶,销毁则从栈顶出栈,也就是经典的“先进后出,后进先出”逻辑。
运行状态就是一个活动正常运行在当前页面,也就是在活动栈的栈顶。
暂停状态就是一个活动不在栈顶,但仍然可见时的状态。一般是调用出了对话框形式的活动或者透明的视图的活动。
停止状态则是活动不在栈顶,且完全不可见时的状态。系统会为这种活动保留相应的状态和成员变量,但是并不可靠,因为这种状态的活动在内存不足时容易被系统回收。
销毁状态则是从返回栈移除的活动的状态。
Activity中定义了七个回调方法来覆盖活动的生命周期。
onCreate()方法:这个方法在活动创建时会被调用,一般我们会在这个地方把加载布局和控件初始化的逻辑做完,应该是最常见最常用的回调方法了。
onStart()方法:这个方法在活动由不可见变为可见时调用。
onResume()方法:这个方法在活动准备好和用户进行交互的时候调用,个人理解是一个唤醒到当前屏幕的意思,此时的活动一个处于活动栈的栈顶,并且处于运行状态。
onPause()方法:这个方法在系统准备去启动或者恢复另一个活动的时候调用。与onResume()方法相对应,这个方法执行了之后可能当前活动就变成了暂停状态。
onStop()方法:这个方法在活动完全不可见的时候调用,与onStart() 方法相对应。
onDestroy() 方法:这个方法用来销毁活动用,调用后活动的状态也跟着进入销毁状态。
onRestart() 方法:这个方法在活动由停止状态变为运行状态之前调用,也就跟名字一样,重启。
简单描述一下上面图的几种过程就是:
假如我们创建一个activity到前台,然后直接back键退出,那么这个活动调用方法顺序就分别是onCreate(),onStart(),onResume()三连创建显示到当前,然后onPause(),onStop(),onDestroy()三连退出
假如我们创建了一个activity到前台,然后再创建跳转到另一个新的activity,再从新的activity按back键返回到上一个activity,那么整个过程就是第一个活动onCreate(),onStart(),onResume()三连创建显示到当前,然后跳转的时候这边的方法onPause(),如果第二个activity是完全挡住第一个activity则还要onStop()方法,然后第二个活动onCreate(),onStart(),onResume()三连创建显示到当前,再然后back键的时候onPause(),onStop(),onDestroy()三连退出销毁,然后再调用第一个activity的onRestart() 方法回来,再onStart(),onResume()二连再将第一个活动展示到当前屏幕,如果只到了onPause()方法,那么回来的时候也只调用onResume()就足够了。
而假如一个活动调用了 onStop()方法后,由于系统内存紧张的关系被回收了,此时再重新返回这个活动则不会调用上述过程,而是重新调用onCreate(),onStart(),onResume()三连回到这个活动,如果活动的逻辑中有必需的临时数据(比如用户已经登录的状态),要在onSaveInstanceState()方法中通过Bundle保存下来,然后在onCreate()方法中判断Bundle是否为空来把这个数据获取下来防止因为临时数据的缺失在这种特殊情况下活动无法正常显示的问题。
五、活动的启动模式
活动是有四个启动模式的,分别帮助我们完成不同场景下需要的activity的状态,启动模式的指定就在AndroidManifest文件中activity的标签里加入对应的启动模式名字即可
android:launchMode="singleTop"
standard:标准模式,也是活动的默认启动方式。该模式下,每次创建新的活动,都不会管有没有本身这个活动的实例存在,依旧创建该活动的新的实例入栈。大部分情况下
singleTop:对应standard模式,在这个启动模式下,当一个活动已经在栈顶的时候,再创建这个活动都不会产生它的实例了
singleTask:再针对singleTop模式而论,假如我们不想重复创建的activity不在栈顶,那我们一旦重复创建的时候它还是会重复创建,而singleTask模式则可以解决这个问题,singleTask模式下,只要栈中有这个活动实例存在,就不会再重复创建,每次启动该活动会直接调用已经存在栈中的这个实例。在一些app的主页中,由于跳转其他活动进行各种逻辑操作,最后返回主页我们也希望调用回原来第一次创建的主页活动就好而不是新建立一个实例,因此主页的activity基本都会使用这个模式。
singleInstance:这个启动模式跟上述三个不太一样,这个启动模式下活动会启动一个新的栈来管理这个活动。应用场景在于这样启动的活动新开辟的栈可以用来被其他程序共享这个实例。