主頁 > 移動端開發 > ?? Android 應用是如何啟動的???

?? Android 應用是如何啟動的???

2021-10-01 08:41:29 移動端開發

目錄

前言

Android 啟動程序

1、Zygote是什么

1.1 app_main.cpp

1.1.1 main()

1.2 AndroidRuntime.cpp

1.2.1 start()

1.3 ZygoteInit.java

1.3.1 main()

1.3.2 forkSystemServer()

2、SystemServer

2.1 SystemServer.java

2.1.1 main()

2.1.2 run()

2.1.3 createSystemContext()

2.1.4 startBootstrapServices()

2.1.5 startCoreServices()

2.1.6 startOtherServices()

3、Launcher是什么

3.1 LauncherActivity.java

小結

相關推薦

??Android Runtime (ART) 和 Dalvik??

??Android 從原始碼解讀 Apk 的安裝程序 ??

??Android Apk 的打包程序 ?? 只需兩幅圖


前言

作為多年的 Android 開發,寫了不少應用,但是一個App到底是怎么啟動起來的?你要說桌面點一下就啟動了,那也對,但是它的啟動程序呢?帶著這樣的疑問,咱們來一步步學習,

Android 啟動程序

一般在任何平臺上,都會逐步加載和執行以下組件:

  • Boot loader

  • U-boot (optional)

  • Kernel

  • Android

Android行程有以下順序:

  • Init

  • Zygote

  • System Server

  • Service Manager

  • Other Daemons and processes

  • Applications

具體情況如下圖,這兩幅圖結合起來比較有意思:

  • Boot ROM:當電源按下時,引導芯片代碼會從預定義的地方(固化在ROM)開始執行,加載引導程式BootLoader到RAM,然后執行,(這一步由"芯片廠商"負責設計和實作)

  • Boot loader:Bootloader開始執行,首先負責完成硬體的初始化,引導作業系統啟動,(這一步由"設備廠商"負責設計和實作)

  • Kernel:Linux 內核是 Android 的核心,負責行程創建、行程間通信、設備驅動程式、檔案系統管理等, Android 在主流內核上應用自定義補丁來支持 Android 運行所需的某些功能,如喚醒鎖等,內核可以作為未壓縮影像或壓縮影像加載,在加載時,它掛載根檔案系統(通常作為內核命令列引數傳遞)并啟動用戶空間中的第一個應用程式,(這一步則是Android內核開發程序中需要涉及的地方)

  • Android:Android系統以及各大Linux的發行版,他們的Linux內核部分啟動程序都是差不多的,他們之間最大的區別就在于init程式的不同,因為init程式決定了系統在啟動程序中,究竟會啟動哪些守護行程和服務,以及呈現出怎樣的一個用戶UI界面,

因此,init程式是分析Android啟動程序中最核心的程式,

  • init 和 init.rc:啟動內核時執行的第一個用戶空間應用程式是位于根檔案夾中的 init 可執行檔案,該行程決議稱為"init.rc"腳本的啟動腳本,這是用一種專為 android 設計的語言撰寫的,用于啟動所有必要的行程、守護程式和服務,以便 android 正常運行,它提供了各種型別的執行時間,例如 early-init、on-boot、on-post-fs 等,(用戶空間的鼻祖)

  • Demons and Services:init 行程創建了各種守護行程和行程,如 rild、vold、mediaserver、adb 等,每個行程負責自己的功能,這些行程的描述不在本文的范圍內,相反,我們將更多地討論"Zygote"行程,

  • Service Manager:Service Manager行程 管理系統中運行的所有Service,創建的每個服務都會在此行程中注冊自己,并且此資訊供其他行程/應用程式將來參考,

  • Zygote:Zygote 是啟動時創建的第一個 init 行程之一,術語"合子"是基于生物學"形成的初始細胞分裂產生后代",類似地,"zygote in android"初始化 Dalivik VM(ART) 和 fork 以創建多個實體來支持每個 android 行程,它有助于在 VM 實體之間使用共享代碼,從而減少記憶體占用和加載時間,非常適合嵌入式系統,Zygote 除了在服務器套接字上安裝偵聽器外,還預加載了稍后在 Android 應用程式中使用的類和資源,完成后,系統服務器啟動,

  • System Server:SystemServer 行程啟動 Android 中可用的所有服務,

本文咱們重點從 init 開始到應用啟動,

1、Zygote是什么

在Android系統里面,zygote是一個行程的名字,Android是基于Linux System的,當你的手機開機的時候,Linux的內核加載完成之后就會啟動一個叫"init"的行程,在Linux System里面,所有的行程都是由init行程fork出來的,我們的zygote行程也不例外,

Zygote是一個虛擬機行程,同時也是一個虛擬機實體的范訓器,每當系統要求執行一個Android應用程式,Zygote就會fork(分裂)出一個子行程來執行該應用程式,

1.1 app_main.cpp

frameworks/base/cmds/app_process/app_main.cpp

在Zygote啟動后就會執行 app_main.cpp,不管是C/c++/java,他們的入口就是 main(),就跟看到 Activity 咱們直接找 onCreate() 方法一樣,

