讲解这个问题之前我们先来看看这个问题“为什么进程间不能直接进行通信,而需要借助Binder或者Socket通信”, 其实这个问题只要了解过虚拟机的同学大概都会明白怎么回事:
由于在安卓系统中一个进程对应一个虚拟机实例,而在Java虚拟机实例中只存在一个堆区和一个方法区,并且被所有线程共享,所以能够直接通信的只有在虚拟机内部堆区和方法区,进程间通信属于虚拟机对象间通信不能直接通信,当然大家还应该明白一个机器只有一个虚拟机创建多个虚拟机实例来实现多进程也就是多应用。至于栈区、堆区、方法区各自存储的是什么,这就需要同学们认真的去了解一下虚拟机,这里就不做过多详解了。
好了言归正传,再回来聊聊为什么Android进程间通信有时候用Binder有时候用Socket,其实这也是跟Java虚拟机有关的。安卓系统中进程间通信使用Socket通信的,system_server进程与zygote进程通信的时候用到了Socket,system_server进程通过LocalSocket连接zygote进程的serverSocket传递参数ActivityThread,通知zygote进程fork出Android应用进程执行ActivityThread的main()方法。由于zygote进程是所有Android应用进程的父进程,system_server进程也是由zygote进程fork出来的大儿子基于Dalvik(ART) VM虚拟机实例,zygote进程虚拟机实例也是Dalvik(ART),所以zygote进程与system_server进程是父子关系只能使用socket通信,Binder通信属于基于Dalvik(ART) VM虚拟机两个进程兄弟之间的通信方式。那我们就看一下zygote源码为什么说父子进程间只能socket通信,首先看看ZygoteInit的main方法:
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote goes into its own process group.
try {
Os.setpgid(0, 0);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
final Runnable caller;
try {
// Report Zygote start time to tron unless it is a runtime restart
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
MetricsLogger.histogram(null, "boot_zygote_init",
(int) SystemClock.elapsedRealtime());
}
String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
Trace.TRACE_TAG_DALVIK);
bootTimingsTraceLog.traceBegin("ZygoteInit");
RuntimeInit.enableDdms();
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
zygoteServer.registerServerSocketFromEnv(socketName);
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
}
// Do an initial gc to clean up after startup
bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
gcAndFinalize();
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
bootTimingsTraceLog.traceEnd(); // ZygoteInit
// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
Trace.setTracingEnabled(false, 0);
Zygote.nativeSecurityInit();
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
zygoteServer.closeServerSocket();
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
我们可以看到main方法中先是zygoteServer.registerServerSocketFromEnv(socketName);创建ServerSocket,接着preload(bootTimingsTraceLog);预加载基础类、资源等数据提供后续forkSystemServer(abiList, socketName, zygoteServer)复制进程所需数据,那么我们再看看SystemServer的main方法:
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
try {
traceBeginAndSlog("InitBeforeStartServices");
// If a device's clock is before 1970 (before 0), a lot of
// APIs crash dealing with negative numbers, notably
// java.io.File#setLastModified, so instead we fake it and
// hope that time from cell towers or NTP fixes it shortly.
if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
Slog.w(TAG, "System clock is before 1970; setting to 1970.");
SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
}
//
// Default the timezone property to GMT if not set.
//
String timezoneProperty = SystemProperties.get("persist.sys.timezone");
if (timezoneProperty == null || timezoneProperty.isEmpty()) {
Slog.w(TAG, "Timezone not set; setting to GMT.");
SystemProperties.set("persist.sys.timezone", "GMT");
}
// If the system has "persist.sys.language" and friends set, replace them with
// "persist.sys.locale". Note that the default locale at this point is calculated
// using the "-Duser.locale" command line flag. That flag is usually populated by
// AndroidRuntime using the same set of system properties, but only the system_server
// and system apps are allowed to set them.
//
// NOTE: Most changes made here will need an equivalent change to
// core/jni/AndroidRuntime.cpp
if (!SystemProperties.get("persist.sys.language").isEmpty()) {
final String languageTag = Locale.getDefault().toLanguageTag();
SystemProperties.set("persist.sys.locale", languageTag);
SystemProperties.set("persist.sys.language", "");
SystemProperties.set("persist.sys.country", "");
SystemProperties.set("persist.sys.localevar", "");
}
// The system server should never make non-oneway calls
Binder.setWarnOnBlocking(true);
// The system server should always load safe labels
PackageItemInfo.setForceSafeLabels(true);
// Deactivate SQLiteCompatibilityWalFlags until settings provider is initialized
SQLiteCompatibilityWalFlags.init(null);
// Here we go!
Slog.i(TAG, "Entered the Android system server!");
int uptimeMillis = (int) SystemClock.elapsedRealtime();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, uptimeMillis);
if (!mRuntimeRestart) {
MetricsLogger.histogram(null, "boot_system_server_init", uptimeMillis);
}
// In case the runtime switched since last boot (such as when
// the old runtime was removed in an OTA), set the system
// property so that it is in sync. We can | xq oqi't do this in
// libnativehelper's JniInvocation::Init code where we already
// had to fallback to a different runtime because it is
// running as root and we need to be the system user to set
// the property. http://b/11463182
SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());
// Mmmmmm... more memory!
VMRuntime.getRuntime().clearGrowthLimit();
// The system server has to run all of the time, so it needs to be
// as efficient as possible with its memory usage.
VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
// Some devices rely on runtime fingerprint generation, so make sure
// we've defined it before booting further.
Build.ensureFingerprintProperty();
// Within the system server, it is an error to access Environment paths without
// explicitly specifying a user.
Environment.setUserRequired(true);
// Within the system server, any incoming Bundles should be defused
// to avoid throwing BadParcelableException.
BaseBundle.setShouldDefuse(true);
// Within the system server, when parceling exceptions, include the stack trace
Parcel.setStackTraceParceling(true);
// Ensure binder calls into the system always run at foreground priority.
BinderInternal.disableBackgroundScheduling(true);
// Increase the number of binder threads in system_server
BinderInternal.setMaxThreads(sMaxBinderThreads);
// Prepare the main looper thread (this thread).
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
android.os.Process.setCanSelfBackground(false);
Looper.prepareMainLooper();
Looper.getMainLooper().setSlowLogThresholdMs(
SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
// Initialize native services.
System.loadLibrary("android_servers");
// Check whether we failed to shut down last time we tried.
// This call may not return.
performPendingShutdown();
// Initialize the system context.
createSystemContext();
// Create the system service manager.
mSystemServiceManager = new SystemServiceManager(mSystemContext);
mSystemServiceManager.setStartInfo(mRuntimeRestart,
mRuntimeStartElapsedTime, mRuntimeStartUptime);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
// Prepare the thread pool for init tasks that can be parallelized
SystemServerInitThreadPool.get();
} finally {
traceEnd(); // InitBeforeStartServices
}
// Start services.
try {
traceBeginAndSlog("StartServices");
startBootstrapServices();
startCoreServices();
startOtherServices();
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
traceEnd();
}
StrictMode.initVmDefaults(null);
if (!mRuntimeRestart && !isFirstBootOrUpgrade()) {
int uptimeMillis = (int) SystemClock.elapsedRealtime();
MetricsLogger.histogram(null, "boot_system_server_ready", uptimeMillis);
final int MAX_UPTIME_MILLIS = 60 * 1000;
if (uptimeMillis > MAX_UPTIME_MILLIS) {
Slog.wtf(SYSTEM_SERVER_TIMING_TAG,
"SystemServer init took too long. uptimeMillis=" + uptimeMillis);
}
}
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
看看main方法的主要关键部分吧设置完进程属性后直接创建了Looper然后初始化本地service,还发现了createSystemContext();我们进去看看到底做了什么:
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);
final Context systemUiContext = activityThread.getSystemUiContext();
systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
}
这直接调了ActivityThread的systemMain方法:
public static ActivityThread systemMain() {
// The system process on low-memory devices do not get to use hardware
// accelerated drawing, since this can add too much overhead to the
// process.
if (!ActivityManager.isHighEndGfx()) {
ThreadedRenderer.disable(true);
} else {
ThreadedRenderer.enableForegroundTrimming();
}
ActivityThread thread = new ActivityThread();
thread.attach(true, 0);
return thread;
}
看thread.attach(true, 0);这不是ActivityThread的main方法调的吗看样子这不就是走创建Application流程吗,直接创建应用执行生命周期的吗,而且我们还看到activityThread.getSystemContext():
public ContextImpl getSystemContext() {
synchronized (this) {
if (mSystemContext == null) {
mSystemContext = ContextImpl.createSystemContext(this);
}
return mSystemContext;
}
}
这不就是Activity的Context吗,那好我们再回去看看SystemServer到底要用Context干嘛接下来mSystemServiceManager = new SystemServiceManager(mSystemContext);后续都是围绕开启service最好循环Looper;我们都知道Binder通信都是借助service绑定context生命周期的所以如果zygote进程要是有Binder通信的户也需要像system_server进程这样createSystemContext()以及创建不启动service,那么我们回想一下zygote的main方法想想zygote进程到底是干嘛的,zygote进程主要职责不就是fork子进程的吗,如果再创建systemContext创建service这么一通只为了实现Binder通信大家想想这么做是不是有点太累赘了。有人估计会问那你system_server进程创建这些就不是累赘了吗,当然不是累赘了,首先system_server进程职责就是管理Android系统的所有service服务的,再者担任Luncher桌面可视化系统以及安装APK应用职责责任重大。所以zygote进程与system_server进程使用socket通信就代码简单多了没有那么繁杂累赘是最合适的选择方式。
init进程是Linux系统开机最先启动基础进程之一的用户进程,Linux Kernel启动后,会调用/system/core/init/Init.cpp的main()方法,可决定用户级别,Android系统其它进程都是由init进程创建和控制的;
zygote进程是init进程fork出的第一个子进程,由init进程的service通过socket通信开启进程,然后zygote进程调用startVm函数创建虚拟机实例注册JNI开始与Java通信,通过JNI调com.android.internal.os.ZygoteInit的main函数forkSystemServer开始fork出system_server进程设置Dalvik(ART)虚拟机,主要用来与Dalvik(ART)虚拟机通信,所以Dalvik(ART)虚拟机都是有zygote进程孵化器fork出来的,所谓fork其实就是复制zygote进程,这样创建速度快且能够共享系统和框架的资源。system_server进程是zygote进程通过startSystemServer方法fork出的第一个子进程,主要用于启动Android系统中的服务,常见的比如WindowManagerServer(WMS)、ActivityManagerSystemService(AMS)、 PackageManagerServer(PMS)等,ActivityManagerSystemService管理所有Activity,WindowManagerService管理所有窗口,PackageManagerServer主要是安装apk是用来解析AndroidManifest.xml获取Activity、Service、Broadcast Receiver、Content Provider以及权限信息存储Binder中,并以友好的方式在桌面上展现出来(快捷图标的形式)。这里为什么提到了Launcher桌面,因为system_server进程启动Android系统中的服务同时启动了Launcher应用程序,想看Launcher启动流程源码请到我的Android系统应用Launcher的启动流程结合源码详解。
Application进程就是应用进程也就是我们安装的应用,点击桌面luncher图标,通过ActivityManagerSystemService通知system_server进程LocalSocket连接zygote进程serverSocket传递ActivityThread参数,然后system_server进程fork出当前Application应用进程并调度ActivityThread的main()函数启动应用程序,当然这个流程也就是App的启动流程。
总结:所有Binder通信都需要有context、service、aidl三者缺一不可,所以别人再问你为什么使用Socket不有Binder你就可以参考这个Binder通信需要context绑定service然后在service创建aidl接口实现类获取Binder对象来实现通信,如果被问的进程不需要context和service就不需要使用Binder通信直接使用socket通信更简洁。
欢迎关注微信公众号!你的每个赞和在看,都是对我的支持!👍