Como iniciar o aplicativo Android App de interpretação de código-fonte?

prefácio

Como desenvolvedor Android há muitos anos, escrevi muitos aplicativos, mas como um aplicativo é iniciado? Se você disser que a área de trabalho começa com um clique, está certo. Mas e o processo de inicialização? Com essas perguntas, vamos aprender passo a passo.

Processo de inicialização do Android

Geralmente em qualquer plataforma, os seguintes componentes são carregados e executados de forma incremental:

  • Carregador de inicialização
  • U-boot (opcional)
  • Núcleo
  • Android

Os processos do Android têm a seguinte ordem:

  • Iniciar
  • Zigoto
  • Servidor do sistema
  • Gerente de Serviços
  • Outros Daemons e processos
  • Formulários

A situação específica é a seguinte, e é interessante combinar essas duas imagens:

imagem.png

  • Boot ROM : Quando a energia é pressionada, o código do chip de inicialização começará a ser executado a partir de um local predefinido (solidificado na ROM), carregará o programa de inicialização BootLoader na RAM e, em seguida, executará. (Esta etapa é projetada e implementada pelo "fabricante do chip")

  • Bootloader : Bootloader começa a executar, antes de tudo, é responsável por completar a inicialização do hardware e inicializar o sistema operacional para iniciar . (Esta etapa é projetada e implementada pelo "fabricante do equipamento")

  • Kernel : O kernel do Linux é o núcleo do Android, responsável pela criação de processos, comunicação entre processos, drivers de dispositivos, gerenciamento do sistema de arquivos, etc. O Android aplica patches personalizados no kernel principal para oferecer suporte a determinados recursos necessários para a execução do Android, como wakelocks. Os kernels podem ser carregados como imagens descompactadas ou compactadas. Ao carregar, ele monta o sistema de arquivos raiz (geralmente passado como um argumento de linha de comando do kernel) e inicia o primeiro aplicativo no espaço do usuário . (Esta etapa é o que precisa estar envolvido no processo de desenvolvimento do kernel do Android)

  • Android : Sistema Android e principais distribuições Linux, sua parte do kernel Linux do processo de inicialização é semelhante, a maior diferença entre eles é a diferença no programa init, pois o programa init determina o sistema durante o processo de inicialização, o que acontecerá Quais daemons e serviços são iniciados e que tipo de interface do usuário é apresentada.

Portanto, o programa init é o programa mais central na análise do processo de inicialização do Android.

  • init e init.rc : O primeiro aplicativo de espaço do usuário executado quando o kernel é iniciado é o executável init localizado na pasta raiz. O processo analisa um script de inicialização chamado script "init.rc". Isso está escrito em uma linguagem projetada para o Android para iniciar todos os processos, daemons e serviços necessários para que o Android funcione corretamente. Ele fornece vários tipos de tempos de execução, como early-init, on-boot, on-post-fs, etc. (o criador do espaço do usuário)

  • Demônios e Serviços : O processo init cria vários daemons e processos, como rild, vold, mediaserver, adb, etc., cada processo é responsável por suas próprias funções. Uma descrição desses processos está além do escopo deste artigo. Em vez disso, falaremos mais sobre o processo "Zygote".

  • Service Manager : O processo do Service Manager gerencia todos os Serviços em execução no sistema. Todo serviço criado se registra neste processo e esta informação é para referência futura por outros processos/aplicações.

  • Zygote : Zygote é um dos primeiros processos de inicialização criados na inicialização. O termo "zigoto" é baseado na "formação biológica de uma divisão celular inicial para produzir descendentes". Da mesma forma, "zygote in android" inicializa o Dalivik VM (ART) e bifurca para criar várias instâncias para dar suporte a cada processo do Android. Ele facilita o uso de código compartilhado entre instâncias de VM, reduzindo o consumo de memória e o tempo de carregamento, tornando-o ideal para sistemas embarcados. Além de instalar um listener no soquete do servidor, o Zygote também pré-carrega classes e recursos para uso posterior no aplicativo Android. Após a conclusão, o servidor do sistema é iniciado.

  • System Server : O processo SystemServer inicia todos os serviços disponíveis no Android.

Neste artigo, nos concentramos em começar do init até a inicialização do aplicativo.

1. O que é Zigoto?

No sistema Android, zigoto é o nome de um processo. O Android é baseado no sistema Linux. Quando seu telefone é ligado, um processo chamado "init" será iniciado após o kernel Linux ser carregado. No sistema Linux, todos os processos são bifurcados pelo processo init e nosso processo zigoto não é exceção.

Zygote é um processo de máquina virtual e uma incubadora de instâncias de máquinas virtuais. Sempre que o sistema solicitar a execução de um aplicativo Android, o Zygote irá bifurcar (split) um processo filho para executar o aplicativo.

1.1 app_main.cpp

frameworks/base/cmds/app_process/app_main.cpp

app_main.cpp é executado após o início do Zygote. Seja C/c++/java, sua entrada é main(), assim como quando vemos Activity, procuramos diretamente pelo método onCreate().

1.1.1 principal()

int main(int argc, char* const argv[])
{
    ...
    //注释1:初始化AppRuntime(AndroidRunTime)
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ...
    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        //注释2:设置zygote模式
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } 
        ...
    }
    Vector<String8> args;
    if (!className.isEmpty()) {
        ...
    } else {
        // 我们处于 zygote 模式。
        maybeCreateDalvikCache();

        // 注释3:在 zygote 模式下,将参数传递给 ZygoteInit.main() 方法。
        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }

        //PROP_VALUE_MAX = 92;
        char prop[PROP_VALUE_MAX];
        ...
        String8 abiFlag("--abi-list=");
        abiFlag.append(prop);
        args.add(abiFlag);

        for (; i < argc; ++i) {
            args.add(String8(argv[i]));
        }
    }    
    if (zygote) {
        //注释4:调用 AndroidRuntime.start() 方法
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        ...
    } else {
        ...
    }
}

Nota 1: Inicialize o AppRuntime, que na verdade é o AndroidRuntime (ART).

Nota 2: Defina o modo zigoto.

Nota 3: Passe parâmetros para o método ZygoteInit.main().

Nota 4: Inicie o ZygoteInit. ZygoteInit aqui é a classe de inicialização do processo zigoto. Isso é mencionado abaixo. Vejamos primeiro o método start() do AndroidRuntime.

1.2 AndroidRuntime.cpp

frameworks/base/core/jni/AndroidRuntime.cpp

máquina virtual Android

1.2.1 iniciar()

/*
 * Start the Android runtime.  This involves starting the virtual machine and calling the "static void main(String[] args)" method in the class named by "className".
 *
 * Passes the main function two arguments, the class name and the specified
 * options string.
 */
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ...
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    //注释1:启动虚拟机
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);

    //注释2:注册安卓功能(JNI)
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    ...
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    ...
    /*
     * 启动虚拟机。 该线程成为VM的主线程,直到VM退出才会返回。
     */
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ...
    } else {
        ...
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ...
        } else {
            //注释3
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
        }
    }
    ...
}
复制代码

Nota 1: Inicie a VM (Máquina Virtual)

Nota 2: registrando funções do Android (JNI)

Nota 3: Use JNI para chamar o método main() do Zygotelnit. Zygotelnit aqui é um arquivo de classe, ou seja, ele entra no campo java daqui.

JNI: Uma ponte conectando a camada nativa (C/C++) e a camada Java.

1.3 ZygoteInit.java

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

/**
 * zygote 进程的启动类。
 */
public class ZygoteInit {
    ...
}
复制代码

Este é o ponto de entrada para o processo Zygote. Ele cria serviços Zygote, carrega recursos e lida com outras tarefas relacionadas ao processo de preparação para bifurcação em um aplicativo.

1.3.1 principal()

    @UnsupportedAppUsage
    public static void main(String[] argv) {
        ZygoteServer zygoteServer = null;

        try {
            ...
            boolean startSystemServer = false;
            //argv:用于指定 Zygote 配置的命令行参数。
            ...
            if (!enableLazyPreload) {
                //注释1:预加载资源。
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                        SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            }
            ...
            //注释2:创建Zygote 的 LocalServerSocket 。
            zygoteServer = new ZygoteServer(isPrimaryZygote);

            if (startSystemServer) {
                //注释3:开始fork我们的SystemServer进程。
                Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
                ...
            }
            ...
            // 注释4:zygote 永久循环。
            caller = zygoteServer.runSelectLoop(abiList);            
        } catch (Throwable ex) {
            ...
        } finally {
            if (zygoteServer != null) {
                zygoteServer.closeServerSocket();
            }
        }
        ...
    }
