目录结构:
allapps 目录:主要存放主菜单界面相关代码。
anim目录:存放动画相关,
badge目录:存放图标标识相关
compat目录:存放解决兼容性相关。
config目录:配置Launcher相关功能的宏开关
dragndrop目录:存放拖拽相关
graphics目录:存放处理图标大小、颜色、自适应
model目录:存放Launcher加载流程相关模块化
notification目录:存放通知相关
pageindicators目录:存放桌面页面指示器相关
popu目录:存放长按图标显示弹出框相关
provider目录:存放Launcher数据库相关
qsb目录:存放搜索功能相关
shortcuts目录:存放桌面所属应用某些功能的快捷图标相关
3、Launcher控制中心
3、1Android的状态保持机制
- 当一个activity启动另一个activity时,启动方activity的状态会被Android的应用程序管理服务,调整为停止状态,在启动方actiivity达到此过程的最终状态之前,android框架会调用activity中的onSaveInstanceState的方法,来保存当前状态。
proctected void onSaveInstanceState(Bundle outState){
//默认保存当前应用程序窗口中视图的层次
outSate.putBundle(WINDOW_HIERARCHY_TAG,mWindow.saveHierarchyState());
//若当前被结束的Activity中使用了Fragment,会调度各个Fragment将状态保持在outState中
Pacelable p=mFragments.saveAllState();
if(p!=null){
outState.putParcelabe(FRAGMENTS_TAG,p);
}
getApplication().dispatchActivitySaveInstanceState(this,outState);
void dispatchActivitySaveInstanceState(Activity activity,Bundle outState){
//获取Activity生命周期监控回调接口实例列表
Object[] callbacks=collectActivityLifecycleCallbacks();
if(callbacks!=null){
for(int i=0;i<callbacks.length;this){
((ActivityLifecycleCallbacks)callbacks[i].onActivitySaveInstanceState(activity,outState);
}
}
}
让Activity处于停止态
- 在应用程序中启动了另一个应用程序
- 按Back键退出应用程序
- 主动结束自己,采用调用finish方法
- 在长期没有使用的情况下,设备黑屏
仅有在第一条和第四条中,会调用onSaveInstanceState以保持状态,若是Activity被结束或者主动结束自己,则不会发生状态保存的过程
当启动的Activity被动、定义为Dialog风格的时候,情况会发生变化,在这种情况下,Activity并没有最处于停止状态,只是处于暂停状态,但在这种情况下,框架仍然启动了状态保存机制。
- 注解1
- 注解2
在Launcher被回收的时候有些Launcher的桌面组件正在处于等待被加载的状态,那么此类信息也需要保存的,提升了使用的连贯性。
- 注解3
- 注解4
应用程序菜单也是分标签分页显示的,因此还需要保存Launcher的应用程序菜单所在标签及页面索引:
3、2Android的状态恢复机制
onRestoreInstanceState的调用
使用private Activity performLaunchActivity(ActivtyClientRecord r, Intent customIntent)
- 完成ActivityClientRecord实例中关于包信息(packageInfo)的信息
- 完善ActivityClientRecord实例中被启动的Activity的组件信息。
- 如果被启动的Activity在AndroidManifest.xml配置中是一个别名,那么就需要获取真正的Activity的组件信息。
- 实例化Activity
- 创建应用程序
- 创建并获取应用程序基本信息,应用程序上下文Context、应用程序标题Title和当前的环境配置。
- 将Activity添加到窗口中。
activity.attch(appContext,this,getInstrumentation(),r.token,r.ident,app,r.intent,r.activityInfo,title,r.parent,r.embeddedID,r.lastNoConfigurationInstances,config,r.voiceInteractor);
acttch方法完成的任务;
①设置基础的上下文
②创建应用程序窗口
确定主线程和UI线程
- 设置Activity的主题,调用Activity的创建相关的生命周期回调
- 调用Activity的onStart生命周期,只有当Activity没有处于结束状态而被重新创建的时候,才会出现onStart方法的回调
- 执行状态恢复onRestanceState。
- 执行创建后处理
onRestoreInstanceState的调用条件
这个Activity必须处于未正常结束的状态,并且需要恢复的Activity执行了保存状态的操作。
会恢复的两种情况:因为处于后台而被Android系统回收释放内存。
配置改变的情况下,Android会先强行销毁当前的Activity,然后会重新创建这个Activity
Activity中的状态恢复
每个Activity的基类中实现的状态恢复主要是恢复Activity的层次
Launcher中的状态恢复
恢复自身每一页的状态:
for(int page: mSynchronousBoundPages){
mWorkspace.restoreInstanceStateForChild(page);
}
四、初始化桌面
launcher创建的总体流程
4、1初始化Launcher运行环境
需要对于运行环境进行初始化,这些信息将保存在LauncherAppState实例中,并且通过LauncherAppState建立与桌面数据库的联系,以便能及时响应数据库的变化
LauncherAppState.setApplicationContext(getApplicationCOntext());
LauncherAppState app=LauncherAppState.getInstance();
LauncherAPPstate.getLauncherProvider().setLauncherProviderChangeListener(this);
Launcher是一个实现了LauncherProviderChangeListener接口的类,它通过LauncherProvider的setLauncherProviderChangeListener方法将接口部分设置到LauncherProvider中,以实现Launcher与数据库的联通。
4、2生成Launcher的动态网格进行必要的准备
动态网格的决定因素是当前设备的长宽及密度等相关属性,
获取设备属性的代码如下所示:
Ponit smallestSize=new Point();
Point largestSize=new Point();
Display display=getWindowManager().getDefaultDisplay;
display.getCurrentSizeRanage(smallestSize,largestSize);
display.getRealSize(realSize);
DisplayMetrics dm=new DisplayMetrics();
display.getMetrics(dm);
- 1、获取当前屏幕的最大和最小尺寸
使用getCurrentSizeRange方法来返回这个范围
public void getCurrentSizeRange(Point outSmallestSize,Point outLargestSize)
该方法返回了应用程序可以操作的最大和最小尺寸范围
outSmallSize:最小尺寸,返回参数中的X将与Configuration.smallestScreenWidthDp一致,该方法有可能获取更小的值
outLargestSize:返回应用程序可以使用的最大空间,当状态栏等常驻窗口被隐藏可能返回更大值
- 获取当前屏幕的实际尺寸
使用getReakSuze方法来获取尺寸。 - 获取设备的有效宽高
使用getMetrics方法来获取尺寸,可以获取显示设备的尺寸和密度,依据当前设备的方向提供的,返回结果不包括状态栏、导航栏之类的常驻元素。
4、3生成动态网格
桌面有多少个格子,格子尺寸如何,信息保存在grid(DeviceProfile的对象)中
DeviceProfile grid=app.initDynamicGrid(this,Math.min(smallSize.x,smallSize.y
,Math.min(largestSize.x,largerstSize.y),realSize,x,realSize.y,dm.widthPixels,dm.heightPixels);
4、4获取共享数据对象以及建立Launcher与LauncherModel的联系
mSharedPrefs=getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),Context.MODE_PRIVATE);
mModel=app.setLauncher(this);
- 获取共享文件的接口示例
信息保存在叫prefs的文件中,通过上下文的getSharedPreferences来获取接口对象类
文件信息如下
①launcher.first_load_complete:布尔型字段,第一次加载完成
②launcher.first_run_activity_displayed:布尔型字段,第一次被使用
③launcher.intro_screen_dismissed:布尔型字段,介绍界面曾经被显示 - 建立Launcher与LauncherModel的连接
通过LauncherModel.Callbacks接口来实现,生成的数据通过该接口传递给Launcher来处理,通过LauncherAppState的setLauncher方法来完成。
4、5动态网格刷新图标缓存区
LauncherAppState咋实例化的过程中完成了Launcher用于存储应用程序图标的缓存区,此时可能没有得到精确初始化,所以要根据动态网格的属性刷新图标缓存区,IconCache的flushInvalidIcons方法将完成这个任务
mIconCache=app.getIconCache();
mIconCache.flushInvalidIcons(grid);
4、6初始化拖拽控制器
需要通过DragController来进行协调,在onCreate生命周期中会完成这个控制器的初始化工作
mDragController=new DragController(this);
- 控制器的初始化代码如下所示:
public DragController(Launcher launcher){
Resources r=launcher.getResources();
mLauncher=launcher;
//屏幕边缘曲剧可以使拖拽时左右移动工作空间。
mScollZone=r.getDimensionPixelSize(R.dimen.scroll_zone);
//用于跟踪屏幕滑动的速度
mVelocitTracker=VelocityTracker.obtain();
float desity=r.getDisplayMetrics().density;
mFlingToDeleteThresholdVelocity=(int)(r.getInteger(R.integer.config_flingToDeleteMinVelocity)*density);
}
4、7获取应用程序运行状态
Launcher中有一个stats.log文件,用于保存保曾经被启动的应用程序信息,Launcher在启动时需要Stats实例去获取这些信息,用于后续使用:mStats=new Stats(this);
- Stats类实例化时读取stats.log文件中的信息,保存在Stats实例中
public Stats(Launcher launcher){
mLauncher=launcher;
loadStats();
}
//loadStats方法中的相关代码
stats=new DataInputStream(mLauncher.openFileInput(STATS_FILE_NAME));
final int version=stats.readInt();//状态版本号
if(version==STATS_VERSION){
final String pkg=stats.readUTF():
final int count=stats.readInt();
mIntents.add(pkg);
mHistogram.add(count):
}
4、8创建启动WidgetHost(桌面小组件)
Launcher实现了LauncherAppWidgetHost和LauncherAppWidgetHostView
4、9检查配置是否有更新
当一些本地配置发生变化时,需要更新桌面元素,通过 ** checkForoacleChange** 方法完成这个工作
- 读取保存的Local信息
重要配置信息会保存在LocaleConfiguration类型的静态变量中,启动初期变量值是null值,Launcher将从launcher.preferences的配置文件中读取信息,后并重新发布到checkLocalConfiguration中
if(sLocaleConfiguration==null){
new AsyncTask<Void,Void,LoacleConfiguration>(){
@Override
protected LocaleConfiguration doInBackground(Void... unused){
LocaleConfiguration localeConfiguration=new LocaleConfiguration();
readConfiguration(Launcher.this,localeConfiguration);
return localeConfiguration;
}
@Override
protected void onPostExecute(LocalConfiguration result){
sLocaleConfiguration=result;//设置结果
checkForLocaleChange();//重新发布
}
}
}
- 从文件中读取历史配置信息
检查配置是否有改变的关键是读取历史配置,保存在launcher.preferences文件中。
private static void readConfiguration(Context context,LocaleConfiguration configuration){
DataInputStream in=null;
try{
in =new DataInputStream(context.openFileInput(PREFERENCES));
configuration.locale=in.readUTF();//local信息保存在UTF格式的字符串
configuration.mcc=in.readInt();//mcc信息保存为整型
configuration.mnc=in.readInt();
}catch(FileNotFoundException e){}
catch(IOException e){}
finally{
if(in!=null){
try{
in.close();
}catch(IOExecption e){}
}
}
}
- 更新配置
checkForLoacleChange继续后续的更新流程。
//获取当前设备配置信息
final Configuration configuration=getResources().getConfiguratio;
//获取历史和当前的local信息
final String previousLocale=sLocaleConfiguration.locale;
final String locale=configuration.locale.toString();
final int previusMcc=sLocaleConfiguration.mcc;
final int mc=configurtion.mcc;
//获取历史和当前的mnc信息
final int previousMnc=sLocaleConfiguration.mnc;
final int mnc=configruation.mnc;
//比较是否有变化
boolean localeChanged=!locale.equals(previousLocale)||mcc!=previousMcc||mnc!=previousMnc;
- 更新Launcher本地配置文件以及刷新图片缓存
当前设备与本地设备不同时,会完成配置文件和刷新图片缓存的任务,也会在此时决定桌面图标等资源的使用
//如果发生了改变
if(loacleChanged){
//更新本地配置
sLocaleConfiguration.locale=locale;
sLocaleConfiguration.mcc=mcc;
sLoacleConfiguration.mnc=mnc;
//刷新图片缓存区
mIconCache.flush();
//更新Launcher配置文件
final LocaleConfigurationlocaleConfiguration=sLocaleConfiguration;
new AsyncTask<Void,Void,Void>(){
public Void doInBackground(Void ... args){
writeConfiguration(Launcher.this,localeConfiguration);//更新Launcher配置文件
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POLL_EXECUTOR,(Void)null);
}
//flush方法刷新图片缓存区。writeConfiguration方法如下:
private static void writeConfiguartion(Context context,LocalConfiguration configuration){
DataOutputStream out=null;
try{
out=new DataOututStream(context.openFileOutput(PREFERENCES,MODE_PRIVATE));
out.writeUTF(configuration.locale);
out.writeInt(configuration.mcc);
out.writeInt(configuration.mnc);
}
catch(FileNotFoundException e){
context.getFileStreamPath(PREFERENCES).delete();
}finally{
if(out!=null){
try{
out.close();}
catch(IOException e){}
}
}
}
4、10装载布局并实例化控件
首先要将Activity填充到窗口中去:
setContentView(R.layout.launcher);
setupViews();
- setupViews()流程如下
- 先获取UI中的控件实例再对这些控件进行设置
//给Workspace设置页面监听器
mWorkspace.setPageSwitchListener(this);
//设置全屏
mLauncherView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI+FLAG_LAYOUT_HIDE_NAVIGATION);
//获取桌面的背景图片
mWorkspaceBackgroundDrawabe=getResources().getDrawable(R.drawable.wirkspace_bg);
//设置拖拽层
mDragLayer.setup(this,dragController);
- 对HotSeat的一些设置,建立HotSeat与Launcher之间的联系及设置其长按事件的监听
- 对Launcher预览模式下的控件以及预览模式面板进行必要的设置
mOvervierPanel=(ViewGroup)findViewById(R.id.overview_panel);
//对于桌面小部件按钮进行设置,监听点击及触摸事件
View widgetButton=findViewById(R.id.widget_button);
widgetButton.setOnClickListenr(new onClickListener){
@Override
public void onClick(View arg0){}
});
widgetButton.setOnTouchListener(getHapticFeedbackTouchLisener()):
View wallpaperButton=findViewById(R.id.wallpaper_button);
wallpaperButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0){}
});
wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
View settingsButton=findViewById(R.id.settings_button);
if(hasSettings()){
settingsButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0){}
});
settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
}else{
settingsButton.setVisibility(View.GONE);
}
//将预览模式面板设置成透明
mOverviewPanel.setAlpha(of);
- 还需要对Workspace以及搜索/投放条进行一些设置
//Workspace无操作反馈
mWorkspace.setHapticFeedbackEnabled(false);
//设置Workspace的长按监听器
mWorkspce.setOnLongClickListener(this);
//接收拖拽器的控制
mWorlspace.setup(dragController);
dragController.addDragListener(mWorkspace);
//获取搜索/投放条实例
mSearchDropTargetBar=(SerchDropTargetBar)mDraagLayer.findViewById(R.id.search_drop_target_bar);
- 对于应用程序菜单进行一些必要的设置:
//设置应用程序菜单与Launcher以及拖拽控制器之间的关联
mAppCustomizeTabHost=(AppsCustomizeTabHost)findViewById(R.id.apps_customize_pane);
mAppCustomizeContent=(AppsCustomizeTabHost)findViewById(R.id.apps_customize_pane_content);
mAppsCustomizeContent.setup(this,dragController);
4、11根据动态网格属性部署桌面
该功能的实现由DeviceProfile中的layout方法来实现
//获取搜索条控制的实例
View searchBar=launcher.getSearchBar();
//获取其中的当前布局数据
lp=(FrameLayout.LayoutParams) searchBar.getLayoutParams();
//竖屏:将搜索条调整到容器的左上角并且设置宽度和高度
if(hashVerticalBarLayout){
lp.gravity=Gravity.TOP|Gravity.LEFT;
lp.width=searchBarSpaceHeightPx;
lp.height=LayoutParams.WRAP_CONTENT;
LinearLayout targets=(LinearLayout)searchBar.findViewById(R.id.drag_target_bar);
targets.setOrientation(LinearLayout.VERTICAL);
}
//容器顶端并且横向居中
else{
lp.gravity=Gravity.TOP|Gravity.CENTER_HORIZONIAL;
lp.width=searchBarSpaceWidthPx;
lp.height=searchBarSpaceHeightPx;
}
//使布局生效
searchBar.setLayoutParams(lp);
4、12Launcher的状态恢复(创建时)
在创建阶段的恢复主要通过onCreate方法中的saveInstanceState的Bundle实现
restoreState(savedInstanceState);
- 注解1
final long pendingAddContainer=savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER,-1);
final long pendingAddScreen=savedState.getLong(RUNTIME_STATE_PENDING_ADD_SCREEN,-1);
if(pendingAddContainer!=ItemInfo.NO_ID&&pendingAddScreen>-1){
mPendingAddInfo.container=pendingAddContainer;
mPendingAddInfo.screenId=pendingAddScreen;
mPendingAddInfo.cellX=savedState.getInt(PUNTIME_STATE_PENDING_ADD_CELL_X);
mPendingAddInfo.cellY=savedState.getInt(PUNTIME_STATE_PENDING_ADD_CELL_Y);
mPendingAddInfo.spanX=savedState.getInt(PUNTIME_STATE_PENDING_ADD_SPAN_X);
mPendingAddInfo.spanY=savedState.getInt(PUNTIME_STATE_PENDING_ADD_SPAN_Y);
mPendingAddInfo.cellX=savedState.getInt(PUNTIME_STATE_PENDING_ADD_CELL_X);
mPendingAddWidgetInfo=savedState.getInt(PUNTIME_STATE_PENDING_ADD_WIDGGET_INFO);
mPendingAddWidgetId=savedState.getInt(PUNTIME_STATE_PENDING_ADD_WIDGET_ID);
setWaitingForResult(true);
mRestoring=true;//标志等待后续使用
}
- 恢复重命名文件夹信息
boolean renameFolder=savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME,false);
if(renameFolder){//先判断是否存在需要重命名的文件夹
long id=saveState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
mFolderInfo=mModel.getFolderById(this,sFolders,id);
mrestoring=true;
}
- 恢复应用程序菜单的Tab状态:
if(mAppsCustomizeTabHost!=null){
String curTab=savedState.getString("apps_customize_currentTab");
if(curTab!=null){
mAppsCustomizeTabHost.getContentTypeImmediate(
mAppCustomizeTabHost.getContentTypeForTabTag(curTab)
);
mAppsCUstomizeContent.loadAssociatedPages(
mAppCustomizeContent.getCurrentPage()
);
int currentIndex=savedState.getInt("apps_customize_currentIndex");
mAppsCustomizeContent.restorePageForIndex(currentIndex);
}
}
4、13监听Widget配置变化
当Launcher的数据库第一次被创建后,需要对AppWidgetHost进行初始化,更新配置,数据库通过CONTENT_APPWIDGET_RESER_URI发出更新通知
private void registerContentObservers(){
ContentResolver resolver=getContentResolver();
resolver.registerContentOvserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,true,mWidgetObserver);
}
4、14加载桌面数据
当前Launcher没有执行过数据恢复工作,那么需要按照不同的场景对于桌面进行必须的刷新,Launcher3被设定为只能重新加载被恢复的桌面页,如果每一次都对页面进行重新加载,线程压力很大,调用LauncherModel的startLoader方法进行加载。
4、15更新必要的图标:
第三方实现自己的搜索应用的接口,updateGlobalIcons完成这个动作,寻找系统中应用市场相关应用,并显示市场图标
4、16解锁Launcher的方向设定
在AndroidManifest.xml文件规定了Launcher 方向,为了可以有任意方向的选择,需要调用unlockScreenOrientation方法解除限制
public void unlockScreenOrientation(boolean immediate){
if(isRotationRnabled()){
if(immediate){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIRFIED);
}
else{
mHandler.postDelated(new Runnable(){
public void run(){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
},mRestoreScreenOrientationDelay);
}
}
}
//isRotationEnabled方法:
public boolean isRotationEnabled(){
boolean enableRotation=sForceEnableRotation||getResources().getBoolean(R.bool.allow_rotation);
return enableRotation;
}
是否允许选择的决定条件
4、17首次使用时显示桌面的帮助界面
if(shouldShowIntroScreen()){
showIntroScreen();
}else{
showFirstRunActivity;
showFirstRunClings();
}
- 判断是否需要界面介绍
private boolean shouldShowIntroScreen(){
return hasDismissableIntroScreen()&&!mSharedPrefs.getBoolean(INTRO_SCREEN_DISMISSED,false);
}
若此界面曾经被显示过,则prefs文件中的INTRO_SCREEN_DISMISSED将设置成true
- 显示介绍界面
protected void showIntroScreen(){
View introScreen=getIntroScreen();//获取介绍屏幕的实例
changeWallpaperVisiblity(false);//改变壁纸的可见性
if(introScreen!=null){
mDragLayer.showOverlayView(introScreen);
}
} - 首次使用Launcher显示的帮助界面
五、Home键:Launcher的另一种启动方式
配置MAIN和HOME在AndroidManifest.xml中才会将Activity当做是Home
5、2Home的框架行为
5、2、1导航栏的实现(Navigation Bar)
布局实现名为navigation_bar.xml
5、2、2窗口管理器的处理
处理时调用调用launcherFromHotKey方法处理短按事件,用handleLongPressOnHome方法处理长按事件。
- 处理短按事件的方法
void startDockOrHome(){
awakenDreams();
Intent dock=createHomeDockIntent();
if(dock!=null){
try{
mConetext.startActivityAsUser(dock,UserHandle.CURRENT);
return;
}catch(ActivityNotFoundException e){
}
}
mContext.startActivityAsUser(mHomeIntent,UserHandle.CURRENT);
//mHomeIntent的定义
mHomeIntent=new Intent(Intent.ACTION_MAIN,null);
mHomeIntent.addCategory(Intent.GATEGORY_HOME);
mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
- 处理长按事件的方法
此功能实现中不同的厂商可能做出不同的定制
Home长按分成了3种行为:什么都不做、显示最近运行的应用程序列表界面以及搜索接入,该行为由一个名为mLongPressOnHomeBehavior的变量来决定,该变量又由框架的配置项config_longPressOnHomeBehavior来决定:
5、2、3Launcher的相关处理
使用onNewIntent方法进行处理Home键的Intent
- 注解1
//关闭所有系统面板并且取消一些等待操作
closeSystemDialogs();
//获取状态:判断Launcher当前是否处于前台
final boolean alreadyOnHome=mHasFocous&&((intent.getFlags()&Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)!=Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
//获取信息:获取当前被打开的文件夹如果没有openFolder为null;
Folder openFolder=mWorkspace.getOpenFolder();
//取消调整AppWidget尺寸操作
mWorkspace.exitWidgetResizeMode();
- 注解2
//获取状态:是否需要移动到默认桌面页
boolean moveToDefaultScreen=mLauncherCallbacks!=null? mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent():true;
//满足Launcher正在显示、Launcher的桌面正在显示状态、桌面没有手势操作、
//没有被打开的文件夹以及需要移动到默认桌面页的条件下Launcher将恢复到默认的桌面页
if(alreadyHome&&mState=State.WORKSPACE&& !mWorkspace.isTouchActive()&& openFolder== null&& moveToDefaultScreen){
mWorkspace.moveToDefaultScreen(true);
}
//关闭文件夹
closeFolder();
//退出拖拽状态
exirSpringLoadedDragMode();
- 注解3
//如果输入法正在显示,那么就需要将输入法进行隐藏
final View v=getWindow.peelDecorView();
if(v!=null&&v.getWindowToken()!=null){
InputMethodManager imm=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
//重置应用程序菜单
if(!alreadyOnHome&&mAppsCustomizeTabHost!=null){
mAppsCustomizeTabHost.reset();
}
//通知应用程序Home键被按下
if(mLauncherCallbacks!=null){
mLauncherCallbacks.onHomeIntent();
}
}
六、Launcher的显示
6、1关于Activity的onResume
只有经历从未显示的状态到显示的状态,必须经历过resume阶段,这样才真正处于运行态
在以下方法之后被调用
onRestoreInstanceState(Bundle)v:Activity状态恢复回调函数
onStart:Activity开始启动
onPause:Activity处于暂停状态
- 在进入onResume状态不意味着Activity对于用户可见,因为有一些窗口可以将Activity覆盖,使用onWindowFocusChanged(boolean)判断是否可以用户可见
6、2Activity的onResume方法实现
6、3Launcher的onResume实现
- 注解1
恢复Launcher的UI状态
//mOnResumeState指示Launcher之前的显示状态
if(mOnResumeState==State.WORKSPACE){
//无动画立即显示桌面
showWorkspace(false);
}else if(mOnResumeState ==State.APPS_CUSTOMIZE){
//无动画立即显示应用程序菜单,并且设置正确的标签
showAllApps(false,mAppsCustomizeContent.getContentType(),false);
}
mOnResumeState=State.NONE;
//根据显示的状态调节背景
setWorkspaceBackground(mState==State.WORkSPACE);
- 注解2
if(mRestoring||mOnResumeNeedsLoad){
//将桌面设置成正在加载状态
setWorkspaceLoading(true);
//执行Launcher数据加载
mModel.startLoader(true,PagedView.INVALID_RESTORE_PAGE);
mRestoring=false;//用于标志当前Launcher是否正在恢复
mOnResumeNeedLoad=false;//是否需要在Launcher在被显示出来时加载一些东西
}
- 注解3
- 注解4
//如果从Launcher中启动了某个应用程序,被点击的快捷方式会处于被选中状态
//在Launcher显示的时候需要期初这个选中状态
if(mWaitingForResume!=null){
mWaitingForResume.setStayPressed(false);
}
//重新填充桌面小部件
getWorkspace().reinflateWidgetsIfNecessary();
//执行还没有被执行的快捷方式安装任何任务并且清理任务列表
InstallShrotcutReceiver.disableAndFlushInstallQueue(this);
//显示第三方定制页面
if(mWorkspace.getCustomContentCallbacks()!=null){
if(mWorkspace.isOnOrMovingToCustomContent()){
mWorksapce.getCustomContentCallbacks().onshow(true);
}
}
- 注解5
通知关注它的组件
//通知桌面显示
mWorkspace.onResume();
//应用程序安装启动
PackageInstallerCompat.getInstance(this).onResume();
七、Launcher的状态
7、1Acitivty的暂停状态
7、1、1Activity暂停状态发生的场景
该状态发生在Activity即将消失的时候,在此状态下,Activity并没有完全消失,只是不在拥有用户焦点,使用onPause回调方法来通知应用程序
- 当启动一个Activity导致当前的Activity被覆盖时
在某一个Activity被启动时,服务端需要将用户的Intent等信息发送到应用程序端进行处理:
schduleLaunchActiviy-》ActivityClientRecord-——>处于ActivityThread中的main方法
在完成scheduleLaunchActivity方法后,由运行在应用程序进程中的Handler对所有来自ActivityManagerService的消息进行分发,使用了ActivityThread中的handleLaunchActivity进行处理
- 当重新启动Activity时
需要先将这个Activity销毁再从新启动,该过程由ActivityThread的handleRelaunchActivity方法进行处理 - 当Activity通过finish方法而被结束时
先通过ActivityThread的scedulePauseActivity方法调用被结束的Activity的暂停流程
①
②当结束消息到应用程序的进程后
7、1、2Activity中暂停的实现
需要重写onPause接口就必须调用基类的onPause方法
使用了performPause方法:
暂停阶段完成的任务:
7、2Activity的启动状态
当Activity被创建出来以后,或者被重新启动之后。Android框架会马上调用这个Activity的启动生命周期回调函数
7、2、2Launcher的onStart实现
7、3Activity的停止状态
处于停止状态的Activity的三种结果:
- 应用程序长期处于运行状态而又被长期覆盖。
- 有可能走向销毁onDestroy状态
- 有可能被重新启动,对Launcher来说,启动的应用程序结束而回到Launcher
7、3、1Activity中onStop的实现
7、3、2Launcher的onStop实现
典型场景是在Launcher中启动一个应用程序
@Override
protected void onStop(){
super.onStop();
FirstFrameAnimatorHelper.setIsVisible(false);
}
7、4Launcher的销毁状态
- 提供了对外调度Activity销毁的接口sceduleDestroyActivity。之后处理器调用handleDestoryActivity方法处理来自服务器的调度
- 注解2
保证Activity在销毁之前处于停止状态,如果木有则需要调度Activity的停止生命周期回调
- 注解3
额外操作:
//重置调用基类已方法已调用标志
r.activity.mCalled=false;
//执行Activity的onDestroy生命周期
mInstrumentation.callActivityOnDestroy(r.activity);
//关闭与Activiy相关的系统面板
if(r.window!=null){
r.window.closeAllPanels();
}
- 销毁状态的处理
- launcher的销毁流程
- 注解1
移除所有需要在Launcher上执行的任务
mHander.removeMessages)ADVNCE_MSG);
mHander.removeMessages(0);
mWorkspace.removeCallbacks(mBuildLayerRunnable);
- 注解2
处理Launcher与LauncherModel之间的关系,以及终止LauncherModel的工作:
LauncherAppState app=(LauncherAppState.getInstance());
if(mModel.isCurrentCallbacks(this)){
//停止LauncherModel的工作
mModel.stopLoader();
//切断Launcher与LauncherModel之间的联系
app.setLauncher(null);
}
- 注解3
处理Launcher上运行的桌面小部件容器的 相关事宜
- 注解4 处理 Launcher中管理的视图的一些事宜:
八、Launcher的启动应用及等待结果
8、1在Launcher中启动应用
8、1、1安全启动应用程序的通用方法:
startActivitySafely,此方法可以保证启动应用程序的安全性:
boolean startActivitySafely(View v,Intent intent,Object tag){
boolean success=false;
//需要保证在安全模式下
if(mIsSafeModeEnabled&& !Utilies.isSystemApp(this,intent){
return false;
}
try{
success=startActivity(v,intent,tag);
}
catch(ActivityNotFoundException e){
}
return success;
}
8、1、2真正启动应用程序的方法
- 注解1
首先要对启动方式做一些定义:
//Launcher启动Activity通常使用新任务方式
intent.addFlags(Intent.FLAG_AcTIVITY_NEW_TASK);
//确定启动Activity的过场动画来源
//如果Intent中包含了INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION标志,则使用Launcher默认动画
boolean useLaunchAnimation=(v!=null)&&!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
LauncherAppsCompat launcherApps=LauncherAppsCompat.getInstance(this);
UserManagerCompat userManager=UserManagerCompat.getInstance(this);
//获取使用当前使用设备的用户
UserHandleCompat user=null;
if(intent.hasExtra(AppInfo.ExTRA_PROFILE)){
long serialNumber=intent.getLongExtra(AppInfo.EXTRA_PROFILE,-1);
user =userManager.getUserFirSerualNumber(serialNumber);
}
- 注解2
确定启动Activity的过场动画
- 注解3
在不同用户下,可以使用的应用程序是不同的:
//使用默认用户或者当前用户即Launcher所在的用户
if(user==null||user.equals(UserHandleCompat.myUserHandle())){
startActivity(intent,optsBundle);
}else{
//考虑用户因素
launcherApps.startActtivityForProfile(intent.getComponent(),user,intent.getSourceBounds(),optsBundle());
}
小 技巧:启动Activity时的过场动画
8、2启动应用并等待结果
Launcher需要在启动此类请求时通知某些组件处于等待状态,隐藏重写框架的startActivityForResult方法:
//requestcode用于标识请求,这个值必须大于或者等于0,否则将无法返回。
@Override
public void startActivityForResult(Intent intent,int requestcode){
if(requestcode>=0){
setWaitingForResult(true);
}
super.startActivityForResult(intent,requestcode);
}
8、3launcher的应用场景
8、3、1桌面添加桌小部件
需要通过binAppwidgetIdIfAllowed验证当前的应用程序是否有权限棒等桌面小部件,若没有,则需要以启动并返回结果的方式启动它Activity去处理
- 注解1
桌面小部件信息会临时缓存在mPendingAddInfo中,当有新的安装桌面快捷方式请求时,Launcher需要对于这个缓存进行必要的清理,并缓存当前的数据以便于后续使用:
//重置桌面小部件缓存
resetAddInfo();
//保存当前需要处理的桌面小部件信息
//保存小部件所处容器、桌面页ID
mPendingAddInfo.container=info.container=container;
mPendingAddInfo.screenId=info.screenId=screenId;
//保存所投放的位置偏移量
mPendingAddInfo.dropPos=loc;
//小部件在X轴和Y轴上最小空间需求
mPendingAddInfo.minSpanx=info.minSpanX;
mPendingAddInfp.minSpanx=info.minSpanY;
//处理小部件的位置
if(cell!=null){
mPendingAddInfo.cellX=cell[0];
mPendingAddInfo.cellY=cell[1];
}
//处理小部件的真实宽度和高度
if(span!=null){
mPendingAddInfo.spanX=span[0];
mPendingAddInfo.spanY=span[1];
}
- 注解2
如果当前操作是有权限的,那么直接将小部件添加到当前的AppWidget容器中,
appWidgetId=hostView.getAppWidgetId();
addAppWidgetImpl(appWidgetId,info,hostView,info.info);
- 注解3
8、3、2添加应用程序的快捷方式
添加配置节点:
<action android:name="android.intent.action.CREATE_SHORTCUT">
<catergory android:name="amdroid.intent.categroy.DEFAULT">
通过processShortcutFromDrop方法处理这种请求
void processShortcutFromDrop(ComponentName comonentName,long container,long screenId,int[] cell,int[] loc){
resetAddInfo();
mPendingAddInfo.container=cotainer;
mPendingAddInfo.screenId=screenId;
mPendingAddInfo.dropPos=loc;
if(cell!=null){
mPendingAddInfo.cellX=cell[0];//存放发位置
mPendingAddInfo.cellT=cell[1];
Intent createShortcutIntent=new Intent(Intent.ACTION_CREATE_SHORTCUT);
createShortcutIntent.setComponent(componentName);//组件名称及相关Action
processShortcut(createShortcutIntent);
}
void processShortcut(Intent intent){
Utilities.startActivityForResultSafely(this,intent,REQUEST_CREATE_SHORTCUT);
}
8、3、3用户启动选择壁纸时
选择壁纸的入口被设置在Launcher的界面上通过点击进入,onClickWallpaperPicker方法将请求处理
protected void onClickWallpaperPicker(View v){
if(LOGD) Log.d(TAG,"onClickWallpaperPicker");
final Intent pickWallpaper=new Intent(Intent.ACTION_SET_WALLPAPER);
pickWallpaper.setComponent(getWallpaperPickerComponent());
startActivityForResult(pickWallpaper,REQUEST_PICK_WALLPRPER);//启动壁纸并且使Luncher处于等待状态
}
8、3、4 创建桌面小部件
需要在AndroidMainfest.xml中做必要的配置
节点的相关属性:
8、3、5桌面小部件的视图未能完全恢复时
收到来自框架的android.appwidget.action.APPWIDGET_HOST_RESTORED广播,或者在桌面未被正确配置的情况下,将调度小部件重新配置
** 总结 **
8、4应用程序处理端如何设置必要的返回
Launcher通过必要的方式启动快捷方式的界面,启动的是设置应用程序中的CreateShortcut,这是一个列表组成的界面,我们点击列表项就会返回Launcher,并且添加指定的快捷方式入口。
点击列表中的项,会有必要的返回项,并结束自己完成选择的过程:
EXTRA_SHORTCUT_ICON_RESOURCE:快捷方式的图标资源ID
EXTRA_SHORTCUT_INTENT:启动快捷方式的Intent
EXTRA_SHORTCUT_NAME:快捷方式的名称ID
完成后需要回到调用中的onActivityResult方法中
8、5处理返回结果:
回到调用者并由调用者处理结果。
通过启动方的onActivityResult方法,将被启动方的处理结果传递给启动方:
protected void onActivtyResult(final int requestCode,final int resultCode,final Intent data)
requestCode:调用者的请求码,前面分析的若干场景下的各种请求码
resultCode:结果码:参数由处理者通过setResult方法设置,用于指示处理结果,常用的又RESULT_CANCELED和RESULT_OK两种
data:是Intent的实例,主要包括了实现约定的结果扩展信息。
九、Launcher的返回键处理
处理Home键外,按下任何按键都会分为两个消息分成到Activity中,分别是按下和抬起
9、1Activity的onKeyDown方法:
public boolean onKeyDown(int keyCode,keyEvent event){
if(keyCode==keyEvent.KEYCODE_BACK){
if(getApplicationInfo().targetSdkVersion>=Build.VERSION_CODES.ECLAIR){
event.startTracking();
}
else{
onBackPressed();
}
return true;
}
}
keyCode:表示按下的是什么按键
keyEvent类的实例:包含了按下按键的一些信息
9、2onKeyUp方法的实现
public boolean onKeyUp(int keyCode,keyEvent event){
if(getApplicationInfo().targetSdkVersion>=Build.VERSION_CODES.ECLAIR){
if(keyCode==keyEvent.KEYCODE_BACK&& event.isTracking()&&!event.isCanceled()){
onBackPressed();
return true;
}
}
return false;
}
9、3Activity的onBackPressed方法的实现
public void onBackPressed(){
if(mActionBar!=null&& mActionBar.collapseActionView()){
return;
}
if(!mFragments.popBackStackImmediate()){
finishAfterTransition();
}
}
Launcher的onBackPressed方法:
- 注解1:
按下Back键的时候需要提出这个界面:
if(mAppsCustomizeContent.ContentType.Applications){
//如果当前停留在应用程序菜单的应用程序标签,那么需求回退到桌面上
showWorkspace(true);
}
else{
//如果当前停留在应用程序菜单的其他标签下,那么需要回退到预览模式下
showOverviewMode(true);
}
- 注解2
若在按下Back键的时候,处于预览模式,则需要提出预览模式,恢复到正常模式:
mWorkspace.exitOverviewMode(true);
- 注解3
当按下Back键时,launcher有正在打开的文件,则Back将关闭这些文件夹:
Folder opneFolder=mWorkspace.getOpenFolder();//用于获取是否有正在打开的文件夹,若没有会返回null
if(openFolder.isEditingName()){
openFolder.dismissEditingName();//判断当前用户是否正在编辑文件夹的名字
}else
{
closeFolder();
}
十、Launcher的按键处理
10、1onkeyDown和onKeyup方法:
10、1、1Activity中的onKeyDown方法:
- 注解1
需要调度到Activity的onBackPressed方法中去,然后继续分发 - 注解2
通过setDefaultKeyMode方法可以改变按键模式:
if(getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,KeyCode,event,Menu,FLAG_ALWAYS_PERFORM_CLOSE)){
return true;
}
- 注解3:
按键重复或者当前按下的是系统按键,Activity的onKeyDown方法会将这个消息继续分发下去,自己并不做过多的处理
- 注解4:
Activity会根据不同的场景并当验证输入成功的情况下启动不同的处理:
handled=TextKeyListener.getInstance().onKeyDown(null,mDefalutKeySsb,keyCode,event);
if(handled&&mDefaultKeySsb.length()>0{
final String str=mDefaultKeySsb.toSting();
clearSpannable=true;
switch(mDefaultKeyMode){
case DEFAULT_KEYS_DIALER:
Intent intent=new Intent(Intent.ACTION_DIAL,Uri.parse("tel:"+str));
intent.addFlags=(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
break;
case DEFAULT_KEYS_SEARCH_LOCAL:
startSerch(str,false,null,fasle);
break;
case DEFAULT_KEYS_SEARCH_GLOBAL:
startSerch(str,false,null,true);
break;
}
}
- eg:一选项菜单的快捷方式模式(DEFAULT_KEYS_SHORTCUT)为例:
(1)修改项目菜单配置文件(res/menu/main.xml):
在选项菜单中ID为action_settings菜单项的快捷键为A
(2)在MainActivity的onCreate方法中调用setDefaultKeyMode,并且设置成选项菜单的快捷方式模式:
10、1、2Launcher的onKeyDown方法:
除Home键以外都会优先调用Launcher的onKeyDown方法
@override
public boolean onKeyDown(int keyCode,KeyEvent event){
final int uniChar=event.getUnicodeChar();
final boolean handled=super.onKeyDown(keyCode,event);
final boolean isKeyNotWhitespace=uniChar>0&&!Character.isWhitespace(uniChar);
if(!handled&&acceptFilter()&&isKeyNotWhitespace){
boolean gotKey=TextKeyListener.getInstance().onKeyDown(mWorkspace,mDefaultKeySsb,keyCode,event);
if(gotkey&&mDefaultKeySsb!=null&&mDefaultKeySsb.length()>0){
return onSearchRequested();
}
}
if(keyCode==KeyEvent.KEYCODE_MENU&&eventisLongPress()){
rreturn true;
}
return handled;
}
10、2自主分发按键事件
按键分发方法:dispatchKeyEvent(不能处理Hmoe键)
10、2、1Activity的dispatchKeyEvent实现
主要的任务是往Activity窗口中的窗口视图分发按键事件:
public boolean dispatchKeyEvent(KeyEvent event){
onUserInteraction();
if(event.getKeyCode()==KeyEvent.KEYCODE_MENU&&mActionBar!=null&&mActionBar.onMenuKeyEvent(event)){
return true;
}
Window win=getWindow();
if(win.superDispatchKeyEvent(event)){
return true;
}
if(decor==null)decor=win.getDecorView();
retutn event.dispatch(this,decor!=null?decor.getKeyDispatcherState():null,this);
}
10、2、2Launcher中的按键分发:
十一、Launcher的窗口周期
11、1应用程序中Activity的创建
应用程序的窗口创建先于应用程序中的Activity创建,performLauncheActivity方法过程
- 注解1:
- 注解2:
①创建被启动的Activity的实例:
② 创建该Activity所在应用程序的Application实例:
Applictaion app=r.packageInfo.makeApplication(false,mInstrumentation);
③创建应用程序上下文、识别该Activity的标题信息、创建Activity所依赖的设备配置信息:
//创建上下文
Context appContext=createBaseContextForActovoty(r,activity);
//识别标题
CharSequence title=r.activityInfo.loadLabel(appContext.getPackageManager());
//根据当前的设备信息配置信息创建Activity使用的配置实例:
Configuration config=new Configuration(mCpmpatConfiguration);
//添加到Activity中
activity.attch(appContext,this,getInstrumentation(),r.token,r,ident,app,r.intent,r.activityInfo,title,r.parent,r.embeddedID,r.lastNonCofigurationInstances,config,r.referrer,r.voiceInteractor);
(4)最后是给Activity设置需要的主题:
int theme=r.activityInfo.getThemeResource();
if(theme!=0){
activity.setTheme(theme);
}
- 注解3
回调的方式通知Activity可以进入创建的生命周期中:
activity.mCalled=false;
//调用Activty的onCreate生命周期回调
if(r.ispersistable()){
mInstrumentation.callActivityOnCreate(activity,r.state,r.
}else{
mInstrumentation.callActivityOnCreate(activtiy,r.state);
}
//如果Activtiy的onCreate没有被执行,抛出异常
if(!activty.mCalled){
throw new SuperNotCalledException(
"Activtiy"+r.intent.getCompontent().toShortString()+"did not call through to super.onCreate()");
)
}
- 注解4:
Activity创建后可能出现的是onStart生命周期
//保存创建的Activity实例
r.activity=activity;
r.stopped=true;
//执行Activity的onStart生命周期回调
if(!!r.activty.mFinished){
activity.performStart();
r.stopped=false;
}
- 注解5
Activity的状态恢复问题,
*注解6 创建后的处理
11、2应用窗口的创建:
- 注解1:创建窗口初始化
- 创建窗口管理器
11、3窗口中设置UI
通常发生在Activity的onCreate生命周期中,调用setContentView方法填充到窗口中
两个版本:一个需要输入一个布局的ID,另一个需要填充到窗口的View实例:
public void setContentView(int layoutResID){
getWindow().setContentView(layoutResID);
initWidowDecorActionBar();
}
public void setContentView(View view){
getWindow.setContentView(view){
initWindow DecprActionBar();
}
}
11、3、1在窗口中设置布局
- 注解1
setContentView(int layoutResID)的View生成方法
final Scence newScence=Scence.get SceneForLayout(mContentParent,layoutResID,getContext());
transitionTo(newScene);
setContentView(View view)的View生成方法:
view.setLayoutParams(params);
final Scene newScene=new Scene(mContentParent,view);
transitionTo(newScene);
- 注解2
若没有包含FEATURE_CONTENT_TRANSITIONS标志
11、3、2安装窗口容器
installDecor方法为应用程序生成窗口容器视图以及窗口父容器
11、3、3初始化应用程序的ActionBar
private void initWindowDecorActionBar(){
Window window=getWindow();
window.getDecorView();
if(isChild()||!window.hasFeature(Window.FEATURE_ACTION_BAR)||mAction!=null){
return;
}
mActionBar=new WindowDecorActionBar(this);
mActionBar.setDefaultDisplayHomeAsupEnabled(mEnableDefaultActionBarUp);
mWindow.setDefaultIcon(mActivityInfo.getIconResource());
mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
}
11、4Launcher中实现的窗口状态变化接口
11、4、1应用程序添加到窗口中:
调用onAttachedToWindow通知应用程序窗口被显示出来,该方法只有当应用程序第一次运行的时候才会调用,只有调用Activity的finish方法、使用Back键或者应用程序被销毁,否则不会重复调用
Ativity管理服务是通过Activity进程类的handleResumeActivity方法来调用Activity的onResume中;
- 分析handleResumeActivity方法:
- 注解1:
执行Activity中的onResume生命周期回调函数,需要完成的代码:
ActivityClientRecord r=performResumeActivity(token,clearHide);
若成功则performResumeActivity方法会返回显示的Activity的记录。
- 注解2:
若执行显示失败,则通知Activity管理服务显示失败,并请求Activity管理服务结束该Activity:
ActivityManagerNative.getDefault().finishActivity(token,Activity.RESULT_CACELED,null,false);
- 注解3:
①:若Activity的窗口没有被添加在窗口服务中,并且这个Activity没有结束自己,则需要通知Activity管理服务这个Activity显示
boolean willBeVisible=!a.mStartedActivity;
if(!willBeVisible){
try{
willBeVisible=ActivityManagerNative.getDefault().willActivityBeVisible(a.getActivity());
}catch(RemoteExecption e){}
}
② 若当前Activity没有相关联的窗口记录,这里需要窗机一些相关的处理:
③:执行窗口的一些清理工作:
leanUpPendingRemoveWindows(r);
④:若当前Activity即将显示,并没有再启动其他的Activity:则进行Activity的显示工作:
//如果Activity的配置有更新,则需要执行一次Activity的配置变化
performConfigurationChanged(r.activity,r.tempConfig);
freeTextLayoutCahcesIfNeed(r.activity.mCurrentConfig.diff(r.tmpConfig));
r.newConfig=null;
//更新Activity的视图
ViewManager wm=a.getWindowManager();
View decor=r.window.getDecorView();
wm.updateViewLayout(decor,l);
//显示Activity
if(r.activty.mVisibleFromClient){
r.activity.makeVisible();
}
在没有调用makeVisible方法前,Activity的窗口虽然被创建,并且加入了窗口管理器中,但此时窗口并不可见。
void makeVisible(){
if(!mWindowAdded){
ViewManager wm=getWindowManager();
wm.addView(mDecor,getWinow().getAttributes());
mWindowAdded=true;
}
mDecor.setVisibilitu(View.VISIBLE);
}
Launcher需要onAttacherToWindow方法完成广播的注册等功能:
11、4、2应用程序从窗口中拆离
从窗口管理器中拆除时,触发Activity的onDetacgedFromWindow方法,窗口被拆离发生在这个Activity被销毁的时候,通常通过应用线程类中的sceduleDestroyActivity方法实现,最后由ActivityThread的hadleDestroyActivity方法处理这个销毁消息,handleDestoryActivity如下所示:
- 注解1:
handleDestoryActivity方法的第一步也是调用Activity的销毁(onDestroy)生命周期回调函数
ActivityClientRecord r=performDestroyActivity(token,finishing,confingChanges,getNonConfigInstance);
- 注解2:子销毁一个Activity的过程中:
①销毁前首先需要清理掉一些预先缓存的窗口,并获取一些必要的信息:
//清理被维持的窗口实例
cleanUpPendingRemoveWindows(r);
//取得窗口管理器
WindowManager wm=r.activity.getWindowManager();
//获取该Activityd Decor;
View v=r.activty.mDecor;
②获取与当前的Decor管理的窗口Token,并且执行销毁添加在当前窗口的view:
IBinder wtoken=v.getWindowToken();
····
wm.removeViewImmediate(v);
③通知窗口管理服务关闭 所有与该Activity窗口相管理的内容:
if(token!=null &&r.mPendingRemoveWindow==null){
WindowManangerGlobal.getInstance().closeAll(wtoken,r.activity.getClass().getName(),"Activity");
}
//清理Activity的Decor
r.activity.mDecor=null;
- 注解3
清理了Activity相关窗口后,需要清理上下文
Context=r.activity.getBaseContext();
if(c instanceof ContextImpl){
((ContextImpl c).sceduleFinalCleanup(
r.activity.gerClass().getName(),"Activity");
}
- 注解4:
无论Activity是否能被成功地销毁,都需要通知Activity管理服务对应销毁保存在服务端的Activity记录信息:
try{
ActivityManagerNative.getDefault().activityDestroyed(token);
}
catch(RemoteException ex{}
对于Launcher而言,onDetachedFromWindow方法实现如下;
@Override
public void onDetacherFromWindow(){
super.onDetachedFromWindow();
mVisible=false;
if(mAttached){
unregisterReceiver(mReceiver);
mAttached=false;
}
updateRunning();
}
11、4、3窗口焦点变化
当焦点发送变化时:onWindowFocusChanged会被调用,如果当前获取窗口的焦点,其输入参数设置成true,反正为false。
从ActivityA到ActivityB:
十二、Launcher实现的框架接口
12、1控件点击事件监听接口
使用setOnclickListener方法
12、1、1设置监听器方法:
12、1、2点击事件的处理机制:
控件由View类的performClick方法进行分发:
- 触摸事件的处理“
发生在View的onTouchEvent方法处理ACTION_UP动作中,
发送在控件处理按键事件的时候触发:
public boolean onKeyUp(int keyCode,KeyEvent event){
if(KeyEvent.isConfirmkey(KeyCode)){
if((mViewFlags & ENABLED_MASK)==DISABLED){
return true;
}
if((mViewFlags& CLICKBLE)==CLICKABLE&&isPressed()){
setPressed(false);
if(!mHashPerformedLongPress){
removeLongPressCallback();
return performClick();
}
}
}
return false;
}
12、1、3Launcher的点击事件处理:
Launcher类的声明:
- 注解3
点击的类似快捷方式的桌面组件,对应不同的点击事件处理方式
1、Workspace,CellLayout的处理
需要查看是否处于桌面的预览模式(mWorkspace.isInoverviewMode)若是则会退出预览模式: - 2、应用程序快捷方式:
点击的是快捷方式则会调用onClickAppshortcut方法处理这个点击请求:
启动一个快捷方式:
- 文件夹的点击处理
使用onClickFolderIcon方法进行实现: