android4 SystemUI 流程分析
什么是SystemUI?
对于Phone来说SystemUI指的是:StatusBar(状态栏)、NavigationBar(导航栏)。而对于Tablet或者是TV来说SystemUI指的是:建立工程导入到eclipse中代码具体图示:
先从 AndroidManifest.xml 看看有哪些东东,以前说过android中有四大组件,这里就有如下的三大部分:
系统服务 Service :
SystemUIService
TakeScreenshotService
LoadAverageService
广播接收器 BroadcastReceive:
BootReceiver
Activity 应用:
USB的挺多哟...
UsbStorageActivity
UsbConfirmActivity
UsbPermissionActivity
UsbStorageActivity
UsbAccessoryUriActivity
NetworkOverLimitActivity
Nyandroid
具体定义请看 AndroidManifest.xml 文件,上面只是简单的列一下
先看第一个Activity -- Nyandroid 这里做了什么呢?
就是网上传说中的 好多安卓机器人飞过去。。。。其中代码很简单,简单说一下动画效果的代码:
public class FlyingCat extends ImageView {
public FlyingCat(Context context, AttributeSet as) {
super(context, as);
setImageResource(R.drawable.nyandroid_anim); // @@@
if (DEBUG) setBackgroundColor(0x80FF0000);
}
...
}
startSystemUi(contextF);
static final void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
Slog.d(TAG, "Starting service: " + intent);
context.startService(intent);
}
http://blog.csdn.net/andyhuabing/article/details/7346203 android启动--深入理解init进程
http://blog.csdn.net/andyhuabing/article/details/7349986 android启动--深入理解zygote
http://blog.csdn.net/andyhuabing/article/details/7351691 android启动--深入理解zygote (II)
http://blog.csdn.net/andyhuabing/article/details/7353910 android启动--深入理解启动HOME
那么就继续跟踪 SystemUIService 中代码:
/**
* The class names of the stuff to start.
*/
final Object[] SERVICES = new Object[] {
0, // system bar or status bar, filled in below.
com.android.systemui.power.PowerUI.class,
};
@Override
public void onCreate() {
// Pick status bar or system bar.
IWindowManager wm = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
try {
SERVICES[0] = wm.canStatusBarHide()
? R.string.config_statusBarComponent
: R.string.config_systemBarComponent;
} catch (RemoteException e) {
Slog.w(TAG, "Failing checking whether status bar can hide", e);
}
final int N = SERVICES.length;
mServices = new SystemUI[N];
for (int i=0; i
SERVICES[0] = wm.canStatusBarHide()
? R.string.config_statusBarComponent
: R.string.config_systemBarComponent;
public boolean canStatusBarHide() {
return mStatusBarCanHide;
}
public void setInitialDisplaySize(int width, int height) {
...
// Determine whether the status bar can hide based on the size
// of the screen. We assume sizes > 600dp are tablets where we
// will use the system bar.
int shortSizeDp = shortSize
* DisplayMetrics.DENSITY_DEFAULT
/ DisplayMetrics.DENSITY_DEVICE;
mStatusBarCanHide = shortSizeDp < 600;
}
@Override
public void start() {
super.start(); // will add the main bar view
}
调用到 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
public void start() {
// First set up our views and stuff.
View sb = makeStatusBarView();
// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
ArrayList notificationKeys = new ArrayList();
ArrayList notifications = new ArrayList();
mCommandQueue = new CommandQueue(this, iconList);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
int[] switches = new int[7];
ArrayList binders = new ArrayList();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
disable(switches[0]);
setSystemUiVisibility(switches[1]);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4]);
setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
// Set up the initial icon state
int N = iconList.size();
int viewIndex = 0;
for (int i=0; i
在这里,完成了SystemUI的整个初始化以及设置过程,并最终呈现到界面上。
启动过程中完成如下操作:
1、获取icon list,addIcon(iconList.getSlot(i), i, viewIndex, icon);
2、获取notification,addNotification(notificationKeys.get(i), notifications.get(i));
3、显示StatusBar,WindowManagerImpl.getDefault().addView(sb, lp);
显示NavigationBar,WindowManagerImpl.getDefault().addView(
mNavigationBarView, getNavigationBarLayoutParams());
时序图如下:
3、最近任务缩略图显示
长按home键,列出最近启动过的任务缩略图,重要的两个类
// Recent apps
private RecentsPanelView mRecentsPanel;
private RecentTasksLoader mRecentTasksLoader;
SystemUI 获取按键事件,获取缩略图并将其显示出来,最后响应view上按键响应相应事件:
对于我们来说,关注点主要有如下几个:
1、缩略图如何获取
RecentsPanelView.java 中
refreshRecentTasksList(recentTaskDescriptions);
-->
mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
-->
RecentTasksLoader.java 中
// return a snapshot of the current list of recent apps
ArrayList getRecentTasks() {
cancelLoadingThumbnails();
ArrayList tasks = new ArrayList();
final PackageManager pm = mContext.getPackageManager();
final ActivityManager am = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE);
final List recentTasks =
am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
.resolveActivityInfo(pm, 0);
HashSet recentTasksToKeepInCache = new HashSet();
int numTasks = recentTasks.size();
// skip the first task - assume it's either the home screen or the current activity.
final int first = 1;
recentTasksToKeepInCache.add(recentTasks.get(0).persistentId);
for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
TaskDescription item = createTaskDescription(recentInfo.id,
recentInfo.persistentId, recentInfo.baseIntent,
recentInfo.origActivity, recentInfo.description, homeInfo);
if (item != null) {
tasks.add(item);
++index;
}
}
// when we're not using the TaskDescription cache, we load the thumbnails in the
// background
loadThumbnailsInBackground(new ArrayList(tasks));
return tasks;
}
这里利用 ActivityManager 中的方法:getRecentTasks 获取当前任务的列表,然后再利用 getTaskThumbnails 获取
按键View 就是几个按键相应的View
public View getRecentsButton() {
return mCurrentView.findViewById(R.id.recent_apps);
}
public View getMenuButton() {
return mCurrentView.findViewById(R.id.menu);
}
public View getBackButton() {
return mCurrentView.findViewById(R.id.back);
}
public View getHomeButton() {
return mCurrentView.findViewById(R.id.home);
}
相应的应用缩略图,调用序列图如下:
2、显示缩略图
public void show(boolean show, boolean animate,
ArrayList recentTaskDescriptions) {
if (show) {
// Need to update list of recent apps before we set visibility so this view's
// content description is updated before it gets focus for TalkBack mode
refreshRecentTasksList(recentTaskDescriptions);
// if there are no apps, either bring up a "No recent apps" message, or just
// quit early
boolean noApps = (mRecentTaskDescriptions.size() == 0);
if (mRecentsNoApps != null) { // doesn't exist on large devices
mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
} else {
if (noApps) {
if (DEBUG) Log.v(TAG, "Nothing to show");
return;
}
}
}else {
mRecentTasksLoader.cancelLoadingThumbnails();
mRecentTasksDirty = true;
}
...
}
如果 mRecentsNoApps 为空则表示没有任务,显示 "No recent apps" 否则显示应用列表
否则则显示任务的缩略图。时序图如下:
3、点击某个缩略图执行
这里分为点击某个缩略图执行程序及长按缩略图执行程序
这里直接继承了 View.OnItemClickListener 所以可以直接执行子项按键事件
public class RecentsPanelView extends RelativeLayout implements OnItemClickListener, RecentsCallback,
StatusBarPanel, Animator.AnimatorListener, View.OnTouchListener
处理点击事件方法:
public void onItemClick(AdapterView> parent, View view, int position, long id) {
handleOnClick(view);
}
public void handleOnClick(View view) {
TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
final Context context = view.getContext();
final ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
if (ad.taskId >= 0) {
// This is an active task; it should just go to the foreground.
am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME);
} else {
Intent intent = ad.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
| Intent.FLAG_ACTIVITY_TASK_ON_HOME
| Intent.FLAG_ACTIVITY_NEW_TASK);
if (DEBUG) Log.v(TAG, "Starting activity " + intent);
context.startActivity(intent);
}
hide(true);
}
注意代码:context.startActivity(intent); 这里就是执行对应的 Activity
处理长按键点击事件方法:
public void handleLongPress(
final View selectedView, final View anchorView, final View thumbnailView) {
thumbnailView.setSelected(true);
PopupMenu popup = new PopupMenu(mContext, anchorView == null ? selectedView : anchorView);
popup.getMenuInflater().inflate(R.menu.recent_popup_menu, popup.getMenu());
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.recent_remove_item) {
mRecentsContainer.removeViewInLayout(selectedView);
} else if (item.getItemId() == R.id.recent_inspect_item) {
ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
if (viewHolder != null) {
final TaskDescription ad = viewHolder.taskDescription;
startApplicationDetailsActivity(ad.packageName);
mBar.animateCollapse();
} else {
throw new IllegalStateException("Oops, no tag on view " + selectedView);
}
} else {
return false;
}
return true;
}
});
popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
public void onDismiss(PopupMenu menu) {
thumbnailView.setSelected(false);
}
});
popup.show();
}
这里弹出一个PopupMenu,分别是 A:"Remove from list" 及 B:"App Info"
其中A项表示将此任务移除出列表,执行 mRecentsContainer.removeViewInLayout(selectedView);
另外B是启动另外一个Acitivty列出应用信息:
private void startApplicationDetailsActivity(String packageName) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
}
总结:
这里详细的对SystemUI 的两个最重要的 StatusBar NavigationBar(SystemUIService) 及缩略图代码流程分析。
因此各家厂商根据自家的需求,需要定制SystemUI或者美化SystemUI,不同的平台也会有不同的修改,但大体框架是没有变的,
无非是在原有基础上的修修改改或者增加一些自己的类等等。