我理解两者都是一样的。但我最近(派对有点晚)遇到了 android support annotations。同一个注释中的注释
但是,UI线程可能与主线程不同 在系统应用程序的情况下具有不同的多个视图的线程 线程
我无法理解这里的情景。有人可以解释一下吗?
编辑:我已经阅读了开发人员文档,这与此问题中链接的支持文档相矛盾。请停止发布两者是相同的。
答案 0 :(得分:66)
感谢您提出一个非常有趣的问题。
原来,UI和主线程不一定相同。但是,正如您引用的文档中所述,区别仅在某些系统应用程序(作为操作系统的一部分运行的应用程序)的上下文中很重要。因此,只要您不构建自定义ROM或为手机制造商定制Android,我就不用费心了。
答案很长:
首先,我发现了将@MainThread
和@UiThread
注释引入支持库的提交:
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
评论中不包含与该问题相关的任何信息,因为我没有与Tor Norbye(叹气)的沟通渠道,所以这里没有运气。
也许这些注释正在AOSP的源代码中使用,我们可以从那里获得一些见解?让我们在AOSP中搜索任何一个注释的用法:
aosp $ find ./ -name *.java | xargs perl -nle 'print "in file: ".$ARGV."; match: ".$& if m{(\@MainThread|\@UiThread)(?!Test).*}'
aosp $
以上命令会在AOSP中的任何.java文件中找到@MainThread
或@UiThread
的任何用法(后面跟不是其他Test
字符串)。它一无所获。这里也没有运气。
所以我们需要去寻找AOSP来源的提示。我猜我可以从Activity#runOnUiThread(Runnable)
方法开始:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
这里没什么特别有趣的。让我们看看如何初始化mUiThread
成员:
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 ...
}
累积奖金!最后两行(其他因为它们不相关而省略)是“主”和“ui”线程可能确实是不同线程的第一个迹象。
“ui”线程的概念在这一行mUiThread = Thread.currentThread();
中是清楚的 - “ui”线程是调用Activity#attach(<params>)
方法的线程。所以我们需要找出什么是“主”线程并比较两者。
看起来可以在ActivityThread
课程中找到下一个提示。这个类很意大利面,但我认为有趣的部分是ActivityThread
对象被实例化的地方。
只有两个地方:public static void main(String[])
和public static ActivityThread systemMain()
。
这些方法的来源:
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");
}
和
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;
}
请注意这些方法传递给attach(boolean)
的不同值。为了完整起见,我也将发布其来源:
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) {
}
});
}
为什么有两种方法可以初始化ActivityThread
(它将成为应用程序的“主要”线程)?
我认为会发生以下情况:
每当新应用程序启动时,public static void main(Strin[])
的{{1}}方法正在执行。正在初始化“主”线程,并且所有对ActivityThread
生命周期方法的调用都是从该确切的线程进行的。在Activity
方法(其源代码如上所示)中,系统将“ui”线程初始化为“this”线程,这也恰好是“主”线程。因此,对于所有实际情况,“主”线程和“ui”线程是相同的。
所有应用程序都是如此,但有一个例外。
首次启动Android框架时,它也作为应用程序运行,但此应用程序是特殊的(例如:具有特权访问权限)。这个“专业”的一部分是它需要一个特殊配置的“主”线程。由于它已经运行Activity#attach()
方法(就像任何其他应用程序一样),因此它的“main”和“ui”线程被设置为同一个线程。为了获得具有特殊特征的“主”线程,系统应用程序执行对public static void main(String[])
的静态调用并存储获得的引用。但它的“ui”线程没有被覆盖,因此“主”和“ui”线程最终不一样。
答案 1 :(得分:3)
简单回答是 你的主线程也是UI线程。
因此,主线程有时也称为 UI线程。如Android文档中进程和线程的主题部分所述。 Android Documentation
此外,UI工具包不是线程安全的,不能处理工作线程。我再次引用Android Documentation,因为它是Android的参考指南:
因此,Android的单线程模型只有两个规则:
1.不要阻止UI线程
2.不要从UI线程外部访问Android UI工具包
希望我回答你的要求。
答案 2 :(得分:2)
最简单的示例是:Android服务在主线程上运行,但服务没有用户界面。 您无法在此处调用主线程作为UI-Thread 。
的信用答案 3 :(得分:1)
在Android中,“主”应用程序线程有时称为UI线程。
从官方API引用主线程:
[...]您的应用程序与Android UI工具包中的组件(来自android.widget和android.view包的组件)交互的线程。因此,主线程有时也称为UI线程。
官方API找到here.
答案 4 :(得分:0)
启动应用程序时,系统会为应用程序创建一个执行线程,称为&#34; main。&#34;此线程非常重要,因为它负责将事件分派给适当的用户界面小部件,包括绘图事件。它也是您的应用程序与Android UI工具包中的组件(来自android.widget和android.view包的组件)交互的线程。因此,主线程有时也称为UI线程。
阅读本教程文档。 https://developer.android.com/guide/components/processes-and-threads.html#Threads