关于Activity你应该知道的一切

本文详细介绍了Android中的Activity概念、启动方式及生命周期,包括Activity间的跳转、数据传递方式、任务栈管理等内容。

Activity

Context上下文

可以理解为语文中的上下文,也就是语境

  • 从Android系统的角度来理解,Context就是当前应用所处的环境,
  • 从程序的角度上来理解,Context是一个抽象类,Activity、Service、Application都是Context的子类

在Android中,可以通过上下文访问跟当前应用相关的资源

  • 获取系统资源(getsystemservice 获取系统的服务 getwallpaper 获取壁纸)
  • 获取应用的私有的资源(getAssets 获取assets目录下的内容)
  • 获取res目录的资源(getResource())
  • startActivity+startService
  • 获取内部文件(夹)路径
  • 创建View

XXXActivity.this和getApplicationContext的区别?

一个是当前Activity的实例,一个是当前应用的实例,两个的生命周期不同,使用时要注意使用的场景,如果使用不当就会造成一些问题。

Context的应用场景

凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有(生命周期),防止内存泄漏

1.什么是Activity

Activity是Android四大组件之一,用于展示界面并和用户交互。

Activity之间通过Intent进行通信。Intent即意图,用于描述一个页面的信息,同时也是一个数据的载体。

2. Activity之间的跳转

Activity之间的跳转分为2种:

  • 显式跳转:直接通过字节码 .class打开对应activity,在打开自己应用activity的时候 一般使用显示意图
  • 如果是打开其他应用的activity,intent创建之后要通过intent.setClassName(“包名”,”全类名”); 这个方法打开其它应用的activity(一般不用)

    Intent intent = new Intent(this, SecondActivity.class);
    startActivity(intent);
    
  • 隐式跳转:通过在清单文件中activity节点下配置一个intent-filter,可以让自己的应用或其他应用通过匹配意图过滤器来打开当前的activity

  • 需要注意:想通过隐式意图打开activity,意图过滤器必须要写,category节点配置为DEFAULT 虽然代码中不用匹配category
  • 如果 data节点中既有scheme节点也有mimetype 需要使用setDataAndType来匹配这两个信息

    //创建一个Intent对象
    Intent intent = new Intent();
    //设置Action
    intent.setAction("android.intent.action.VIEW");
    //设置category
    intent.addCategory("android.intent.category.BROWSABLE");
    //设置参数
    intent.setData(Uri.parse("http://www.gxs.com"));
    //启动Activity
    startActivity(intent);
    
    清单文件中
    * action 指定动作(自定义)
    * data   指定数据
    * category 类别 (机顶盒,车载电脑)
    * 通常我们需要去开发一个和系统应用一样功能的软件时,就可以拿取他的action和data和category
    

    一般打开自己应用的activity使用显示意图,打开别人应用的activity使用隐式意图

当打开系统应用Activity时,需要寻找系统应用的action/Category/Data

  1. 打开相应的系统应用,观察logcat中activitymanager打印的信息

    I/ActivityManager: START u0 {cmp=com.android.mms/.ui.ComposeMessageActivity} from pid 1081
    I/ActivityManager: Displayed com.android.mms/.ui.ComposeMessageActiy
    
  2. 根据ComposeMessageActivity在系统上层源码里对应的清单文件中找到对应的结点(Activity-intentfilter)
  3. 如果需要传递数据的话,可以去代码中找到传输数据的格式
通过隐式意图开启一个Activity,可以先进行一下判断

如果不判断,当没有Activity匹配的时候,会出现错误

  1. PackageManager.resolveActivity
  2. Intent.resolveActivity

当返回值为空时,就是没有。另外注意第二个参数为MATCH_ DEFAULT_ ONLY

intent-filter节点及其子节点都可以同时定义多个,隐式启动时只需与任意一个匹配即可

IntentFilter中的过滤信息有:action,category,data