复制代码

Nota 1: Pré-carregue recursos.

Nota 2: Crie o LocalServerSocket do Zygote.

Nota 3: Comece a bifurcar nosso processo SystemServer.

Nota 4: loops de zigoto para sempre.

Aqui nós olhamos para forkSystemServer();

1.3.2 forkSystemServer()

    /**
     * Prepare the arguments and forks for the system server process.
     *
     * @return A {@code Runnable} that provides an entrypoint into system_server code in the child
     * process; {@code null} in the parent.
     */
    private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        ...
        //命令行来启动SystemServer
        //ZygoteInit.main(String argv[])里面的argv 跟这个类似
        String[] args = {
                "--setuid=1000",
                "--setgid=1000",
                "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
                        + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
                "--capabilities=" + capabilities + "," + capabilities,
                "--nice-name=system_server",
                "--runtime-args",
                "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
                "com.android.server.SystemServer",
        };
        //处理与 zygote spawner 相关的 args 的参数解析。
        ZygoteArguments parsedArgs;
        int pid;
        try {
            ZygoteCommandBuffer commandBuffer = new ZygoteCommandBuffer(args);
            try {
                parsedArgs = ZygoteArguments.getInstance(commandBuffer);
            } catch (EOFException e) {
                throw new AssertionError("Unexpected argument error for forking system server", e);
            }
            commandBuffer.close();
            ...

            //请求 fork 系统服务器进程
            /* Request to fork the system server process */
            pid = Zygote.forkSystemServer(
                    parsedArgs.mUid, parsedArgs.mGid,
                    parsedArgs.mGids,
                    parsedArgs.mRuntimeFlags,
                    null,
                    parsedArgs.mPermittedCapabilities,
                    parsedArgs.mEffectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }

        /* For child process */
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }

        return null;
    }
复制代码

Um servidor do sistema é iniciado aqui. Vamos dar uma olhada nele abaixo.

2、SystemServer

servidor do sistema também é SystemServer. SystemServer também é um processo, incluindo 92 serviços como ActivityTaskManagerService, ActivityManagerService, PackageManagerService e WindowManagerService.

Existem dois processos muito importantes no Android Framework:

  • Processo SystemServer.

  • Processo do zigoto.

2.1 SystemServer.java

frameworks/base/services/java/com/android/server/SystemServer.java

public final class SystemServer {
    ...
}
复制代码

2.1.1 principal()

    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }
    public SystemServer() {
        // Check for factory test mode.
        mFactoryTestMode = FactoryTest.getMode();
        ...
    }    
复制代码

Vamos dar uma olhada no que é usado em run()?

2.1.2 executar()

    private void run() {
        try {
            ...
            // 注释1:加载动态库libandroid_service.so。
            System.loadLibrary("android_servers");

            // 注释2:创建系统上下文。
            createSystemContext();

            // 调用每个进程的主线模块初始化。
            ActivityThread.initializeMainlineModules();

            // 注释3:创建 SystemServiceManager。
            mSystemServiceManager = new SystemServiceManager(mSystemContext);
            mSystemServiceManager.setStartInfo(mRuntimeRestart,
                    mRuntimeStartElapsedTime, mRuntimeStartUptime);
            LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
            // 为可并行化的 init 任务准备线程池
            SystemServerInitThreadPool.start();
            ...
        } finally {

        }
        // 注释4:Start services。
        try {
            //下面咱们看看这个三个方法启动什么服务
            startBootstrapServices(t);
            startCoreServices(t);
            startOtherServices(t);
        } catch (Throwable ex) {
            ...
        } finally {
            t.traceEnd(); // StartServices
        }

        ...
        // 注释5:Loop 永久循环。
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

复制代码

Nota 1: Carregue a biblioteca dinâmica libandroid_service.so.

Nota 2: Crie um contexto de sistema.

Nota 3: Crie SystemServiceManager.

Nota 4: Iniciar Serviços (startBootstrapServices, startCoreServices, startOtherServices)

Nota 5: Loop loops para sempre.

2.1.3 criarSystemContext()

    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);
    }
