안드로이드 응용 프로그램 시작 설명 (1) 의 안드로이드 시스템과 안드로이드 응용 프로그램 시작 과정

Android 시스템 부팅 프로세스
안드로이드 시스템이 사실 리눅스 시스템을 기반으로 하고 있다는 것을 우리는 모두 알고 있을 것이다.우리가 전원을 길게 눌러 휴대전화를 가동할 때, 유도칩은 ROM에 고착된 예비 코드부터 실행하기 시작한다.부트 부트 loader를 RAM에 로드한 다음 실행합니다.BootLoader는 Linux 운영체제가 실행되기 전의 작은 프로그램으로 시스템 OS를 끌어올리고 실행하는 것이 주요 역할이다.시스템 커널이 시작되면 캐시, 보호된 메모리, 계획 목록, 마운트 드라이브를 설정합니다.커널이 시스템 설정을 완성하면, 시스템 파일에서 init를 먼저 찾습니다.rc 파일을 열고 init 프로세스를 시작합니다.
init 프로세스
Linux에서 처음 시작하는 프로세스는 init 프로세스입니다.그리고 뒤에 있는 모든 프로세스는 init 프로세스에서 직접 또는 간접적으로 만들어집니다.init 프로세스는 시작한 후에 속성 서비스를 초기화하고 시작하며, 이름이 재미있는 새로운 프로세스를 만듭니다. 그것이 바로 Zygote 프로세스입니다.
Zygote 프로세스
Zygote 프로세스도 중국어로'수정란 프로세스'라고 직접 부르는 사람이 있다.재미있는 이름은 그것이 어디에서 신성한지 쉽게 기억하고 알 수 있게 한다.물론 안드로이드 공성사자의 이름은 마음대로 정해진 것이 아니다. 왜냐하면 Zygote 프로세스가 시작된 후 안드로이드는 자원 공용과 빠른 시작 속도를 실현하기 위해 Zygote 프로세스를 통해 하위 프로세스를 직접 만든다. 즉, 안드로이드의 모든 앱은 Zygote에 기반을 두고 있기 때문이다. Zygote가 초기화되면 자바VM을 만들고 자바VM에 JNI를 등록하고 서버 소켓을 만든다.첫 번째 하위 프로세스 SystemServer를 시작합니다.
SystemServer 프로세스
SystemServer 프로세스는 Binder 스레드 풀과 System Service Manager를 시작하고 Activity Manager Service, PowerManager Service, Package Manager Service, Window Manager Service 등 80여 개의 시스템 서비스를 시작하는 데 중요한 역할을 합니다.
Activity Manager Service 프로세스
Activity Manager Service는 말 그대로 Activity를 관리하는 것이지만, 사실 4대 구성 요소는 모두 Activity Manager Service가 있는 프로세스가 시작되면 시스템 데스크톱 Launcher를 시작합니다.
Launcher
Launcher는 특수한 앱으로widget을 표시하고 설치된 단축 아이콘을 인터페이스에 표시하는 데 주로 사용된다.Launcher의 단축 아이콘을 누르면 앱을 시작할 수 있습니다.
Android 애플리케이션의 시작 프로세스
안드로이드에 있는 모든 앱은 자신의 프로세스에서 독립적으로 실행된다.위에서 언급한 바와 같이 Launcher의 단축 아이콘을 클릭하면 앱이 시작된다.왜 바로 가기 아이콘을 누르면 앱이 시작됩니까?Launcher 소스는 내부에 startActivity가 호출되어 있음을 알려 주기 때문입니다.
       public void onClick(View v) {
        ……
        Object tag = v.getTag();
        if (tag instanceof ShortcutInfo) {
            // Open shortcut
            final Intent intent = ((ShortcutInfo) tag).intent;
            int[] pos = new int[2];
            v.getLocationOnScreen(pos);
            intent.setSourceBounds(new Rect(pos[0], pos[1],
                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
            //    
            boolean success = startActivitySafely(v, intent, tag);
            if (success && v instanceof BubbleTextView) {
                mWaitingForResume = (BubbleTextView) v;
                mWaitingForResume.setStayPressed(true);
            }
        }
        ……
}
       boolean startActivitySafely(View v, Intent intent, Object tag) {
        boolean success = false;
        try {
            //    
            success = startActivity(v, intent, tag);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
        }
        return success;
    }
       boolean startActivity(View v, Intent intent, Object tag) {
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            // Only launch using the new animation if the shortcut has not opted out (this is a
            // private contract between launcher and may be ignored in the future).
            boolean useLaunchAnimation = (v != null) &&
                    !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
            if (useLaunchAnimation) {
                ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
                        v.getMeasuredWidth(), v.getMeasuredHeight());
                startActivity(intent, opts.toBundle());
            } else {
                startActivity(intent);
            }
            return true;
        } catch (SecurityException e) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
                    "or use the exported attribute for this activity. "
                    + "tag="+ tag + " intent=" + intent, e);
        }
        return false;
    }

