Android中的四大组件详解

Android中的四大组件详解

我们都知道Android系统应用层框架中,为开发者提供了四大组件来便于应用的开发,它们是Activity、Service、BroadcastReceiver、ContentProvider。
它们用于在应用开发过程中,不同场景的功能实现。

Activity:Activity是开发中最常用的,也是最复杂的一个组件。它是用户可以专注做一些事情的东西。它的主要功能就是可以和用户进行交互操作,所以几乎所有的Activity都会负责创建一个显示窗口,然后通过setContentView显示特定的UI。

Service:除了Activity,Service是第二复杂的组件。和Activity相比,Service是一种处于后台长时间运行的组件,它没有UI界面,不需要与用户交互。它被设计用来后台执行耗时任务或者为其他应用程序提供功能调用的服务。

BroadcastReceiver:广播接收者,这个组件比较简单,比较好理解了。类似于观察者模式,应用程序可以添加自己关心的广播事件,从而为用户提供更好的使用体验。这些广播可以是来自于操作系统、其他的应用程序、甚至是自己的应用程序。例如网络连接变化、电池电量变化、开机启动等。

ContentProvider:内容提供者,它被设计用来在不同的应用程序之间共享数据。例如电话程序中的联系人数据,就可以被其他应用程序读取。如果仅仅是在同一个程序中存取数据的话,用SQLiteDatabase接口就可以了。

四大组件的使用方法,官网有比较详细的说明和示例,这里主要是分析他们的实现原理和注意事项。

一般性问题

系统是如何管理四大组件的

除了BroadcastReceiver可以动态注册外,四大组件在使用之前必须先在 AndroidManifest.xml清单文件中进行声明。类似下面代码:

//Activity声明
<activity
  android:name=".xxxActivity"
  android:screenOrientation="portrait"
  android:windowSoftInputMode="adjustPan">
  <intent-filter android:label="@string/app_name">
      <action android:name="android.intent.action.VIEW" />

      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data
          android:host="host"
          android:scheme="scheme" />
  </intent-filter>
</activity>

//Service声明
<service
  android:name=".xxxService"
  android:exported="true" />

//BroadcastReceiver声明  
<receiver android:name=".xxxReceiver">
  <intent-filter>
      <action android:name="android.intent.action.BOOT_COMPLETED" />
      <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
      <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
      <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
  </intent-filter>
</receiver>

//ContentProvider声明
<provider
  android:name="androidx.core.content.FileProvider"
  android:authorities="${PACKAGE_NAME}.fileprovider"
  android:exported="false"
  android:grantUriPermissions="true">
  <meta-data
      android:name="android.support.FILE_PROVIDER_PATHS"
      android:resource="@xml/sharesfilepaths" />
</provider>

在应用程序安装时,应用安装程序通过PackageInstaller服务解析应用安装包,并将AndroidManifest.xml中声明的四大组件信息保存到PackageManagerService中。

public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender {
    
    ...
    
    //组件解析器,存储了系统所有应用程序的四大组件信息
    private final ComponentResolver mComponentResolver;
    
}

public class ComponentResolver {
    /** All available activities, for your resolving pleasure. */
    @GuardedBy("mLock")
    private final ActivityIntentResolver mActivities = new ActivityIntentResolver();

    /** All available providers, for your resolving pleasure. */
    @GuardedBy("mLock")
    private final ProviderIntentResolver mProviders = new ProviderIntentResolver();

    /** All available receivers, for your resolving pleasure. */
    @GuardedBy("mLock")
    private final ActivityIntentResolver mReceivers = new ActivityIntentResolver();

    /** All available services, for your resolving pleasure. */
    @GuardedBy("mLock")
    private final ServiceIntentResolver mServices = new ServiceIntentResolver();
    
    ...
}

什么是Context