复制代码

Inicialize o objeto de contexto do sistema mSystemContext e defina o tema padrão, mSystemContext é na verdade um objeto Context (ContextImpl).

Quando ActivityThread.systemMain() é chamado, ActivityThread.attach(true) é chamado, e em attach(), o objeto Application é criado e Application.onCreate() é chamado.

2.1.4 startBootstrapServices()

    /**
     * 启动系统引导服务,因为这些服务之间有复杂的相互依赖关系,所以都放在了这个方法里面。
     */
    private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
        ...
        final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
        SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);

        // PlatformCompat Service 由 ActivityManagerService, PackageManagerService 和 其他服务做使用
        PlatformCompat platformCompat = new PlatformCompat(mSystemContext);
        ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
        ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
                new PlatformCompatNative(platformCompat));
        AppCompatCallbacks.install(new long[0]);

        mSystemServiceManager.startService(FileIntegrityService.class);
        Installer installer = mSystemServiceManager.startService(Installer.class);
        mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
        mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class);
        startMemtrackProxyService();

        // StartActivityManager
        ActivityTaskManagerService atm = mSystemServiceManager.startService(
                ActivityTaskManagerService.Lifecycle.class).getService();
        //初始化 ActivityManagerService
        mActivityManagerService = ActivityManagerService.Lifecycle.startService(
                mSystemServiceManager, atm);
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
        mWindowManagerGlobalLock = atm.getGlobalLock();

        mDataLoaderManagerService = mSystemServiceManager.startService(
                DataLoaderManagerService.class);

        mIncrementalServiceHandle = startIncrementalService();
        t.traceEnd();

        //初始化PowerManagerService(电源服务),需要提前启动,因为其他服务需要它。
        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
        mSystemServiceManager.startService(ThermalManagerService.class);

        // 电源管理已经开启,ActivityManagerService负责电源管理功能
        mActivityManagerService.initPowerManagement();

        mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class);
        ...

        mSystemServiceManager.startService(LightsService.class);

        // Package manager isn't started yet; need to use SysProp not hardware feature
        if (SystemProperties.getBoolean("config.enable_sidekick_graphics", false)) {
            mSystemServiceManager.startService(WEAR_SIDEKICK_SERVICE_CLASS);
        }

        // 初始化DisplayManagerService(显示管理器)
        mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);

        mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);

        // Start the package manager.
        try {
            mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        } finally {

        }

        // 现在PackageManagerService已经启动,注册 dex 加载报告器来捕获系统服务加载的任何 dex 文件。
        // 这些 dex 文件将由 BackgroundDexOptService 优化。
        SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService);

        mFirstBoot = mPackageManagerService.isFirstBoot();
        mPackageManager = mSystemContext.getPackageManager();
        ...
        //将AMS等添加到ServiceManager中
        mActivityManagerService.setSystemProcess();
        if (!mOnlyCore) {
            boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
                    false);
            if (!disableOtaDexopt) {
                try {
                    OtaDexoptService.main(mSystemContext, mPackageManagerService);
                } catch (Throwable e) {

                } finally {
                }
            }
        }

        ...
        mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
            TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
            startSensorService();
        }, START_SENSOR_SERVICE);

        // startBootstrapServices
    }
复制代码

Principais mudanças:

  • ActivityTaskManagerService(ATMS): Responsável por gerenciar atividades e processos, incluindo ciclo de vida e comutação de estado.

  • ActivityManagerService (AMS): Uma subclasse de AMN, responsável por gerenciar três componentes principais (exceto Activity) e processos, incluindo ciclo de vida e comutação de estado. O AMS é extremamente complexo porque precisa interagir com a interface do usuário, envolvendo janelas.

ActivityTaskManagerService: gerado pela remoção do conteúdo relacionado à atividade de ActivityManagerService.

PowerManagerService(PMS): Serviço de gerenciamento de energia.

