目录
- Activity的简介
- Intent 的显式和隐式跳转
- 显式跳转
- 隐式跳转
- 程序入口
- 传递数据
- 可传递类型
- 传递方式
- Activity 间传递图片的过程
- Activity 的生命周期
- 活动状态
- Activity 跳转会执行哪些方法
- 横竖屏切换时 Activity 的生命周期
- 如何保存 Activity 的状态
- 固定屏幕方向
- 安全退出应用的所有 Activity
- Activity 的任务栈
- 任务栈的简介
- 任务栈的缺点
- Activity 的四种启动模式(LaunchMode)
- 启动 SingleTop 模式的 Activity 的情形 -> `onNewIntent(Intent)`
- Context简介
- Context,Activity,Appliction的区别
- 同
- 异
- 菜单 Menu
Activity的简介
- Android 四大组件之一,是 Context 的子类,同时实现了 window.callback 和 keyevent.callback
- 可处理窗体与用户的交互事件,可以通过 setContentView(View) 来显示指定页面
- 一个 Activity 即一个单独的屏幕,它上面可以显示一些控件并监听事件作出响应
- Activity 之间通过 Intent(意图:用于描述一个页面的信息,同时也是一个数据的载体)进行通信
- 常见 Activity 类型有:FragmentActivitiy、ListActivity、TabAcitivty
Intent 的显式和隐式跳转
显式跳转
必须知道并且能够使用要跳转的 Activity 的字节码
一般只能用于自己程序的内部,而不能跳转到其他应用程序的 Activity
- 在清单文件中注册(告诉系统已有该 Activity)
<activity android:name="com.catface.activityjump.SecondActivity" />
- 在 MainActivity 中
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
隐式跳转
不需引用具体类,但必须知道界面的动作(action)和信息(category)[在清单文件中配置]
可以在当前应用程序跳转到另一个应用程序的页面(比如系统应用:浏览器、短信发送界面、拨打电话……)
- 在清单文件中配置 action 和 category
<activity android:name="com.catface.activityjump.SecondActivity">
<intent-filter >
<action android:name="com.catface.activityjump.SecondActivity"/>
<!-- category使用默认DEFAULT即可 -->
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
- 在 MainActivity 中
Intent intent = new Intent();
intent.setAction("com.catface.activityjump.SecondActivity");
// 通常为默认,可以省略不写
intent.addCategory("android.intent.category.DEFAULT");
startActivity(intent);
- 常见隐式跳转
- 浏览器
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
intent.addCategory("android.intent.category.BROWSABLE");
intent.setData(Uri.parse("http://www.youtube.com"));
startActivity(intent);
- 发送短信
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
intent.setData(Uri.parse("sms:10086"));
intent.putExtra("sms_body", "难过");
startActivity(intent);
- 拨号
Intent intent = new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:12315"));
startActivity(intent);
程序入口
若清单文件中的Activity声明为如下,则此Activity将作为整体程序的入口(一个APP可有多个入口)
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
传递数据
可传递类型
八大基本类型及其数组、String及String数组、Bundle、ArrayList、两个序列化接口(Parcelable与Serializable)
不要用 Intent 传递过多的数据,否则可能会拖慢速度甚至黑屏,从而影响到程序的使用
传递方式
- Intent
- 普通传递
// 将数据封装至意图中并跳转
Intent intent = new Intent(this, OneActivity.class);
intent.putExtra("to_1", "你好");
startActivity(intent);
// 接收意图中的数据并打印
Intent intent = getIntent();
String text = intent.getStringExtra("to_1");
Log.d("whatmsg", text);
- 回传
Intent intent = new Intent(this, TwoActivity.class);
startActivityForResult(intent, 1);
// 接收回传的封装在意图中的数据
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1:
if (resultCode == 1) {
Log.d("whatmsg", data.getStringExtra("back_msg"));
}
break;
}
}
Intent intent = new Intent(getApplicationContext(), HomeActivity.class);
intent.putExtra("back_msg", "返回给主页");
setResult(1, intent);
finish();
- BroadcastReceiver
- ContentProvider
- public static 静态数据
- 外部存储
- File
- SharePreferences
- Sqlite 数据库
- 若不跨进程,可使用 EventBus 等第三方框架
- 剪贴板
/** 设置数据 */
ClipboardManager manager = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
String reason = "还不是因为你长得不好看";
manager.setText(reason);
Intent intent = new Intent(MainActivity.this, SencedActivity.class);
startActivity(intent);
/** 获取数据 */
ClipboardManager clipboardManager = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
String getReason = clipboardManager.getText().toString();
Activity 间传递图片的过程
- Intent 可以传递基本数据类型、Uri 和序列化对象
- Bitmap 对象实现了 Parcelable 序列化接口,但是不建议放到 Intent 里传递. 因为 Pacelable 对象序列化过程是将对象 A 的属性暂存一份到内存里,反序列化时再使用暂存的数据,创建一个属性完全相同的对象 B
- 对于 Bitmap 而言,就是把图片的二进制数据data_a 复制一份到内存里构成二进制数据data_b,反序列化时根据二进制数据data_b 创建 Bitmap 对象,此时会生成二进制数据data_c. 也就是同一个图片的数据在内存里存放了三份
- 把 Bitmap 放到 Intent 里会导致巨大的内存损耗,所以在传递图片时应该是传递 Uri 地址,新界面根据 Uri 生成新图片. 同时还可以到图片缓存里使用 Uri 查找已有图片,节约内存
Activity 的生命周期
- 细分(7个)
-
onCreate()
: 初始化操作(加载布局、绑定事件、初始化数据等) -
onStart()
: 活动从不可见变为可见时调用. 可见不可交互 -
onResume()
: 可见可交互. 准备好和用户交互时调用 -
onPause()
: 部分可见不可交互. 释放资源,保存关键数据 -
onStop()
: 当新 Activity 是对话框,不会执行到该方法 -
onDestroy()
: 把数据给释放掉,节省内存. Activity销毁前调用 -
onRestart()
: Activity 由 onStop
还未 onDestroy
前被再次启动时调用
- 粗分(3个)
- 完整生命周期:
从onCreate() -> onDestroy()
- 可见生命周期:
从onStart() -> onStop()
- 前台生命周期:
从onResume() -> onPause()
活动状态
- 运行状态[active-Activity处于栈顶可见可交互]:在屏幕前台响应用户操作. 同一时刻仅有一个 Activity 处于运行状态
- 暂停状态[paused-可见不可交互]:失去焦点部分可见但不可交互状态. 暂停的 Activity 仍是存活状态(保留所有状态和成员信息并保持和窗口管理器的连接),但当系统内存极小时可被系统杀掉
- 停止状态[stopped-不可见不可交互]:当它完全被另一个 Activity 覆盖遮挡不可见,但仍保持状态和成员信息(不在栈顶且完全不可见)
- 销毁状态[killed]:当一个活动从返回栈中移除后便成了销毁状态
Activity 跳转会执行哪些方法
- startActivity 开启一个Activity时
onCreate -> onStart -> onResume
- 点击 back 键关闭一个 Activity 时
onPause -> onStop -> onDestroy
- 当开启一个新的 Activity(以对话框形式覆在上面)或者锁屏,后面的 activity 执行
onPause
- 当把关闭一个新开启 Activity(以对话框形式)或者解锁屏,后面 activity 生命周期执行的方法
onResume
- 当开启一个新的 Activity 或者点击 HOME 键后,生命周期的方法执行顺序
onPause ->onStop
特殊情况:新 Activity 主题为透明时,当前 Activity 不会回调 onStop
- 当用户再次回到原 Activity
onRestart ->onStart ->onResume
-
onWindowFocusChanged
:Activity 窗口获得焦点(onResume
后)或失去焦点(onPause
后)调用 -
onSaveInstanceState
:系统资源不足,Activity 被杀死;屏幕方向改变(保存临时数据);Activity 跳转或点击 HOME 键(保存当前窗口各个 View 组件状态) -
onRestoreInstanceState[其中参数bundle一定不会为空]
:重新回到由于系统资源不足被杀死的 Activity;屏幕方向改变的重建过程中(onStart
后),恢复保存的临时数据
tip将一个 Activity 设置成窗口的样式
配置 Activity 的属性:android:theme="@android:style/Theme.Dialog"
横竖屏切换时 Activity 的生命周期
- 不设置 Activity 的
android:configChanges
时:
onPause
- onStopd
- onCreate
- onStart
- onResume
- 设置
android:configChanges = "orientation|keyboardHidden|screenSize"
时:
只会调用 onConfigurationChanged()
方法
如何保存 Activity 的状态
- 通常情况,Activity 的状态是自动保存的,只有当需要保存额外的数据时才需使用此功能
- 一般来说,调用
onPause
和 onStop
方法后的 Activity 实例仍然存在于内存中,activity 的所有信息和状态数据不会消失,当 Activity 重新回到前台后,所有的改变都会保留. 重写 onSaveInstanceState(Bundle outState)
,将状态数据存储到该 Bundle 对象中. 这样即使 Activity 销毁,当重新启动时调用 onCreate
方法,Bundle 对象会作为实参传递给 onCreate
方法,此时从 Bundle 中取出保存的数据,恢复到 Activity 销毁之前的状态
- 如果调用
onSaveInstanceState()
方法,调用将发生在 onPause()
和 onStop()
方法之前
如在 onSaveInstanceState
中调用 outState.putString("data", "temp")
那么数据将会保存,当重新打开该 Activity 时可以从 onCreate()
的参数 savedInstanceState
中 getString("data")
取出保存的数据,存取方式如下:
// 保存临时数据
@Override protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("temp", "活动被回收了怎么办");
}
// 在 onCreate 方法中获取临时保存的数据
if (savedInstanceState != null) {
Log.d("tempmsg", savedInstanceState.getString("temp"));
}
固定屏幕方向
- 横竖屏切换时,若不设置 Activity 的 android:configChanges,默认情况下会把 Activity 销毁再重新创建并加载
- 避免 Activity 在横竖屏切换时销毁,只需在清单文件声明 Activity 时配置
<activity>
节点的几个属性
- 4.0以下版本
android:configChanges="orientation | keyboardHidden"
- 4.0以上版本
android:configChanges="orientation | screenSize"
- 兼容所有版本
android:configChanges="orientation | screenSize | keyboardHidden"
- 写死横竖屏(大部分都是这样解决的)
android:screenOrientation="landscape" || android:screenOrientation="portrait"
- 或者在 Activity 中
// 竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// 横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
安全退出应用的所有 Activity
- 通常按返回键即可退出一个 Activity
- 在代码中直接调用
finish()
方法即可退出所在 Activity - 将打开的 Activity 保存到集合中,在需要退出时,遍历集合调用
finish()
关闭每一个 Activity 实例 - 在需要结束应用时,发送特定广播,每个 Activity 收到广播后调用
finish()
关闭当前的 activity(耗性能,不推荐) - 使用
startActivityForResult()
打开新的 Activity,设置标志,在 onActivityResult()
方法中递归关闭 - 可以通过 Intent 的 flag 来实现
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
激活一个新的 activity. 此时如果该任务栈中已经有该 Activity,那么系统会把这个 Activity 上面的所有 Activity 干掉. 其实相当于给 Activity 配置的启动模式为 SingleTop
Activity 的任务栈
任务栈的简介
- 程序打开时就创建了一个任务栈,用于存储当前程序的Activity实例. 所有的Activity实例属于一个任务栈
- 一个任务栈包含一个Activity实例的集合,只有在任务栈栈顶的Activity实例才显示在界面并可以与用户进行交互
- 任务栈可移到后台,且保留每个Activity实例的状态. 且有序的给用户列出其任务,且不丢失状态信息
- 退出应用程序时:当把所有的任务栈中所有的Activity实例清除出栈时,任务栈会被销毁,程序退出
任务栈的缺点
- 每开启一次页面都会在任务栈中添加一个Activity,而只有栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用户体验差,需要点击多次返回才可以退出程序
- 每开启一次页面都会在任务栈中添加一Activity还会造成数据冗余,导致内存溢出(OOM)
Activity 的四种启动模式(LaunchMode)
清单文件中配置 <activity>
的 android:launchMode
属性(四种)
- Standard(默认):不管栈中有没有已存在的 Activity 实例,都生成新的 Activity 实例
- SingleTop:有对应的 Activity 实例正位于栈顶,则重复利用,不再生成新的实例
- SingleTask:有对应的 Activity 实例位于栈中,则使其之上的 Activity 实例全部出栈,使其成为栈顶对象
- SingleInstance(电话拨号界面、系统桌面):该任务栈中只有一个 Activity 实例,不再有其他 Activity 实例;启用一个新的栈来单独管理该 Activity – 可共享该 Activity 实例
启动 SingleTop 模式的 Activity 的情形 -> onNewIntent(Intent)
- 位于栈顶的 Activity 被重新启动时未创建新的 Activity 会调用
onNewIntent()
- 当 Activity 接收新的 Intent 时会处于暂停,在此可统计
onResume()
次数(onNewIntent()
调用后) - 若在 Activity 中调用
getIntent()
,返回第一次启动该 Activity 传入的 Intent 对象. 若想得到最新的 Intent,可通过 setIntent(Intent in)
设置
Context简介
- 它描述的是一个应用程序环境的信息,即上下文
- 该类是一个抽象类,Android提供了该抽象类的具体实现类(ContextIml)
- 通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播,接受Intent,信息,等
Context,Activity,Appliction的区别
同
- Activity 和 Application 都是 Context 的子类
- Context 从字面上理解就是上下文的意思,在实际应用中它也确实是起到了管理上下文环境中各个参数和变量的总用,方便我们可以简单的访问到各种资源
异
- 维护的生命周期不同. Context 维护的是当前的 Activity 的生命周期,Application 维护的是整个项目的生命周期
- 使用 context 时,小心内存泄露,防止内存泄露,注意一下几个方面
- 不要让生命周期长的对象引用 Activity Context,即保证引用 Activity 的对象要与 Activity 本身生命周期是一样的
- 对于生命周期长的对象,可以使用 Application Context
- 避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化
菜单 Menu
啊,你有多久没用这个功能啦,好多人都找不到在哪打开 Menu 菜单了吧. 我锤还要在单手优化中设置才能打开菜单…
- A_ res 目录下新建 menu 目录,新建 Menu resource file
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_set"
android:title="SET" />
<item
android:id="@+id/menu_add"
android:title="ADD" />
<item
android:id="@+id/menu_rm"
android:title="REMOVE" />
</menu>
- B_ 在 Activity 中加载 Menu 布局、处理 Menu 选项
@Override public boolean onCreateOptionsMenu(Menu menu) {
// 加载 Menu 布局
getMenuInflater().inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu) // 返回true;
}
@Override public boolean onOptionsItemSelected(MenuItem item) {
// 处理 Menu 选项
switch (item.getItemId()) { // 判断具体Menu选项
case R.id.menu_set:
Toast.makeText(this, "set", Toast.LENGTH_SHORT).show();
break;
case R.id.menu_add:
Toast.makeText(this, "add", Toast.LENGTH_SHORT).show();
break;
case R.id.menu_rm:
Toast.makeText(this, "rm", Toast.LENGTH_SHORT).show();
break;
default:
}
return super.onOptionsItemSelected(item) // 返回true;
}