1.1. 设计并实现用户接口
衍生自 View 类的视图,控制 Activity 中特定的矩形空间,例如按钮
衍生自 ViewGroup 类的视图,是布局,例如线性布局
利用视图定义布局的最常见方法是借助保存在你的应用资源内的 XML 布局文件
1.2. 配置manifest
1.2.1. 在 manifest 中声明 activity
1.2.2. 配置使用 intent 过滤器
过滤器的作用就是声明其他 app 组件是如何激活该 activity 的
下面过滤器声明本 activity 对"main"动作进行响应,并处于"launcher"这个类别中
1.2.3. 声明权限
android:permission="com.google.socialapp.permission.SHARE_POST" //定义权限
//声明权限
1.3. 启动 Activity
(显式Intent)一个 Activity 启动另一个名为 SignInActivity 的 Activity:
Intent intent = new Intent(this, SignInActivity.class);
//从 this 启动 SignInActivity
startActivity(intent);
(隐式Intent)还可以对要执行的操作进行描述,若有多个 Activity 可以处理该 Intent,则由用户选择
如果你想允许用户发送电子邮件,可以创建以下 Intent:
Intent intent = new Intent(Intent.ACTION_SEND); //这个和等同于 intent.setAction
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
启动并获得返回结果
private void pickContact() {
// Create an intent to "pick" a contact, as defined by the content provider URI
Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
startActivityForResult(intent, PICK_CONTACT_REQUEST);
}
//PICK_CONTACT_REQUEST,主调和被调之间的暗号,自定义的常量
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// If the request went well (OK) and the request was PICK_CONTACT_REQUEST
if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
// Perform a query to the contact's content provider for the contact's name
Cursor cursor = getContentResolver().query(data.getData(), new String[] {Contacts.DISPLAY_NAME}, null, null, null);
if (cursor.moveToFirst()) { // True if the cursor is not empty
int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
String name = cursor.getString(columnIndex);
// Do something with the selected contact's name...
}
}
}
//resultCode == Activity.RESULT_OK,被调正确执行并返回正确结果
//requestCode == PICK_CONTACT_REQUEST,请求码,通过暗号判断结果是返回给谁的
1.4. 关闭一个Activity
可以使用
finish()
方法或者finishActivity (int requestCode)
来关闭一个 activity,但是我们不建议这么做,因为activity有其生命周期,系统会对activity的生命周期进行管理 2. 启动另一个Activity
代码出现错误 记得 import 对应的类
2.1. 响应 Send 按钮
在相应的 MainActivity.java 中添加方法 如
sendMessage()
为了匹配 android:onClick
属性值,该方法必须满足:1.是 public
2.返回值为 void
3.有且仅有一个 View 作为参数
2.2. 构建一个Intent
Intent 构造方法的第二个参数为系统发送Intent的目标,为某应用组件的Class 也就是被启动的组件的类名
例:显式Intent 通过 putExtra 方法添加 “额外" 数据信息
public static final String EXTRA_MESSAGE = "com.example.myfirstapp.MESSAGE";
public void sendMessage(View view) {
Intent intent = new Intent(this, DisplayMessageActivity.class);
EditText editText = (EditText) findViewById(R.id.editText);
String message = editText.getText().toString();
//将这个文本字符串以键值对的形式附加到 Intent 中,
//通常我们用应用的包名作为前缀,以保证键的唯一性
intent.putExtra(EXTRA_MESSAGE, message);
//通过 intent 启动另一个 activity,同时传递数据
startActivity(intent);
}
2.3. 创建第二个 Activity
File > New > Activity > EmptyActivity或者项目里右击 > New > Activity > EmptyActivity
注 LauncherActivity 的意思是主 Activity ,
2.4. 显示消息
通过 getStringExtra 方法提取 Intent 传递的额外数据
//获取启动本 activity 的 Intent
Intent intent = getIntent();
//从该 Intent 中提取传递过来的数据
String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
TextView textView = findViewById(R.id.textView);
textView.setText(message)
2.5. 添加向上导航
就是指明该 Activity 的逻辑父项
3. Activity生命周期
Activity 的三个稳定状态:继续 (Resumed)、暂停 (Paused)、停止 (Stopped)
六个生命周期方法:onCreate(), onStart(), onResume(), onPause(), onStop(), 和 onDestroy()
3.1. 实现生命周期回调方法
3.2. 调用onCreate()方法启动Activity
onCreate()执行结束后,系统立刻调用 onStart()和 onResume()方法,
你的Activity不会停留在 Created 和 Started 状态
3.3. Activity生命周期的最后一个回调方法onDestroy()
大部分的app不需要实现该方法,因为已经在onPause()和onStop()中执行了大部分的清理工作
特殊情况,在 onCreate() 中调用 finish(),系统立即调用 onDestroy() 而不会调用其它的生命周期方法
3.4. 暂停及重启一个Activity
暂停状态,并未被完全遮盖,仍可见
onPause()方法中应该执行尽量简单的操作
3.5. 停止和重启activity
停止状态完全遮盖,不可见
会释放几乎所有的不再需要的资源
每次你的activity变得可见的时候都会调用onStart()方法,只有在activity从stopped状态中恢复的时候才调用onRestart()方法
3.6. 保存 Activity 状态
用户不知道系统销毁 Activity 后又对其进行了重建
确保有关 Activity 状态的重要信息得到保留:onSaveInstanceState()
当你的activity开始stop的时候,系统会调用onSaveInstanceState(),系统会向该方法传递一个 Bundle
可以对 Bundle 使用 putString() 和 putInt() 等方法以名称-值对形式保存有关 Activity 状态的信息
static final String STATE_SCORE = "playerScore";
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// 保存用户当前的游戏状态
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
// 总是首先调用父类中的onSaveInstanceState方法
super.onSaveInstanceState(savedInstanceState);
}
恢复你的 Activity 状态
当用户离开你的activity时,系统调用这个方法并传递一个Bundle对象,如果系统必须重建该activity实例的时候,会把同一个Bundle对象传递给onRestoreInstanceState()和onCreate()方法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // 总是首先调用父类中的 onCreate方法
// 检查我们是否是在重建以前销毁的本 Activity的实例
if (savedInstanceState != null) {
// 从保存的状态中恢复成员的值
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
} else {
// 新实例中的变量可能需要用默认值进行初始化
}
}
2.该方法在onStart()方法之后被调用只在有保存的数据需要恢复的时候用,不必检查Bundle
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 从保存实例中恢复状态变量值
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
}
3.7. 重建一个activity
新建和重建 用onCreate()
每次用户旋转屏幕的时候,你的activity都会销毁并重建
如果小部件没有 ID,则系统无法保存其状态
3.8. 协调 Activity
启动第二个 Activity 的过程与停止第一个 Activity 的过程存在重叠
1.A 执行 onPause()
2.B 执行 onCreate、onStart、onResume (Activity B 现在具有用户焦点)
3.然后,若 A 在屏幕上不再可见,则其 onStop() 方法执行
3.9. Activity 生命周期回调方法汇总表
在从 onPause() 返回时到 onResume() 被调用时 之间,系统可以终止 Activity
如果系统在紧急情况下必须恢复内存,则可能不会调用 onStop() 和 onDestroy()
因此,应该使用 onPause() 向存储设备写入至关重要的持久性数据(例如用户编辑)
不过,你应该对 onPause() 调用期间必须保留的信息有所选择,因为该方法中的任何阻止过程都会妨碍向下一个 Activity 的转变并拖慢用户体验
一个 task 就是一个用户用于完成某项工作的 activity 集合
这些 activity 以栈 (back stack) 来进行管理
4.1. standard(默认)
不管有没有已存在的实例,都生成新的实例(activity 可以被多次实例化)4.2. singleTop
如果发现有对应的Activity实例正位于栈顶,则重复利用,不再生成新的实例4.3. singleTask
如果发现有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到屏幕前 若无实例,系统创建一个新的 task 并且实例化 activity 为新 task 的根4.4. singleInstance
这种模式限定同一个task只能有一个activity (除此之外和 singleTask 相同),因此,这种模式一般用在一个app是由一个activity完成的情况 5. Intent 和 Intent 过滤器Intent 是一个消息传递对象,可以使用它从其他应用组件请求操作
5.1. Intent 类型
显式 Intent:按名称(完全限定类名)指定要启动的组件 隐式 Intent :没有组件名称,而是声明要执行的操作,从而允许其他应用中的组件来处理它 显式 Intent 始终会传递给其目标,无论组件声明的 Intent 过滤器如何均是如此如果没有为 Activity 声明任何 Intent 过滤器,则 Activity 只能通过显式 Intent 启动
不要为服务声明 Intent 过滤器,
使用隐式 Intent 启动服务存在安全隐患,因为你无法确定哪些服务将响应 Intent,且用户无法看到哪些服务已启动
5.2. 构建 Intent
组件名称、操作、数据、类别;Extra、标志
用 setDataAndType() 同时设置 URI 和 MIME 类型
// 因为是在 Activity中执行,因此,'this'是指本 Activity
// fileUrl是一个字符串 URL, 比如 "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
5.2.2. 隐式 Intent 示例注:若没有 Activity 接收 Intent,应用会崩溃,所以需要判断一下
// 使用字符串创建文本消息
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
//系统并没有使用 URI,但已声明 Intent 的数据类型,用于指定 extra 携带的内容
// 验证是否有 activity处理该 Intent
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
5.2.3. 强制使用应用选择器(同6.1.3)强制打开对话框,让用户自己选择
Intent intent = new Intent(Intent.ACTION_SEND);
// UI中的文字要使用字符串资源 something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog 强制创建应用选择器
Intent chooser = Intent.createChooser(intent, title);
// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
5.3. 接收隐式 Intent
过滤器中的测试(需全部通过)action
:在 name 属性中,声明接受的 Intent 操作,必须是文本字符串data
:使用一个或多个指定数据 URI 各个方面(scheme、host、port、path 等)和 MIME 类型的属性,声明接受的数据类型category
:在 name 属性中,声明接受的 Intent 类别,必须是文本字符串
为了接收隐式 Intent,必须将 CATEGORY_DEFAULT 类别包括在 Intent 过滤器中 (原因见5.5.2)
防止其他开发者显式访问该组件,针对该组件将 exported
属性设置为 "false"
启动器图标,ACTION_MAIN 和 CATEGORY_LAUNCHER 配对使用,Activity 才会显示在应用启动器中
5.4. 使用待定 Intent(略)*
5.5. Intent 解析
注:filter 中至少一个 action,否则所有 intent 都不能通过测试
通过:至少匹配其中一个 Action 5.5.2. 类别测试
通过:Intent 中的类别是过滤器中类别的子集,不声明类别也可通过
注:Android 会自动将 CATEGORY_DEFAULT 类别应用于
传递给 startActivity() 和 startActivityForResult() 的 所有隐式 Intent 5.5.3. 数据测试
每个 元素均可指定 URI 结构和数据类型(MIME 媒体类型)
URI 的每个部分均包含单独的 scheme 方案、host 主机、port 端口 和 path 路径 属性
://:/
注:如果没有指定scheme,host会被忽略如果没有指定host,则port会被忽略
如果没有指定scheme和host,则path会被忽略 6. 与其他应用交互
6.1. 把用户转向另一个应用
6.1.1. 构建隐含 IntentUri 数据
通常,系统基于所包含的 Uri 数据确定 Intent 需要的相应 MIME 类型
若 Intent 不包含 Uri,应使用 setType() 指定与 Intent 关联的数据的类型。
设置 MIME 类型可进一步指定哪些类型的 Activity 应接收该 Intent
查看网页:
Uri webpage = Uri.parse("http://www.android.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
可以通过 putExtra 来添加额外的数据
6.1.2. 验证是否存在接收 Intent 的应用
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent, 0); //0处:PackageManager.MATCH_DEFAULT_ONLY
boolean isIntentSafe = activities.size() > 0;
if (isIntentSafe) {
startActivity(intent);
}
6.1.3. 显示应用选择器(同5.2.3)
6.2. 获取 Activity 的返回结果
响应的 Activity 必须设计为返回结果,如下:
public void returnTOMain(View view) {
EditText editText = (EditText)findViewById(R.id.ap_et_inputPhoneNumber);
String phoneNumber = editText.getText().toString();
Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse(phoneNumber));
setResult(Activity.RESULT_OK, result);
finish();
}
6.3. 启动activity
startActivityForResult(intent, REQUEST_CODE)
第二个参数,用于标识你的请求
6.4. 接收结果
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == PICK_CONTACT_REQUEST) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
TextView textView = (TextView)findViewById(R.id.ma_tv_phoneNumber);
textView.setText(data.getDataString());
}
}
}
7. 允许其他应用启动您的 Activity
7.1. 添加 Intent 过滤器
Action:对要执行的操作命名的字符串。通常是平台定义的值之一 Data:若无需声明关于数据的具体信息 Uri(比如,你的 Activity 处理其他类型的"额外"数据而不是 URI 时),可只指定 android:mimeType 属性声明您的 Activity 处理的数据类型,比如 text/plain 或 image/jpeg Category:通常与用户手势或 Activity 启动的位置有关 若有两对操作和数据的行为相斥,则需另创过滤器7.2. 处理您的 Activity 中的 Intent
通常应在 onCreate() 和 onStart() 中执行 getIntent()
Intent intent = getIntent();
Uri data = intent.getData();
// Figure out what to do based on the intent type
if (intent.getType().indexOf("image/") != -1) {
// Handle intents with image data ...
} else if (intent.getType().equals("text/plain")) {
// Handle intents with text ...
}
7.3. 返回结果
只管调用 setResult,若 Intent 调用的是 startActivity,会自动忽略
// Create intent to deliver some kind of result data
Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri"));
setResult(Activity.RESULT_OK, result);
finish();
附:task与back栈
当用户启动一个新的task或者是使用Home按钮回到Home屏,task可以作为一个整体移入后台。当在后台的时候,task中的所有activity都停止了,但是back栈还保持完整,只是失去焦点而已
多个 task 处于后台时,系统可能会销毁后台 activity 来收回内存,这会引起 activity 状态的丢失
activity 停止时,系统会保持它的状态
Activity可以被多次实例化,甚至在其他task中
保存 activity 的状态实现 onSaveInstanceState() 回调方法来预先保存它的状态,以便重建 管理 task
back 按钮作用:返回到前一个 activity
2.1. 定义加载(launch)模式
两种方式1.在 manifest 中定义
2.在 Intent 中包含标志
后者优先级更高,且二者有些模式不相通
2.2. 使用manifest文件
四个模式,launchMode 属性的四种值
2.3. 使用Intent标志
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
FLAG_ACTIVITY_NEW_TASK = singleTask
FLAG_ACTIVITY_SINGLE_TOP = singleTop
FLAG_ACTIVITY_CLEAR_TOP,与singleTask不同的是 不创建新的 task
2.4. 处理affinity
affinity表示一个activity属于哪个task
1.当 intent 启动一个 activity 的时候包含 FLAG_ACTIVITY_NEW_TASK 标志
2.当一个 activity 的属性 allowTaskReparenting 设置为 true 的时候(可以移动)
2.5. 清除back栈
用户离开 task 时间较长
总是保持该栈中所有 activity 的状态 clearTaskOnLaunch,立即清除除根外的所有 activity finishOnTaskLaunch,同上,不过只针对一个 activity
2.6. 启动一个task
action.MAIN 和 category.LAUNCHER 配对使用
End.
Earnest~ 原创文章 92获赞 186访问量 2万+ 关注 私信 展开阅读全文作者:Earnest~