13、四大组件--Activity

本文全面解析Android中的Activity组件,包括其基本概念、配置方法、启动模式、生命周期管理等内容,并介绍了如何处理Activity之间的数据传递及动画设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Activity

1.1、Activity介绍

Activity 是用户接口程序,原则上它会提供给用户一个交互式的接口功能。它是 android 应用程序的基本功能单元。Activity 本身是没有界面的。所以activity

类创建了一个窗口,开发人员可以通过setContentView(View)接口把UI放到activity创建的窗口上,当activity指向全屏窗口时,也可以用其他方式实现:作为漂浮

窗口(通过windowIsFloating的主题集合),或者嵌入到其他的activity(使用ActivityGroup)。activity是单独的,用于处理用户操作。

1.2、清单文件配置

<intent-filter>
   代表的应用程序的入口界面
   <action android:name="android.intent.action.MAIN" />
   应用程序在桌面上会产生一个快捷图标
   <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

一个应用程序不但可以有多个界面外,还可以在桌面上有多个图标。

1.3、Intent(意图)

1.显示意图

显示意图只能调用当前应用程序的界面,也就是能获取到字节码的类。

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);

2.隐式意图

隐式意图不但可以调用当前应用程序的界面,还能调用其他应用程序的界面,比如系统自带的拨号,发送短信...

a)指定动作,可以有多个,且区分大小写。系统预定义部分action,当然动作是可以自定义的:intent.setAction("自定义")。

b)设置数据,intent.setData(前缀):需要在清单文件中进行声明。

c)在目标activity中使用getIntent()获取Intent实例,以及getData()来获取发送过来的数据。

注:在代码和清单文件中都可以配置过滤器,在清单文件配置data可以更精准的指定当前活动能响应什么类型的数据。

data的语法格式如下:

<intent-filter >
    <data android:scheme="string"
        android:host="string"
        android:port="string"
        android:path="string"
        android:pathPattern="string"
        android:pathPrefix="string"
        android:mimeType="string"/>
</intent-filter>

