我们都知道,Launcher中最经常做的的操作就是启动应用程序。那么Launcher是怎么启动的呢?
public boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
// 是否是安全模式 根据intent判断是否为系统应用
if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
}
// 调用intent方法
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
}
return success;
}
下面我们再次看一下startactivity里面做了什么:
private boolean startActivity(View v, Intent intent, Object tag) {
// 设置activity为new task
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
// 这个是判断是否用默认的启动动画
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
UserManagerCompat userManager = UserManagerCompat.getInstance(this);
UserHandleCompat user = null;
// extra profile
if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
user = userManager.getUserForSerialNumber(serialNumber);
}
Bundle optsBundle = null;
if (useLaunchAnimation) {
// 如果使用的是默认的启动动画,那么根据系统的版本来获得其启动动画
ActivityOptions opts = null;
if (Utilities.ATLEAST_MARSHMALLOW) {
int left = 0, top = 0;
int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
if (v instanceof TextView) {
// Launch from center of icon, not entire view
Drawable icon = Workspace.getTextViewIcon((TextView) v);
if (icon != null) {
Rect bounds = icon.getBounds();
left = (width - bounds.width()) / 2;
top = v.getPaddingTop();
width = bounds.width();
height = bounds.height();
}
}
opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
} else if (!Utilities.ATLEAST_LOLLIPOP) {
// Below L, we use a scale up animation
opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
v.getMeasuredWidth(), v.getMeasuredHeight());
} else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
// On L devices, we use the device default slide-up transition.
// On L MR1 devices, we a custom version of the slide-up transition which
// doesn't have the delay present in the device default.
opts = ActivityOptions.makeCustomAnimation(this,
R.anim.task_open_enter, R.anim.no_anim);
}
optsBundle = opts != null ? opts.toBundle() : null;
}
//启动的动画保存在optsBundle里面,安卓中有类似win的用户概念,那么这里就是进行判断,如果use为空,或者
// 用户是launcher所在的用户
if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
} else {
// TODO Component can be null when shortcuts are supported for secondary user
launcherApps.startActivityForProfile(intent.getComponent(), user,
intent.getSourceBounds(), optsBundle);
}
return true;
} catch (SecurityException e) {
if (Utilities.ATLEAST_MARSHMALLOW && tag instanceof ItemInfo) {
// Due to legacy reasons, direct call shortcuts require Launchers to have the
// corresponding permission. Show the appropriate permission prompt if that
// is the case.
if (intent.getComponent() == null
&& Intent.ACTION_CALL.equals(intent.getAction())
&& checkSelfPermission(Manifest.permission.CALL_PHONE) !=
PackageManager.PERMISSION_GRANTED) {
// TODO: Rename sPendingAddItem to a generic name.
sPendingAddItem = preparePendingAddArgs(REQUEST_PERMISSION_CALL_PHONE, intent,
0, (ItemInfo) tag);
requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
REQUEST_PERMISSION_CALL_PHONE);
return false;
}
}
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Launcher does not have the permission to launch " + intent +
". Make sure to create a MAIN intent-filter for the corresponding activity " +
"or use the exported attribute for this activity. "
+ "tag="+ tag + " intent=" + intent, e);
}
return false;
}
我们都知道,在我们开发的时候,有时候会掉哟个系统的服务,例如相册,照相机,这时候我们就会使用startactivityforresult,同样的,laucher中也是调用这个方法,只是他对这个方法进行了重写,代码如下:
@Override
public void startActivityForResult(Intent intent, int requestCode) {
onStartForResult(requestCode);
super.startActivityForResult(intent, requestCode);
}
在方法里多了一个onstartforresult,这个是什么用的呢,点击去
private void onStartForResult(int requestCode) {
if (requestCode >= 0) {
setWaitingForResult(true);
}
}
private void setWaitingForResult(boolean value) {
boolean isLocked = isWorkspaceLocked();
mWaitingForResult = value;
if (isLocked != isWorkspaceLocked()) {
onWorkspaceLockedChanged();
}
}
大概的意思是说,如果请求码大于0,则使workspace处于锁定状态
绑定小组件
/**
* Process a widget drop.
*
* @param info The PendingAppWidgetInfo of the widget being added.
* @param screenId The ID of the screen where it should be added
* @param cell The cell it should be added to, optional
*/
/*
*
* */
private void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId,
int[] cell, int[] span) {
// 对存放桌面小部件信息的缓存进行清理
resetAddInfo();
mPendingAddInfo.container = info.container = container;
mPendingAddInfo.screenId = info.screenId = screenId;
mPendingAddInfo.dropPos = null;
// xy轴的最小空间需求
mPendingAddInfo.minSpanX = info.minSpanX;
mPendingAddInfo.minSpanY = info.minSpanY;
//小部件的位置
if (cell != null) {
mPendingAddInfo.cellX = cell[0];
mPendingAddInfo.cellY = cell[1];
}
// 部件的宽高
if (span != null) {
mPendingAddInfo.spanX = span[0];
mPendingAddInfo.spanY = span[1];
}
//获得小部件的宿主
AppWidgetHostView hostView = info.boundWidget;
int appWidgetId;
if (hostView != null) {
// 小部件的容器不为空,则直接进行添加
appWidgetId = hostView.getAppWidgetId();
addAppWidgetImpl(appWidgetId, info, hostView, info.info);
// Clear the boundWidget so that it doesn't get destroyed.
info.boundWidget = null;
} else {
// In this case, we either need to start an activity to get permission to bind
// the widget, or we need to start an activity to configure the widget, or both.
// 这种情况,我们要么需要获得绑定小部件的权限,或者需要启动配置小部件的界面,或者都需要
// 为小部件获得id
appWidgetId = getAppWidgetHost().allocateAppWidgetId();
Bundle options = info.bindOptions;
//是否允许绑定小部件
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
appWidgetId, info.info, options);
if (success) {
addAppWidgetImpl(appWidgetId, info, null, info.info);
} else {
// 注意这里,我需要在onresult中处理返回的数据,确定当
mPendingAddWidgetInfo = info.info;
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
mAppWidgetManager.getUser(mPendingAddWidgetInfo)
.addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
// TODO: we need to make sure that this accounts for the options bundle.
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);根据请求码进行判断
startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
}
}
}
添加快捷方式
/**
* Process a shortcut drop.
*
* @param componentName The name of the component所在屏幕的id
* @param screenId The ID of the screen where it should be added
* @param cell The cell it should be added to, optional
*/
private void processShortcutFromDrop(ComponentName componentName, long container, long screenId,
int[] cell) {
// 重置添加的信息
resetAddInfo();
// 快捷方式的信息
mPendingAddInfo.container = container;
mPendingAddInfo.screenId = screenId;
mPendingAddInfo.dropPos = null;
//设置位置
if (cell != null) {
mPendingAddInfo.cellX = cell[0];
mPendingAddInfo.cellY = cell[1];
}
Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
// 快捷方式名字
createShortcutIntent.setComponent(componentName);
Utilities.startActivityForResultSafely(this, createShortcutIntent, REQUEST_CREATE_SHORTCUT);
}
启动选择壁纸
/**
* Event handler for the wallpaper picker button that appears after a long presslauncher中有一个activity是用来处理壁纸的
*
* on the home screen.
*/
protected void onClickWallpaperPicker(View v) {
if (LOGD) Log.d(TAG, "onClickWallpaperPicker");
startActivityForResult(new Intent(Intent.ACTION_SET_WALLPAPER).setPackage(getPackageName()),
REQUEST_PICK_WALLPAPER);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onClickWallpaperPicker(v);
}
}
## 创建桌面小部件 ##
/*
*在桌面的小部件的时候会需要必要的配置,例如我们选择图库的时候,会让我们选择图片专辑
* */
void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo,
int delay) {
// 如果configure不为空,这时候就需要进行配置
if (appWidgetInfo.configure != null) {
mPendingAddWidgetInfo = appWidgetInfo;
mPendingAddWidgetId = appWidgetId;
//如果需要进行配置,则启动
// Launch over to configure widget, if needed
mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
} else {
// Otherwise just add it,如果不需要配置,则直接进行添加
Runnable onComplete = new Runnable() {
@Override
public void run() {
// Exit spring loaded mode if necessary after adding the widget
exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT,
null);
}
};
completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
appWidgetInfo);
mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
}
}
“`
我们通过startactivityforresult启动activity,目标activity在处理完毕之后,把必要的信息进行返回。这时候我们就需要通过requestcode和resultcode进行最终的处理。也就是OnActivityResult方法