您的位置:首页 > 移动开发 > Android开发

Android主线程到底是什么(一)

2015-08-07 12:28 447 查看

Android主线程到底是什么(一)

很多做Android应用层开发的同学都知道UI主线程这个说法,且很多初学者在它身上吃了不少亏,常见的错误如“在主线程中做耗时的操作,比如下载大文件”。尽管我们已经非常熟悉这个所谓的“主线程”了,但是恐怕并没有多少人对它有一个清晰的认识。

作为一个Android应用层的开发人员,如果你想开发出性能更优,体验更好的app,那么了解它是必须的。所有的ui效果,事件处理都是在主线程执行的,我们怎么能不了解它呢?接下来开始我们的代码之旅吧。

在阅读下文之前,我希望大家能准备一份android系统源代码,我的是4.0的,然后是一个阅读源码的工具,我用的是source insight。

(一)、简单来说说主线程是如何启动的

我们的故事从哪里开始说起呢?首先考虑这么个问题:主线程是什么时候会启动?我们都知道当我们点击手机应用列表的图标后,应用就会启动,所以这里必然会有主线程的启动。手机的桌面、应用列表等其实也是一个app,这个应用叫做Launcher。

在系统源码目录下,有一个packages目录,这个目录下都是android系统app的源代码,Launcher的源码就在里面。

我们找到路径android4.0\packages\apps\Launcher2\src\com\android\launcher2\Launcher.java文件,如果你的系统源码和我版本不一致,那可能会有不一样的路径。一般来说packages下或其子目录下一定有个应用叫Launcher2的应用,有个Launcher.java的类,如果你是用source insight阅读源码的,那很方便就能根据类名搜索到。

Launcher.java即应用列表的Activity,其中有一个onClick函数(我省略掉非重要代码),为点击图标的点击事件,我们来看看点击图标时,程序做了啥?

public void onClick(View v) {
.....

Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {

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(intent, tag);

....
} else if (tag instanceof FolderInfo) {
....
} else if (v == mAllAppsButton) {
.....
}
}

boolean startActivitySafely(Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //注意这里的标志  是新的task
try {
startActivity(intent);
return true;
} catch (ActivityNotFoundException e) {
....
} catch (SecurityException e) {
......
}
return false;
}


原来Launcher内部启动应用也是通过startActivity。

我们先明确这么几个概念,这几个概念不了解的同学不需要去深究,而我也不可能去细说,因为光这两个机制足够写一本书。我们这里的重点是ui主线程。

android系统框架层有一个服务叫ActivityManagerService,它运行在系统的某个进程中。

android系统通过binder机制实现进程间通信,应用开发中常见的aidl就是对binder的一种封装。我们只要知道进程之间能通过这种机制远程调用其它进程的函数并传递数据。

我们继续,Launcher的startActivity运行在Launcher这个应用的进程中,它会通过binder机制告诉ActivityManagerSertvice我需要创建一个新的Activity了。ActivityManagerService中会根据intent中的flag Intent.FLAG_ACTIVITY_NEW_TASK判断出新的Activity是运行在新的进程中,于是会创建一个新的进程(新的进程最终是通过linux的fork函数拷贝zygote进程来实现的,学过linux系统开发的应该会很熟悉,这又是另一个故事了,我们这里不详细分析)。新的进程会加载ActivityThread.java类,并执行它的main函数, 主角终于出现了,自此,主线程从main函数开始执行了。

好了,知道了主线程是如何启动的,接下去才是这篇的重点。

(二)、主线程中到底做了什么

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

Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}

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

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}
public static void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}

public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

private Looper() {
mQueue = new MessageQueue();
....
}


Looper、MessageQueue有没有很熟悉,简单来说,android中的每个线程具备这样一种能力:创建自己独有的Looper对象,和一个独有的MessageQueue,MessageQueue是一个消息队列,里面存放有很多消息。Looper对象会通过loop()函数不断循环从MessageQueue中取出消息并处理,而当MessageQueue中没有消息时,loop()函数将阻塞在那,直到有消息为止。那么消息是谁塞进MessageQueue的呢,塞消息的方法有很多,但是android为我们封装了Handler来简化这种机制。

来看代码理解

prepare()中sThreadLocal为线程局部变量,即只当前线程可访问,如果当前线程还没有这个局部变量,就创建新的looper对象,我们来看Looper的构造函数,它内部创建了一个MessageQueue对象mQueue。

myLooper()获取前面创建的Looper对象

public static Looper myLooper() {
return sThreadLocal.get();
}


setMainLooper将创建的Looper对象设置为主Looper。

ActivityThread thread = new ActivityThread();

thread.attach(false);

下面是attach函数的关键代码 , ActivityManagerNative为ActivityManagerService的本地调用类,也就是说我们可以通过

ActivityManagerNative远程调用ActivityManagerService进程中的方法,以下代码其实就是远程调用ActivityManagerService的attachApplication(mAppThread)方法。

private void attach(boolean system) {
sThreadLocal.set(this);
mSystemThread = system;
if (!system) {
.....
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread); //注意里面传的参数
} catch (RemoteException ex) {
// Ignore
}
} else {
......
}
......
}


见代码android4.0\frameworks\base\services\java\com\android\server\am\ActivityManagerService.java的attachApplication(IApplicationThread thread)函数

public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
...
attachApplicationLocked(thread, callingPid);
...
}
}


下面这段代码很长,但是我们只需要关心我们需要关注的代码,也就是会和主线程交互的部分,