PackageManagerService(PKMS): O serviço de gerenciamento de pacotes, não chamado de PMS, serve para distingui-lo do serviço de gerenciamento de energia.

2.1.5 startCoreServices()

    /**
     * 启动核心服务。
     */
    private void startCoreServices(@NonNull TimingsTraceAndSlog t) {
        // Service for system config
        mSystemServiceManager.startService(SystemConfigService.class);
        // Tracks the battery level.  Requires LightService.
        mSystemServiceManager.startService(BatteryService.class);
        ...
        mSystemServiceManager.startService(LooperStatsService.Lifecycle.class);

        mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);

        mSystemServiceManager.startService(NativeTombstoneManagerService.class);

        mSystemServiceManager.startService(BugreportManagerService.class);

        mSystemServiceManager.startService(GpuService.class);

        // startCoreServices
    }

复制代码

2.1.6 iniciarOutrosServiços()

    /**
     * 启动其他服务。
     */
    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {

        final Context context = mSystemContext;
        VibratorService vibrator = null;
        DynamicSystemService dynamicSystem = null;
        IStorageManager storageManager = null;
        NetworkManagementService networkManagement = null;
        IpSecService ipSecService = null;
        VpnManagerService vpnManager = null;
        VcnManagementService vcnManagement = null;
        NetworkStatsService networkStats = null;
        NetworkPolicyManagerService networkPolicy = null;
        NsdService serviceDiscovery = null;
        WindowManagerService wm = null;
        SerialService serial = null;
        NetworkTimeUpdateService networkTimeUpdater = null;
        InputManagerService inputManager = null;
        TelephonyRegistry telephonyRegistry = null;
        ConsumerIrService consumerIr = null;
        MmsServiceBroker mmsService = null;
        HardwarePropertiesManagerService hardwarePropertiesService = null;
        PacProxyService pacProxyService = null;
        ...
        // 现在便可以开始启动三方APP应用(如Launcher启动桌面)
        mActivityManagerService.systemReady(() -> {
        ...
         }, t);       
        // startOtherServices
    }
复制代码

Após as etapas acima, quando chamamos createSystemContext() para criar o contexto do sistema, concluímos a criação de mSystemContext e ActivityThread.

Objetos como ATMS, AMS, WMS e PKMS foram criados e as variáveis ​​de membro foram inicializadas.

Nota: Este é o processo quando o processo do sistema é iniciado. Depois disso, o sistema será iniciado.

O programa Launcher conclui o carregamento e a exibição da interface do sistema.

No design do framework do Android, o lado do servidor refere-se aos serviços do sistema compartilhados por todos os aplicativos, como ATMS, AMS, WMS, PKMS, etc. Esses serviços básicos do sistema são compartilhados por todos os aplicativos.

3. O que é o Launcher

No sistema Android, o aplicativo é iniciado pelo Launcher. Na verdade, o Launcher em si também é um aplicativo . Após a instalação de outros aplicativos, um ícone correspondente aparecerá na interface do Launcher. aplicativo correspondente é iniciado.

Claro, você também pode iniciar aplicativos em outros aplicativos. Mas, em essência, ele chama startActivity().

3.1 LauncherActivity.java

frameworks/base/core/java/android/app/LauncherActivity.java

/**
 * Displays a list of all activities which can be performed
 * for a given intent. Launches when clicked.
 *
 * @deprecated Applications can implement this UI themselves using
 *   {@link androidx.recyclerview.widget.RecyclerView} and
 *   {@link android.content.pm.PackageManager#queryIntentActivities(Intent, int)}
 */
@Deprecated
public abstract class LauncherActivity extends ListActivity {
    ...
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Intent intent = intentForPosition(position);
        startActivity(intent);
    }
}
复制代码

resumo

Uma imagem completa está anexada

Se você conhece startActivity(), pode dar uma olhada diretamente. Há tanto conteúdo neste post que fico irritado só de olhar para ele. Se você não viu, dê uma onda, e os detalhes serão explicados no próximo artigo: ❤️ Análise de código fonte Android startActivity ❤️.

Acho que você gosta

Origin blog.csdn.net/ajsliu1233/article/details/120711444
Recomendado
Clasificación