分析各属性的匹配规则:

  1. action的匹配规则
    • 隐式启动必须要有action
    • action的匹配规则要求intent中的action和IntentFilter中的action完全相同
  2. category的匹配规则
    • intent中可以没有category,但若是有,不管有几个,只要能够和过滤规则中任何一个category相同即可
  3. data的匹配规则

    • 这个和action类似,如果过滤规则中定义了data,那么intent必须也要定义能够匹配的data
    • data的结构:(data由mimeType和URI两部分组成

      MIME是描述消息内容类型的因特网标准。
      MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。
      类型/子类型
      * mimeType指媒体类型
          * image/jpg/video....可以表示图片,文本,视频等不同的媒体格式
      
      * URI包含的数据就比较多了,下面是URI的结构
          <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
          eg:
          http://www.baidu.com:80/search/info
          content://com.jike.project:200/folder/subfolder/etc
      
          下面介绍一下每个数据的含义:
          scheme:URI的模式,比如http,file,content等
          Host:URI的主机名,比如www.baidu.com
          port:URI中的端口号,比如80
          path,pathPrefix,pathPattern:这三个参数表示路径信息
      
      //data的语法
      <data
          android:host="string"
          android:mimeType="string"
          android:path="string"
          android:pathPattern="string"
          android:pathPrefix="string"
          android:port="string"
          android:scheme="string" />
      

若清单文件中的Activity声明为:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

则此Activity将作为程序的入口,有几个作为入口的Activity,apk文件安装的时候就会生成几个图标。

3. 使用Intent传递数据

Intent可传递的数据类型有: 基本数据类型及其数组 String String数组 String集合 Integer集合 序列化(serilizeble parcelable) 数组和集合,也可以通过Bundler来传递数据 把数据封装到一个对象中

  • 注意:Intent传递的数据过多可能会造成跳转速度极慢甚至黑屏一会,不要用Intent传递过多的数据,会影响到应用程序的使用。

    意图设计目的

    1. 高内敛,低耦合。
    2. 使得程序组件之间彼此独立,又可以互相调用。

    数据传递有两种情况:

  • 1.从A界面打开B界面,把A界面的数据传递给B界面

示例如下:

在清单文件中设置SecondActivity的intent-filter参数

<activity android:name="com.gxs.activitySkip.SecondActivity">
    <!-- 配置意图过滤器 -->
    <intent-filter >
        <!-- 在意图过滤器中设置action和category,当有匹配的action和category的时候启动该Activity -->
        <action android:name="com.gxs.activitySkip.SecondActivity"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <!-- 设置数据协议 -->
        <data android:scheme="money"/>
        <!-- 配置数据的mimeType:必须为xxx/xxx的格式,否则会报异常  -->
        <data android:mimeType="data/mymime"/> 
    </intent-filter>
 </activity>

在FirstActivity布局中添加一个按钮,给该按钮绑定事件,点击该按钮实现跳转到SecondActivity界面

// 发送数据给SecondActivity
public void sendData2Second(View view) {
    // 创建一个Intent对象
    Intent intent = new Intent();
    // 设置Action
    intent.setAction("com.gxs.activitySkip.SecondActivity");
    // android.intent.category.DEFAULT为Android系统默认,省略也可以
    intent.addCategory("android.intent.category.DEFAULT");
**  //当有setData和setType需要使用这个,这两个会互相清除对方的值**
    intent.setDataAndType(Uri.parse("money:转账100元。"), "data/mymime");

    //给Intent设置数据
    intent.putExtra("name", "张三");

    ArrayList<String> list = new ArrayList<String>();
    for(int i=0;i<10;i++){
        list.add("list"+(i+1));
    }
    //给Intent设置字符串类型的集合
    intent.putStringArrayListExtra("list", list);

    //声明一个Bundle对象
    Bundle bundle = new Bundle();
    //在Bundle对象中绑定数据
    bundle.putString("pwd", "123456");
    //给Intent设置Bundle对象
    intent.putExtras(bundle);

    // 启动Activity
    startActivity(intent);

}

编写SecondActivity类,使其接收数据

public class SecondActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        //从当前Context对象中获取Intent
        Intent intent = getIntent();
        //获取数据
        String data = intent.getData().toString();
        System.out.println(data);

        //从Intent中获取Bundle对象
        Bundle extras = intent.getExtras();
        //从Bundle中获取key为name的数据
        String name = (String) extras.get("name");
        System.out.println("name="+name);

        //从Bundle对象中获取key为pwd的数据
        String pwd = (String) extras.get("pwd");
        System.out.println(pwd);

        //从Bundle中获取ley为list的数据
        List<String> list = (List<String>) extras.get("list");
        System.out.println(list);
    }
  • 2.从A界面打开B界面,B界面关闭的时候,返回一个数据给A界面

跳转时使用startActivityForResult方法开启新页面

startActivityForResult(intent, 请求码);

在被开启的新的页面里, 调用setResult方法设置回传的数据

//获取当前Intent
Intent intent = getIntent();
//获取Intent中绑定的数据
Bundle extras = intent.getExtras();
int data = (Integer) extras.get("rp");

