【Android】Activity

  • 总结自老师的文档

1. 创建一个Activity的流程


1.1. 设计并实现用户接口

  • 衍生自 View 类的视图,控制 Activity 中特定的矩形空间,例如按钮
    衍生自 ViewGroup 类的视图,是布局,例如线性布局
    利用视图定义布局的最常见方法是借助保存在你的应用资源内的 XML 布局文件

1.2. 配置manifest

  • 1.2.1. 在 manifest 中声明 activity
<activity android:name=".ExampleActivity" />
  • 1.2.2. 配置使用 intent 过滤器

过滤器的作用就是声明其他 app 组件是如何激活该 activity 的
下面过滤器声明本 activity 对"main"动作进行响应,并处于"launcher"这个类别中

<intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
  • 1.2.3. 声明权限
android:permission="com.google.socialapp.permission.SHARE_POST"	//定义权限
<uses-permission android:name="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 的逻辑父项

<activity android:name=".DisplayMessageActivity"
         android:parentActivityName=".MainActivity" >

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()方法

  • 1.必须检查一下Bundle是否是null,若null,系统会创建一个新的实例而不是重建该activity
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都会销毁并重建
  • 为了恢复你的 activity 中 view 的状态,该 view 必须有一个唯一的ID
    如果小部件没有 ID,则系统无法保存其状态

3.8. 协调 Activity
启动第二个 Activity 的过程与停止第一个 Activity 的过程存在重叠

  • 现有 Activity A、B,A停止,B启动的过程如下
    1.A 执行 onPause()
    2.B 执行 onCreate、onStart、onResume (Activity B 现在具有用户焦点)
    3.然后,若 A 在屏幕上不再可见,则其 onStop() 方法执行

3.9. Activity 生命周期回调方法汇总表

  • 在从 onPause() 返回时到 onResume() 被调用时 之间,系统可以终止 Activity
    如果系统在紧急情况下必须恢复内存,则可能不会调用 onStop() 和 onDestroy()
    因此,应该使用 onPause() 向存储设备写入至关重要的持久性数据(例如用户编辑)
    不过,你应该对 onPause() 调用期间必须保留的信息有所选择,因为该方法中的任何阻止过程都会妨碍向下一个 Activity 的转变并拖慢用户体验

4. Activity的四种launchMode


一个 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 类型

  • 5.2.1. 显式 Intent 示例
// 因为是在 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 解析

  • 5.5.1. Action 测试
    注: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>:<port>/<path>
  • 注:如果没有指定scheme,host会被忽略
    如果没有指定host,则port会被忽略
    如果没有指定scheme和host,则path会被忽略

6. 与其他应用交互


6.1. 把用户转向另一个应用

  • 6.1.1. 构建隐含 Intent
    Uri 数据
    通常,系统基于所包含的 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中
  1. 保存 activity 的状态
    实现 onSaveInstanceState() 回调方法来预先保存它的状态,以便重建
  2. 管理 task
    back 按钮作用:返回到前一个 activity

2.1. 定义加载(launch)模式

  • 两种方式
    1.在 manifest 中定义
    2.在 Intent 中包含标志
    后者优先级更高,且二者有些模式不相通

2.2. 使用manifest文件
四个模式,launchMode 属性的四种值

  • 不管activity是在新的task中,还是在同一个task中,back按钮都能把前一个activity带到前台。然而,如果你以singleTask来启动一个activity,而已经有该activity的实例在后台task中,则整个后台task会来到前台

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

  • taskAffinity 属性的默认值为清单中定义的包名
  • affinity 在两种环境下起作用
    1.当 intent 启动一个 activity 的时候包含 FLAG_ACTIVITY_NEW_TASK 标志
    2.当一个 activity 的属性 allowTaskReparenting 设置为 true 的时候(可以移动)

2.5. 清除back栈
用户离开 task 时间较长

  • alwaysRetainTaskState,根 activity 设置该属性值为 true
    总是保持该栈中所有 activity 的状态
  • clearTaskOnLaunch,立即清除除根外的所有 activity
  • finishOnTaskLaunch,同上,不过只针对一个 activity

2.6. 启动一个task
action.MAIN 和 category.LAUNCHER 配对使用


End.

原创文章 93 获赞 187 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43763494/article/details/105853535