最开始先从架构设计入手大致介绍一下模块的基本构成。Launcher 模块基本上是按照改进版的MVC架构进行设计的。
Model层主要负责数据的加载和处理,然后通过回调接口,把数据传递给Controller层,最后由Controller层通知View的显示与更新。其中,View层与Model层只有轻度的耦合,View层有些操作会直接通过Model层来更新数据。用户的交互事件主要在Controller层完成。
作为Controller层的 Launcher 就是程序的主入口,继承自Activity。本文主要关注各个层之间是如何建立联系的。其他内容后面再仔细分析。onCreate里将各个层之间的联系建立起来。
public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
......
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//建立Model层与Controller层的关联
LauncherAppState app = LauncherAppState.getInstance();
mModel = app.setLauncher(this);
......
setContentView(R.layout.launcher);
......
//建立View层与Controller层的关联
setupViews();
mDeviceProfile.layout(this); //根据默认配置参数对控件布局作调整
......
//加载桌面用于显示的数据
if (!mRestoring) {
if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(mWorkspace.getRestorePage());
}
}
......
}
}
一、Model层与Controller层建立关联
首先获取了LauncherAppState类的单例对象。该对象初始化时对桌面的默认配置数据进行了初始化和计算。如:桌面图标和文字大小等。这个后面的文章再仔细分析。
public class LauncherAppState {
......
LauncherModel setLauncher(Launcher launcher) {
getLauncherProvider().setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
new LauncherAccessibilityDelegate(launcher) : null;
return mModel;
}
......
}
1.1、Launcher与LauncherProvider通过接口类LauncherProviderChangeListener建立关联。
将Launcher作为接口实现类传递给LauncherProvider 的成员对象mListener,需要时就可以通过该成员对象向Launcher传递数据和状态更新。
public class LauncherProvider extends ContentProvider {
......
@Thunk LauncherProviderChangeListener mListener;
public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {
mListener = listener;
mOpenHelper.mListener = mListener;
}
...
}
public interface LauncherProviderChangeListener {
public void onLauncherProviderChange();
public void onSettingsChanged(String settings, boolean value);
public void onAppWidgetHostReset();
}
1.2、Launcher与LauncherModel通过接口Callbacks建立关联
将Launcher的实例对象作为接口实现类传递给LauncherModel,这里采用弱引用的形式持有Launcher的对象。这样在需要的时候,LauncherModel就可以通过mCallbacks调用相应接口方法,把数据传递给Launcher。
public class LauncherModel extends BroadcastReceiver
implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
......
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
// Disconnect any of the callbacks and drawables associated with ItemInfos on the
// workspace to prevent leaking Launcher activities on orientation change.
unbindItemInfosAndClearQueuedBindRunnables();
mCallbacks = new WeakReference(callbacks);
}
}
......
public interface Callbacks {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void startBinding();
public void bindItems(ArrayList shortcuts, int start, int end,
boolean forceAnimateIcons);
public void bindScreens(ArrayList orderedScreenIds);
public void bindAddScreens(ArrayList orderedScreenIds);
public void bindFolders(LongArrayMap folders);
public void finishBindingItems();
public void bindAppWidget(LauncherAppWidgetInfo info);
public void bindAllApplications(ArrayList apps);
public void bindAppsAdded(ArrayList newScreens,
ArrayList addNotAnimated,
ArrayList addAnimated,
ArrayList addedApps);
public void bindAppsUpdated(ArrayList apps);
public void bindShortcutsChanged(ArrayList updated,
ArrayList removed, UserHandleCompat user);
public void bindWidgetsRestored(ArrayList widgets);
public void bindRestoreItemsChange(HashSet updates);
public void bindWorkspaceComponentsRemoved(
HashSet packageNames, HashSet components,
UserHandleCompat user);
public void bindAppInfosRemoved(ArrayList appInfos);
public void notifyWidgetProvidersChanged();
public void bindWidgetsModel(WidgetsModel model);
public void bindSearchProviderChanged();
public boolean isAllAppsButtonRank(int rank);
public void onPageBoundSynchronously(int page);
public void dumpLogsToLocalData();
}
......
}
二、View层与Controller层建立关联
MVC框架中建议View通过XML语言来描述,Launcher 的主布局通过 launcher.xml 来描述。setContentView(R.layout.launcher) ,将xml布局进行解析和加载。
public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
......
private void setupViews() {
final DragController dragController = mDragController;
mLauncherView = findViewById(R.id.launcher);
mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
mWorkspace.setPageSwitchListener(this);
mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg);
// Setup the drag layer
mDragLayer.setup(this, dragController);
// Setup the hotseat
mHotseat = (Hotseat) findViewById(R.id.hotseat);
if (mHotseat != null) {
mHotseat.setOnLongClickListener(this);
}
// Setup the overview panel
setupOverviewPanel();
// Setup the workspace
mWorkspace.setHapticFeedbackEnabled(false);
mWorkspace.setOnLongClickListener(this);
mWorkspace.setup(dragController);
dragController.addDragListener(mWorkspace);
// Get the search/delete bar
mSearchDropTargetBar = (SearchDropTargetBar)
mDragLayer.findViewById(R.id.search_drop_target_bar);
// Setup Apps and Widgets
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
} else {
mAppsView.setSearchBarController(new DefaultAppSearchController());
}
// Setup the drag controller (drop targets have to be added in reverse order in priority)
dragController.setDragScoller(mWorkspace);
dragController.setScrollView(mDragLayer);
dragController.setMoveTarget(mWorkspace);
dragController.addDropTarget(mWorkspace);
if (mSearchDropTargetBar != null) {
mSearchDropTargetBar.setup(this, dragController);
mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
}
if (TestingUtils.MEMORY_DUMP_ENABLED) {
TestingUtils.addWeightWatcher(this);
}
}
}
三、Launcher通知LauncherModel 开始加载数据
首先判断一个变量mRestoring,该值用于确认Launcher启动时,是正常启动还是系统资源紧张导致Launcher被kill之后的重启。如果mRestoring为true,表示是被kill之后的重启,那么通过onSaveInstanceState保存下来的数据,恢复Launcher被kill之前的状态,不走这段重新加载数据的代码。如果mRestoring为false,则表示正常启动,那么进入数据加载的流程。
这里的DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE是常量开关,默认是false,这里不用管它,数据加载走的代码是 mModel.startLoader(mWorkspace.getRestorePage())。
数据加载完毕后,通过 LauncherModel.Callbacks 接口的回调方法把数据传递给Launcher,Launcher再把数据跟View绑定进行显示,这个过程比较复杂。后面会专门写一篇文章。
public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
......
@Override
protected void onCreate(Bundle savedInstanceState) {
......
//加载桌面用于显示的数据
if (!mRestoring) {
if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(mWorkspace.getRestorePage());
}
}
......
}
}
至此,Launcher的框架及联系就over了。如有问题,欢迎讨论。
下一篇将会详细讲解 初始化过程中 图标和字体大小等显示相关参数是如何初始化和处理的