四大组件 - Activity
定义
Activity实际上只是一个与用户交互的接口
生命周期
1. Activity生命周期中的4种状态
- Active:当前Activity正处于运行状态,即获得焦点
- Paused:当前Activity正处于暂停状态,即失去焦点。但仍然没有被销毁,仍然可见。
- Stopped:当前Activity处于停止状态,Activity不可见,没有被销毁
- Killed:当前Activity已经被销毁,它的内存里面的成员变量等信息被一并回收
2. Activity生命周期里的方法
- 3.1 onCreate()
- 3.2 onStart()
- 3.3 onResume()
- 3.4 onPause()
- 3.5 onStop()
- 3.6 onDestory()
- 3.7 onRestart()
3.1 onCreate()
该方法表示Activity正在被创建,这是Activity生命周期的第一个方法。通常在里面进行初始化工作
3.2 onStart()
该方法表示Activity正在被启动,这个时候的Activity已经被创建好了,完全过了准备阶段,但是没有出现在前台,还不能与用户进行交互
3.3 onResume()
该方法表示Activity已经可见了,即出现在前台且可以与用户进行交互了。处于Active状态
3.4 onPause()
该方法表示Activity正在暂停,大多情况下Activity执行完onPause()方法后会继续执行onStop()方法。至于什么情况下会继续执行onStop()方法可以简单的分为两种情况:
1. 当前Activity启动了另外一个Activity或者回切到上一个Activity时就会执行onStop()方法
2. 当前Activity启动了类似于话框的东西时,就值执行onPause()方法,而不会执行onStop()方法
3.5 onStop()
该方法表示Activity即将停止,我们应该在此方法中做一些不那么耗时的轻量级回收操作
3.6 onDestory()
该方法表示Activity要被销毁了,这是Activity生命周期的最后一个阶段,我们可以在该方法里面做一些回收工作和资源释放
3.7 onRestart()
该方法表示Activity正在重新启动。一般情况下,一个存在于后台不可见的Activity变为可见状态,都会执行onRestart()方法,然后再执行onStart()方法
3. Activity的生命周期分析
-
3.1 正常情况下的生命周期:
1. Activity启动 → onCreate() → onStart() → onResume() ---------进入Active状态
2. 点击Home键回到桌面 → onPause() → onStop() -----------进入Stopped状态
3. 再次回到原Activity → onRestart() → onStart() → onResume() --------又回到Active状态
4. 退出当前Activity时 → onPause → onStop → onDestroy() ----------Killed状态 -
3.2 异常情况下的生命周期:
中间涉及两个方法:
onSaveInstanceState() 方法:
主要用来存储数据(调用的时机在onStop()方法之前)onRestoreInstanceState() 方法:
主要用来恢复数据(调用时机在onstart()之后)
3.2.1 因资源配置发生改变导致终止的时候:Activity会被销毁然后重建。
当销毁时,其onPause()、onStop()、onDestory()方法均会被调用。系统会自动调用onSaveInstanceState()方法来保存当前Activity的状态。
当重建时,系统会调用onRestoreInstanceState()方法,并且把onSaveInstanceState()方法所保存的Bundle对象作为参数传递给onRestoreInstanceState()方法和onCreate()方法。然后通过这两个方法来取出之前保存的数据进行恢复。
- 当异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启后为我们恢复这些数据(例如:文本框输入的数据、ListView滚动的位置)
- 这些View相关的状态系统都能默认为我们恢复,具体针对某一特定的View系统能为我们恢复哪些数据,就得查看View的源码
关于保存和恢复View层次结构,系统的工作流程:
- 当Activity被意外终止时,Activity会调用onSaveInstanceState()方法去保存数据。
1.Activity会委托Window类(PhoneWindow类)去保存数据
2.Window类会委托它的顶级容器(DecorView类)去保存数据
3.DecorView类再去通知它的子元素来保存数据
3.2.2 资源内存不足导致优先级低的Activity被杀死
Activity的优先级是指一个Activity对于用户的重要程度,。按照这种规律分为以下等级:
- 最高优先级:前台Activity
- 中等优先级:可见但非前台的Activity (Pause状态下的Activity)
- 最低优先级:完全存在后台的Activity (Stopped状态下的Activity)
注:当内存严重不足时,系统会按照优先级去kill掉Activity所在的进程,并在后续通过onSaveInstanceState()方法和onRestoreInstanceState()方法来储存和恢复数据。
-
3.3 特殊情况下的生命周期
3.3.1 Activity的横竖屏切换
与横竖屏生命周期方法有关调用的属性是“ android:configChanges ”,关于它的属性值设置,如下:
android:configChanges=" orientation " :消除横竖屏的影响 android:configChanges=" keyboardHidden " :消除键盘的影响 android:configChanges=" screenSize " :消除屏幕大小的影响
情况 1 当我们设置Activity的 android:configChanges 属性为
orientation 、(消除横竖屏影响)
orientation|keyboardHidden(消除横竖屏、键盘的影响)
或者不设置该属性的时候,其生命周期如下启动Activity
onCreate() → onStart() → onResume()由竖屏切换到横屏
onPause() → onSaveInstanceState() → onStop() → onDestory()
/////到这里竖屏的已经销毁了,下面开始创建横屏的
→ onCreate() → onStart() → onRestoreInstanceState() → onResume()由横屏切换到竖屏(与竖屏切换横屏过程一致)
- 情况 2 当我们设置其android:configChanges 属性为
orientation | screenSize
或 orientation | screenSize | keyboardHidden 时
启动Activity
onCreate() → onStart() → onResume()由横屏切换竖屏(由竖屏切换横屏):什么也没调用
-
情况 3 当我们设置为 orientation | screenSize 时
在进行横竖屏切换的时候调用的方法是onConfiguraionChanged(),而不会回调Activity的任何一个生命周期方法
屏蔽横竖屏有两种:
1. xml布局文件设置
2. java代码中设置1.xml布局文件设置:
android:screenOrientation = " portrait " 始终以竖屏显示 android:screenOrientation = " landscape " 始终以横屏显示
2.java代码中设置:
Acticity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) //始终以竖屏显示 Acticity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) //始终以横屏显示
- 情况 2 当我们设置其android:configChanges 属性为
3.3.2 什么情况下导致Activity的onDestory()方法不执行
当用户后台强杀应用程序时,onDestory()有时会不执行。分两种情况:
- 情况 1:当前返回栈仅有一个Activity实例,这时候强杀是会执行onDestory()方法的
-
情况 2:当返回栈里存在多个Actiivty实例时,栈里面的第一个没有销毁的Activity会执行onDestory()方法,其他不会执行。
例如:从 MainActivity 跳转到 Activity_A 。这时候,从后台强杀,只会执行MainActivity的onDestory()方法,至于Activity_A的onDestory()是不会执行的
Activity的启动模式
说到启动模式,就会联想起 Android任务栈 ,关于Android任务栈:
- 它是用来储存Activity实例的一种数据结构,Activity的跳转以及回调都与任务栈有关
- 启动一个Activity后,这个Activity实例就会放进任务栈中,当点击返回键时,位于任务栈顶层的Activity就会被清理出去。
- 当任务栈不存在任何Activity的实例的时候,系统就会回收这个任务栈,也就是退出程序了。
启动模式的作用
杜绝浪费内存的行为
如果一个Activity频繁启动,那么便会往任务栈里面放进多个相同的实例。这对内存而言不是好事,明明一个Activity实例就可以应付所有的启动需求,实际上缺放了这么多个,造成了内存的浪费。因为此启动模式也就应运而生。
启动模式类型
- standard
- singleTop
- singleTask
- singleInstance
-
standard启动模式
系统默认的启动模式。
每次启动一个Activity就会新建一个实例(不管其实例是否存在)假如 Activity_A 启动了 Activity_B ,即 Activity_B 会进入Activity_A 所在的任务栈
- 注:非Activity的context(ApplicationContext)没有所谓的任务栈。即当用ApplicationContext去启动 standard模式 的Activity会报错。解决方法就是为待启动的Activity指定 FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会为它创建一个新的任务栈。
-
singleTop启动模式
栈顶复用模式,在这种模式下:
1. 如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建。同时它的onNewIntent()方法会被回调,通过此方法的参数我们可以取出当前请求的信息(此时其onCrate()、onStart()方法不会被调用)。
2. 如果新的Activity在栈中,但不在栈顶。则新的Activity仍然会被创建,并放进其中。 -
singleTask启动模式
栈内复用模式,这是一种单例模式
在这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例。和singleTop一样也会回调其onNewIntent()方法。
当一个具有singleTask模式的Activity请求启动后,系统首先寻找任务栈中是否已存在该Activity的实例,如果已经存在,那么系统就会把它跳到栈顶并调用onNewIntent()方法。否则创建该Activity的实例并压栈
singleTask模式默认具有 clearTop 的效果,即系统将目标Activity跳到栈顶的方法是:将在该Activity上面的Activity全部出栈,以达到跳到栈顶的效果
-
singleInstance启动模式
单实例模式
这是一种加强版的 singleTask 模式,此外此种模式的Activity只能单独位于一个任务栈中在此模式下的Activity由以下特点:
1. 具有全局唯一性,即整个系统中只会存在一个这样的实例
2. 在整个系统中是单例的,如果在启动这样的Activity时,已经存在一个实例,那么会把它所在的任务调度到前台,重用这个实例
3. 具有独占性,即它会独自占用一个任务栈,被它开启的Activity都会运行在其他任务栈中
4. 能够在新的任务栈中启动,但不一定开启新的任务栈,也有可能在已有的任务栈中开启
启动模式的使用
-
在AdnroidMainifest.xml 文件注册活动中设置
<activity android:name=" .MainActivity " ..... <android:launchMode=" singleInstance "> </activity>
-
在代码中指定启动模式
Intent pack=new Intent(MainActivity.this,Main2Activity.class); pack.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(pack);
Activity组件之间的通信
-
Activity之间的通信
1. 类静态变量
2. 全局变量
3. Intent / Bundle
Bundle bundle = new Budle();
bundle.putString("data","数据");
Intent intent = new Intent(MainActivity.this,Main2Activity.class);
intent.putExtras(bundle);
startActivity(intent);
-
Activity与Service之间的通信
1. 通过调用bindService()方法绑定服务
2. 启动Service时通过传入Intent对象进行通信//在Activity中 Intent intent = new Intent(MainActivity.this,MyService.class); intent.putExtra("data","数据"); startService(intent); //在Service中 public int onstartCommand(Intent intent,int flags,int startId){ String str=intent.getStringExtra("data"); return super.onStartCommand(intent,flags,startId); }
3. CallBack + Handler ,监听服务的进程变化
Service中的代码:
public class MyService extends Service{ public Callback callback; public MyService(){} @Override public IBinder onBind(Intent intent){ return new Binder(); } public void setCallBack(CallBack callBack){ this.callback = callBack; } public CallBack getCallBack(){ return callback; } public interface CallBack{ void onDataChange(String data); } public class Binder extends android.os.Binder{ public MyService getMyService(){ return MyService.this; } }
Activity中的代码:
public class MainActivity extends AppcompatActivity implements ServiceConnection{ public MyService.Binder binder = null; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg){ super.handleMessage(msg); Bundle bundle = msg.getData(); String data = budle.getString("data"); //接下来就是UI更新 } }; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public void onServiceConnected(ComponentName componentName,IBinder iBinder){ binder = (MyService.binder) iBinder; binder.getMyservice().setCallBack(new MyService.CallBack(){ //此方法提供给MyService在子线程调用 @Override public void onDataChange(String data){ Message message = new Message(); Bundle bundle = new Bundle(); bundle.putString("data","数据"); message.setData(bundle); //通过handler进行异步通信,耗时操作应放在MyService中 handler.sendMessage(message); } }); } @Override public void onServiceDisconnected(ComponentName componentName){ } }
-
Activity 与 Fragment 之间的通信
1. Bundle
在创建 Fragment实例 的时候,调用setArguments()方法将一个Bundle对象传递给 Fragment ,然后在 Fragment 中先去判断是否和当前 Activity 绑定上了,如果绑定上了就可以拿出这个Bundle中的数据Activity中的代码:
Bundle bundle = new Bundle(); bundle.putString("data","数据"); Fragment fragment = new MyFragment(); fragment.setArguments(bundle);
MyFragment中的代码:
if(isAdded()){ // isAdded()方法判断是否与Activity进行了绑定 // 因为如果没有进行绑定的话,那么Bundle对象是无法从Activity传递给Fragment的 Bundle bundle = getArguments(); String data =bundle.getString("data"); }
2. 直接进行方法调用
在Activity里通过Fragment的引用,可以直接调用Fragment中定义的任务方法MyFragment myFragment = new MyFragment(); myFragment.toString("数据");
-
scheme 跳转协议
Android中的一种跳转协议,通过自定义scheme协议,可以非常方便的跳转到app中的各个页面,通过该协议,服务器可以定制化告诉app跳转到哪个页面,可以通过通知栏消息定制化跳转页面,可以通过 H5 页面跳转到相应页面等。