//创建一个新的Intent
Intent data = new Intent();
data.putExtra("phone", phone);
//设置一个结果数据,数据会返回给调用者
setResult(结果码, data); 
//关闭掉当前的activity,才会返回数据
finish();

当页面被关闭后,回传数据给前一个页面的onActivityResult方法,在此方法中对数据进行处理即可,如果有多个页面会返回数据 可以通过requestcode 或者 resultcode区分 不同数据

//请求码  结果码  传递的数据
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    //从当前Intent中获取数据(需要进行非空判断)
    Bundle extras = data.getExtras();
    String result  = (String) extras.get("result");
}

注意:如果请求码设为-1,则使用startActivityForResult(intent, 请求码),没有结果返回。

4. 生命周期

生命周期方法

* oncreate()  activity被创建的时候调用的方法
> 作用:ui界面的初始化 setContentView()

* onStart() 当界面变成用户可见的时候调用的方法(可见不可交互)
> 作用:更新ui的操作,eg:播放视频

* onResume()//resume继续 ,当界面获取到焦点的时候(可见可交互)
> 作用:界面获取到了焦点,按钮可以相应点击事件

* onPause()//pause暂停,当界面失去焦点的时候调用的方法(部分可见不可交互)
> 作用:界面失去了焦点,按钮不可以被点击了
> 不能在onPause中做重量级操作

* onStop() activity的界面用户不可见(完全不可见)
> 界面不可见,eg:暂停视频播放

* onDestroy() activity被销毁的时候调用的方法
> 作用:界面退出之前的扫尾操作,eg:线程的销毁,退出前数据的保存。

* onRestart()//activity重新启动
>作用:activity被最小化了,并没有销毁,如果下次再去打开这个activity就会调用此方法

完整生命周期

onCreate-->onStart-->onResume-->onPause-->onStop-->onDestory

可视生命周期

onStart-->onResume-->onPause-->onStop

前台生命周期

onResume-->onPause

见图——activity生命周期

正常情况下的生命周期分析:

  1. onStart和onStop是针对Activity**是否可见来说的,OnResume和OnPause是针对Activity是否位于前台**来说的。
  2. 当前是ActivityA,这时打开ActivityB,A的OnPause先于B的OnResume执行。
  3. 不能在onPause中做重量级操作,因为必须onPause执行完成之后新Activity才能Resume

    异常情况下的生命周期分析:

  4. 相关的系统配置发生改变(屏幕旋转)或者内存不足而导致Activity被杀死并重新创建

    • 由于Activity是在异常情况下终止,系统会调用onSaveInstanceState(Bundle outState)来保存当前Activity的状态(这个方法在Onstop之前调用,并且这个方法只会在Activity异常终止的情况下调用,正常不会调用),
    • 当Activity被重新创建,系统会调用onRestoreInstanceState(Bundle savedInstanceState)方法,并把onSaveInstanceState保存的Bundle对象传递给onRestoreInstanceState和onCreate方法。
    • 因此我们可以通过onRestoreInstanceState判断Activity是否重建,如果重建了可通过onRestoreInstanceState中的Bundle来恢复数据(在OnStart之后调用)
    • 在activity重建过程中,系统会保存当前Activity的视图结构,并自动恢复,比如文本框中的数据,listview滚动的位置,并自动恢复
    • 和Activity一样,每个View都有这两个方法,通过查看源码可知系统为每个View保存恢复哪些数据。

      保存恢复View层次结构,系统工作流程:
      activity被异常终止--activity调用onSaveInstanceState保存数据--然后Activity会委托Window去保存数据--接着window委托它里面的顶级容器DectorView去保存数据
      --DectorView通知子View保存数据。
      这是一种委托思想,上层委托下层。类似于View的绘制,事件的分发机制。
      
  5. 内存不足导致低优先级的Activity被杀死
    • 其数据存储和恢复过程与1过程一致。
  6. activity优先级
    1. 前台activity——正在和用户交互,优先级最高
    2. 可见但非前台activity——比如activity中弹出一个对话框,优先级其次
    3. 后台activity——执行了OnStop
横竖屏切换的生命周期

默认情况下 ,横竖屏切换, 会销毁当前的activity,重新创建一个新的activity

  1. 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

  2. 设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

  3. 设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

需求:让屏幕旋转不再重新调用生命周期

  1. 横竖屏写死

    android:screenOrientation="landscape"
    android:screenOrientation="portrait"
    //screen 屏幕 orientation 方向 landscape 风景(躺) portrait 肖像
    
  2. 让系统的环境 不在去敏感横竖屏的切换。

     android:configChanges="orientation|screenSize|keyboardHidden"
    

    在Activity结点声明

