50 votes

La différence entre le thread principal et le thread de l'INTERFACE utilisateur

Je me suis fait comprendre que les deux sont les mêmes. Mais récemment, j'ai(un peu en retard à la fête) est venu à travers le soutien android annotations . La note dans le même lit

Toutefois, il est possible pour un thread d'INTERFACE utilisateur à être différent de celui du principal thread dans le cas d'applications avec de multiples points de vue sur différentes fils

Je ne suis pas en mesure de comprendre la scène ici. Quelqu'un peut m'expliquer la même chose?

EDIT: je suis passé par le développeur de la documentation et de la même, en contradiction avec le soutien doc liés à cette question. De bien vouloir arrêter de poster les deux sont les mêmes.

92voto

Vasiliy Points 8645

Merci pour exceptionnellement intéressante question.

S'avère, de l'INTERFACE et des threads Principaux ne sont pas nécessairement les mêmes. Toutefois, comme indiqué dans la documentation que vous avez cité, la distinction est importante seulement dans le contexte de certaines applications du système (les applications qui s'exécutent dans le cadre de l'OS). Par conséquent, tant que vous ne créez pas une ROM custom ou de travailler sur la personnalisation d'Android pour les fabricants de téléphones, je n'aurais pas pris la peine de faire une distinction à tous.

La réponse longue:

Tout d'abord j'ai trouvé le commit qui a introduit @MainThread et @UiThread des annotations dans la bibliothèque de prise en charge:

commit 774c065affaddf66d4bec1126183435f7c663ab0
Author: Tor Norbye <tnorbye@google.com>
Date:   Tue Mar 10 19:12:04 2015 -0700

    Add threading annotations

    These describe threading requirements for a given method,
    or threading promises made to a callback.

    Change-Id: I802d2415c5fa60bc687419bc2564762376a5b3ef

Le commentaire ne contient pas toutes les informations relatives à la question, et depuis je n'ai pas de canal de communication à Tor Norbye (soupir), pas de chance ici.

Peut-être que ces annotations sont utilisées dans le code source de PSBA et nous avons pu tirer quelques idées à partir de là? Nous allons chercher des usages de l'une des annotations dans PSBA:

aosp  $ find ./ -name *.java | xargs perl -nle 'print "in file: ".$ARGV."; match: ".$& if m{(\@MainThread|\@UiThread)(?!Test).*}'
aosp  $

la commande ci-dessus serait de trouver toute utilisation de la @MainThread ou @UiThread en tout .fichier java dans PSBA (pas suivie par d'autres Test chaîne de caractères). Il n'a rien trouvé. Pas de chance ici.

Nous avons donc besoin d'aller chercher des informations dans la source de PSBA. J'ai deviné que je pouvais commencer à partir de Activity#runOnUiThread(Runnable) méthode:

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

rien de particulièrement intéressant ici. Nous allons voir comment mUiThread membre est en cours d'initialisation:

final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
    attachBaseContext(context);

    mFragments.attachActivity(this, mContainer, null);

    mWindow = PolicyManager.makeNewWindow(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    mUiThread = Thread.currentThread();

    mMainThread = aThread;

    // ... more stuff here ...
}

Jackpot! Les deux dernières lignes (les autres omis parce qu'ils ne sont pas pertinents) sont la première indication que "principal" et "interface utilisateur" threads peut en effet être distincts de fils.

La notion de "ui" thread est clair à partir de cette ligne, mUiThread = Thread.currentThread(); - "l'interface utilisateur" fil est le fil sur lequel Activity#attach(<params>) méthode est appelée. Nous avons donc besoin de savoir ce que "principal" thread et de comparer les deux.

Il ressemble à la prochaine astuce pourrait être trouvée dans l' ActivityThread classe. Cette classe est tout à fait un spaghetti, mais je pense que les parties intéressantes sont où ActivityThread des objets sont instanciés.

Il existe seulement deux lieux: public static void main(String[]) et public static ActivityThread systemMain().

Les sources de ces méthodes:

public static void main(String[] args) {
    SamplingProfilerIntegration.start();

    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    CloseGuard.setEnabled(false);

    Environment.initForCurrentUser();

    // Set the reporter for event logging in libcore
    EventLogger.setReporter(new EventLoggingReporter());

    Security.addProvider(new AndroidKeyStoreProvider());

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

et:

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()) {
        HardwareRenderer.disable(true);
    } else {
        HardwareRenderer.enableForegroundTrimming();
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(true);
    return thread;
}

Remarque la valeur différente de ces méthodes passent attach(boolean). Pour être complet, je vais poster sa source ainsi:

private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        ViewRootImpl.addFirstDrawHandler(new Runnable() {
            @Override
            public void run() {
                ensureJitEnabled();
            }
        });
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                UserHandle.myUserId());
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            // Ignore
        }
        // Watch for getting close to heap limit.
        BinderInternal.addGcWatcher(new Runnable() {
            @Override public void run() {
                if (!mSomeActivitiesChanged) {
                    return;
                }
                Runtime runtime = Runtime.getRuntime();
                long dalvikMax = runtime.maxMemory();
                long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
                if (dalvikUsed > ((3*dalvikMax)/4)) {
                    if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
                            + " total=" + (runtime.totalMemory()/1024)
                            + " used=" + (dalvikUsed/1024));
                    mSomeActivitiesChanged = false;
                    try {
                        mgr.releaseSomeActivities(mAppThread);
                    } catch (RemoteException e) {
                    }
                }
            }
        });
    } else {
        // Don't set application object here -- if the system crashes,
        // we can't display an alert, we just want to die die die.
        android.ddm.DdmHandleAppName.setAppName("system_process",
                UserHandle.myUserId());
        try {
            mInstrumentation = new Instrumentation();
            ContextImpl context = ContextImpl.createAppContext(
                    this, getSystemContext().mPackageInfo);
            mInitialApplication = context.mPackageInfo.makeApplication(true, null);
            mInitialApplication.onCreate();
        } catch (Exception e) {
            throw new RuntimeException(
                    "Unable to instantiate Application():" + e.toString(), e);
        }
    }

    // add dropbox logging to libcore
    DropBox.setReporter(new DropBoxReporter());

    ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            synchronized (mResourcesManager) {
                // We need to apply this change to the resources
                // immediately, because upon returning the view
                // hierarchy will be informed about it.
                if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
                    // This actually changed the resources!  Tell
                    // everyone about it.
                    if (mPendingConfiguration == null ||
                            mPendingConfiguration.isOtherSeqNewer(newConfig)) {
                        mPendingConfiguration = newConfig;

                        sendMessage(H.CONFIGURATION_CHANGED, newConfig);
                    }
                }
            }
        }
        @Override
        public void onLowMemory() {
        }
        @Override
        public void onTrimMemory(int level) {
        }
    });
}