Context是关于应用程序环境的全局信息接口,Context是一个抽象类,它的实现都是由系统类实现的。Context允许访问应用程序特定的资源和类,如

  1. 资源管理器AssetsManager、
  2. 包管理器PackageManager、
  3. 文本图片主题资源Resource、
  4. 主线程消息循环Looper
  5. 四大组件操作,如启动Activity、BroadcastReceiver,接收Intent
  6. SharedPreferences操作
  7. 私有目录文件操作
  8. 数据库创建、删除操作
  9. 获取系统服务
  10. 权限操作
//android.content.Context

public abstract class Context {
    public abstract AssetManager getAssets();
    
    public abstract Resources getResources();
    
    public abstract PackageManager getPackageManager();

    public abstract ContentResolver getContentResolver();
    
    public abstract Looper getMainLooper();
    
    public final CharSequence getText(@StringRes int resId) {
        return getResources().getText(resId);
    }
    
    public final String getString(@StringRes int resId) {
        return getResources().getString(resId);
    }
    
    public abstract String getPackageName();
}

Context的实现类为ContextImpl,ContextImpl为Activity或其他应用程序组件提供了基础的Context实现。

//android.app.ContextImpl

class ContextImpl extends Context {

    //ContextImpl的构造方法是私有的,只能通过几个静态方法创建ContextImpl实例
    private ContextImpl(@Nullable ContextImpl container, 
                        @NonNull ActivityThread mainThread,
                        @NonNull LoadedApk packageInfo, 
                        @Nullable String splitName,
                        @Nullable IBinder activityToken, 
                        @Nullable UserHandle user, int flags,
                        @Nullable ClassLoader classLoader, 
                        @Nullable String overrideOpPackageName) {
        ...
    }

    ....

    //创建系统应用的上下文
    @UnsupportedAppUsage
    static ContextImpl createSystemContext(ActivityThread mainThread) {
        LoadedApk packageInfo = new LoadedApk(mainThread);
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, null, null);
        context.setResources(packageInfo.getResources());
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), ontext.mResourcesManager.getDisplayMetrics());
        return context;
    }
    
    //基于系统应用Context创建的用于UI的系统上下文,此上下文具有可以主题化的资源
    static ContextImpl createSystemUiContext(ContextImpl systemContext, 
                                            int displayId) {
        final LoadedApk packageInfo = systemContext.mPackageInfo;
        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, 
                                packageInfo, null,null, null, 0, null, null);
        context.setResources(createResources(null, packageInfo, null, displayId, 
                                null,packageInfo.getCompatibilityInfo()));
        context.updateDisplay(displayId);
        return context;
    }
    
    //创建普通应用级别的上下文,packageInfo指定了某个已安装的应用
    static ContextImpl createAppContext(ActivityThread mainThread, 
                                        LoadedApk packageInfo,
                                        String opPackageName) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo,        
                                                null, null, null, 0,
                                                 null, opPackageName);
        context.setResources(packageInfo.getResources());
        return context;
    }
    
    //创建Activity级别的上下文
    static ContextImpl createActivityContext(ActivityThread mainThread,
                                       LoadedApk packageInfo, 
                                       ActivityInfo activityInfo, 
                                       IBinder activityToken, //代表一个Activity
                                       int displayId,
                                       Configuration overrideConfiguration) {
        if (packageInfo == null) 
            throw new IllegalArgumentException("packageInfo");

        String[] splitDirs = packageInfo.getSplitResDirs();
        ClassLoader classLoader = packageInfo.getClassLoader();

        if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
            try {
           classLoader=packageInfo.getSplitClassLoader(activityInfo.splitName);
                splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
            } catch (NameNotFoundException e) {
        // Nothing above us can handle a NameNotFoundException, better crash.
                throw new RuntimeException(e);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
            }
        }

        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, 
            activityInfo.splitName,activityToken, null, 0, classLoader, null);

        // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
        displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : 
                                                    Display.DEFAULT_DISPLAY;

        final CompatibilityInfo compatInfo = (displayId == 
                                            Display.DEFAULT_DISPLAY)
                                    ? packageInfo.getCompatibilityInfo()
                                : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;

        final ResourcesManager resourcesManager =ResourcesManager.getInstance();

        // Create the base resources for which all configuration contexts for this Activity
        // will be rebased upon.
        context.setResources(resourcesManager.createBaseActivityResources(
                activityToken,
                packageInfo.getResDir(),
                splitDirs,
                packageInfo.getOverlayDirs(),
                packageInfo.getApplicationInfo().sharedLibraryFiles,
                displayId,
                overrideConfiguration,
                compatInfo,
                classLoader));
        context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
                context.getResources());
        return context;
    }
    ...
}