5. 任务栈

任务栈创建的目的:管理activity

  1. 程序打开时就创建了一个任务栈, 用于存储当前程序的activity,所有的activity属于一个任务栈。
  2. 只有在任务栈栈顶的activity才可以跟用户进行交互。
  3. 任务栈可以移动到后台, 并保留了每一个activity的状态. 并有序的给用户列出它们的任务, 而且还不丢失它们状态信息。
  4. 当把任务栈中所有的activity清除出栈时,任务栈会被销毁,程序退出。
  5. 一般一个应用程序对应一个任务栈,手机操作系统里面有多个任务栈
  6. 任务栈的id是应用程序第一次开启的时候分配出来的,是一个自增长的int类型的数据。如果应用程序退出,id被回收。
  7. 一个Task中的activity可以来自不同的APP,一个APP的activity也可以在不同的栈中。

任务栈的缺点:

  1. 每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用户体验差, 需要点击多次返回才可以把程序退出了。
  2. 每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。

为了解决任务栈产生的问题,Android为Activity设计了启动模式,那么下面的内容将介绍Android中Activity的启动模式

activity启动模式,修改任务栈

启动模式(launchMode)在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例共用一个task。

Activity一共有以下四种launchMode:

  1. standard 标准启动模式
    • 默认的启动模式,每次启动activity都会创建新的实例。
  2. singletop 栈顶复用模式

    • 当栈顶有将要开启的Activity时,会复用这个Activity,同时这个activity的onNewIntent方法会被回调。这个activity的onCreate,OnStart方法不会被调用,因为它没有发生改变。若是栈顶没有,那么就会重新创建
    • 应用场景:适用于接收到消息后显示的界面。例如:QQ接收到消息后会弹出activity,但如果一次来10条消息,总不能一次弹出10个activity。
  3. singetask 栈内复用模式,在当前任务栈里面只能有一个实例存在

    • 当一个启动模式为singleTask的activityA请求启动后,系统会先寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例并将A放到栈中。如果存在A所需的任务栈,这时要看栈中有是否有要开启的activityA,如果有则直接复用并删除A上面的activity,将A移动到栈顶,同singletop一样,也会回调这个activity的onNewIntent方法。如果没有实例,则创建A实例并压入栈中

    • 现在有两个任务栈,前台任务栈中有BA,后台任务栈中有DC,假设DC启动启动模式都为singleTask。现在请求启动D,那么整个后台任务栈都会被切到前台,这时候前台任务栈为DCBA,当按back键时,前台栈中activity会一一出栈;如果请求启动C,那么情况就不一样了,会把D删除,C切换到前台。(具体看图——singleTask启动模式特例.pptx)

    • 所以这种启动模式通常可以用来退出整个应用程序。。将主activity设为singleTask,然后在要退出的activity中转到主Activity,从而将主Activity上的其他activity全部清除,然后在主Activity中的onNewIntent()中加上finish(),将最后一个activity结束。

  4. singleInstance 单实例模式,可以看作加强版singleTask模式

    • activity会开启一个新的任务栈,并且这个任务栈里面只有一个实例存在。
    • 这种启动模式和浏览器的工作原理类似。当多个程序访问浏览器时,如果浏览器没有打开,则打开浏览器,否则会在当前打开的浏览器中访问。举个例子来说,当应用A的任务栈创建了ActivityA实例,并且其启动模式为sinleInstance,如果应用B也要激活ActivityA,则不需要创建,两个应用共享即可。
    • 如果你要保证一个activity在整个手机操作系统里面只有一个实例存在,使用singleInstance
    • 关于singleInstance这种启动模式还有一点需要特殊说明:
      • 如果在一个singleInstance的activityA中通过startActivityForResult()去启动另一个activityB,那么在A中拿不到数据。因为android不允许task间互相传递数据。

onNewIntent()

protected void onNewIntent(Intent intent)通过这个方法可以取出当前请求的信息

* 第一次创建Activity A时,执行的逻辑顺序是:
* onCreate() ­­­>onStart()­­­>onResume()
* 而如果使用singleTask模式第二次启动Activity A,且A处于任务栈的顶端,则执行的逻辑顺序是:
* onNewIntent() ­­­>onRestart> onStart>onResume()。
注意,getIntent()仍返回原来的意图。你可以使用setIntent来设置新的意图。