data由两部分组成,mimeType和URI,其中mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic或video/*等,可以表示图片、文本、适配等

不同的媒体格式,而URI包含的数据则比较多,下面是它的结构:

<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

下面看看示例,结构就非常清晰:

content://com.example.project:200/folder/subfolder/etc
或
http://www.baidu.com:80/search/info

image

ath、PathPrefix、PathPattern:它们都表示路径信息,path和pathPattern都表示完整路径信息,其中pathPattern可以包含通配符“*”,

“*”表示0个或多个任意字符。由于正则表达式的规范,如果想表示真实的字符串,那么“*”要写成“\\*”,“\”写成“\\\\”;

pathPrefix则表示路径前缀信息。

a)过滤规则实例1

<intent-filter>
    <data android:mimeType="image/*"/>
    ...
</intent-filter>

这种情况没有指定URI,但是URI默认值为content和file。那么匹配如下:

intent.setDataAndType(Uri.parse("file://abc"), "image/png");

如果要为Intent指定完整的data,必须调用setDataAndType方法,不能单一的调用setData和setType,它们会彼此清除对方。

b)过滤规则实例2

<intent-filter>
    <data android:mimeType="video/mpeg"    android:scheme="http" .../>
    <data android:mimeType="audio/mpeg"    android:scheme="http" .../>
    ...
</intent-filter>

这种规则指定了两组data规则,且每个data都指定完整的属性值,匹配如下:

intent.setDataAndType(Uri.parse("http://abc"), "video/mpeg");
或
intent.setDataAndType(Uri.parse("http://abc"), "audio/mpeg");

c)两种特殊写法

<intent-filter ...>
    <data android:scheme="file" android:host="www.baidu.com"/>
</intent-filter><intent-filter ...>
    <data android:scheme="file"/>
    <data android:host="www.baiud.com"/>
</intent-filter>

这是和action不同的地方,其实作用是一样的。
1.当我们通过隐式意图启动Activity时,可以做一下判断,查看是否有Activity能够匹配我们隐式的Intent,判断有两种方式:

a) PackageManager的resolveActivity()方法。

b) Intent的resolveActivity()方法。

PackageManager manager = this.getPackageManager();
manager.resolveActivity(intent, flags);

它们如果找不到符合的Activity则会返回null。

2.其中PackageManager还提供有queryIntentActivities()方法,它返回的是所有成功匹配的Activity。

PackageManager manager = this.getPackageManager();
manager.queryIntentActivities(intent, flags);

resolveActivity方法和queryIntentActivities方法第一个参数是intent,第二个参数 MATCH_DEFAULT_ONLY

该标记位仅仅匹配在过滤器中配置默认类别的Activity:

<category android:name="android.intent.category.DEFAULT"/>

下面我们来可以看下面这个打开浏览器的例子:

Intent intent = new Intent();
// 设置Action
intent.setAction("android.intent.action.VIEW");
// 设置category
intent.addCategory("android.intent.category.BROWSABLE");
// 设置参数
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);

清单文件配置:

<intent-filter>
     <action android:name="android.intent.action.VIEW" />
     <category android:name="android.intent.category.DEFAUT" />
     <category android:name="android.intent.category.BROWSABLE" />
     <data android :scheme="http"/>
     <data android :scheme="https"/>
     <data android :scheme="about"/>
     <data android :scheme="javascript"/>
</intent-filter>

注:

  • 隐式调用时,代码中的action、category必须保持一致,否则无法正确匹配。

1.4、Intent通信

1.传递数据

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivityForResult(intent, 1);

startActivityForResult()方法来启动 SecondActivity,请求码只要是一个唯一值即可。

2.接收数据

Intent intent = new Intent();
intent.putExtra("data_return", "Hello FirstActivity");
setResult(RESULT_OK, intent);
finish();

setResult()方法接收两个参数,第一个参数用于向上一个活动返回处理结果,一般只使用 RESULT_OK 或RESULT_CANCELED 这两个值

3.接收反馈

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
    case 1:
        if (resultCode == RESULT_OK) {
            String returnedData = data.getStringExtra("data_return");
            Log.d("FirstActivity", returnedData);
        }
        break;
    }
}

onActivityResult()方法带有三个参数:

第一个参数 requestCode,即我们在启动活动时传入的请求码。

第二个参数 resultCode, 即我们在返回数据时传入的处理结果。

第三个参数 data,即携带着返回数据的 Intent。

4.返回键的隐患

用户在数据达到的目标界面如果按下返回键,那么,数据就无法反馈给发送的界面,所以在目标界面重写返回键的方法。

@Override
public void onBackPressed() {
    Intent intent = new Intent();
    intent.putExtra("data_return", "Hello FirstActivity");
    setResult(RESULT_OK, intent);
    finish();
}

1.5、生命周期

1.四种状态

(1)运行状态:活动位于返回栈的栈顶时,这时活动就处于运行状态。

(2)暂停状态:当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。

(3)停止状态:当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。

(4)销毁状态:当一个活动从返回栈中移除后就变成了销毁状态。

image

2.七个方法

image

以上七个方法中除了 onRestart()方法,其他都是两两相对的,从而又可以将活动分为三种生存期:

(1)完整生存期:活动在 onCreate()方法和 onDestroy()方法之间所经历的,就是完整生存期。

(2)可见生存期:活动在 onStart()方法和 onStop()方法之间所经历的,就是可见生存期。

(3)前台生存期:活动在 onResume()方法和 onPause()方法之间所经历的,就是前台生存期。

Ps:当启动一个新的Activity时,旧Activity会先调用onPause()方法,才会执行新Activity的回调方法。

3.configChanges配置

它有很多属性,类似于屏幕尺寸改变,外接键盘接入...等一系列操作,这里不详细说明。

横竖屏切换时,默认情况会把activity先销毁再创建,这样就影响到正常的生命周期。

4.0之前:

android:configChanges="orientation|keyboardHidden"

4.0之后

android:configChanges="orientation|screenSize"

兼容所有

android:configChanges="orientation|keyboardHidden|screenSize"

当配置该属性后,横竖屏切换不会影响生命周期,但是会调用onConfigurationChanged方法:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    // 做一些自己的操作
}

4、异常生命周期

a) 当旋转屏幕导致生命周期改变,会销毁当前活动,再创建一个新的活动。

b) 当程序位于后台,在资源不足的情况下被回收,活动也会重建。需要注意保存数据并回显。

Ps:我们可以通过onSaveInstance或onRestoreInstance()方法来保存一些临时数据,切忌保存Bundle(有时候取出为null)。

1.6、返回栈

Android 是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。栈是一种后进先出的数据结构,

在默认情况下,当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。每当我们按下 Back 键或调用 finish()方法去销毁一个活动时,处于栈顶

的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。

1.7、启动模式

1、启动模式的配置:

a) 清单文件中为activity配置启动模式:

<activity
    android:name="cn.legend.toggleview.FirstActivity"
    android:launchMode="standard" >
</activity>

b) 代码中使用Intent的标记位来动态设置activity启动模式:

Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
//setFlags方法是替换掉之前的标识位,addFlags是添加一个标识位。
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

c) 区别:

  • 第二种方式比第一种方式的优先级高,当两种同时存在时以第二种为准。
  • 第一种方式无法设定Intent.FLAG_ACTIVITY_CLEAR_TOP标记位,第二种无法指定singleInstance模式。

2、四种启动模式

image

3、Activity的Flags
除了在清单文件设置启动模式外,我们还可以通过Intent来设置Activity的启动模式

/**启动的Activity都在新的任务栈中,通常用于Service启动Activity的场景*/
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/**和SingleTop启动模式的效果相同*/
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
/**和SingleTask启动模式的效果相同*/
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
/**当该Activity启动其他Activity后,该Activity会移除任务栈中*/
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