Launcher 클래스는Activity에 계승되기 때문에 위에서 호출한 startActivity ()는Activity입니다.java의 startActivity () 방법입니다.startActivity () 방법을 바꾸면 app의 프로세스가 존재하는지 여부를 판단합니다. 존재하지 않으면 먼저 app의 프로세스를 만듭니다.그 절차는 대략 다음과 같다. Activity Manager 서비스를 통해 서버 소켓을 요청하고 서버 소켓이 다시 Zygoto 프로세스를 요청하면 Zygoto 프로세스는 스스로 app 프로세스를 만들 것이다.앱 프로세스가 생성될 때 메인 라인도 생성됩니다. 이 때 앱의 첫 번째 종류인ActivityThread의main () 방법이 호출되고main () 방법에서 관련 초기화가 진행됩니다.이것도 우리가 흔히 말하는 안드로이드에서 앱의 첫 번째 입구 코드는Activity Thread이다.main().
startActivity()에서 ActivityThread까지.main () 프로세스 소스 분석
Activity.java
@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        startActivityForResult(intent, -1);
    }
}
public void startActivityForResult(Intent intent, int requestCode) {
    startActivityForResult(intent, requestCode, null);
}
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
    if (mParent == null) {
        //     1
        Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);        
               ……
} else {
        //     2
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
    ……
}

보시다시피 startActivity () 방법은 마지막으로 startActivityForResult () 방법을 호출했습니다. 방법 안에 mParent 변수가 비어 있는지 아닌지를 판단했습니다. mParent도 사실은Activity이고 핵심 코드 2의 mParent입니다.startActivityFromChild () 방법에서도 mInstrumentation이 호출되었습니다.execStartActivity () 방법입니다. 따라서 키 코드 1의 execStartActivity () 방법을 계속 보겠습니다.
Instrumentation.java
public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    ……
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess();
        //     
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
    }
    return null;
}

주석의 키 코드인 ActivityManagerNative를 보십시오.getDefault (), IACtivity Manager 대상을 되돌려줍니다. getDefault () 방법을 보십시오.
ActivityManagerNative.java
static public IActivityManager getDefault() {
    return gDefault.get();
}
private static final Singleton gDefault = new Singleton() {
    protected IActivityManager create() {
        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);
        }
        return am;
    }
};

getDefault () 방법은 전역 변수 gDefault의 get () 방법으로 대상을 되돌려주고, gDefault 변수는 서비스 관리자를 통해 되돌려줍니다.getService("activity")가 Ibinder 객체를 초기화합니다.그것은 Binder를 통해 프로세스 간의 통신을 실현한다.분명히 여기는'activity'키로 대상을 얻었습니다. 이 키는 어디에 set되어 있습니까? 답은Activity Manager 서비스에 있습니다. 원본 코드를 보십시오.
ActivityManagerService.java
public void setSystemProcess() {
    try {
        //     
        ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
        ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
        ServiceManager.addService("meminfo", new MemBinder(this));
        ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
        ServiceManager.addService("dbinfo", new DbBinder(this));
        ……
    } catch (PackageManager.NameNotFoundException e) {
        throw new RuntimeException("Unable to find android system package", e);
    }
}

핵심 코드 행을 보십시오.Context.ACTIVITY_SERVICE는 "activity"이고, 여기에 set이 들어간 것은Activity Manager 서비스 자신입니다.IACtivity Manager 객체가 있는 곳으로 돌아가면 Activity Manager Service가 됩니다.따라서 startActivity는 여러 프로세스에서 Activity Manager Service의 startActivity () 방법을 호출합니다.
ActivityManagerService.java
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options) {
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
        resultWho, requestCode, startFlags, profilerInfo, options,
        UserHandle.getCallingUserId());
}

@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
        ……
    //     
    return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
            profilerInfo, null, null, options, userId, null, null);
}

