This chapter describes how the four components of Android work. The purpose of this chapter is to deepen the reader’s understanding of how the four components work. Due to the particularity of the four components, it is necessary to have a certain understanding of their working process, which will also help deepen the understanding of Android’s overall architecture.
The operating status of the four components
In addition to BroadcastReceiver, the other three components must be registered in Androidmanifest.xml.
The Activity is responsible for foreground and user interaction. A Service is a computational component that has two states: a startup state and a binding state. A BroadcastReceiver is a messaging component used to transmit messages between different components and different applications. ContentProvider is a data-sharing component used to share data with other components and even other applications.
The working process of an Activity
Activity#startActivityForResult
@Override
public void startActivityForResult(
String who, Intent intent, int requestCode, @Nullable Bundle options) {
Uri referrer = onProvideReferrer();
if(referrer ! =null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
options = transferSpringboardActivityOptions(options);
// Start execStartActivity with Instrumentation
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
// Execute sendActivityResult by ActivityThread
if(ar ! =null) {
mMainThread.sendActivityResult(
mToken, who, requestCode,
ar.getResultCode(), ar.getResultData());
}
cancelInputsAndStartExitTransition(options);
}
Copy the code
The mInstrumentation is initialized in the handleBindApplication() of the ActivityThread. Pass the Activity in Attach () when it starts.
HandleBindApplication () is called by bindApplication() of ActivityThread$ApplicationThread.
Instrumentation#execStartActivity
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
// ...
try {
// ...
/ / really start Activity is ActivityManagerNative getDefault
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target, requestCode, 0.null, options);
// Check the result of the Activity launch and throw some exceptions
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
Copy the code
Take a look at ActivityManagerNative,
public abstract class ActivityManagerNative extends Binder implements IActivityManager
Copy the code
ActivityManagerService inherits from ActivityManagerNative
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor.BatteryStatsImpl.BatteryCallback
Copy the code
This method returns AMS(ActivityManagerService)
/** * Retrieve the system's default/global activity manager. */
static public IActivityManager getDefault(a) {
return gDefault.get();
}
Copy the code
GDefault is a Singleton Singleton object that is called create() to create AMS when it is first created.
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create(a) {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager"."default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager"."default service = " + am);
}
returnam; }};Copy the code
AMS and ActivityStack call each other
Now the startActivity execution goes into AMS.
The logical order of calls within startActivity() in AMS.
startActivity()
startActivityAsUser()
startActivityMayWait()
startActivityLocked()
startActivityUnchecked()
resumeTargetStackIfNeeded()
- ActivityStackSupervisor#
resumeFocusedStackTopActivityLocked()
- ActivityStack#
resumeTopActivityUncheckedLocked()
- ActivityStack#
resumeTopActivityInnerLocked()
- ActivityStackSupervisor#
startSpecificActivityLocked()
- ActivityStackSupervisor#
realStartActivityLocked()
Will eventually go realStartActivityLocked (com). The android. Server. Am. ActivityStackSupervisor# realStartActivityLocked
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
// ...
// This is where the Activity is actually started.
/ / app. The thread is IApplicationThread
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
newConfiguration(task.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, ! andResume, mService.isNextTransitionForward(), profilerInfo);// ...
return true;
}
Copy the code
IApplicationThread and its implementor
IApplicationThread is a Binder interface that contains a number of interfaces for starting and stopping activities and services.
As you might guess, the implementers of IApplicationThread perform a number of functions related to Activity and Service start/stop.
public interface IApplicationThread extends IInterface
Copy the code
The implementor of IApplicationThread is the inner class ApplicationThread of ActivityThread.
private class ApplicationThread extends ApplicationThreadNative
Copy the code
public abstract class ApplicationThreadNative extends Binder
implements IApplicationThread
Copy the code
ApplicationThreadNative does exactly what the system generates for AIDL files.
ApplicationThreadNative is the implementer of IApplicationThread. Since ApplicationThreadNative is defined by the system as an abstract class, So ApplicationThread becomes the ultimate implementor of IApplicationThread.
android.app.ActivityThread$ApplicationThread#scheduleLaunchActivity
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
/ /...
sendMessage(H.LAUNCH_ACTIVITY, r);
}
Copy the code
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + what + "" + mH.codeToString(what)
+ ":" + arg1 + "/" + obj);
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
Copy the code
Finally, use mH to send a MSG notification to start the Activity.
MH is a Handler named H.
H Handles the logic for starting the Activity:
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null."LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
Copy the code
The details of starting the Activity
ActivityThread#handleLaunchActivity().
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// ...
// Initialize before creating the activity
WindowManagerGlobal.initialize();
// The Activity object is created by performLaunchActivity
Activity a = performLaunchActivity(r, customIntent);
// ...
if(a ! =null) {
// ...
// Start resume
handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed, r.lastProcessedSeq, reason);/ / pause
if(! r.activity.mFinished && r.startsNotResumed) { performPauseActivityIfNeeded(r, reason);if(r.isPreHoneycomb()) { r.state = oldState; }}}else {
try {
// finish Activity
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throwex.rethrowFromSystemServer(); }}}Copy the code
PerformLaunchActivity does five things:
- Get component information for the Activity to be started from ActivityClientRecord
- Use the class loader to create an Activity object through the newActivity method of Instrumentation
- Try to create an Application object through LoadedApk’s makeApplication method
- Create the ContextImpl object and initialize some important data using the Attach method of the Activity
- Call the Activity’s onCreate method
ActivityThread#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 1. Obtain the component information of the Activity to be started from the ActivityClientRecord
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if(r.activityInfo.targetActivity ! =null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
// 2. Use the class loader to create an Activity object through the newActivity method of Instrumentation
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if(r.state ! =null) { r.state.setClassLoader(cl); }}catch (Exception e) {
if(! mInstrumentation.onException(activity, e)) {throw new RuntimeException(
"Unable to instantiate activity " + component
+ ":"+ e.toString(), e); }}try {
//3. Use LoadedApk's makeApplication method to try to create an Application object
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
// ...
// 4. Create ContextImpl and initialize some important data using the Attach method of the Activity
if(activity ! =null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
// ...
// ContextImpl connects the attach and Activity with the Activty
// In attach, the Activity completes the creation of the Window and associates it with the Window.
// So that the Window receives an external input event and passes it to the 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, window);
// ...
// 5. Call the Activity's onCreate method,
// Now the Activity is started
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
// ...
mActivities.put(r.token, r);
// ...
return activity;
}
Copy the code
NewActivity () creates an Activity object through the class loader
android.app.Instrumentation#newActivity
public Activity newActivity(Class
clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws InstantiationException,
IllegalAccessException {
Activity activity = (Activity)clazz.newInstance();
ActivityThread aThread = null;
activity.attach(context, aThread, this, token, 0, application, intent,
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration(), null.null.null);
return activity;
}
Copy the code
android.app.LoadedApk#makeApplication
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if(mApplication ! =null) {
// An app can only have one Application
return mApplication;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
if(! mPackageName.equals("android")) {
initializeJavaContextClassLoader();
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
// create an object with the newApplication of Instrumentation. It's also loaded with the class loader.
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
// ...
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
if(instrumentation ! =null) {
instrumentation.callApplicationOnCreate(app);
}
// Rewrite the R 'constants' for all library apks.
SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
.getAssignedPackageIdentifiers();
final int N = packageIdentifiers.size();
for (int i = 0; i < N; i++) {
final int id = packageIdentifiers.keyAt(i);
if (id == 0x01 || id == 0x7f) {
continue;
}
rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return app;
}
Copy the code
The working process of a Service
A Service has two working states:
- The startup state is used to perform background computing
- Binding state, mainly used for interaction between other components and services
These two states can coexist.
Intent intentService = new Intent(this,MyService.class);
/ / start
startService(intentService);
/ / binding
bindService(intentService,mServiceConnection,BIND_AUTO_CREATE);
Copy the code
The boot process
Start with ContextWrapper’s startService.
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
Copy the code
The Activity is created by attaching () to a ContextImpl object, which is the mBase above.
Most of the implementation of ContextWrapper is implemented by mBase. This is bridge mode.
StartServiceCommon (); startServiceCommon();
android.app.ContextImpl#startServiceCommon
private ComponentName startServiceCommon(Intent service, UserHandle user) {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
// Call startService remotely from AMS
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(),
service,service.resolveTypeIfNeeded(getContentResolver()), getOpPackageName(),
user.getIdentifier());
/ /...
return cn;
}
Copy the code
com.android.server.am.ActivityManagerService#startService
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
// ...
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
// mServices is ActiveService, assisting AMS management Service.
ComponentName res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
returnres; }}Copy the code
The call chain inside AMS’s startService().
- ActiveService#
startServiceLocked()
- ActiveService#
startServiceInnerLocked()
- ActiveService#
bringUpServiceLocked()
- ActiveService#
sendServiceArgsLocked()
- ActiveService#
realStartServiceLocked()
com.android.server.am.ActiveService#startServiceInnerLocked
// ServiceRecord describes a Service record that runs through the Service startup process.
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
ServiceState stracker = r.getTracker();
if(stracker ! =null) {
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
}
r.callStart = false;
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
}
// Give the startup details to bringUpServiceLocked
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false.false);
if(error ! =null) {
return new ComponentName("!!!!!", error);
}
if (r.startRequested && addToStarting) {
boolean first = smap.mStartingBackground.size() == 0;
smap.mStartingBackground.add(r);
r.startingBgTimeout = SystemClock.uptimeMillis() + BG_START_TIMEOUT;
// ...
if(first) { smap.rescheduleDelayedStarts(); }}else if (callerFg) {
smap.ensureNotStartingBackground(r);
}
return r.name;
}
Copy the code
com.android.server.am.ActiveService#realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
// ...
r.app = app;
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
final boolean newService = app.services.add(r);
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false.null);
mAm.updateOomAdjLocked();
boolean created = false;
try {
// ...
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
// Create a Service object through the scheduleCreateService of ActivityThread
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
r.postNotification();
created = true;
} catch (DeadObjectException e) {
// ...
} finally {
// ...
}
if (r.whitelistManager) {
app.whitelistManager = true;
}
requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null.true);
// If the service is in the started state, and there are no
// pending arguments, then fake up one so its onStartCommand() will
// be called.
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null.null));
}
// Call other methods of the Service. For example, onStartCommand.
sendServiceArgsLocked(r, execInFg, true);
// ...
}
Copy the code
MSG is sent via Handler just like an Activity.
ActivityThread#scheduleCreateService
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
Copy the code
ActivityThread#handleCreateService
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
// Create an instance of the Service through the class loader
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
// ...
}
try {
// Create ContextImpl object
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
/ / create the Application
Application app = packageInfo.makeApplication(false, mInstrumentation);
// Attach the Service object to the ContextImpl object.
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
// 调用 service 的 onCreate
service.onCreate();
// Put the service into the collection
mServices.put(data.token, service);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
} catch (RemoteException e) {
}
}
// ...
}
Copy the code
ActivieService#sendServiceArgsLocked() internally calls the scheduleServiceArgs() of ActivityThread, which eventually calls handleServiceArgs()
android.app.ActivityThread#handleServiceArgs
private void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if(data.args ! =null) {
data.args.setExtrasClassLoader(s.getClassLoader());
data.args.prepareToEnterProcess();
}
int res;
if(! data.taskRemoved) {// Call service onStartCommand()
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
QueuedWork.waitToFinish();
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token,
SERVICE_DONE_EXECUTING_START,
data.startId, res);
ensureJitEnabled();
}
Copy the code
Service binding process
It starts with bindServiceCommon() in ContextImpl as the startup process does.
ContextImpl#bindServiceCommon
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
IServiceConnection sd;
/ / client ServiceConnection object into ServiceDispatcher. InnerConnection.
// Because service bindings may be cross-process, the ServiceConnection object must be invoked
// Binder allows remote services to call back their own methods.
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
IBinder token = getActivityToken();
if (token == null && (flags&BIND_AUTO_CREATE) == 0&& mPackageInfo ! =null
&& mPackageInfo.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
service.prepareToLeaveProcess(this);
// 调用 AMS 的 bindService
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
returnres ! =0;
}
Copy the code
android.app.LoadedApk#getServiceDispatcher
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices;
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
// mService is a map
synchronized (mServices) {
// If there is a map, get it from the map. If there is no map, create it and save it.
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
if(map ! =null) {
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
// Return the internal IServiceConnection object
returnsd.getIServiceConnection(); }}Copy the code
ActivityManagerService#bindService
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId) throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
// ...
synchronized(this) {
returnmServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, callingPackage, userId); }}Copy the code
AMS call process:
- bindService()
- ActiveServices#bindServiceLocked()
- ActiveServices#bringUpServiceLocked()
- ActiveServices#realStartServiceLocked()
- ActiveServices#requestServiceBindingsLocked()
- ActiveServices#requestServiceBindingLocked()
- ActivityThread$ApplicationThread#scheduleBindService()
And then similar to the startup process. Both end up calling ActivityThread to create the Service instance and execute onCreate().
Service binding process is called ActiveServices requestServiceBindingsLocked (). ScheduleBindService () of ActivityThread$ApplicationThread is finally called. We then turn the H Handler to the ActivityThread’s handleBindService().
ActivityThread#handleBindService()
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
if(! data.rebind) {// Call the Service's onBind method
IBinder binder = s.onBind(data.intent);
// call AMS publishService. Notifies the client that the connection has been successful
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
// Call the onReBind method of the Service
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
}
ensureJitEnabled();
}
Copy the code
Notifies the client that the binding is successful
AMS 的 publishService()
调用了 ActiveServices#publishServiceLocked()
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if(b ! =null && !b.received) {
b.binder = service;
b.requested = true;
b.received = true;
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
for (int i=0; i<clist.size(); i++) {
ConnectionRecord c = clist.get(i);
/ / c is ConnectionRecord
/ / conn LoadedApk. ServiceDispatcher. InnerConnection
// Service is the Binder returned by service onBind()
c.conn.connected(r.name, service);
}
}
}
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}
Copy the code
Take a look at InnerConnection:
LoadedApk.ServiceDispatcher.InnerConnection
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
}
public void connected(ComponentName name, IBinder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if(sd ! =null) {
Connected of ServiceDispatcher is calledsd.connected(name, service); }}}Copy the code
LoadedApk.ServiceDispatcher#connected
public void connected(ComponentName name, IBinder service) {
if(mActivityThread ! =null) {
// mActivityThread is the H in ActivityThread and will not be empty
// So RunConnection will run in the main thread, so the client
Methods in ServiceConnection are called back in the main thread.
// doConnected is also called inside RunConnection
mActivityThread.post(new RunConnection(name, service, 0));
} else{ doConnected(name, service); }}Copy the code
LoadedApk.ServiceDispatcher#doConnected
public void doConnected(ComponentName name, IBinder service) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;
synchronized (this) {
old = mActiveConnections.get(name);
if(old ! =null && old.binder == service) {
// Huh, already have this one. Oh well!
// just return the same as before
return;
}
if(service ! =null) {
// The new service is connected
// A new service is being connected... set it all up.
info = new ConnectionInfo();
info.binder = service;
info.deathMonitor = new DeathMonitor(name, service);
service.linkToDeath(info.deathMonitor, 0);
mActiveConnections.put(name, info);
} else {
// The named service is being disconnected... clean up.
mActiveConnections.remove(name);
}
if(old ! =null) {
old.binder.unlinkToDeath(old.deathMonitor, 0); }}// If there was an old service, it is now disconnected.
if(old ! =null) {
// Disconnect the old one
mConnection.onServiceDisconnected(name);
}
// If there is a new service, it is now connected.
if(service ! =null) {
// New connectionmConnection.onServiceConnected(name, service); }}Copy the code
So the binding will only bind once.
The execution of the system is similar to the process of stopping and unbinding.
The working process of the BroadcastReceiver
Mainly includes:
- The registration process for broadcasts
- The sending and receiving of a broadcast
Defining broadcast:
public class MyBroadcastReceiver extends BroadcastReceiver {
// Do not perform time-consuming operations here. Reference value: within 10s
@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); }}Copy the code
Registered broadcast:
- Static registration
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="me.luwenjie.chapter9.receiver"/>
</intent-filter>
</receiver>
Copy the code
- Dynamic registration
IntentFilter filter = new IntentFilter();
filter.addAction("me.luwenjie.chapter9.receiver");
registerReceiver(new MyBroadcastReceiver(),filter);
Copy the code
Send broadcast
Intent intent = new Intent("me.luwenjie.chapter9.receiver");
sendBroadcast(intent);
Copy the code
The registration process
Static registered broadcasts are automatically registered by the system during application installation, and the PackageManagerServcie (PMS) completes the registration process.
Dynamic registration starts with registerReceiver() for ContextWrapper. The interior is implemented by ContextImpl.
Call chain:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if(mPackageInfo ! =null&& context ! =null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
// Get the IIntentReceiver object from mPackageInfo
// This is an IPC, BroadcastReceiver as Android
// A component cannot be passed directly across processes, but through IIntentReceiver
/ / transshipment. IIntentReceiver is a Binder interface.
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null.true).getIIntentReceiver();
}
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if(intent ! =null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
}
Copy the code
LoadedApk#getReceiverDispatcher
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
// If you are registered, look it up from the storage
if (registered) {
map = mReceivers.get(context);
if(map ! =null) { rd = map.get(r); }}// Otherwise create a new storage
if (rd == null) {
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
map = newArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>(); mReceivers.put(context, map); } map.put(r, rd); }}else {
rd.validate(context, handler);
}
rd.mForgotten = false;
returnrd.getIIntentReceiver(); }}Copy the code
AMS#registerReceiver
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
// ...
synchronized (this) {
// ...
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
// ...
// Store the remote IIntentReceiver object
mRegisteredReceivers.put(receiver.asBinder(), rl);
}
// ...
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
// Store the IntentFilter object
mReceiverResolver.addFilter(bf);
// ...
returnsticky; }}Copy the code
Sending and receiving processes
ContextImpl#sendBroadcast
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null.null.null, AppOpsManager.OP_NONE, null.false.false,
getUserId());
}
Copy the code
AMS finally calls broadcastIntentLocked() inside
Since Android 3.1, the system has added two flags for intEnts:
- FLAG_INCLUDE_STOPPED_PACKAGES
Contains stopped applications, and broadcasts are sent to stopped applications
- FLAG_EXCLUDE_STOPPED_PACKAGES
Does not include stopped applications. Broadcasts are not sent to stopped applications
FLAG_EXCLUDE_STOPPED_PACKAGES is added by default for all broadcasts.
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.
// By default, broadcasts are not sent to stopped applications
//
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
// Find the matching broadcast receiver according to intentFilter and filter it through a series of criteria.
// Add qualified broadcast receivers to the BroadcastQueue
// The broadcast is sent to the appropriate broadcast receiver.
// ...
if((receivers ! =null && receivers.size() > 0) || resultTo ! =null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if(! replaced) { queue.enqueueOrderedBroadcastLocked(r);// Send the broadcast to the appropriate receiverqueue.scheduleBroadcastsLocked(); }}else {
// There was nobody interested in the broadcast, but we still want to record
// that it happened.
if (intent.getComponent() == null && intent.getPackage() == null
&& (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
// This was an implicit broadcast... let's record it for posterity.
addBroadcastStatLocked(intent.getAction(), callerPackage, 0.0.0); }}return ActivityManager.BROADCAST_SUCCESS;
}
Copy the code
com.android.server.am.BroadcastQueue#scheduleBroadcastsLocked
public void scheduleBroadcastsLocked(a) {
if (mBroadcastsScheduled) {
return;
}
// Send a MSG to the internal Handler. This Handler runs on the main thread
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
Copy the code
com.android.server.am.BroadcastQueue#processNextBroadcast
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
BroadcastRecord r;
mService.updateCpuStats();
if (fromMsg) {
mBroadcastsScheduled = false;
}
// First, deliver any non-serialized broadcasts right away.
// Distribute an unordered broadcast first. Unordered broadcasts are stored in mParallelBroadcasts.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
final int N = r.receivers.size();
/ / traverse
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
// Distribute the broadcast
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ mQueueName + "]" + r);
}
/ /...
}
Copy the code
The final call
LoadedApk$ReceiverDispatcher#performReceive
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
// Create an Args object
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
// ...
// Execute the logic in arGS through the H in ActivityThread
if (intent == null| |! mActivityThread.post(args)) {if(mRegistered && ordered) { IActivityManager mgr = ActivityManagerNative.getDefault(); args.sendFinished(mgr); }}}}Copy the code
The run of Arg () :
public void run(a) {
// ...
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
// call onReceive
receiver.onReceive(mContext, intent);
}
if(receiver.getPendingResult() ! =null) { finish(); }}}Copy the code
How the ContentProvider works
ContentProvider is a content sharing component that provides data to other components or applications through Binder.
Start the
When the ContentProvider process starts, the ContenProvider is started and published to AMS at the same time.
At this time of ContentProvideronCreate()
Should precede ApplicationonCreate()
The execution.
Code snippet from ActivityThread#handleBindApplication:
/ /...
// Initialize the ContentProviders
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);
}
// ...
// Then call application onCreate
mInstrumentation.callApplicationOnCreate(app);
Copy the code
After the ContentProvider is started, the outside world can manipulate the data in the ContentProvider through the four interfaces it provides. Binder calls insert, DELETE, Update, and Query.
The external world cannot access ContentProvider directly. AMS can only obtain IContentProvider, the Binder interface of ContentProvider, according to Uri, and then access internal data through it.
A single instance
In general, contentProviders are single instances. This is determined by the attribute Android: multiProcess, where false is a single instance and the default is false.
When multiple instances are enabled, there is a ContentProvider object in each caller’s process. In the actual development, there is no specific use scenario of multi-instance, which is explained by the official explanation to reduce the overhead of inter-process communication.
Access to the ContentProvider
A ContentResolver is required. This is an abstract class. The implementation class is $ApplicationContentResolver ContextImpl
public abstract class ContentResolver
Copy the code
When the thread in which the ContentProvider is created is started, the first access to it triggers the creation of the ContentProvider. All four methods can be fired through the ContentResolver.
ContextImpl$ApplicationContentResolver#query
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {
/ /...
try {
// ...
// Wrap the cursor object into CursorWrapperInner object.
// Get the IContentProvider object
finalIContentProvider provider = (stableProvider ! =null)? stableProvider : acquireProvider(uri);// ...
return wrapper;
} catch (RemoteException e) {
} finally{}}Copy the code
AcquireProvider () finally calls ActivityThread’s acquireProvider().
ActivityThread#acquireProvider
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// Check if it already exists
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if(provider ! =null) {
return provider;
}
// When two processes request at the same time, only the first one is guaranteed to succeed. The lock cannot be used because the request takes a long time and the same process may re-enter the lock.
// Send an interprocess request to AMS to start if it does not find it
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
// Modify the reference count
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
Copy the code
How does AMS start a ContentProvider?
The getContentProvider() above calls getContentProviderImpl()
AMS#getContentProviderImpl
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
// ...
// Start the process with startProcessLocked
proc = startProcessLocked(cpi.processName,
cpr.appInfo,
false.0."content provider".new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false.false.false);
// ...
returncpr ! =null ? cpr.newHolder(conn) : null;
}
Copy the code
Inside startProcessLocked(), a new process is started by Progress’s start().
The entry method after a new process starts is the main() of ActivityThread. Then do the same startup process as above.