深入理解安卓中的四大组件

Activity

参见这篇博客


题外话:桥接模式

  在Activity的设计中用到了桥接模式,利用这个模式达到了对修改封闭,对扩展开放的目的。
  在对Activity的创建过程进行分析时发现,在创建一个Activity后都会对其绑定一个ContextImpl实例。查看源码后发现,这个contextImpl实例是以context的形式作为成员变量被保存在activity(ContextWrapper)中。

Context mBase;

  这里的mBase其实相当于一座桥,Activity的许多操作例如getSystemService,都是通过mBase来调用的,而Context只是一个接口,这里就给Context的扩展留下机会。它可以是Context的任意子类。
  以这样的方式来提升系统的扩展性是非常不错的选择。


Service

  由于篇幅原因,关于Service的启动过程分析放在这里


BoradCastReceiver

  上面分析了Activity和Service的相关过程,广播作为四大组件之一,注册,发送广播过程其实和上面两个组件也都基本相同。由于静态广播由PMS(PackageManagerService)统一注册,这里暂不分析。下面来看一看动态广播的注册和发送过程。
  注册过程同样由ContextImpl真正去执行。

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;//这里类比Service的IServiceConnection
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = mPackageInfo.getReceiverDispatcher(//这里类比Service启动中的ServiceDispatcher
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
                //由AMS真正去注册
            final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

  可以看到注册过程和Service的启动过程极为类似。由于广播也可能运行在独立线程,因此广播的回调要以binder的方式来传给AMS。
  在AMS中这个回调和对应的filter会被保存起来,当发送广播时,同样是ContextImpl来跨进程调用AMS的发送广播方法。这时AMS会遍历广播回调,如果有对应的应用进程存在,那么就把广播就给app去处理,否则直接调用广播回调的相关方法。

 void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        // Send the intent to the receiver asynchronously using one-way binder calls.
        if (app != null) {//app对应的进程
            if (app.thread != null) {//对applicationThread的判断
                // If we have an app thread, do the call through that so it is
                // correctly ordered with other one-way calls.
                try {
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);
                // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
                // DeadObjectException when the process isn't actually dead.
                //} catch (DeadObjectException ex) {
                // Failed to call into the process.  It's dying so just let it die and move on.
                //    throw ex;
                } catch (RemoteException ex) {
                    // Failed to call into the process. It's either dying or wedged. Kill it gently.
                    synchronized (mService) {
                        Slog.w(TAG, "Can't deliver broadcast to " + app.processName
                                + " (pid " + app.pid + "). Crashing it.");
                        app.scheduleCrash("can't deliver broadcast");
                    }
                    throw ex;
                }
            } else {
                // Application has died. Receiver doesn't exist.
                throw new RemoteException("app.thread must not be null");
            }
        } else {//否则直接跨进程调用
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        }
    }

  ApplicationThread中同样会调用receiver.performReceive方法。我对这里的理解是,如果activity发送了一个广播,那么AMS负责把这个广播对应的Reciver交给activity,如果找不到activity,则直接调用onReceive方法。就算是activity去处理Receiver,广播和activity也可能不在一个进程。这里可以自己体会一下。
  广播对应的基本流程就是这样。还有一个要注意的地方。在Android3.1之后,发送广播时会有这么一段:

final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
            intent = new Intent(intent);
        ......
        // By default broadcasts do not go to stopped apps.
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
        ......
}

  就是说会给intent自动添加一个不给已经停止的应用发广播的标记。这里的停止指的是安装后从未启动和被用户手动杀死的应用。如果确实要给已经停止的应用发广播,那么则要手动加上:

Intent.FLAT_INCLUDE_STOPPED_PACKAGES

  两种标记位共存时,会取后者。


ContentProvider

  ContentProvider的启动过程和其他组件略有不同。要了解ContentProvider的启动过程,可以先看一下Application的创建过程。
  Application的创建要从应用程序入口说起。
  ActivityThread的入口是main方法。在这个方法中初始化了主线程handler,并且创建了ActivityThread对象,调用了其attach方法。

private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ......
            final IActivityManager mgr = ActivityManager.getService();//获取AMS的binder client
            try {
                mgr.attachApplication(mAppThread);//调用AMS的attachApplication方法
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            ......
        }
        ......
    }

  可以看到获取AMS和调用attachApplication方法。

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
        ProcessRecord app;
        long startTime = SystemClock.uptimeMillis();
        if (pid != MY_PID && pid >= 0) {
            synchronized (mPidsSelfLocked) {
                app = mPidsSelfLocked.get(pid);
            }
        } else {
            app = null;
        }
        ......
        boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
        List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
        ......
        if (app.instr != null) {
                thread.bindApplication(processName, appInfo, providers,
                        app.instr.mClass,
                        profilerInfo, app.instr.mArguments,
                        app.instr.mWatcher,
                        app.instr.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
            } else {
                thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial);
            }
    }

  可以看到这里会尝试从AMS中获取Provider,其中generateApplicationProvidersLocked方法大概就是去PackageManager里面找所有声明过的Provider。
  然后AMD中调用了thread的bindApplication方法。这里的thread就是ApplicationThread。是ActivityThread和AMS通信的binder。
  bindApplication中向主线程handler发送了一个消息,最终会调用handleBindApplication方法。

private void handleBindApplication(AppBindData data) {
    ......
    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            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);
                }
            }

            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            catch (Exception e) {
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            }

            try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
    ......
}

  这个方法很长,我们主要关注这几行。首先是创建Application对象,这个过程在makeApplication中进行。同时被创建的还有ApplicationContext。然后会对data.providers进行判空,也就是AMS传过来的providers。如果有数据,则进行初始化:

private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
            ......
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

  ContentProvider对象的创建是在installProvider中。接着创建的ContentProvider都会保存在AMS中,方便别的进程调用。
  值得注意的是,ContentProvider的创建是在Application的onCreate方法之前,有兴趣的同学可以回到初始化Application的代码处,你会发现在创建了Application后并没有立刻执行其onCreate方法,而是在创建完ContentProvider之后由mInstrumentation来调用其onCreate方法。
  当我们在Activity中调用getContentResolver的acquireContentProviderClient方法时,ContextImpl会去ActivityThread中查找有没有本地保存的ContentProvider。如果有就返回,如果没有就去找AMS要。AMS会检查它的mProviderMap(ContentProvider在创建后会回调给AMS,就保存在这个mProviderMap中),如果有就返回,如果发现其所在进程没有启动,则会先启动对应进程。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值