前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >「Android」四大组件,你真的都掌握了?

「Android」四大组件,你真的都掌握了?

作者头像
圆号本昊
发布2021-09-24 11:52:29
1K0
发布2021-09-24 11:52:29
举报
文章被收录于专栏:github@hornhuang

前言

  • Android 的四大组件指:Activity Service Broadcast ContentProvider
  • 本文将着重分析 Activity 和 Service

一、 Activity


  • 提供一个界面让用户点击和各种滑动操作

1.1 生命周期

  • 启动Activity: onCreate()—>onStart()—>onResume(),Activity进入运行状态。
  • Activity退居后台: 当前Activity转到新的Activity界面或按Home键回到主屏: onPause()—>onStop(),进入停滞状态。
  • Activity返回前台: onRestart()—>onStart()—>onResume(),再次回到运行状态。
  • Activity退居后台,且系统内存不足, 系统会杀死这个后台状态的Activity,若再次回到这个Activity,则会走onCreate()–>onStart()—>onResume()
  • 锁定屏与解锁屏幕 只会调用onPause(),而不会调用onStop方法,开屏后则调用onResume()
1.1.1 两个活动间跳转
  • Activity A 启动另一个Activity B,回调如下: Activity A 的onPause() → Activity B的onCreate()onStart()onResume() → Activity A的onStop(); 如果B是透明主题又或则是个DialogActivity,则不会回调A的onStop

如何将一个 Activity 设置成窗口的样式? 只需要给我们的 Activity 配置如下属性即可: android:theme="@android:style/Theme.Dialog"

1.1.2 onSaveInstanceState

Activity 的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。

  • 调用条件
  • 应用场景
代码语言:javascript
复制
lateinit var textView: TextView [注1]
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)
    setContentView(R.layout.activity_main)
    textView = findViewById(R.id.text_view)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    super.onSaveInstanceState(outState)
}
1.1.3 拓展
代码语言:javascript
复制
private var name0: String //报错
private var name1: String = "xiaoming" //不报错
private var name2: String? = null //不报错
  • 可是有的时候,我并不想声明一个类型可空的对象,而且我也没办法在对象一声明的时候就为它初始化,那么这时就需要用到Kotlin提供的延迟初始化。
  • Kotlin中有两种延迟初始化的方式。
  • 一种是lateinit var,一种是by lazy。

1.2 启动模式


LaunchMode

说明

standard

系统在启动它的任务中创建 activity 的新实例

singleTop

如果activity的实例已存在于当前任务的顶部,则系统通过调用其onNewIntent(),否则会创建新实例

singleTask

系统创建新 task 并在 task 的根目录下实例化 activity。但如果 activity 的实例已存在于单独的任务中,则调用其 onNewIntent() 方法,其上面的实例会被移除栈。一次只能存在一个 activity 实例

singleInstance

相同 singleTask,activity始终是其task的唯一成员; 任何由此开始的activity 都在一个单独的 task 中打开注2

1.2.1 standard
  • 默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。
1.2.2 singleTop
  • 可以有多个实例,但是不允许多个相同Activity叠加。 即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。
1.2.3 singleTask
  • 只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
  • 如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task中共存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。
1.2.4 singleInstance
  • 只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。
1.2.5 task
  • 我们知道,一个应用中可能有多个 Activity,而这些 Activity 被以栈的形式管理。当我们新打开 Activity 或者按返回时,会导致 Activity 的入栈/出栈。
  • Task 是指在执行特定任务时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈中。

1.3 启动过程

  • 这里需要结合源码自己看,不做展开,针对面试的话,只需记住方法/调用链即可
1.3.1 ActivityThread.java

  • 在Android中它就代表了Android的主线程,注意是代表而不是说它就是一个Thread类
  • 它是创建完新进程之后(肯定是在一个线程中啊),main函数被加载,然后执行一个loop的循环使当前线程进入消息循环,并且作为主线程。
  • 接下来还会初始化很多必要的属性. 它的很多成员变量和内部类十分的重要,深入理解这些成员变量有助于我们进一步的认识Android系统的各个组件的交互。