1.1.1 main()

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 {
        ...
    }
}

注釋1:初始化 AppRuntime ,其實就是 AndroidRuntime(ART),

注釋2:設定zygote模式

注釋3:將引數傳遞給 ZygoteInit.main() 方法,

注釋4:啟動 ZygoteInit ,這里的 ZygoteInit 就是 zygote 行程的啟動類,這個下面講到,咱們先看看AndroidRuntime 的 start() 方法,

1.2 AndroidRuntime.cpp

frameworks/base/core/jni/AndroidRuntime.cpp

Android 虛擬機

1.2.1 start()

/*
 * 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 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    ...
}

注釋1:啟動VM(虛擬機)

注釋2:注冊安卓功能(JNI)

注釋3:便用JNI呼叫 Zygotelnit 的 main() 方法,這里的 Zygotelnit 是class檔案,也就是說從這里開始就進入java領域嘍,

JNI:連接 native(C/C++) 層 和 java 層的橋梁,

1.3 ZygoteInit.java

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

/**
 * zygote 行程的啟動類,
 */
public class ZygoteInit {
    ...
}

這是 Zygote 行程的入口點, 它創建 Zygote 服務,加載資源,并處理與準備分叉到應用程式的程序相關的其他任務,

1.3.1 main()

    @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();
            }
        }
        ...
    }

注釋1:預加載資源,

注釋2:創建Zygote 的 LocalServerSocket ,

注釋3:開始fork我們的SystemServer行程,

注釋4:zygote 永久回圈,

這里咱們看看 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;
    }

這里啟動了一個 system server ,下面咱們就看看他,

2、SystemServer

system server 也就是 SystemServer,SystemServer也是一個行程,包括ActivityTaskManagerService、ActivityManagerService、PackageManagerService、WindowManagerService等92種服務,

Android Framework里面兩大非常重要的行程:

  • SystemServer行程,

  • Zygote行程,

2.1 SystemServer.java

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

public final class SystemServer {
    ...
}

2.1.1 main()

    /**
     * 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();
        ...
    }    

下面 咱們看看 run () 里面都用什么?

2.1.2 run()

    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");
    }

注釋1:加載動態庫libandroid_service.so,

注釋2:創建系統背景關系,

注釋3:創建 SystemServiceManager,

注釋4:啟動服務(startBootstrapServices、startCoreServices、startOtherServices)

注釋5:Loop 永久回圈,

2.1.3 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);
    }

初始化系統背景關系物件mSystemContext,并設定默認的主題,mSystemContext實際上是一個Context(ContextImpl)物件,

呼叫ActivityThread.systemMain()的時候,會呼叫ActivityThread.attach(true),而在attach()里面,則創建了Application物件,并呼叫了Application.onCreate(),

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
    }

改動比較大的地方:

  • ActivityTaskManagerService(ATMS):負責管理除Activity和行程,包括生命周期和狀態切換,

  • ActivityManagerService(AMS):AMN的子類,負責管理三大組件(除Activity)和行程,包括生命周期和狀態切換,AMS因為要和ui互動,所以極其復雜,涉及window,

ActivityTaskManagerService:把 Activity 相關的內容從 ActivityManagerService 剝離出來而產生的,

PowerManagerService(PMS):電源管理服務,

PackageManagerService(PKMS):包管理服務,不叫PMS是為了和電源管理服務區分開,

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 startOtherServices()

    /**
     * 啟動其他服務,
     */
    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
    }

經過上面這些步驟,我們呼叫呼叫createSystemContext()創建系統背景關系的時候,也已經完成了mSystemContext和ActivityThread的創建,

ATMS、AMS、WMS、PKMS等物件已經創建好了,并且完成了成員變數初始化,

注意:這是系統行程開啟時的流程,在這之后,會開啟系統的 Launcher程式,完成系統界面的加載與顯示,

在Android的框架設計中,服務器端指的就是所有App共用的系統服務,比如我們這里提到的ATMS、AMS、WMS、PKMS等等,這些基礎的系統服務是被所有的App公用的,

3、Launcher是什么

在Android系統中,應用程式是由Launcher啟動起來的,其實,Launcher本身也是一個應用程式,其它的應用程式安裝后,就會Launcher的界面上出現一個相應的圖示,點擊這個圖示時,Launcher就會對應的應用程式啟動起來,

當然也可以在 其他應用 啟動應用,但是本質上都是呼叫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);
    }
}

小結

附送整圖一張

關于 startActivity() 有了解的可以直接看看,本篇內容太多我自己看著都鬧心,沒看過的關注走一波,詳細內容會在下一篇: ?? Android startActivity原始碼分析 ??中講解,

相關推薦

??Android Runtime (ART) 和 Dalvik??

??Android 從原始碼解讀 Apk 的安裝程序 ??

??Android Apk 的打包程序 ?? 只需兩幅圖

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/304532.html

標籤:其他

上一篇:Android之底部Dialog里面放EditText點擊布局頂上去效果

下一篇:iOS代上架,蘋果App代上架

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more