ActivityStackSupervisor.java
final int startActivityMayWait(IApplicationThread caller, int callingUid,
        String callingPackage, Intent intent, String resolvedType,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        IBinder resultTo, String resultWho, int requestCode, int startFlags,
        ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
        Bundle options, int userId, IActivityContainer iContainer, TaskRecord inTask) {
         ……
         //     
        int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                voiceSession, voiceInteractor, resultTo, resultWho,
                requestCode, callingPid, callingUid, callingPackage,
                realCallingPid, realCallingUid, startFlags, options,
                componentSpecified, null, container, inTask);
        ……
        return res;
    }
}
final int startActivityLocked(IApplicationThread caller,
        Intent intent, String resolvedType, ActivityInfo aInfo,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        IBinder resultTo, String resultWho, int requestCode,
        int callingPid, int callingUid, String callingPackage,
        int realCallingPid, int realCallingUid, int startFlags, Bundle options,
        boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container,
        TaskRecord inTask) {
    ……
    //     
    err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
            startFlags, true, options, inTask);
    ……
    return err;
}
final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
        boolean doResume, Bundle options, TaskRecord inTask) {
     ……
if (r.resultTo == null && inTask == null && !addingToTask
            && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
      ……
    } else if (sourceRecord != null) {
       ……
        if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
            ……
        } else if (!addingToTask &&
                (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
            final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
            if (top != null) {
                final TaskRecord task = top.task;
                task.moveActivityToFrontLocked(top);
                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
                top.updateOptionsLocked(options);
                top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
                targetStack.mLastPausedActivity = null;
                if (doResume) {
                    //     
                    targetStack.resumeTopActivityLocked(null);
                }
                return ActivityManager.START_DELIVERED_TO_TOP;
            }
        }       
    }
    return ActivityManager.START_SUCCESS;
}

startActivity Unchecked Locked () 방법에서 우리는 익숙한 FLAG를 많이 보았다. 왜냐하면 이 방법은Activity의 네 가지 시작 모드인standard,singleTop,singleTask,singleInstance를 처리하기 때문이다. 코드가 비교적 많기 때문에 여기서는 생략하고 분석을 전개하지 않는다.계속해서 ResumeTopActivityLocked 메서드를 살펴보겠습니다.
ActivityStack.java
final boolean resumeTopActivityLocked(ActivityRecord prev) {
    return resumeTopActivityLocked(prev, null);
}
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
    if (mStackSupervisor.inResumeTopActivity) {
        return false;
    }
    boolean result = false;
    try {
        ……
        //     
        result = resumeTopActivityInnerLocked(prev, options);
    } finally {
        mStackSupervisor.inResumeTopActivity = false;
    }
    return result;
}
final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
    if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
    ……
    //         finish Activity ActivityRecord.
    final ActivityRecord next = topRunningActivityLocked(null);
    ……
    if (next == null) {
        //    Activity,    Launcher
        ……
        return isOnHomeDisplay() &&
                mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, "noMoreActivities");
    }
    ……
    // Activity         
    if (next.app != null && next.app.thread != null) {
        ……
        try {
            ……
            //     Activity
            if (next.newIntents != null) {
                next.app.thread.scheduleNewIntent(next.newIntents, next.appToken);
            }
            ……
            //   Activity onResume()
            next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                    mService.isNextTransitionForward(), resumeAnimOptions);
            ……
        } catch (Exception e) {
            //       ,   Activity
            ……
            mStackSupervisor.startSpecificActivityLocked(next, true, false);
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return true;
        }
        ……
    } else {
        // Activity        ,  Activity
        if (!next.hasBeenLaunched) {
            ……
        } else {
            ……
        }
        //     
        mStackSupervisor.startSpecificActivityLocked(next, true, true);
    }
    ……
    return true;
}

메서드에 코드가 많으므로 메모에 대한 설명을 직접 참조하고 startSpecificActivityLocked 메서드를 살펴보십시오.
ActivityStackSupervisor.java
void startSpecificActivityLocked(ActivityRecord r,
        boolean andResume, boolean checkConfig) {
    ProcessRecord app = mService.getProcessRecordLocked(r.processName,
            r.info.applicationInfo.uid, true);
    r.task.stack.setLaunchTime(r);

    //    Activity      ,     ,    Activity
    if (app != null && app.thread != null) {
        try {
            if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0 || !"android".equals(r.info.packageName)) {

                app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, mService.mProcessStats);
            }
            //     1
            realStartActivityLocked(r, app, andResume, checkConfig);
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception when starting activity "
                    + r.intent.getComponent().flattenToShortString(), e);
        }
    }
    //     2
    mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true);
}
startSpecificActivityLocked() , Activity :  
Activity 프로세스가 존재할 때 키 코드의 1을 직접 호출하는 RealStartActivityLocked () 방법으로Activity를 시작합니다.(먼저 기억하세요: 이것은 응용 프로그램에서Activity를 시작하는 지점입니다. 나중에 다시 분석해 보겠습니다)
Activity의 프로세스가 존재하지 않을 때 키 코드 2의Activity Manager Service의 start Process Locked () 방법을 호출하여 프로세스를 만듭니다. 우리는 이번에Launcher에서 시작한Activity입니다. 프로세스는 당연히 존재하지 않기 때문에 mService를 실행합니다.startProcessLocked () 를 사용하여 새 프로세스를 만듭니다. 구체적인 코드를 살펴보겠습니다.
ActivityManagerService.java
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
        boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
        boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
        String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
        ……
    if (app == null) {
        checkTime(startTime, "startProcess: creating new process record");
        app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
        if (app == null) {
            Slog.w(TAG, "Failed making new process record for "
                    + processName + "/" + info.uid + " isolated=" + isolated);
            return null;
        }
        app.crashHandler = crashHandler;
        mProcessNames.put(processName, app.uid, app);
        if (isolated) {
            mIsolatedProcesses.put(app.uid, app);
        }
        checkTime(startTime, "startProcess: done creating new process record");
    } else {
        // If this is a new package in the process, add the package to the list
        app.addPackage(info.packageName, info.versionCode, mProcessStats);
        checkTime(startTime, "startProcess: added package to existing proc");
    }
     ……
     //     
    startProcessLocked(app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
    checkTime(startTime, "startProcess: done starting proc!");
    return (app.pid != 0) ? app : null;
}
private final void startProcessLocked(ProcessRecord app, String hostingType,
        String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
    ……
    try {
        ……
        //     
        if (entryPoint == null) entryPoint = "android.app.ActivityThread";
        Process.ProcessStartResult startResult = Process.start(entryPoint,
                app.processName, uid, uid, gids, debugFlags, mountExternal,
                app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                app.info.dataDir, entryPointArgs);
        ……
    } catch (RuntimeException e) {
        ……
    }
}