如何给activity设置模式

  1. 在清单文件中设置

     <activity
        android:name=".B"
        android:launchMode="singleTask"*******
        --------------------------------------------------------------
        android:taskAffinity="kk" //任务栈名称,默认为包名
    
        //taskAffinity属性主要和singleTask启动模式配对使用或者allowTaskPeparenting属性配对使用
        * 当一个应用A启动了应用B的某个activity,如果这个activity的allowTaskPeparenting设置为true的话,那么当应用B被启动后,此activity会直接从应用A的任务栈转移到应用B的任务栈中。
        />  
    
  2. 在代码中通过Intent设置

    Intent intent =new Intent(this,MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);*********
    startActivity(intent);
    
    Intent Flag设置启动模式
    * Intent.FLAG_ ACTIVITY_ NEW_TASK
        * 开启一个新的task来存放这个activity
        * 通常用于在广播,服务中开启一个新的activity。由于他们中并不存在activity栈,所以需要这样。
    * intent.FLAG_ ACTIVITY_ NO_HISTORY
        * 使用这种模式启动的activity,当该activity启动其他activity的时候,这个activity就消失了。eg:A-B,B中以这种模式启动C,C在启动D,则当前栈为ABD
    * FLAG_ ACTIVITY_ SINGLE_ TOP
        * ==singleTop
    * FLAG_ ACTIVITY_ clear_ Top
        * 清除位于它上面的Activity
    

二者区别:
1. 第二种优先级大于第一种
2. 当两个同时设置时,以第二种为准,

任务栈_清单文件中相关参数

TaskAffinity
    标识了一个Activity需要的任务栈。默认情况下,所有Activity的任务栈的名字是包名

这个属性主要和singleTask/allowTaskReparenting配合使用
1. 当TaskAffinity和singleTask配合使用时,TaskAffinity为当前任务栈的名字,这个Activity会运行在名字和TaskAffinity相同的任务栈中
2. 当TaskAffinity与allowTaskReparenting配合使用的时候。当应用A启动应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true,那么当B被启动后,此Activity会直接从A的任务栈转移到B的任务栈中(栈顶)

Android中允许将四大组件放到单独的进程中运行,只有四大组件可以放到别的进程中

补充

android:allowBackup

它表示是否允许应用程序参与备份。如果将该属性设置为false,则即使备份整个系统,也不会执行这个应用程序的备份操作,而整个系统备份能导致所有应用程序数据通过ADB来保存。该属性必须是一个布尔值,或为true,或为false,其默认值为true。