代码语言:javascript
复制
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        //step 1: 创建LoadedApk对象
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                Context.CONTEXT_INCLUDE_CODE);
    }
    ... //component初始化过程

    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    //step 2: 创建Activity对象
    Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    ...

    //step 3: 创建Application对象
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);

    if (activity != null) {
        //step 4: 创建ContextImpl对象
        Context appContext = createBaseContextForActivity(r, activity);
        CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
        Configuration config = new Configuration(mCompatConfiguration);
        //step5: 将Application/ContextImpl都attach到Activity对象
        activity.attach(appContext, this, getInstrumentation(), r.token,
                r.ident, app, r.intent, r.activityInfo, title, r.parent,
                r.embeddedID, r.lastNonConfigurationInstances, config,
                r.referrer, r.voiceInteractor);

        ...
        int theme = r.activityInfo.getThemeResource();
        if (theme != 0) {
            activity.setTheme(theme);
        }

        activity.mCalled = false;
        if (r.isPersistable()) {
            //step 6: 执行回调onCreate
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }

        r.activity = activity;
        r.stopped = true;
        if (!r.activity.mFinished) {
            activity.performStart(); //执行回调onStart
            r.stopped = false;
        }
        if (!r.activity.mFinished) {
            //执行回调onRestoreInstanceState
            if (r.isPersistable()) {
                if (r.state != null || r.persistentState != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                            r.persistentState);
                }
            } else if (r.state != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
            }
        }
        ...
        r.paused = true;
        mActivities.put(r.token, r);
    }

    return activity;
}
1.3.2 Instrumentation
  • Android instrumentation是Android系统里面的一套控制方法或者”钩子“。 这些钩子可以在正常的生命周期(正常是由操作系统控制的)之外控制Android控件的运行。 它们同时可以控制Android如何加载应用程序。

Base class for implementing实现 application instrumentation code工具代码. When running with instrumentation turned on, this class will be instantiated for被实例化 you before any of the application code, allowing you to monitor all of the interaction交互 the system has with the application. An Instrumentation implementation is described to the system through an AndroidManifest.xml’s tag.

1.3.3 ActivityManagerService「AMS」
1.3.4 ActivityStarter
  • ActivityStarter是加载Activity的控制类,收集所有的逻辑来决定如何将Intent和Flags转为Activity并将其与Task和Stack关联。
1.3.5 ActivityStackSupervisor
  • AMS 通过操作ActivityStackSupervisor来管理Activity
1.3.6 ActivityStack
  • ActivityStack从名称来看是跟栈相关的类,其实它是一个管理类,用来管理系统所有Activity的各种状态。
  • 它由ActivityStackSupervisor来进行管理的,而ActivityStackSupervisor在AMS中的构造方法中被创建。
1.3.7 ApplicationThread
  • 它是ActivityThread的私有内部类,也是一个Binder对象。
  • 在此处它是作为IApplicationThread对象的server端等待client端的请求然后进行处理,最大的client就是AMS.

1.4 关于 Content 和 Application

1.4.1 Context 须知
  • Context 从字面上理解就是上下文的意思,在实际应用中它也确实是起到了管理 上下文环境中各个参数和变量的总用,方便我们可以简单的访问到各种资源。
  • Context继承关系

二、Service


  • 可以在后台执行长时间运行操作而没有用户界面的应用组件
  • Service 分为两种工作状态,一种是启动状态,主要用于执行后台计算;另一种是绑定状态,主要用于其他组件和 Service 的交互。
  • 对于同一 app 来说,默认情况下 Service 和 Activity 是在同一个线程中的,main Thread (UI Thread)。

2.1启动过程

2.1.1 注册Service
  • Service还是运行在主线程当中的,所以如果需要执行一些复杂的逻辑操作,最好在服务的内部手动创建子线程进行处理,否则会出现UI线程被阻塞的问题
2.1.2 ActivityThread.java
  • Service 的启动

代码语言:javascript
复制
@UnsupportedAppUsage
private void handleCreateService(CreateServiceData data) {
    ···
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
    } 
    ···

    try {
        if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service);

        Application app = packageInfo.makeApplication(false, mInstrumentation);
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        service.onCreate();
        mServices.put(data.token, service);
        try {
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } 
    ··· 
}
2.1.3 ContextWrapper
  • Proxying 代理 implementation 实现 of Context that simply 简单的 delegates 委托 all of its calls to another Context. Can be subclassed to 归为 modify 修改 behavior without changing the original Context.
2.1.4 ContextImpl

ContextImpl 作为 Context 的抽象类,实现了所有的方法,我们常见的 getResources() , getAssets() , getApplication() 等等的具体实现都是在 ContextImpl

2.1.5 AMP/AMN/AMS AT/ATP/ATN

这块内容太多,比如可以看看这个 AMP/AMN/AMS AT/ATP/ATN

2.1.6 Zygote

Zygoteandroid 系统应用中一个相当重要的进程,其主要功能是执行 Android 应用程序。在 android 系统中运行新的应用,需要跟Zygote 进程(拥有应用程序运行时所需要的各种元素和条件)结合后才能执行。