private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
....
thread.bindApplication(processName, appInfo, providers,
app.instrumentationClass, profileFile, profileFd, profileAutoStop,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
isRestrictedBackupMode || !normalMode, app.persistent,
mConfiguration, app.compat, getCommonServicesLocked(),
mCoreSettingsObserver.getCoreSettingsLocked());

.....
if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}

....
}


attachApplicationLocked中调用了IApplicationThread的bindApplication函数,IApplicationThread是什么?我们回到attach(boolean system)函数里,mgr.attachApplication(mAppThread);

final ApplicationThread mAppThread = new ApplicationThread();

其实IApplicationThread就是ApplicationThread的本地远程调用类,即这里在ActivityManagerService的进程中调用bindApplication函数,会通过binder机制最终调用ApplicationThread中的bindApplication函数。ApplicationThread就定义在ActivityThread.java类中,

public final void bindApplication(String processName,
ApplicationInfo appInfo, List<ProviderInfo> providers,
ComponentName instrumentationName, String profileFile,
ParcelFileDescriptor profileFd, boolean autoStopProfiler,
Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
int debugMode, boolean isRestrictedBackupMode, boolean persistent,
Configuration config, CompatibilityInfo compatInfo,
Map<String, IBinder> services, Bundle coreSettings) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
setCoreSettings(coreSettings);
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.debugMode = debugMode;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfileFile = profileFile;
data.initProfileFd = profileFd;
data.initAutoStopProfiler = false;
//这句是最关键的代码
queueOrSendMessage(H.BIND_APPLICATION, data);

}

private void queueOrSendMessage(int what, Object obj) {
queueOrSendMessage(what, obj, 0, 0);
}

private void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
synchronized (this) {
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+ ": " + arg1 + " / " + obj);
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
mH.sendMessage(msg);
}
}


看到这里是不是很熟悉,我们应用开发中最常用的Handler就是这么使用的,那么mH是不是个Handler呢

final H mH = new H();

private class H extends Handler {

….

}

果然如我们上面猜测的,它确实是个Handler,我们再来看看sendMessage内部实现

public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
//注意这句  这里将Message的target变量赋值为当前Handler对象
msg.target = this;
//这里就是向消息队列中添加一个Message
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}


即bindApplication中通过Handler的sendMessage向当前线程的MessageQueue塞进了一个消息,这个消息的what值为H.BIND_APPLICATION。

好了,回到我们的ActivityThread的main函数中。 thread.attach(false);的分析到此为止,我们总结一下分析到现在,main函数中完成了哪些工作

1.Looper.prepareMainLooper();创建了一个Looper对象和一个MessageQueue对象

2.thread.attach(false);向MessageQueue队列中加入了一个Message,这个Message的what为H.BIND_APPLICATION

接着来看 Looper.loop();依然是留下最关键的代码

public static void loop() {
Looper me = myLooper();
...
MessageQueue queue = me.mQueue;

...

while (true) {
Message msg = queue.next(); // might block
....
msg.target.dispatchMessage(msg);
....

msg.recycle();
}
}


获取Looper对象,再从Looper对象中拿到MessageQueue对象,调用它的next()函数拿队列中下一个消息,然后调用Message对象的target成员的dispatchMessage函数,target其实就是一个Handler对象,回到上面的Handler的sendMessageAtTime函数中,有一句 msg.target = this;所以这里在取出消息后,调用的就是这个消息对应的Handler的dispatchMessage函数。

我们这里处理的第一个消息就是前面thread.attach(false);调用后向messageQueue中添加的消息,所以主线程在loop()函数中会调用H的dispatchMessage函数,H继承了Handler,但是并没有重写dispatchMessage函数,所以调用的还是Handler本身的dispatchMessage函数。

public void dispatchMessage(Message msg) {

if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}


这里处理消息有三种方式,首先判断消息对象中的callback是不是空,不为空的话直接执行callback的run函数。为空的话在判断当前Handler有没有回调接口mCallback是不是为空,不为空就回调mCallback的handleMessage(msg)。最后才会调用Handler自身的 handleMessage(msg);


一般应用开发中最常用的处理方式就是最后一种。接下来我们就看H类的handleMessage(msg);

private class H extends Handler {
....
public static final int BIND_APPLICATION        = 110;

public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
switch (msg.what) {
...
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
break;
...
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
}
....}


之前向消息队列发消息时 what值为H.BIND_APPLICATION,所以我们直接找到H.BIND_APPLICATION,取出msg中app相关的信息,然后调用handleBindApplication(data);

private void handleBindApplication(AppBindData data) {
......
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
...

try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
....
}
}


handleBindApplication中调用makeApplication函数,该函数中根据app信息中Application类的路径加载并创建对象,然后调用callApplicationOnCreate函数。callApplicationOnCreate中调用Application对象的onCreate函数。原来我们在应用开发中常用的Application的onCreate就是这里调用的呀。

public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {

....
Application app = null;
String appClass = mApplicationInfo.className;

java.lang.ClassLoader cl = getClassLoader();
ContextImpl appContext = new ContextImpl();
appContext.init(this, null, mActivityThread);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
...

return app;
}

public void callApplicationOnCreate(Application app) {
app.onCreate();
}


到这里我们第一个消息处理就完成了,但是loop()函数却不会退出,注意它是一个死循环,处理完一个紧接着取下一个消息。那些我们熟悉的Activity的onCreate onResume等回调函数都是在loop()中处理消息时执行的,关于Activity的创建我们在后面的文章中再详细分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android android应用