4、清空任务栈

通常情况下,可以在清单文件中的<activity>标签中使用以下属性来清理任务栈:

  • clearTaskOnLaunch

每次返回该Activity时,都将该Activity之上所有Activity清除,可以让任务栈每次初始化时,都只有这一个Ativity。

  • finishOnTaskLaunch

该属性和clearTaskOnLaunch类似,它的作用是针对自己的,当离开这个Activity所处的Task,那么用户返回时,该Activity会销毁。

  • alwaysRetainTaskState

相当于免死金牌,如果将该属性设置为true,那么该Activity所在的Task将不接受任何清理命令。

5、ApplicationContext不具备任务栈:

当我们使用ApplicationContext去启动standard模式的Activity时,Activity会报错:

  因为当ActivityA启动ActivityB(Standard模式)时,ActivityB会进到ActivityA的任务栈中,由于ApplicationContext是非Activity类型的Context,

  并没有所谓的任务栈,所以就出现问题。解决方式是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动时就创建一个新的任务栈。

  但是,此时待启动的Activity实际上是以singleTask模式启动。

Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

6、onNewIntent的触发时机:

当一个应用程序加载singleTask或singleTop的Activity时,首先该Activity会检查是否存在相同的任务栈。

a)存在,那么检查是否实例化,如果已经实例化,那么销毁在该Activity以上的Activity并调用onNewIntent。如果没有实例化,那么该Activity实例化并入栈。

b)不存在,那么就重新创建任务栈,并入栈。

LaunchMode=""SingleTask"  taskAffinity="com.tencent.mm" (com.tencent.mm是借助于工具找到的微信包名)

把自己的Activity放到微信默认的Task栈里面,回退时就会遵循“Task只要有Activity一定从本Task剩余Activity回退"的原则,不会回到自己的客户端。

7、TaskAffinity

TaskAffinity可以翻译成任务相关性,这个参数标识了Activity所需的任务栈名字,默认情况下,所有Activity所需的任务栈名字为应用的包名。

当然我们也可以指定TaskAffinity属性,但是必须不能和应用包名相同,否则就相当于没有指定。

它主要和SingleTask模式或allowTaskReparenting属性配置使用,且任务栈分为前台任务栈和后台任务栈:

这个涉及到跨进程的任务栈知识,等待后期慢慢理解。

1.8、保存临时数据

当Activity进入后台就有可能被系统未经许可就回收,如此一来我们的数据和状态都会丢失,Android为我们提供了恢复数据的机制。

1.触发时机

a)当某个Activity被系统销毁时,该Activity的onSaveInstanceState就会被执行,如果是用户主动销毁时则不会执行该方法。

@Override
public void onSaveInstanceState(Bundle savedInstanceState){
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putString("message", text.getText().toString());
}

b)当某个Activity被系统销毁并重新创建时会调用onRestoreInstanceState方法,它和onSaveInstance不一定成对出现。

@Override
public void onRestoreInstanceState(Bundle savedInstanceState){
    super.onRestoreInstanceState(savedInstanceState);
    message = savedInstanceState.getString("message");
}

c)onRestoreInstanceState的bundle参数也会传递到onCreate方法中,这里需要判断是否为空,以此判定是第一次创建活动还是被系统回收后回显数据的操作。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if(savedInstanceState != null){
        message = savedInstanceState.getString("message");
    }
}

Ps:

目前除了SharePerences或Sqlite来存储全局变量外,其他任何方法都不靠谱,onSaveInstanceState和onRestoreInstanceState方法只能保存临时数据。

当Activity进入后台就会调用onSaveInstanceState,它在onStop前调用,可能在onPause前后,并且它不是Activity生命周期的一部分。

当Activity在未经允许的情况下被系统回收时,Activity会被重建,此时onRestoreInstanceState会被回调,它的调用时机在onStart后。

1.9、管理活动退出

