Activity生命周期
onCreate、onStart、onResume、onPause、onStop、(onRestart())、onDestroy
Activity启动模式
Activity的管理方式 = 任务栈
任务栈 采用的结构 = “后进先出” 的栈结构
每按一次Back键,就有一个Activity出栈
4种启动模式,标准模式(Standard)、栈顶复用(SingleTop)、栈内复用(SingleTask)、单例(SingleInstance)
- 4种启动模式特点
- 4种启动模式区别
- 启动模式有2种设置方式
AndroidMainifest设置、通过Intent设置标志位
- 在AndroidMainifest设置
在AndroidMainifest的Activity配置进行设置
<activity=
android:launchMode="启动模式"
//属性
//standard:标准模式
//singleTop:栈顶复用模式
//singleTask:栈内复用模式
//singleInstance:单例模式
//如不设置,Activity的启动模式默认为**标准模式(standard)**
</activity>
- 通过Intent设置标志位
Intent inten = new Intent (ActivityA.this,ActivityB.class);
intent,addFlags(Intent,FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
标记位属性
标记位属性 含义
FLAG_ACTIVITY_SINGLE_TOP 指定启动模式为栈顶复用模式(SingleTop)
FLAG_ACTIVITY_NEW_TASK 指定启动模式为栈内复用模式(SingleTask)
FLAG_ACTIVITY_CLEAR_TOP 所有位于其上层的Activity都要移除,SingleTask模式默认具有此标记效果
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 具有该标记的Activity不会出现在历史Activity的列表中,即无法通过历史列表回到该Activity上
二者设置的区别
优先级不同
Intent设置方式的优先级 > Manifest设置方式,即 以前者为准
限定范围不同
Manifest设置方式无法设定 FLAG_ACTIVITY_CLEAR_TOP;Intent设置方式 无法设置单例模式(SingleInstance)
- 4种启动模式应用场景
singleTop适用点击通知栏通知启动的页面。
1)某个应用通知来了几十条,那么使用此模式能避免每次打开一个界面。
2)很多电商类的App,商品列表点击进去是商品详情页,商品详情页下面又有商品列表点进去还是商品详情页。
singleTask适用App的入口页面。
1)例子:浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。
2)例子:购物App的主界面,在陆续打开商品搜索界面、商品详细界面、订单界面、付款成功界面后,在付款成功界面一键返回主界面。
singleInstance适合需要保持活动独立性的场景,这种启动模式确保在整个手机操作系统中,只有一个实例存在,并且保证不再有其他Activity实例进入该Activity的任务栈中。它适用于以下具体场景:
如 经常调用的拨打电话、系统通讯录、系统Launcher、锁屏键、来电显示等系统应用。
缓存页面:对于播放视频或音频的页面,使用singleInstance模式可以确保在按返回键后继续播放,同时通过全局悬浮窗随时打开,且打开时不会总是新建页面,保持页面的独立性。
与程序分离的页面:例如闹铃提醒功能,将闹铃提醒与闹铃设置分离,使得功能更加独立和清晰。
独立的任务栈:需要在不同的应用程序或系统中启动该活动,并保持独立的任务栈,适用于需要在活动启动时始终显示在最顶层,不受其他活动影响的情况。
如 闹铃提醒,将闹铃提醒与闹铃设置分离。
singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。
Activity启动方式
启动Activity的方式主要是:显式Intent & 隐式Intent
- 显式Intent(3种)
// 1. 使用构造函数 传入 Class对象
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
// 2. 使用 setClassName()传入 包名+类名 / 包Context+类名
Intent intent = new Intent();
// 方式1:包名+类名
// 参数1 = 包名称
// 参数2 = 要启动的类的全限定名称
intent.setClassName("com.hc.hctest", "com.hc.hctest.SecondActivity");
// 方式2:包Context+类名
// 参数1 = 包Context,可直接传入Activity
// 参数2 = 要启动的类的全限定名称
intent.setClassName(this, "com.hc.hctest.SecondActivity");
startActivity(intent);
// 3. 通过ComponentName()传入 包名 & 类全名
Intent intent = new Intent();
// 参数1 = 包名称
// 参数2 = 要启动的类的全限定名称
ComponentName cn = new ComponentName("com.hc.hctest", "com.hc.hctest.SecondActivity");
intent.setComponent(cn);
startActivity(intent);
- 隐式Intent
// 通过Category、Action设置
Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addCategory("com.hc.second");
intent.setAction("com.hc.action");
startActivity(intent);
启动App过程(从系统角度分析)
https://blog.51cto.com/u_16213595/9838739
普通Activity启动过程(从系统角度分析)
Launcher启动根Activity启动过程(从系统角度分析)
具体流程:
当请求启动Activity时:
Launcher进程通过Binder驱动向ActivityManagerService类发起startActivity请求;
ActivityManagerService类接收到请求后,向ActivityStack类发送启动Activity的请求;
ActivityStack类记录需启动的Activity的信息 & 调整Activity栈 将其置于栈顶、通过 Binder 驱动 将 Activity 的启动信息传递到ApplicationThread线程中(即Binder线程)
ApplicationThread线程通过Handler将Activity的启动信息发送到主线程ActivityThread
主线程ActivityThread类接收到该信息 & 请求后,通过ClassLoader机制加载相应的Activity类,最终调用Activity的onCreate(),最后 启动完毕
Activity卡顿原因
- 总结有以下几点:
1、内存泄漏
原因:内存泄漏导致内存占用高,导致JVM频繁触发GC
2、加载大数据
原因:内存泄漏导致内存占用高,导致JVM频繁触发GC
3、UI线程做了耗时逻辑
原因:主线程被阻塞
解决方案:另起工作线程,执行耗时操作
4、UI视图过度绘制
原因:绘制时间过长
解决方案:缩短UI视图绘制时间
- 加速启动Activity方式总结
Activity中提供3种工作线程访问UI线程方法
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
Fragment生命周期
- 生命流程图
-
详细解析
-
常见场景的生命周期流程
-
与Activity生命周期对比
保存 & 恢复Activity 状态缓存
Activity保存临时数据 & 状态onSaveInstanceState()
-
核心方法
onSaveInstanceState() -
调用时机
当系统 未经你许可 时,可能 销毁了你的Activity,则会被系统调用 。
特别说明:
“可能“ 仅表达一种可能性,而不是确实销毁,下面会继续讲解
若是 被用户主动销毁(如 用户按Back键),则不会调用
肯定在 调用onStop()前被调用,但不保证在onPause()前 / 后
3. 具体调用场景
假定为Activity A显示在当前Activity栈的最上层时,以下情况会执行onSaveInstanceState()
注:系统不知道你切换到其他地方后要运行多少其他的程序,自然也不知Activity A是否会被销毁,故系统会调用onSaveInstanceState(),下面所说的所有情况该遵循这原则
4. 使用说明
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// 通过Bundle参数以键值对的方式进行数据的存储
// 数据恢复:onRestoreInstanceState() & onCreate()
// 上述二者都有一个Bundle类型的参数用于恢复数据
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// ...
super.onSaveInstanceState(savedInstanceState);
}
补充说明:
布局每1个View默认实现:onSaveInstanceState(),即UI的任何改变都会自动的存储和在activity重新创建的时候自动的恢复(只有在为该UI提供了唯一ID后才起作用)
若需复写该方法从而存储额外的状态信息时,应先调用父类的onSaveInstanceState()(因为默认的onSaveInstanceState()帮助UI存储它的状态)
只使用该方法记录Activity的瞬间状态(UI的状态),而不是去存储持久化数据,因为onSaveInstanceState()调用时机不确定性;可使用 onPause()存储 持久化数据
Activity恢复临时数据 & 状态onRestoreInstanceState
-
核心方法
onRestoreInstanceState() -
调用时机
当系统“未经你许可”时,确实销毁了你的Activity,则重新启动时会被系统调用
特别说明:
与onSaveInstanceState()区别:此处是 “确实销毁”后才调用
若是 被用户主动销毁(如 用户按Back键),则不会调用
肯定在调用 onStop()前被调用,但不保证在onPause()前 / 后
3. 具体调用场景
若 异常关闭了Activity,即调用了onSaveInstanceState() & 下次启动时会调用onRestoreInstanceState()
注:此时结合Activity的生命周期的调用顺序是:
onCreate()
onStart()
onRestoreInstanceState()
onResume()
- 使用示例
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}
onSaveInstanceState()、onRestoreInstanceState()不一定 成对被调用
如:当正在显示Activity A时,用户按下HOME键回到主界面,然后用户紧接着又返回到Activity A,此时Activity A一般不会因为内存的原因被系统销毁,故Activity A的onRestoreInstanceState()不会被执行
onSaveInstanceState的bundle参数会传递到onCreate方法中,可选择在onCreate()中做数据还原
Activityg&Fragment关系与通信
Activity传递数据到 Fragment,通过Bundle方式
具体Demo步骤如下:
步骤1:Activity的布局文件
activcity_2_fragment.xml
步骤2:设置 Fragment的布局文件
fragment.xml
步骤3:设置Activity的类文件
Activity2Fragment
public class Activity2Fragment extends AppCompatActivity {
TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activcity_2_fragment);
text = (TextView) findViewById(R.id.text);
// 步骤1:获取FragmentManager
FragmentManager fragmentManager = getFragmentManager();
// 步骤2:获取FragmentTransaction
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 步骤3:创建需要添加的Fragment
final mFragment fragment = new mFragment();
// 步骤4:创建Bundle对象
// 作用:存储数据,并传递到Fragment中
Bundle bundle = new Bundle();
// 步骤5:往bundle中添加数据
bundle.putString("message", "I love Google");
// 步骤6:把数据设置到Fragment中
fragment.setArguments(bundle);
// 步骤7:动态添加fragment
// 即将创建的fragment添加到Activity布局文件中定义的占位符中(FrameLayout)
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
}
}
步骤4:设置Fragment的类文件
mFragment.java
public class mFragment extends Fragment {
Button button;
TextView text;
Bundle bundle;
String message;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.fragment, container, false);
// 设置布局文件
button = (Button) contentView.findViewById(R.id.button);
text = (TextView) contentView.findViewById(R.id.text);
// 步骤1:通过getArgments()获取从Activity传过来的全部值
bundle = this.getArguments();
// 步骤2:获取某一值
message = bundle.getString("message");
// 步骤3:设置按钮,将设置的值显示出来
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 显示传递过来的值
text.setText(message);
}
});
return contentView;
}
}
至此,Activity 传递数据到 Fragment 讲解完毕。
Fragment 传递数据到 Activity,通过接口回调
具体Demo
步骤1:在Activity的布局文件定义1占位符(FrameLayout)
activity_main.xml
步骤2:设置Fragment的布局文件
fragment.xml
步骤3:设置回调接口
该接口用于用于Activity与Fragment通信
ICallBack.java
public interface ICallBack {
void get_message_from_Fragment(String string);
}
步骤4:设置Fragment的类文件
mFragment.java
public class mFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.fragment, container, false);
// 设置布局文件
return contentView;
}
// 设置 接口回调 方法
public void sendMessage(ICallBack callBack){
callBack.get_message_from_Fragment("消息:我来自Fragment");
}
}
步骤5:设置Acticvity的类文件
Main_Activity.java
public class MainActivity extends AppCompatActivity {
Button button;
TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button);
text = (TextView)findViewById(R.id.text);
// 步骤1:获取FragmentManager
FragmentManager fragmentManager = getFragmentManager();
// 步骤2:获取FragmentTransaction
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 步骤3:创建需要添加的Fragment
final mFragment fragment = new mFragment();
// 步骤4:动态添加fragment
// 即将创建的fragment添加到Activity布局文件中定义的占位符中(FrameLayout)
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通过接口回调将消息从fragment发送到Activity
fragment.sendMessage(new ICallBack() {
@Override
public void get_message_from_Fragment(String string) {
text.setText(string);
}
});
}
});
}
}