除了ContextImpl类外,ContextWrapper类也继承了Context,但是ContextWrapper类并不自己实现了Context的方法,而是通过构造方法,代理给另外一个Context的实现。这样ContextWrapper的子类就可以在不修改ContextWrapper类的情况下,修改其调用方法的实现。

public class ContextWrapper extends Context {
    Context mBase;
    
    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    
    ...
}

我们在开发中用到的Activity、Application、Service类都是集成自ContextWrapper类的,然后在构建出实例后,通过ContextWrapper的attachBaseContext(Context)方法,将真正的Context实现类添加进去。这样就可以动态控制Activity、Application、Service类调用Context的方法实现了。

例如创建Application的过程:

//android.app.LoadedApk

public Application makeApplication(boolean forceDefaultAppClass,
                                    Instrumentation instrumentation) {
    Application app = null;    
    String appClass = mApplicationInfo.className;
    java.lang.ClassLoader cl = getClassLoader();
    //创建一个普通应用的上下文
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread,
                                 this);
    //通过Instrumentation类创建Application,并设置上下文
    app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
}
public Application newApplication(ClassLoader cl, String className, 
                                Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
   Application app = getFactory(context.getPackageName())
           .instantiateApplication(cl, className);
   app.attach(context);
   return app;
}
final void attach(Context context) {
   attachBaseContext(context);
   mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

最终通过Application类的attach(Context)方法将上下文的实现类设置给ContextWrapper。

Hook四大组件的实例化过程

我们都知道,四大组件或者Application类的实例都是在ActivityThread中,通过反射调用无参构造函数创建的,那么我们可不可以控制这个创建过程呢?例如添加一个带参数的构造函数。
答案是AppComponentFactory类。我们可以通过继承该类,并重写Application、Activity等创建的方法就可以了。
然后再AndroidManifest.xml文件中,指定Application标签的android:appComponentFactory="xxxAppComponentFactory"属性即可。

AppComponentFactory类的定义如下:

//android.app.AppComponentFactory

public class AppComponentFactory {
    public @NonNull ClassLoader instantiateClassLoader(@NonNull ClassLoader cl,
            @NonNull ApplicationInfo aInfo) {
        return cl;
    }
    
    public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Application) cl.loadClass(className).newInstance();
    }
    
    public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
            @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Activity) cl.loadClass(className).newInstance();
    }
    
    public @NonNull BroadcastReceiver instantiateReceiver(@NonNull ClassLoader cl,
            @NonNull String className, @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (BroadcastReceiver) cl.loadClass(className).newInstance();
    }
    
    public @NonNull Service instantiateService(@NonNull ClassLoader cl,
            @NonNull String className, @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Service) cl.loadClass(className).newInstance();
    }
}

但是AppComponentFactory接口只能在Api28以上的系统版本中使用。

Activity、ContentProvider、Service三者的启动顺序

一个新的应用启动时,会优先初始化其Application类,创建了Application实例后,会立即调用其attach方法。
然后就会初始化应用中声明的ContentProvider:

if (!data.restrictedBackupMode) {
 if (!ArrayUtils.isEmpty(data.providers)) {
     installContentProviders(app, data.providers);
     // For process that contains content providers, we want to
     // ensure that the JIT is enabled "at some point".
     mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
 }
}

ContentProvider初始化完成后,会再调用Application类的onCreate方法。

AMS在初始化完客户端的Application类后,会检查是否有需要运行的Service和BroadcastReceiver。