Zygote 进程运行时,会初始化 Dalvik 虚拟机,并启动它。android 的应用程序是由 java 编写的,不能直接以本地进程的形态运行在 linux 上,只能运行在 Dalvik 虚拟机中。并且每个应用程序都运行在各自的虚拟机中,应用程序每次运行都要重新初始化并启动虚拟机,这就相当耗时。在 android 中,应用程序运行前,Zygote 进程通过共享已运行的虚拟机的代码与内存信息,缩短应用程序运行所耗费的时间。并且,它会事先将应用程序要使用的 android Fromework 中的类和资源加载到内存中,并组织形成所用资源的链接信息。新运行的 android 应用程序在使用所需要的资源时不必每次重新形成资源的链接信息,这样提高程序运行速度。

android 中,使用Zygote进程的目的?对于手机,为了是应用程序在有限的资源型有更快的运行响应速度,提高资源利用率和设备使用时间。android 使用 Zygote 来有效的减少系统负担,提高运行速度。

2.2 绑定过程

2.2.1 只使用startService启动服务的生命周期
  • 如果仅仅只是为了开启一个后台任务,那么可以使用startService方法。
  • 如果想获取Service中提供的代理对象,那么必须通过bindService方法,进行绑定服务。 使用场景比如:音乐播放器,第三方支付等。
2.2.2 只使用BindService绑定服务的生命周期
  • Service 的 onRebind(Intent)方法, 如果在 onUnbind() 方法返回 true 的情况下会执行,否则不执行。
2.2.3 同时使用startService()启动服务、BindService()绑定服务的生命周期
  • 在Activity中可以通过startService和bindService方法启动Service。
2.2.4 ActivityThread.java
代码语言:javascript
复制
private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    ···
    if (s != null) {
        try {
            data.intent.setExtrasClassLoader(s.getClassLoader());
            data.intent.prepareToEnterProcess();
            try {
                if (!data.rebind) {
                    IBinder binder = s.onBind(data.intent);
                    ActivityManager.getService().publishService(
                            data.token, data.intent, binder);
                } else {
                    s.onRebind(data.intent);
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                }
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        } 
        ···
    }
}

2.3 生命周期

说明

START_NOT_STICKY

如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务

START_STICKY

如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务

START_REDELIVER_INTENT

如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务

2.4 Service 与 Activity 通信

2.5 启用前台服务

代码语言:javascript
复制
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
代码语言:javascript
复制
Notification notification = new Notification(icon, text, System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, title, mmessage, pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);

2.6 IntentService

IntentService是Service的子类,是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题

  • 区别

三、BroadcastReceiver


  • 一种广泛运用在应用程序之间传输信息的机制,通过发送Intent来传送我们的数据
  • 内部通信实现机制:通过android系统的Binder机制.

3.1 target 26 之后,无法在 AndroidManifest 显示声明大部分广播,除了一部分必要的广播,如:

  1. ACTION_BOOT_COMPLETED :关于BOOT_COMPLETED广播-自启动
  2. ACTION_TIME_SET :与时间相关,计时广播
  3. ACTION_LOCALE_CHANGED : locale信息改变之后,会发广播消息Intent.ACTION_LOCALE_CHANGED
代码语言:javascript
复制
LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(receiver, filter);

3.2 注册过程

  • 情况文件注册(静态广播)
  1. 一个app里:自定义一个类继承BroadcastReceiver然后要求重写onReveiver方法
代码语言:javascript
复制
public class MyBroadCastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("MyBroadCastReceiver", "收到信息,内容是 : " + intent.getStringExtra("info") + "");
    }
}
  1. 清单文件注册,并设置Action,就那么简单完成接收准备工作
代码语言:javascript
复制
<receiver android:name=".MyBroadCastReceiver">
    <intent-filter>
        <action android:name="myBroadcast.action.call"/>
    </intent-filter>
</receiver>
  • 代码注册(动态广播)
  1. 在和广播接受者相同的app里的MainActivity添加一个注册按钮,用来注册广播接收者
  2. 设置意图过滤,添加Action
代码语言:javascript
复制
//onCreate创建广播接收者对象
mReceiver = new MyBroadCastReceiver();              

//注册按钮
public void click(View view) {
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("myBroadcast.action.call");
    registerReceiver(mReceiver, intentFilter);
}
  1. 销毁的时候取消注册
代码语言:javascript
复制
@Override
protected void onDestroy() {
    unregisterReceiver(mReceiver);
    super.onDestroy();
}
  • 调用链

