小工具知识网

当前位置: 首页 > 行业知识 > Android 四大组件之Activity详解

行业知识

Android 四大组件之Activity详解

2026-11-01 03:13:14 来源:互联网转载

  最近在整理Android方面的知识,也算是对Android知识的一个复习总结。

文章目录

  • 一、 Activity的概念
  • 二、Activity的创建与注册
    • 2.1、 利用系统创建Activity
    • 2.2 、自己创建Activity
    • 三、Activity的启动方式
      • 3.1 、显式启动
      • 3.2 、隐式启动
      • 3.3 、MAIN
      • 3.4、 显式启动和隐式启动对比
      • 3.5、 启动其他应用的Activity
      • 四、activity的生命周期
      • 五、activity的四种状态
      • 六、Activity的栈式管理
      • 七 、Activity的启动模式
      • 八、其他
        • 8.1、 Activity跳转方法
        • 8.2、Intent 可以传递的数据类型:
        • 8.3、 多个Activity跳转时的生命周期

          一、 Activity的概念

            Activity是Android组件中最基本也是最为常见用的四大组件之一,它提供一个可视化的用户界面,放置各种UI组件,与用户进行交互。一般来说,你所能看到界面都属于Activity。

          二、Activity的创建与注册

          2.1、 利用系统创建Activity

          右击包名——New——Activity,或者左上角选择File——New——Activity,你会看到下发的界面;

          然后我们一般选择Empty Activity,你会会看到类似于下面的界面;

          输入Activity和布局的名字,☑️Generate a Layout 后,Android系统会自动为我们创建布局,且在AndroidManifest.xml文件中自动注册当前Activity。

          2.2 、自己创建Activity

          • 右击包名——New——Java Class,或者左上角选择File——New——Java Class,然后输入你要创建的Activity名字,如MainActivity3;
          • 在AndroidManifest文件中application 标签中注册Activity;
             
            • 右击layout——New—— Layout Resource File——输入布局名称,创建一个新布局;

            • 让MainActivity3继承Activity,重写onCreate()方法,并在onCreate()方法中用setContentView引用你刚才创建的布局;

              public class MainActivity3 extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main3);    }}

              三、Activity的启动方式

                Activity的启动也可以叫做Activity间的跳转,是从一个Activity 跳转到另一个Activity。它的启动是通过Intent来表达的,Intent同时也是Android组件之间的通信媒介,专门提供组件互相调用的相关信息,Activity的启动方式,大的方向上可以分为两种: 显式启动和隐式启动。

                原则上启动一个activity不应该既是显示又是隐式,如果二者共存,那么以显示调用为主。

              3.1 、显式启动

              明确的指定要启动的Activity。

              • class 跳转
                Intent intent = new Intent(Activity.this,MainActivity3.class);startActivity(intent);
                • 包名.类名跳转
                  Intent intent = new Intent(); intent.setClassName(FirstActivity.this,"com.lixiang.application.MainActivity3");startActivity(intent);
                  • ComponentName跳转
                    Intent intent = new Intent()ComponentName componentName = new ComponentName(FirstActivity.this,MainActivity3.class);intent.setComponent(componentName);startActivity(intent);

                    3.2 、隐式启动

                    能被隐式启动的Activity必须在AndroidManifest.xml文件中配置Intent-filter (过滤器),而IntentFilter又有action、category和data,所以隐式启动需要Intent能够匹配intent-filter设置的过滤信息。而且可以有多组intent-filter,只要匹配一组即可。系统即可帮我们找出合适的Activity去启动。

                                                                                                    
                    • action的匹配规则

                      action是一个字符串,系统预定了一些,也可以自己定义,区分大小写,必须在Intent-filter添加。

                      注意的是,比如AA启动AB,那么AB的过滤信息中的一组Intent-filter中可以有一个action也可以有多个action,只要有一个action匹配成功就行。再者就是AA中Intent设定action的时候只能有一个。

                      上图是设置action的方法,可以看到,设置多个action也是没有意义的,设置结果以最后一次为准。

                    • category的匹配规则

                      category内容也是一个字符串,系统预定了一些,也可以自己定义,区分大小写,必须在Intent-filter添加。

                      从上图的方法中可以看出,category可以设定多个,那么每个都要和过滤规则中的其中一个相同。也就是说,比如过滤规则中的category有5个,那么intent可以从这个5个中选3个添加,也可以全部添加,也可以重复添加。

                      系统在调用startActivity或者startActivityForResult的时候会默认为Intent加上"android.intent.

                      category.DEFAULT"(无论你的过滤规则中是否设定category,系统都会为你加上),所以必须要在过滤规则intent-filter中加上“”,否则会报错。

                    • data的匹配规则

                      data在intent或者manifest中都可以不定义,因为这不是必需的,当启动某些activity的时候才会需要,但如果intent-filter中定义了data,那么Intent中必须定义可匹配的data。

                      项目中data很少用到,特别是自己的项目作为被启动方时,记住几个常用的系统启动项目即可。

                        

                      从上面的状态数据可以见到,总体来说, data有两部分组成,mimeType和Uri。mimeType指image/jpeg,video/ 等媒体类型,也可以自己自定义,比如:ccddy/test。当然还是用公共语法最好,方便大家阅读。再者就是mimeType可以单独设定,不设定Uri。还有一种情况是不设定mimeType时,可以只设定scheme。

                      • 当只设定mimeType,不设定Uri时:
                                      

                        这个可以注册进打开音乐播放器选择中。

                        但是这种情况虽然没有指定Uri,Uri部分的schema默认值为content或file。也就是说虽然没有指定Uri,但是intent中的Uri部分的schema必须为content或者file才能匹配成功,这点尤其需要注意。

                        • 当只设定Uri,不设定mimeType时:
                              

                          这个的意思是可以匹配http开头,.pdf结尾的路径。比如程序要打开pdf,配置这样的以后用户可以使用自己的app查看pdf。

                          URI的结构如下:

                          ://:/[||]
                          • scheme:URI的模式,比如http、file、content等,如果Uri中没有指定,则整个Uri无效。可以自定义scheme,如:test、ddd等。在过滤器中只设定scheme是无效的,至少有scheme和host。

                          • host:URI的主机名,比如ccddy.top,如果URI中没有指定,则整个URI无效。实际上只设定host会报错,port、path也是一样。而且设定host之后所有的子路径均可匹配。

                          • port:URI的端口号,比如80、8080等。仅当scheme、host指定,port才有意义。

                          • path:完整路径信息,

                          • pathPrefix:表示路径的前缀信息 ,

                          • pathPattern:完整的路径信息,可以包含通配符“*”,表示0个或多个任意字符,需要注意的是通配符前面需要添加“\\”。

                            比如这个链接http://ccddy.top/categories/android/,http和scheme、ccddy.top是host、port这个链接没有,那么对于:

                            • path来讲是/categories/android/

                            • 对于pathPrefix来讲/categories就可以进行匹配了。

                            • pathPattern来讲比较麻烦。这里说下匹配符号和转义。

                              ** 匹配符号:**

                              1、" * " 用来匹配0次或更多,如:“a* ” 可以匹配“a”、“aa”、“aaa”…

                              2、“ . ” 用来匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…

                              3、因此 “ .* ” 就是用来匹配任意字符0次或更多,如:“.*html” 可以匹配“abchtml”、“chtml”,“html”,“sdf.html”…

                              转义:

                              因为当读取 Xml 的时候,“\” 是被当作转义字符的(当它被用作 pathPattern 转义之前),因此这里需要两次转义,读取 Xml 是一次,在 pathPattern 中使用又是一次。所以通配符前面需要添加“\\”。可参考上方的pdf例子。

                            • 需要注意的是,如果 Intent 要使用完整的数据,也就是 Uri 和 mimeType,可以调用 Intent 的 setDataAndType 方法,不能把 Intent 的 setType 和 setData 方法一起调用,它们两种方法都是把对方的值彼此置入空的,要两个方法 Uri 和 mime Type 一个必须是空的,下面看看这两个的源码。

                              3.3 、MAIN

                              这是任何一个APP必须有的程序切入点,算一个特殊的隐式启动,因为它其中没有必要加入android.intent.category.DEFAULT,当然加入也没有问题。

                                      

                              那能不能在manifest中设定两个呢?答案是可以的,设定两个之后会发现在桌面有两个相同图标,比如说A和B。从A和B打开是在一个APP内的,后台运行也是一个。比如有些APP想在桌面有个快速的二维码扫描图标,那么就可以这么实现。

                              3.4、 显式启动和隐式启动对比

                              • 显式启动:直接指定要跳转的Activity类名,目标明确,不用过滤,效率高,但是耦合性强,适用于同一个应用中的不同Activity跳转。
                              • 隐式启动:需要过滤,相对耗时,需要找到与之匹配的应用,目标不明确,但是耦合性弱。适用于不同应用之间的Activity跳转。

                                3.5、 启动其他应用的Activity

                                • 通过隐式启动(上方有详细讲解,这里就不介绍了);
                                • 通过ComponentName启动。

                                  实例化一个ComponentName需要两个参数,第一个参数是要启动应用的包名称,这个包名称是指清单文件中列出的应用的包名称;第二个参数是你要启动的Activity或者Service的全称(包名+类名)。

                                     启动目标APP的启动页

                                   Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER);             ComponentName cn = new ComponentName("com.lixiang.application","com.lixiang.application.MainActivity");             intent.setComponent(cn); startActivity(intent);

                                     有时候,当我们不知道应用程序的启动Activity的类名,而只知道包名,也通过ResolveInfo类来取得启动Acitivty的类名。

                                      private void openApp(String packageName) {        PackageInfo pi = getPackageManager().getPackageInfo(packageName, 0);        Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null);        resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);        resolveIntent.setPackage(pi.packageName);        List apps = pm.queryIntentActivities(resolveIntent, 0);        ResolveInfo ri = apps.iterator().next();        if (ri != null) {            String packageName = ri.activityInfo.packageName;            String className = ri.activityInfo.name;            Intent intent = new Intent(Intent.ACTION_MAIN);            intent.addCategory(Intent.CATEGORY_LAUNCHER);            ComponentName cn = new ComponentName(packageName, className);            intent.setComponent(cn);            startActivity(intent);        }    }

                                     启动目标APP的内部普通页面

                                        ComponentName cn = new ComponentName("com.lixiang.application","com.lixiang.application.MainActivity3");        try {            Intent intent = new Intent();            Bundle bundle = new Bundle();            bundle.putString("type", "1");//传递值            intent.putExtras(bundle);            intent.setComponent(cn);            startActivityForResult(intent, REQUEST_ID);//等待目标页面返回的结果        } catch (Exception e) {            e.printStackTrace();        }

                                  如果你要的启动的其他应用的Activity不是该应用的入口Activity,那么在清单文件中,该Activity节点一定要加上android:exported=“true”,表示允许其他应用打开,对于所有的Service,如果想从其他应用打开,也都要加上这个属性:

                                                                   
                                  Intent it = new Intent();it.setComponent(new ComponentName("com.lixiang.application","com.lixiang.application.MyService"));startService(it);

                                  对于除了入口Activity之外的其他组件,如果不加这个属性,都会抛出“java.lang.SecurityException: Permission Denial…”异常

                                  入口Activity和普通Activity唯一不同的地方就是入口Activity多了一个过滤器,对于包含了过滤器的组件,意味着该组件可以提供给外部的其他应用来使用,它的exported属性默认为true,相反,如果一个组件不包含任何过滤器,那么意味着该组件只能通过指定明确的类名来调用,也就是说该组件只能在应用程序的内部使用,在这种情况下,exported属性的默认值是false。

                                  四、activity的生命周期

                                  当一个Activity正常启动,方法调用的顺序是onCreate -> onStart -> onResume;在Activity被kill掉的时候方法顺序是onPause -> onStop -> onDestroy,此为一个完整的Lifecycle。

                                  那么对于中断处理(比如电话来了),则是onPause -> onStop,恢复时onRestart->onStart -> onResume;如果打开一个为Translucent(半透明) 或者小窗口的Activity那么中断就是onPause ,恢复的时候onResume。

                                  那么对于”Other app need memory”,就是我们手机在运行一个应用程序的时候,有可能打进来电话发进来短信,或者没有电了,这时候程序都会被中断,优先去服务电话的基本功能,另外系统也不允许你占用太多资源,至少要保证一些功能(比如电话),所以资源不足的时候也就有可能被kill掉。

                                  方法在系统中的作用及我们应该做什么:

                                  onCreate:在这里创建界面,做一些数据的初始化工作;

                                  onStart: 到这一步变成“用户可见不可交互”的状态;

                                  onResume:变成和用户可交互的,(在Activity栈系统通过栈的方式管理这些Activity,即当前Activity在栈的最上端,运行完弹出栈,则回到上一个Activity);

                                  onPause:到这一步是可见但不可交互的,系统会停止动画等消耗CPU的事情。从上文的描述已经知道,应该在这里保存你的一些数据,因为这个时候你的程序的优先级降 低,有可能被系统收回。在这里保存的数据,应该在onResume里读出来。

                                  onStop:变得不可见 ,被下一个activity覆盖了。

                                  onDestroy:这是Activity被kill前最后一个被调用方法了,可能是其他类调用finish方法或者是系统为了节省空间将它暂时性的干掉,可以用isFinishing()来判断它,如果你有一个Progress Dialog在线程中运行,请在onDestroy里把他cancel掉,不然等线程结束的时候,调用Dialog的cancel方法会抛异常。

                                  onPause,onstop, onDestroy,三种状态下 activity都有可能被系统kill 掉。

                                  五、activity的四种状态

                                  Android中Activity的四种状态:

                                  1)Active/Runing: 可见,可交互,一个新 Activity 启动入栈后,它在屏幕最前端,处于栈的最顶端,此时它处于可见并可和用户交互的激活状态,onCreate()(一瞬间)–>onStrat()(一瞬间)–>onResume();。

                                  2)Paused:当 Activity 被另一个透明或者 Dialog 样式的 Activity 覆盖时的状态。此时它依然与窗口管理器保持连接,系统继续维护其内部状态,所以它仍然可见,但它已经失去了焦点故不可与用户交互。onPause()–>onResume()(重回前台),onResume()–>onPause();

                                  3)Stoped :不可见,不可交互,当 Activity 被另外一个 Activity 覆盖、失去焦点并不可见时处于 Stoped 状态,onPause()–>onStop()(结束),onStop()–>onRestart()–>onStart()–>onResume()(恢复);。

                                  4)Killed :Activity 被系统杀***回收或者没有被启动时处于 Killed 状态。其中onDestory是在被系统finish期间调用的。(1)用户操作所致:onStop()–>onDestroy()(2)activity处于暂停或停止状态,若内存不足,系统自动强制执行,activity被强行结束。

                                  onDestory():系统内存不足时,跳过不执行。

                                  onPause(),onStop():不能被跳过,不能不执行

                                  六、Activity的栈式管理

                                  Android 是通过一种 Activity 栈的方式来管理 Activity 的,一个 Activity 的实例的状态决定它在栈中的位置。处于前台的 Activity 总是在栈的顶端,当前台的 Activity 因为异常或其它原因被销毁时,处于栈第二层的 Activity 将被激活,上浮到栈顶。当新的 Activity 启动入栈时,原 Activity 会被压入到栈的第二层。一个 Activity 在栈中的位置变化反映了它在不同状态间的转换。Activity 的状态与它在栈中的位置关系如下图所示:

                                  如上所示,除了最顶层即处在 Active 状态的 Activity 外,其它的 Activity 都有可能在系统内存不足时被回收,一个 Activity 的实例越是处在栈的底层,它被系统回收的可能性越大。系统负责管理栈中 Activity 的实例,它根据 Activity 所处的状态来改变其在栈中的位置。

                                  七 、Activity的启动模式

                                  Activity的启动模式是决定生成新的activity实例是否重用已存在的Activity实例,是否和其他Activity实例公用一个栈。在Android12前提供了四种启动模式:(这里的都是没有添加其它参数配置的情况,比如Intent.FLAG_ACTIVITY_NEW_TASK等。)

                                  1、standard:默认启动模式,每次激活activity时,都创建activity实例,并放入任务栈。

                                  2、singleTop: 如果某个Activity自己激活自己,即任务栈栈顶是该Activity,则不需要创建,其余情况都要创建Activity。当从别的Task任务栈切换回当前任务栈时,如果栈顶实例就是同样的Activity,则直接进行复用,并且通过onNewIntent方法进行通知。

                                  3、singleTask:如果要激活的activity在任务栈中,则不需要创建,只需要把这个Activity放入栈顶,并把该Activity以上的Activity实例都出栈。

                                  4、singleInstance:如果不存在包含目标Activity的栈,则创建一个新的Task,这个Task中是目标Activity所独有的,不允许有别的Activity存在,并且只会创建一次,后续如果在启动其它的Activity,这些新的Acitivty属于其原来的task栈。如果存在包含目标Activity的栈,则直接把包含目标Activity的Task栈挪到前台进行显示。

                                  如下图所示,在AndroidManifest文件中Activity节点里增加属性android:launchMode=”xxx” 即可:

                                  但是在Android 12 的时候新加入了singleInstancePerTask类型,所以就有五种了。

                                  5、singleInstancePerTask:如果不存在包含目标Activity的栈,则创建一个新的Task,这个Task中是目标Activity所独有的,并且只会创建一次,后续如果在启动其它的Activity,这些新的Acitivty仍然当前的task栈。如果存在包含目标Activity的栈,则把包含目标Activity的Task栈挪到前台,并且把该栈中目标Activity上面的所有Activity进行出栈操作,从而实现目标Activity显示在前台的效果。(如官方文档所描述,如果启动singleInstancePerTask类型的Activity时,同时设置FLAG_ACTIVITY_MULTIPLE_TASK标记,可以启动多个包含目标Activity的Task栈,这里不过多描述)

                                  如何区分是首次创建还是从栈中返回?

                                  答:通过回调用onNewIntent方法。

                                  如何区分是从栈顶复用还是栈中复用?

                                  答:如果是栈中复用,则onNewIntent方法中,其flags参数中会包含Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT标记。

                                  如果一个Task中的Activity全部出栈,会退到哪里?

                                  答:假设Task1启动了Task2,那么Task2中的Activity如果全部出栈,就会把Task1推到前台,Task1中栈顶的Activity就会被显示。

                                  八、其他

                                  8.1、 Activity跳转方法

                                  1、Activity跳转,无返回结果,直接startActivity(new Intent(当前Activity.this, 下一Activity.class))。

                                  2、Activity跳转,返回数据/结果,使用startActivityForResult (Intent intent, int requestCode),requestCode的值是自定义的,用于识别跳转的目标Activity。

                                  跳转的目标Activity所要做的就是返回数据/结果,setResult(int resultCode)只返回结果不带数据,或者setResult(int resultCode, Intent data)两者都返回!

                                  在setResult后,要调用finish()销毁当前的Activity,否则无法返回到原来的Activity。

                                  接收返回的数据/结果的处理函数是onActivityResult(int requestCode, int resultCode, Intent data),这里的requestCode就是startActivityForResult的requestCode,resultCode就是setResult里面的resultCode,返回的数据在data里面。

                                  8.2、Intent 可以传递的数据类型:

                                  根据上图可以看出,intent 对象可以传递的具体数据类型以及使用的方法。

                                  (1) putExtra( key , value ) 方法

                                  • 可以传递 8 种基本数据类型以及这八种基本数据类型的数组,
                                  • 可以传递 CharSequence以及它的数组,
                                  • 可以传递 Serializable 实现类的对象,
                                  • 可以传递Parcelable 实现类的对象和数组,
                                  • 可以传递Bundle 对象。

                                    (2) putExtras( value )方法

                                    • 可以传递Bundle 对象,
                                    • 可以传递Intent 对象。

                                      (3) Intent 也可以直接传递集合

                                      • 传递 Integer类型 的集合可以直接使用:putIntegerArrayListExtra( key,value)
                                      • 传递 String 类型 的集合可以直接使用: putStringArrayListExtra( key , value)
                                      • 传递 CharSequence 类型 的集合可以直接使用: putCharSequenceArrayList( key , value)
                                      • 传递 ** Parcelable 类型** 的集合可以直接使用 :putParcelableArrayList( key,value)

                                        8.3、 多个Activity跳转时的生命周期

                                        在MainActivity启动MainActivity2,然后在MainActivity2点击返回键。

                                        MainActivityMainActivity2
                                        onCreate
                                        onStart
                                        onResume
                                        启动MainActivity2
                                        onPause
                                        ~
                                        ~onCreate
                                        onStart
                                        onResume
                                        onStop~
                                        ~MainActivity2点击返回键
                                        onPause
                                        onRestart
                                        onStart
                                        onResume
                                        ~
                                        ~onStop
                                        onDestroy

上一篇:解决Unity打包时,Android SDK 报错问题

下一篇:揭秘微商城模板源码,打造电商新利器,吸引顾客必备秘籍!