Launcher源码浅析-----Launcher启动时的资源加载

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值