前言: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和菜单界面上的加载工作流程。