보시다시피 Process.start () 방법이 들어오는 첫 번째 매개 변수는ActivityThread 클래스입니다.ActivityThread 클래스가 app의 입구 클래스라는 것을 알고 아래를 계속 보십시오.
Process.java
public static final ProcessStartResult start(final String processClass,
                              final String niceName,
                              int uid, int gid, int[] gids,
                              int debugFlags, int mountExternal,
                              int targetSdkVersion,
                              String seInfo,
                              String abi,
                              String instructionSet,
                              String appDataDir,
                              String[] zygoteArgs) {
    try {
        //     
        return startViaZygote(processClass, niceName, uid, gid, gids,
                debugFlags, mountExternal, targetSdkVersion, seInfo,
                abi, instructionSet, appDataDir, zygoteArgs);
    } catch (ZygoteStartFailedEx ex) {
        Log.e(LOG_TAG,
                "Starting VM process through Zygote failed");
        throw new RuntimeException(
                "Starting VM process through Zygote failed", ex);
    }
}

핵심 코드 줄을 보십시오. startViaZygote () 방법명은 이미 뚜렷합니다. 바로 Zygote를 통해 프로세스를 시작하고 계속 보십시오.
private static ProcessStartResult startViaZygote(final String processClass,
                              final String niceName,
                              final int uid, final int gid,
                              final int[] gids,
                              int debugFlags, int mountExternal,
                              int targetSdkVersion,
                              String seInfo,
                              String abi,
                              String instructionSet,
                              String appDataDir,
                              String[] extraArgs)
                              throws ZygoteStartFailedEx {
    synchronized(Process.class) {
        ArrayList argsForZygote = new ArrayList();
        ……
        //     
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    }
}
private static ProcessStartResult zygoteSendArgsAndGetResult(
        ZygoteState zygoteState, ArrayList args)
        throws ZygoteStartFailedEx {
    try {
        final BufferedWriter writer = zygoteState.writer;
        final DataInputStream inputStream = zygoteState.inputStream;
        writer.write(Integer.toString(args.size()));
        writer.newLine();

        int sz = args.size();
        for (int i = 0; i < sz; i++) {
            String arg = args.get(i);
            if (arg.indexOf('
') >= 0) {                 throw new ZygoteStartFailedEx("embedded newlines not allowed");             }             writer.write(arg);             writer.newLine();         }         writer.flush();         // Should there be a timeout on this?         ProcessStartResult result = new ProcessStartResult();         result.pid = inputStream.readInt();         if (result.pid < 0) {             throw new ZygoteStartFailedEx("fork() failed");         }         result.usingWrapper = inputStream.readBoolean();         return result;     } catch (IOException ex) {         zygoteState.close();         throw new ZygoteStartFailedEx(ex);     } }
Zygote , Zygote , Zygote fork , ActivityThread main 。
총결산
원본 코드를 보면 지루하고 재미없을 수 있지만 관건을 기억하면 충분하다.
  • 안드로이드 시스템이 시작된 후 첫 번째 프로세스는 init 프로세스이고 그 다음은 Zygote 프로세스
  • 안드로이드의 모든 앱은 자신의 단독 프로세스에서 실행되고 그 프로세스는 Zygote 프로세스 fork 자체를 통해 만들어진다
  • 안드로이드의 모든 앱의 입구는ActivityThread입니다.main() 방법
  • 좋은 웹페이지 즐겨찾기