Pourquoi il y a deux moyens pour initialiser ActivityThread (qui va devenir le "principal" au fil de l'application)?

Je pense que les événements suivants se produisent:

Chaque fois qu'un nouveau démarrage de l'application, public static void main(Strin[]) méthode de ActivityThread est en cours d'exécution. Le "principal" thread est en cours d'initialisation là, et tous les appels à l' Activity du cycle de vie des méthodes sont fabriqués à partir de la même thread. En Activity#attach() méthode (de sa source a été montré ci-dessus), le système s'initialise "ui" thread "ce" thread, qui est aussi le "principal" thread. Par conséquent, pour tous les cas pratiques "principal" thread et "ui" thread sont les mêmes.

Cela est vrai pour toutes les applications, avec une exception.

Quand Android cadre est démarré pour la première fois, elle s'exécute comme une application, mais cette application est spécial (par exemple: dispose d'un accès privilégié). Une partie de cette "spécialité", c'est qu'il a besoin d'un spécialement configuré "principale" de fil. Depuis qu'il a déjà couru dans l' public static void main(String[]) méthode (tout comme n'importe quelle autre application), son "principal" et "interface utilisateur" threads sont créés à la même thread. Afin d'obtenir des "principaux" thread avec des caractéristiques spéciales, le système app effectue un appel statique d' public static ActivityThread systemMain() et les magasins de la référence. Mais son "interface utilisateur" thread n'est pas surchargé, donc "principal" et "interface utilisateur" fils finissent pas ne plus le même.

2voto

Abad Points 57

La Réponse Simple est Votre thread principal dans aussi le thread d'INTERFACE utilisateur.

En tant que tel, le thread principal est aussi parfois appelé le thread d'INTERFACE utilisateur. Comme indiqué dans Android documentation du fil partie des Processus et des Threads. Android Documentation

En outre, UI toolkit n'est pas thread-safe et ne doit pas être traitée avec des threads. Je suis, pour citer à nouveau Android Documentationcomme c'est le guide de référence pour Android:

Ainsi, il y a simplement deux règles Android seul modèle de thread:

1.Ne pas bloquer le thread d'INTERFACE utilisateur

2.Ne pas accéder à l'INTERFACE utilisateur Android toolkit de l'extérieur de la thread de l'INTERFACE utilisateur

Espère que je réponds à ce que vous demandez.

2voto

Enzokie Points 5630

L'exemple le plus simple est: Un Android fonctionne sur le thread Principal, mais le Service ne dispose pas d'une Interface Utilisateur. Vous ne pouvez pas appeler le thread Principal ici que l'UI Thread.

Les crédits à Sqounk

1voto

Dexter Points 459

Dans Android, la "principale" de l'application thread est parfois appelé le thread d'INTERFACE utilisateur.

Citation de officielle de l'API sur le Thread Principal:

[...] le thread dans lequel votre application interagit avec des composants de l'INTERFACE utilisateur Android toolkit (composants à partir de l'android.widget de et android.afficher les packages). En tant que tel, le thread principal est aussi parfois appelé le thread d'INTERFACE utilisateur.

Officielle de l'API trouvé ici.

0voto

Ramchandra Singh Points 369

Lorsqu'une application est lancée, le système crée un thread d'exécution pour l'application, appelé "principal". Ce fil est très important car il est en charge de la distribution des événements de l'utilisateur approprié widgets d'interface, y compris l'établissement des événements. C'est aussi le thread dans lequel votre application interagit avec des composants de l'INTERFACE utilisateur Android toolkit (composants à partir de l'android.widget de et android.afficher les packages). En tant que tel, le thread principal est aussi parfois appelé le thread d'INTERFACE utilisateur.

Lire ce tutoriel Documents. https://developer.android.com/guide/components/processes-and-threads.html#Threads

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X