• android4.0 launcher分析整理


    一、Android的应用程序的入口定义在AndroidManifest.xml文件中可以找出:
    <manifest 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.android.launcher"> 
     
    <original-package android:name="com.android.launcher2" /> 
    ... 
    <application  

      android:name="com.android.launcher2.LauncherApplication"
      android:label="@string/application_name"
      android:icon="@drawable/ic_launcher_home"
      android:hardwareAccelerated="@bool/config_hardwareAccelerated"     --硬加速
      android:largeHeap="@bool/config_largeHeap">      --运行时最小堆内存,避免内存out of memory错误的出现

        > 

        <activity 
            android:name="com.android.launcher2.Launcher" 
            ... 
            > 
            <intent-filter> 
                <action android:name="android.intent.action.MAIN" /> 
                <category android:name="android.intent.category.HOME" /> 
                <category android:name="android.intent.category.DEFAULT" /> 
                <category android:name="android.intent.category.MONKEY"/> 
            </intent-filter> 
        </activity> 
        ... 
    </application> 
    </manifest> 

    二、

    LauncherApplication----onCreate 应用入口

    1)获取屏幕的显示尺寸、来判断是否是大屏幕,同时得到它的屏幕密度全局变量。

    final int screenSize = getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
    sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE || screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE;
    sScreenDensity = getResources().getDisplayMetrics().density;

    2)建立应用图标缓存器;创建LauncherModel 对象,在launcher数据变更时操作数据库。

    mIconCache = new IconCache(this);
    mModel = new LauncherModel(this, mIconCache);

    //LauncherModel主要用于加载桌面的图标、插件和文件夹,同时LaucherModel是一个广播接收器,在程序包发生改变、区域、或者配置文件发生改变时,都会发送广播给LaucherModel,LaucherModel会根据不同的广播来做相应加载操作

    3)注册(Intent.ACTION_PACKAGE_ADDED  Intent.ACTION_PACKAGE_REMOVED  Intent.ACTION_PACKAGE_CHANGED)应用添加、删除、改变监听等;

      LauncherModel提供接收器对上面事件进行监听。

    IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
    filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    filter.addDataScheme("package");
    registerReceiver(mModel, filter);

    4)注册本地化配置变化监听,搜寻相关变化监听,外部存储上的应用变化监听。接收器同上

    filter = new IntentFilter();
    filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
    filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    filter.addAction(Intent.ACTION_LOCALE_CHANGED);
    filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
    registerReceiver(mModel, filter);
    filter = new IntentFilter();
    filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
    registerReceiver(mModel, filter);
    filter = new IntentFilter();
    filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
    registerReceiver(mModel, filter);

    5)注册对桌面favorites content provider 数据变化监听器,触发后执行onChang方法。

    ContentResolver resolver = getContentResolver();
    resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
    mFavoritesObserver);

     三、Launcher Activity:实现了点击、长按、触屏、LauncherModelAllAppViews接口。

    protected void onCreate(Bundle savedInstanceState) { 
        ... 
        mModel = app.setLauncher(this); 
        mIconCache = app.getIconCache(); 
        ... 
        mAppWidgetManager = AppWidgetManager.getInstance(this); 
        mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); 
        mAppWidgetHost.startListening(); 
        ... 
        //检查本地保存的配置是否需要更新 
        checkForLocaleChange(); 
        setContentView(R.layout.launcher); 
        //对UI控件进行初始化和配置 
        setupViews(); 
        //向用户展示指导的页面 
        showFirstRunWorkspaceCling(); 
        registerContentObservers(); 
        ... 
        if (!mRestoring) { 
        //为Launcher加载数据 
            mModel.startLoader(this, true); 
        } 
        ... 

    可以将Launcher.onCreate()所执行的操作大概分为七步:
    1、LauncherAppliaction.setLauncher()。
    2、AppWidgetHost.startListening(),对widget事件进行监听
    3、checkForLocaleChange(),检查更新本地保存的配置文件
    4、setupViews(),配置UI控件
    5、showFirstRunWorkspaceCling(),第一次启动时显示的指导画面
    6、registerContentObservers(),设置内容监听器
    7、LauncherModel.startLoader(),为Launcher加载Workspace和AllApps中的内容

    三、下面具体来看启动过程中到底做了些什么

    1、LauncherModel setLauncher(Launcher launcher) {
      mModel.initialize(launcher);
      return mModel;
    }

    /**
    * Set this as the current Launcher activity object for the loader.
    */
    public void initialize(Callbacks callbacks) {
      synchronized (mLock) {
        mCallbacks = new WeakReference<Callbacks>(callbacks);
      }
    }

    由于Launcher实现了Callback接口。在mModel中,将传入的Launcher对象向下转型为Callback赋值给mCallbacks变量。并在LauncherModel中获得了一个Callbacks的软引用。通过这一过程,将Launcher对象作为Callback与mModel进行绑定,当mModel后续进行操作时,Launcher可以通过回调得到结果。

    2、mAppWidgetHost.startListening()

    LauncherAppWidgetHost继承自AppWidgetHost,它的作用就是帮助Launcher管理AppWidget,并且能够捕获长按事件,使得应用可以正常的删除、添加AppWidget。通过调用mAppWidgetHost.startListening()方法,开启监听。

    3、checkForLocaleChange()

    private void checkForLocaleChange() {
      if (sLocaleConfiguration == null) {
        new AsyncTask<Void, Void, LocaleConfiguration>() {
        @Override
          protected LocaleConfiguration doInBackground(Void... unused) {
            LocaleConfiguration localeConfiguration = new LocaleConfiguration();
            readConfiguration(Launcher.this, localeConfiguration);
            return localeConfiguration;
        }

        @Override
        protected void onPostExecute(LocaleConfiguration result) {
          sLocaleConfiguration = result;
          checkForLocaleChange(); // recursive, but now with a locale configuration
        }
          }.execute();
        return;
        }

      final Configuration configuration = getResources().getConfiguration();

      final String previousLocale = sLocaleConfiguration.locale;
      final String locale = configuration.locale.toString();

      final int previousMcc = sLocaleConfiguration.mcc;
      final int mcc = configuration.mcc;

      final int previousMnc = sLocaleConfiguration.mnc;
      final int mnc = configuration.mnc;

      boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;

      if (localeChanged) {
        sLocaleConfiguration.locale = locale;
        sLocaleConfiguration.mcc = mcc;
        sLocaleConfiguration.mnc = mnc;

        mIconCache.flush();

        final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
        new Thread("WriteLocaleConfiguration") {
          @Override
          public void run() {
          writeConfiguration(Launcher.this, localeConfiguration);
          }
        }.start();
      }
    }

    在这个方法中,先是检查了本地文件的配置与当前设备的配置是否一致,如果不一致,则更新配置,并且清空IconCache,因为配置的改变可能会改变语言环境,所以需要清空IconCache中的内容重新加载。

    4、setupViews()  这个方法中简单的对所有的UI控件进行加载和配置

    I、mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
    mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
    mQsbDivider = (ImageView) findViewById(R.id.qsb_divider);
    mDockDivider = (ImageView) findViewById(R.id.dock_divider);

    // Setup the drag layer
    mDragLayer.setup(this, dragController);

    DragLayer继承自FrameLayout,是整个Launcher的根容器。当快捷图标或者AppWidget被拖拽时,事件的处理就在DragLayer进行操作的。

    public void setup(Launcher launcher, DragController controller) {
      mLauncher = launcher;
      mDragController = controller;
    }

    只是简单的做了赋值操作,使DragLayer持有Launcher和DragController对象的引用。DragController可以帮助其实现拖拽操作。

    II、Hotseat也是FrameLayout的直接子类,代表主屏幕下方的dock栏,可以放置4个快捷图标和一个进入AllApps的按钮。

    mHotseat = (Hotseat) findViewById(R.id.hotseat);
    if (mHotseat != null) {
      mHotseat.setup(this);
    }

    public void setup(Launcher launcher) {
      mLauncher = launcher;
      setOnKeyListener(new HotseatIconKeyEventListener());
    }

    mHotseat.setup()方法调用之后,Hotseat持有Launcher对象的引用,并且用HotseatIconKeyEvenListener对自身的按键进行监听,进入HotseatIconKeyEvenListener

    /**
    * A keyboard listener we set on all the hotseat buttons.
    */
    class HotseatIconKeyEventListener implements View.OnKeyListener {
      public boolean onKey(View v, int keyCode, KeyEvent event) {
        final Configuration configuration = v.getResources().getConfiguration();
        return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event, configuration.orientation);
      }
    }

    调用方法handleHotseatButtonKeyEvent()来处理相应的事件

    /**
    * Handles key events in the workspace hotseat (bottom of the screen).
    */
    static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e, int orientation) {
      final ViewGroup parent = (ViewGroup) v.getParent();
      final ViewGroup launcher = (ViewGroup) parent.getParent();
      final Workspace workspace = (Workspace) launcher.findViewById(R.id.workspace);
      final int buttonIndex = parent.indexOfChild(v);
      final int buttonCount = parent.getChildCount();
      final int pageIndex = workspace.getCurrentPage();

    // NOTE: currently we don't special case for the phone UI in different
    // orientations, even though the hotseat is on the side in landscape mode. This
    // is to ensure that accessibility consistency is maintained across rotations.

      final int action = e.getAction();
      final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
      boolean wasHandled = false;
      switch (keyCode) {
        case KeyEvent.KEYCODE_DPAD_LEFT:
          if (handleKeyEvent) {
          // Select the previous button, otherwise snap to the previous page
            if (buttonIndex > 0) {
              parent.getChildAt(buttonIndex - 1).requestFocus();
            } else {
              workspace.snapToPage(pageIndex - 1);
            }
          }
          wasHandled = true;
          break;
      case KeyEvent.KEYCODE_DPAD_RIGHT:
        if (handleKeyEvent) {
          // Select the next button, otherwise snap to the next page
          if (buttonIndex < (buttonCount - 1)) {
            parent.getChildAt(buttonIndex + 1).requestFocus();
          } else {
            workspace.snapToPage(pageIndex + 1);
            }
          }
          wasHandled = true;
          break;
      case KeyEvent.KEYCODE_DPAD_UP:
        if (handleKeyEvent) {
          // Select the first bubble text view in the current page of the workspace
          final CellLayout layout = (CellLayout) workspace.getChildAt(pageIndex);
          final CellLayoutChildren children = layout.getChildrenLayout();
          final View newIcon = getIconInDirection(layout, children, -1, 1);
          if (newIcon != null) {
            newIcon.requestFocus();
          } else {
            workspace.requestFocus();
           }
         }
        wasHandled = true;
        break;
      case KeyEvent.KEYCODE_DPAD_DOWN:
        // Do nothing
        wasHandled = true;
        break;
      default: break;
      }
      return wasHandled;
    }

    III、未完待续

  • 相关阅读:
    MySQL执行计划解读(转载)
    排序算法
    Linux下在防火墙中开启80端口、3306端口
    Android APN
    PB之——DropDownListBox 与 DropDownPictureListBox
    CSS总则。
    WIN7系统中设置默认登录用户
    Javascript日期比较
    myeclipse中UTF-8设置
    webview loadUrl() 弹出系统浏览器解决办法
  • 原文地址:https://www.cnblogs.com/lyz459/p/2696678.html
Copyright © 2020-2023  润新知