前言:Launcher在刚启动时,会在UI主线程之外创建一个异步消息处理线程来执行相关资源的加载。资源的加载分为两部分,一部分是加载需要在Workspace和Hotseat中显示的资源(应用程序对应的快捷方式、文件夹、widget),另一部分是加载需要在菜单界面中显示的资源(所有应用、小部件)。
一. Launcher启动时资源加载机制
在分析Launcher资源加载之前,先来了解什么是异步消息处理线程。
先看看下面的Demo。
布局文件activity_main代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical" >
<Button
android:id="@+id/main_btn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/main_button"/>
<Button
android:id="@+id/child_btn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/child_button"/>
<EditText
android:id="@+id/main_show"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<ProgressBar
android:id="@+id/bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="100"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"/>
<EditText
android:id="@+id/chid_show"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
MainActivity代码如下:
package com.example.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
//隶属于主线程的Handler和Runnable
private MainHandler mMainHandler;
private MainRunnable mMainRun;
private HandlerThread mHandlerThread;
隶属于子线程的Handler和Runnable
private ChildHandler mChildHandler;
private ChildRunnable mChildRun;
private Button mMainButton, mChildButton;
private EditText mMainShow, mChildShow;
private int count = 0;
private int count1 = 0;
private ProgressBar mChildBar;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMainButton = (Button)findViewById(R.id.main_btn);
mChildButton = (Button)findViewById(R.id.child_btn);
mMainButton.setOnClickListener(this);
mChildButton.setOnClickListener(this);
mMainShow = (EditText)findViewById(R.id.main_show);
mChildShow = (EditText)findViewById(R.id.chid_show);
mChildBar = (ProgressBar)findViewById(R.id.bar);
//创建主线程的Handler
mMainHandler = new MainHandler();
mMainRun = new MainRunnable();
/*HandlerThread继承Thread,属于线程。
* 此处为创建HandlerThread子线程,child_thread为线程名字
*/
mHandlerThread = new HandlerThread("child_thread");
//在创建子线程的ChildHandler之前必须先启动该子线程
mHandlerThread.start();
//创建子线程的ChildHandler
mChildHandler = new ChildHandler(mHandlerThread.getLooper());
mChildRun = new ChildRunnable();
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
//隶属于主线程的Handler
public class MainHandler extends Handler
{
@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
super.handleMessage(msg);
Log.d("MainActivity", "thread-name-->" + Thread.currentThread().getName());
int count = msg.getData().getInt("count");
mMainShow.setText("" + count);
mMainHandler.post(mMainRun);
if (count >= 100)
{
mMainHandler.removeCallbacks(mMainRun);
}
}
}
//隶属于主线的Runnable
public class MainRunnable implements Runnable
{
@Override
public void run()
{
// TODO Auto-generated method stub
count += 1;
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("count", count);
msg.setData(data);
mMainHandler.sendMessage(msg);
try {
//Thread.sleep(1000);
} catch (Exception e)
{
e.printStackTrace();
// TODO: handle exception
}
}
}
//隶属于子线程的Handler
public class ChildHandler extends Handler
{
public ChildHandler(Looper loop)
{
super(loop);
}
@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
Log.d("MainActivity", "thread-name-->" + Thread.currentThread().getName());
super.handleMessage(msg);
int count1 = msg.getData().getInt("count1");
/*如果是给EditText设置显示计数值,将会报错
* 因为除了主线程(UI线程)外,其余新创建的子程序不能对
* Android的UI组件进行操作
*/
//mChildShow.setText("" + count1);
//注:此处对进度条的进度设置不属于UI组件的操作
mChildBar.setProgress(count1);
mChildHandler.post(mChildRun);
if (count1 >= 100)
{
mChildHandler.removeCallbacks(mChildRun);
}
}
}
//隶属于子程序的Runnable
private class ChildRunnable implements Runnable
{
@Override
public void run()
{
// TODO Auto-generated method stub
count1 += 1;
Message msg = new Message();
Bundle data = new Bundle();
data.putInt("count1", count1);
msg.setData(data);
mChildHandler.sendMessage(msg);
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
}
}
@Override
public void onClick(View v)
{
switch (v.getId()) {
case R.id.main_btn:
count = 0;
mMainHandler.post(mMainRun);
break;
case R.id.child_btn:
count1 = 0;
mChildBar.setVisibility(ProgressBar.VISIBLE);
mChildBar.setProgress(0);
mChildHandler.post(mChildRun);
break;
}
// TODO Auto-generated method stub
}
}
该Demo主要实现创建两个异步消息处理线程,其中一个是主线程(UI线程),另一个是子线程。在应用程序进程中,主线程主要负责处理Android相关的UI组件操作,而当我们需要处理一些耗时的操作时,就需要开辟一个新的子线程。当然,有的时候,我们也需要这个新开辟的子线程作为不同于UI线程的异步消息处理线程。
那么,要创建一个新的异步消息处理子线程,就需要创建HandlerThread对象了。HandlerThread继承Thread,属于线程类。当然,竟然是异步消息处理线程,就需要线程相应的Handler了。
Demo效果图如下:
从效果图中,可以知道,这个Demo是存在两个线程的执行流。主线程是通过异步消息,不断更新UI组件EditText的内容显示计数。子线程也是通过异步消息处理,显示进度条(注:进度条的进度添加不属于UI操作)。
小结:综上所述,异步消息处理线程必不可少的组成:线程(Thread) + Handler(线程内部可有多个) + Looper(一个线程对应一个)。
值得注意的是:当我们创建一个有别于主线程的异步消息处理线程时,是需要获取和绑定Looper(作为创建Handler对象时的构造函数的实参)。而在主线程中创建Handler实现异步消息处理时,并不需要获取和绑定Looper,这是因为Android源码中已经为主线程处理好了。
那么,在Launcher启动,就是创建一个异步消息处理子线程来处理相关资源的加载,而主线程负责在相关资源加载完成后,处理Launcher中UI组件的相关操作。
二.Launcher启动时资源加载流程分析
在Launcher启动时,首先会去加载LauncherApplication类,在该类的onCreate方法中,主要是对资源加载工作类LauncherModel、应用图标缓存类IconCache、Launcher的数据共享类LauncherProvider进行一些初始化和注册监听的工作。代码如下:
从上述代码中,可以知道,当Launcher对应的数据共享组件LauncherProvider中的数据操作有改变时,会执行mFavoritesObserver中的onChanger方法,这时候会调用mModel的startLoader方法进行数据更新加载。而这种数据改变情况,只有在Launcher启动加载完成后才会出现。
在LauncherApplication中,提供了setLauncher方法,在该方法中会Launcher对象作为实参传到LauncherModel的initialize方法中进行初始化工作。
我们知道,在Launcher启动时,Launcher类作为一个Activity类型,首先会执行它的onCreate方法,我们来看看Launcher.类中的部分代码,如下:
在Launcher的onCreate方法中,首先会调用LauncherApplication的setLauncher方法进行初始化。前面我们也介绍过setLauncher方法,在该方法中会继续调用LauncherModel的initialize方法进行初始化,然后返回Launcher对象。我们来看看LauncherModel中的initialize方法,代码如下:
在initialize方法中会对Callbacks回调接口对应的弱引用进行初始化工作。我们知道,通过Launcher的onCreate方法传过来的实参是Launcher本身,而Launcher实现Callbacks回调接口,所以在进行初始化后,mCallbacks弱引用指向的对象实际上就是Launcher对象本身。(值得注意得是,此处采用弱引用更有效地防止内存泄露)。
在Launcher的onCreate方法中,当LauncherModel初始化工作完成之后,会根据mRestoring来判断当前Launcher是否重启过,若没有重启过,则执行mModel.startLoader(this, true)进行加载工作。
我们到LauncherModel的startLoader方法中瞧瞧,代码如下:
在startLoader方法中,会新开辟一个HandlerThread异步消息处理线程进行Launcher相关的资源加载处理工作。代码中的LoaderTask为Runnable类型,部分代码如下:
public class LauncherModel extends BroadcastReceiver {
......
/*加载任务,基本上所有的Launcher资源加载都在这个Runnable中。
该任务在新开的异步消息处理线程中执行.
*/
private class LoaderTask implements Runnable {
......
//加载和连接Workspace
private void loadAndBindWorkspace()
{
......
}
//当前线程处理空闲状态
private void waitForIdle() {
......
}
public void run() {
......
keep_running: {
......
if (mStopped) {
break keep_running; //跳出整个方括号里的语句块
}
......
}
......
}
......
//加载的资源信息来之default_workspace.xml文件中的定义
private void loadWorkspace() {
......
}
//绑定Workspace,该方法在sWorkerThread线程中执行
private void bindWorkspace() {
......
}
private void loadAndBindAllApps() {
......
}
private void onlyBindAllApps() {
......
}
......
}
//批量加载所有应用程序
private void loadAllAppsByBatch() {
......
}
......
}
......
}
通过上面的代码可知,在LoaderTask类中,提供了很多加资源加载实现方法(诸如loadAndBindWorkspace、loadAndBindAllApps等),这些方法在LoaderTask类的run方法中被调用。所以在startLoader方法中,LoaderTask被post进sWorker(新开辟异步线程的Handler,非主线Handler)时,会执行LoaderTask中的run方法进行资源加载工作。部分代码如下:
public class LauncherModel extends BroadcastReceiver {
......
/*加载任务,基本上所有的Launcher资源加载都在这个Runnable中。
该任务在新开的异步消息处理线程中执行.
*/
private class LoaderTask implements Runnable {
......
public void run() {
/*mCallbacks在执行Launcher的onCreat方法时已初始化。
* 从mCallbacks弱引用中取出Launcher对象
*/
final Callbacks cbk = mCallbacks.get();
//决定是否先加载Workspace资源的布尔值
final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
keep_running: {
/*注:Process.THREAD_PRIORITY_DEFAULT比Process.THREAD_PRIORITY_BACKGROUND优先级高;
* 1. Process.THREAD_PRIORITY_DEFAULT为默认应用线程的优先级
* 2. Process.THREAD_PRIORITY_BACKGROUND为后台线程的优先级
*/
synchronized (mLock) {
if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
(mIsLaunching ? "DEFAULT" : "BACKGROUND"));
//根据Launcher是否正在加载判断设置加载线程的优先级
android.os.Process.setThreadPriority(mIsLaunching
? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
}
//loadWorkspaceFirst为true,则预先加载和绑定workspace资源
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
loadAndBindWorkspace();
} else {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
loadAndBindAllApps();
}
if (mStopped) {
break keep_running; //跳出整个方括号里的语句块
}
// Whew! Hard work done. Slow us down, and wait until the UI thread has
// settled down.
synchronized (mLock) {
if (mIsLaunching) {
if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
/*设置当前线程优先级为后台优先级,即时将当前的线程设置成为后台线程,
* 这样,当多个线程并发后很多无关紧要的线程分配的CPU时间将会减少,
* 有利于主线程(UI线程)的处理。
*/
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
waitForIdle();
// second step
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
loadAndBindAllApps();
} else {
if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
loadAndBindWorkspace();
}
// Restore the default thread priority after we are done loading items
synchronized (mLock) {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
}
......
}
......
}
......
}
通过上面的代码可知,在run方法中:
1.首先会从弱引用mCallbacks中取出Launcher对象cbk,然后根据cbk得到loadWorkspaceFirst布尔值,由于是第一次Launcher启动资源加载,所以loadWorkspaceFirst为true。
2.接着执行keep_running标记的代码块,在该代码快中,首先会根据mIsLaunching(判断Launcher是否正在加载工作)的值来判断设置加载线程的优先级。
3. 根据loadWorkspaceFirst值的判断执行loadAndBindWorkspace进行workspace上的资源加载工作,或者执行loadAndBindAllApps进行菜单上的资源加载工作。由于loadWorkspaceFirst为true,所以loadAndBindWorkspace得到执行。
4. 执行完loadAndBindWorkspace后,紧接着通过mStopped(判断Launcher是否已经停止加载工作)的值判断是否需要跳出keep_running代码块。由于是第一次执行Launcher加载工作,所以在执行完loadAndBindWorkspace之后,还需要对菜单界面上的资源进行加载,所以此处mStopped为false。
5. mStopped为false后,跳出keep_running代码块没有得到执行,接着会根据mIsLaunching的值判断是否设置加载线程的优先级,由于Launcher加载完workspace资源相关工作后,仍然需要加载菜单界面的资源,所以mIsLaunching为true,这时候,加载线程的优先级被设置为后台优先级,即加载线程被设置成为后台线程,这样做有利于主线程(UI线程)的处理,因为后面的菜单资源加载工作会涉及到很多主线程上的工作。
6. 当加载线程被设置为后台线程后,接着会执行waitForIdle方法,在该方法中,会唤醒Launcher的主线程(UI线程),然后加载线程会被挂起,释放同锁。
7. 执行完waitForIdle方法后,根据loadWorkspaceFirst的值执行loadAndBindAllApps进行菜单资源的加载工作,即加载和布局手机中存在的所有app。
8. Launcher的加载工作执行完后,加载线程的优先级又被设置为原来的等级,即默认应用线程的优先级。
综上所述,LaLoaderTask的run方法中,加载工作大体上主要分为两部分:Workspace上的资源加载工作(loadAndBindWorkspace)和菜单界面上的加载工作(loadAndBindAllApps);
最后来个加载的总体时序图,如下:
至此,Launcher启动时的加载工作就完成了,后面会继续详细分析Workspace和菜单界面上的加载工作流程。