3.3 生命周期

  • BroadCastReceiver的生命周期

四、ContentProvider


  • 内容提供者,它是用在不同的应用程序之间共享数据时,可以把一个应用的数据提供给其他的应用使用。

4.1 基本使用

  • ContentProvider 管理对结构化数据集的访问。它们封装数据,并提供用于定义数据安全性的机制。 内容提供程序是连接一个进程中的数据与另一个进程中运行的代码的标准界面。
  • ContentProvider 无法被用户感知,对于一个 ContentProvider 组件来说,它的内部需要实现增删该查这四种操作,它的内部维持着一份数据集合,这个数据集合既可以是数据库实现,也可以是其他任何类型,如 List 和 Map,内部的 insert、delete、update、query 方法需要处理好线程同步,因为这几个方法是在 Binder 线程池中被调用的。
  • ContentProvider 通过 Binder 向其他组件乃至其他应用提供数据。当 ContentProvider 所在的进程启动时,ContentProvider 会同时启动并发布到 AMS 中,需要注意的是,这个时候 ContentProvider 的 onCreate 要先于 Application 的 onCreate 而执行。
代码语言:javascript
复制
// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    mProjection,                        // The columns to return for each row
    mSelectionClause                    // Selection criteria
    mSelectionArgs,                     // Selection criteria
    mSortOrder);                        // The sort order for the returned rows
代码语言:javascript
复制
public class Installer extends ContentProvider {

    @Override
    public boolean onCreate() {
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

4.2 ContentProvider 和 sql 区别

总结


  • 很多人对于四大组件的理解,一直停留在使用上。本文限于篇幅,不可能展开。平时还是要多看源码,才能有深入地学习。
  • Android 的知识点,如果说只是会使用,或者说能跑起来代码,可以说很少。但是如果要写出一个优秀的 app,就相当艰深了,后续我将继续对Android 的方方面面进行深入地总结,也欢迎大家关注我的 CSDN 一起学习 _yuanhao 的编程世界

相关文章


面试必备:高频算法题终章「图文解析 + 范例代码」之 矩阵 二进制 + 位运算 + LRU 合集!

面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」之 字符串处理+动态规划 合集!

「面试高频」二叉搜索树+双指针+贪心 算法题指北

面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」之 二分 + 哈希表 + 堆 + 优先队列 部分!

面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」必问之 链表 + 栈 + 队列 部分!

? 面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」必问之 排序 + 二叉树 部分!?

高效解决「SQLite」数据库并发访问安全问题,只这一篇就够了

每个人都要学的图片压缩终极奥义,有效解决 Android 程序 OOM

Android 让你的 Room 搭上 RxJava 的顺风车 从重复的代码中解脱出来

ViewModel 和 ViewModelProvider.Factory:ViewModel 的创建者

请点赞!因为你的鼓励是我写作的最大动力!


为了方便大家跟进学习,我在 GitHub 建立了一个仓库

仓库地址:超级干货!精心归纳**视频、归类、总结**,各位路过的老铁支持一下!给个 Star !

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/10/24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、 Activity
    • 1.1 生命周期
      • 1.2 启动模式
        • 1.3 启动过程
          • 1.4 关于 Content 和 Application
          • 二、Service
            • 2.1启动过程
              • 2.2 绑定过程
                • 2.3 生命周期
                  • 2.4 Service 与 Activity 通信
                    • 2.5 启用前台服务
                      • 2.6 IntentService
                      • 三、BroadcastReceiver
                        • 3.1 target 26 之后,无法在 AndroidManifest 显示声明大部分广播,除了一部分必要的广播,如:
                          • 3.2 注册过程
                            • 3.3 生命周期
                            • 四、ContentProvider
                              • 4.1 基本使用
                                • 4.2 ContentProvider 和 sql 区别
                                  • 请点赞!因为你的鼓励是我写作的最大动力!
                              • 总结
                              • 相关文章
                              • 为了方便大家跟进学习,我在 GitHub 建立了一个仓库
                              相关产品与服务
                              图片处理
                              图片处理(Image Processing,IP)是由腾讯云数据万象提供的丰富的图片处理服务,广泛应用于腾讯内部各产品。支持对腾讯云对象存储 COS 或第三方源的图片进行处理,提供基础处理能力(图片裁剪、转格式、缩放、打水印等)、图片瘦身能力(Guetzli 压缩、AVIF 转码压缩)、盲水印版权保护能力,同时支持先进的图像 AI 功能(图像增强、图像标签、图像评分、图像修复、商品抠图等),满足多种业务场景下的图片处理需求。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档