<think>嗯,用户之前问过关于从Launcher启动APP时的IPC次数,现在又针对应用进程启动后绑定系统服务的步骤请求更详细的解释。这说明用户可能对Android应用启动流程中的各个阶段不太清楚,尤其是系统服务和应用进程之间的交互。用户提到“我之前并不了解还需要绑定系统服务”,说明他对这一步骤的具体作用和过程有疑问。需要详细讲解attachApplication()的作用,以及为什么需要绑定系统服务。 首先,用户可能对应用进程启动后的初始化流程不熟悉,特别是ActivityThread.main()的作用。需要解释这个入口方法如何启动主线程,初始化应用环境。然后,attachApplication()这一步是关键,因为应用进程需要告诉系统服务它已经准备好,这样系统服务才能继续调度Activity的启动。这里需要说明绑定服务的必要性,比如权限验证、进程管理、生命周期协调等。 另外,用户可能对Binder通信在此过程中的具体应用感兴趣,需要提到ApplicationThread和IApplicationThread的作用,以及它们如何作为桥梁进行跨进程通信。还可能涉及到AMS(ActivityManagerService)的角色,以及attachApplication()如何将应用进程的信息传递给AMS,使得AMS能够后续发送请求给应用进程。 还要考虑用户可能的背景,可能是Android开发者或者学习者,想深入理解底层机制。因此,需要结合代码示例或流程图,帮助用户更直观地理解。例如,可以描述ActivityThread.main()如何调用attach()方法,进而通过Binder调用到AMS的attachApplication(),然后AMS通过ApplicationThread回调到应用进程,触发创建Application和Activity实例。 此外,用户可能存在的误区是认为应用进程启动后会自动处理一切,但实际上需要主动与系统服务绑定,才能进行后续操作。需要强调系统服务在Android中的核心调度作用,以及应用进程必须注册自己才能被系统管理和协调。 最后,总结整个过程,强调绑定系统服务的目的是为了让应用进程和系统服务建立双向通信,确保后续的生命周期方法能够正确执行,这也是Android系统安全和稳定性的重要机制。</think>好的!这个问题涉及 **Android 应用进程启动后与系统服务(如 `ActivityManagerService`, AMS)的绑定机制**,这是应用进程生命周期中的关键步骤。我来详细拆解这个过程: --- ### **1. 应用进程的入口:`ActivityThread.main()`** 当 Android 系统通过 **Zygote** 进程 fork 出新应用进程后,会调用 `ActivityThread.main()` 作为应用进程的入口。这一步的核心操作是: - **初始化主线程(UI 线程)**:创建 `Looper` 和 `Handler`,进入消息循环(`Looper.loop()`)。 - **创建 `ActivityThread` 对象**:这是应用进程的“大脑”,负责管理应用的主组件(Activity、Service 等)。 - **调用 `attach()` 方法**:向系统服务(AMS)发起绑定请求。 ```java // ActivityThread.java(简化版) public static void main(String[] args) { // 初始化主线程的 Looper Looper.prepareMainLooper(); // 创建 ActivityThread 实例 ActivityThread thread = new ActivityThread(); // 关键:向系统服务发起绑定请求 thread.attach(false); // 启动消息循环 Looper.loop(); } ``` --- ### **2. 绑定系统服务:`attachApplication()`** 在 `attach()` 方法中,应用进程会通过 **Binder IPC** 调用系统服务(如 AMS)的 `attachApplication()` 方法。这一步的核心目的是: - **向系统服务注册应用进程**:告诉系统服务“我已经启动了,可以接收任务了”。 - **传递关键对象 `ApplicationThread`**:这是一个 Binder 对象(`IApplicationThread`),用于系统服务后续向应用进程发送指令(如启动 Activity)。 ```java // ActivityThread.java(简化版) private void attach(boolean system) { // 获取 ActivityManagerService 的 Binder 代理 final IActivityManager mgr = ActivityManager.getService(); // 调用 AMS 的 attachApplication(),传递 IApplicationThread 对象 mgr.attachApplication(mAppThread); } ``` --- ### **3. 系统服务的回应:`bindApplication()`** 当 AMS 收到 `attachApplication()` 请求后,会做两件事: 1. **校验应用进程的权限和参数**:确保应用进程是合法的(比如签名、权限是否匹配)。 2. **通过 Binder 回调应用进程的 `ApplicationThread`**:触发 `bindApplication()` 方法,通知应用进程初始化 `Application` 和主 Activity。 ```java // ApplicationThread.java(应用进程内部) public final void bindApplication(...) { // 通过 Handler 发送消息到主线程,初始化 Application sendMessage(H.BIND_APPLICATION, data); } ``` --- ### **4. 应用进程的初始化流程** 应用进程收到 `bindApplication()` 回调后,会按顺序执行以下操作: 1. **创建 `Application` 实例**:调用 `Application.onCreate()`。 2. **加载主 Activity**:根据 AMS 的指示,通过反射创建 Activity 实例。 3. **触发生命周期方法**:执行 `Activity.onCreate()`、`onStart()`、`onResume()`。 ```java // ActivityThread 内部处理 BIND_APPLICATION 消息 private void handleBindApplication(...) { // 创建 Application 对象 Application app = data.info.makeApplication(...); // 调用 Application.onCreate() app.onCreate(); } ``` --- ### **为什么需要“绑定系统服务”?** 1. **权限与控制**:系统服务(AMS)需要统一管理所有应用进程的生命周期,防止恶意应用直接操作组件。 2. **跨进程通信桥梁**:通过 `IApplicationThread`,系统服务可以向应用进程发送指令(如启动/暂停 Activity)。 3. **资源协调**:系统服务需要知道应用进程的状态(是否存活、优先级等),以管理内存和调度任务。 --- ### **完整流程图示** ``` 应用进程启动 → ActivityThread.main() → attach() → IPC 调用 AMS.attachApplication() ↑ ↓ Looper.loop() ← 处理消息 ← AMS 回调 bindApplication() → 创建 Application → 启动主 Activity ``` --- ### **总结** - **绑定系统服务(`attachApplication()`)** 是应用进程启动后必须执行的操作,目的是向系统服务注册自身并建立双向通信。 - **`ApplicationThread`** 是系统服务控制应用进程的“遥控器”,所有后续的组件生命周期操作(如启动 Activity)都通过它触发。 - 这一机制体现了 Android 的 **安全设计**:系统服务始终是组件的“中央调度器”,应用进程不能绕过它直接操作组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值