// Find any services that should be running in this process...
if (!badApp) {
  try {
      didSomething |= mServices.attachApplicationLocked(app, processName);
      checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
  } catch (Exception e) {
      Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
      badApp = true;
  }
}

// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
  try {
      didSomething |= sendPendingBroadcastsLocked(app);
      checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");
  } catch (Exception e) {
      // If the app died trying to launch the receiver we declare it 'bad'
      Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
      badApp = true;
  }
}

所以初始化顺序是Application.attach() > ContentProvider> Application.onCreate() > Activity/Service/BroadcastReceiver。
ContentProvider比Activity或Service初始化的顺序都要早,所以有些第三方库利用这个特性,通过ContentProvider自动初始化一些功能,而不用在Application中添加初始化代码。

Activity

生命周期

protected void onCreate(Bundle savedInstanceState);
Activity创建,用于初始化数据

protected void onStart();
Activity UI可见

protected void onRestart();
Activity UI从不可见变为可见

protected void onResume();
Activity UI可操作

protected void onPause();
Activity 暂停,UI可见,不可操作

protected void onStop();
Activity停止,UI不可见

protected void onDestroy();
Activity销毁

创建和管理

  1. 通过Activity或ContextImpl的startActivity启动指定的Activity,这里的区别是在非Activity上下文启动Activity时,会判断是否指定了Intent.FLAG_ACTIVITY_NEW_TASK标识。
  2. 经过Instrumentation监控类转发
  3. 通过进程间通信调用到ActivityManagerService中
  4. 根据Intent从PackageManagerService中找出对应的Activity信息,前面提到应用安装时会把四大组件信息解析出来存储到PackageManagerService中
  5. 找到目标Activity对应的进程ProcessRecord,通过ApplicationThread这个Binder接口,告知客户端进程创建指定的Activity。

在这个过程中涉及到了几个数据模型类和操作类:

ProcessRecord:代表一个当前运行的进程全部信息,在创建新进程前,由ActivityManagerServer创建ProcessRecord实例,并存储到ActivityManagerServer中ProcessMap。ProcessRecord中的IApplicationThread thread属性是一个Binder接口,用于AMS向客户端进程发送控制消息。

ActivityRecord:代表一个Activity任务栈中的Activity实例,包含对应Activity信息(从Manifest解析出来)、Activity所属Appl

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android Jetpack是Google提供的一套用于加速Android应用开发的工具包,其包括了许多架构组件,其之一就是ViewModel。 ViewModel是一种设计模式,用于保存和管理与UI相关的数据。在传统的Android开发,当屏幕旋转或者因为其他原因导致Activity或Fragment重建时,之前保存的临时数据就会丢失。而ViewModel的出现解决了这个问题。 ViewModel的主要作用是将数据与UI组件分离。它的工作方式是创建一个ViewModel类,并在其保存需要与UI组件交互的数据。这样,当屏幕旋转或重建时,ViewModel实例不会销毁,数据也会得到保留。然后,在Activity或Fragment,通过获取ViewModel实例,可以轻松地访问这些数据。 使用ViewModel的好处有很多。首先,它可以避免内存泄漏,因为ViewModel的生命周期与Activity或Fragment无关。其次,它可以节省资源,因为当Activity或Fragment销毁时,ViewModel实例可以被系统缓存起来,下次再创建时可以直接返回该实例。另外,由于ViewModel保存了与UI相关的数据,可以减少因为屏幕旋转导致的数据重复加载的问题。 在使用ViewModel时,你可以选择使用Android Jetpack的其他架构组件来进一步提高开发效率,比如通过LiveData实现数据的观察和通知,或者通过DataBinding来实现UI与数据的自动绑定。 总之,ViewModel是Android Jetpack非常重要的一个架构组件,它的出现实现了数据与UI的解耦,提高了开发效率,并且解决了数据丢失的问题。希望通过这篇文档的详解,你对ViewModel有了更深入的理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值