a)我们创建一个ActivityController类用作管理Activity退出的逻辑类

public class ActivityController {    
    // 活动管理器
    public static List<Activity> mActivities = new ArrayList<Activity>();    
    // 添加活动的方法
    public static void addActivity(Activity activity){
        mActivities.add(activity);
    }    
    // 移除活动的方法
    public static void removeActivity(Activity activity){
        mActivities.remove(activity);
    }    
    // 移除所有活动
    public static void finishAll(){
        // 对集合进行迭代判断状态
        for (Activity activity : mActivities) {
            if(!activity.isFinishing()){
                activity.finish();
            }
        }
    }
}

b)如果我们想随时随地的退出应用的话,只需要调用ActivityController.finishAll()方法即可。

2.0、启动活动封装

我们在项目中会频繁的涉及到界面的跳转,所以我们可以将跳转的代码进行封装,这样优化了代码结构

public class StartIntentUtils {
    /**
     * 正常开启Activity
     * @param context    上下文
     * @param clazz        要开启的活动的字节码
     */
    public static void startActivity(Context context,Class clazz){
        Intent intent = new Intent(context,clazz);
        context.startActivity(intent);
    }
    
    /**
     * 开启Activity,并传递数据
     * @param context    上下文
     * @param clazz        要开启活动的字节码
     * @param data1        传递的数据1
     * @param data2        传递的数据2
     */
    public static void startActivity(Context context,Class clazz,String data1,String data2){
        Intent intent = new Intent(context, clazz);
        intent.putExtra("param1", data1);
        intent.putExtra("param2", data2);
        context.startActivity(intent);
    }
}

2.1、活动切换动画

a)Activity的过渡动画是通过overridePendingTransition实现的,该方法会在startActivity(intent)或finish()之后调用。

@Override
public void overridePendingTransition(int enterAnim, int exitAnim) {
    super.overridePendingTransition(enterAnim, exitAnim);
}

第一个参数:eAnim,是新的Activity的进入动画的resourceID。

第二个参数:exitAnim,是旧的Activity(当前Activity)离开动画的resourceID。

b)其中所需传入的动画定义在res\anim\目录下,下面看下其中动画示例

slide_in_bottom文件:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromYDelta="100%p" 
               android:toYDelta="0"
               android:duration="2000"/>
    
    <alpha android:fromAlpha="0.0" 
           android:toAlpha="1.0"
           android:duration="2000" />
</set>

slide_oit_bottom

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromYDelta="0%p" 
               android:toYDelta="100%p"
               android:duration="2000"/>
    
    <alpha android:fromAlpha="1.0" 
           android:toAlpha="0.0"
           android:duration="2000" />
</set>

c)我们调用的时候只需要在跳转后调用该方法就可以

Intent intent = new Intent(TestActivities.this,TestActivityFirst.class);
startActivity(intent);
// transaction animation
overridePendingTransition(R.anim.slide_in_bottom,R.anim.slide_out_bottom);

2.2、Activity对话框                                                             

使用Activity当做弹出对话框有以下好处:

  • 显示位置的设置,直接就是layout.xml,可以实现任何效果以及出现在任意位置。
  • 对话框内控件的事件处理都独立在一个类中,只需要startActivity()既可调用,代码结构更加清晰。

a) 编写对话框的布局效果

<LinearLayout
    android:onClick="tip"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    android:layout_marginTop="46dp"
    android:background="@drawable/title_function_bg"
    android:orientation="vertical" >
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:drawableLeft="@drawable/mm_title_btn_compose_normal"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:text="发起聊天"
        android:textColor="#fff"
        android:textSize="20sp" />
</LinearLayout>

b)在style.xml中定义一个theme(背景透明,无标题,动画效果),一般Activity默认动画效果右进右出,我们可以覆盖。

<style name="MyDialogTopRight">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowAnimationStyle">@style/Anim_scale</item>
</style>

动画效果:

<style name="Anim_scale" parent="@android:style/Animation.Activity">
    <item name="android:activityOpenEnterAnimation">@anim/scale_in</item>
    <item name="android:activityOpenExitAnimation">@anim/scale_out</item>
     <item name="android:activityCloseEnterAnimation">@anim/scale_in</item>
    <item name="android:activityCloseExitAnimation">@anim/scale_out</item>
</style>

在清单文件配置对话框样式的Activity

<activity
    android:name=".MainWeixinTitleRightActivity"
    android:theme="@style/MyDialogTopRight" >
</activity>

c) 在需要使用的地方直接startActity()即可。

 

转载于:https://www.cnblogs.com/pengjingya/p/5508671.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值