前面三篇文章《Android 4.0 ICS SystemUI淺析——SystemUI啟動流程》,、《Android
4.0 ICS SystemUI淺析——StatusBar結構分析》、《Android 4.0 ICS SystemUI淺析——StatusBar加載流程分析》逐步分析了SystemUI中StatusBar的啟動以及加載流程,,本文主要分析StatusBar上的Notification的加載,,如有不正之處還懇請各位幫忙指正。
本文來自:http://blog.csdn.net/yihongyuelan 歡迎轉載 請務必注明出處,!
在上一篇文章《Android 4.0 ICS SystemUI淺析——StatusBar加載流程分析》中,我們主要分析了StatusBar上的系統(tǒng)Icons加載的過程,,包括了耳機圖標,、藍牙圖標、禁音圖標等等,,此文是緊接著上文分析的,,因此我們首先看到/SourceCode/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java的start():
- public void start() {
-
-
- View sb = makeStatusBarView();
-
-
-
-
- StatusBarIconList iconList = new StatusBarIconList();
-
- ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
-
- ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
-
- mCommandQueue = new CommandQueue(this, iconList);
-
- mBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- int[] switches = new int[7];
- ArrayList<IBinder> binders = new ArrayList<IBinder>();
- try {
-
- mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
- switches, binders);
- } catch (RemoteException ex) {
-
- }
- ... ...
-
-
-
- N = notificationKeys.size();
- if (N == notifications.size()) {
- for (int i=0; i<N; i++) {
- addNotification(notificationKeys.get(i), notifications.get(i));
- }
- } else {
- Log.wtf(TAG, "Notification list length mismatch: keys=" + N
- + " notifications=" + notifications.size());
- }
-
- ... ...
- lp.gravity = getStatusBarGravity();
- lp.setTitle("StatusBar");
- lp.packageName = mContext.getPackageName();
- lp.windowAnimations = R.style.Animation_StatusBar;
-
- WindowManagerImpl.getDefault().addView(sb, lp);
- mDoNotDisturb = new DoNotDisturb(mContext);
- }
我們可以看到addNotification()方法主要完成Notification圖標的加載。跟進去看看(因為我們分析的是Phone因此選擇PhoneStatusBar),,代碼如下:
-
- public void addNotification(IBinder key, StatusBarNotification notification) {
-
- StatusBarIconView iconView = addNotificationViews(key, notification);
- if (iconView == null) return;
-
- boolean immersive = false;
- try {
-
- immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
- if (DEBUG) {
- Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
- }
- } catch (RemoteException ex) {
- }
-
- if (immersive) {
- if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
- Slog.d(TAG, "Presenting high-priority notification in immersive activity");
-
-
-
- ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
- TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
- alertIcon.setImageDrawable(StatusBarIconView.getIcon(
- alertIcon.getContext(),
- iconView.getStatusBarIcon()));
- alertText.setText(notification.notification.tickerText);
-
- View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
- button.setOnClickListener(
- new NotificationClicker(notification.notification.contentIntent,
- notification.pkg, notification.tag, notification.id));
-
-
- mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
-
-
- mHandler.removeMessages(MSG_HIDE_INTRUDER);
- mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
- }
-
- } else if (notification.notification.fullScreenIntent != null) {
-
- Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
- try {
- notification.notification.fullScreenIntent.send();
- } catch (PendingIntent.CanceledException e) {
- }
- } else {
-
-
-
-
- tick(notification);
- }
-
-
-
- setAreThereNotifications();
-
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- }
通過對以上代碼的分析,,我們可以大致知道,,Notification的加載主要分為三步:
1.addNotificationViews(key, notification);
2.tick(notification);
3.setAreThereNotifications()和updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
那么接下來我們就通過這三個方法來分析Notification的加載。
(1). addNotificationViews(key, notification);
跟蹤查看代碼如下:
-
- StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
- if (DEBUG) {
- Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
- }
-
-
- final StatusBarIconView iconView = new StatusBarIconView(mContext,
- notification.pkg + "/0x" + Integer.toHexString(notification.id),
- notification.notification);
-
- iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
- final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
- notification.notification.icon,
- notification.notification.iconLevel,
- notification.notification.number,
- notification.notification.tickerText);
-
- if (!iconView.set(ic)) {
- handleNotificationError(key, notification, "Couldn't create icon: " + ic);
- return null;
- }
-
-
- NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
- if (!inflateViews(entry, mPile)) {
- handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
- + notification);
- return null;
- }
-
-
-
- int pos = mNotificationData.add(entry);
- if (DEBUG) {
- Slog.d(TAG, "addNotificationViews: added at " + pos);
- }
-
- updateNotificationIcons();
-
- return iconView;
- }
根據以上代碼,,我們可以知道在addNotificationViews()中,,又可以細分為三步:設置icons,設置ExpanedView,,更新圖標,。其中,設置icons實際上和上一篇文章中設置系統(tǒng)Icons類似,。主要區(qū)別在設置ExpandedView和更新圖標,。那跟蹤inflateViews()方法可以看到:
- private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
- StatusBarNotification sbn = entry.notification;
-
- RemoteViews remoteViews = sbn.notification.contentView;
- if (remoteViews == null) {
- return false;
- }
-
-
- LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
-
- View vetoButton = updateNotificationVetoButton(row, sbn);
-
- vetoButton.setContentDescription(mContext.getString(
- R.string.accessibility_remove_notification));
-
-
-
- ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
- if (sbn.notification.largeIcon != null) {
- largeIcon.setImageBitmap(sbn.notification.largeIcon);
- largeIcon.setContentDescription(sbn.notification.tickerText);
- } else {
- largeIcon.getLayoutParams().width = 0;
- largeIcon.setVisibility(View.INVISIBLE);
- }
- largeIcon.setContentDescription(sbn.notification.tickerText);
-
-
- ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
-
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-
- PendingIntent contentIntent = sbn.notification.contentIntent;
- if (contentIntent != null) {
-
- final View.OnClickListener listener = new NotificationClicker(contentIntent,
- sbn.pkg, sbn.tag, sbn.id);
- largeIcon.setOnClickListener(listener);
- content.setOnClickListener(listener);
- } else {
- largeIcon.setOnClickListener(null);
- content.setOnClickListener(null);
- }
-
- View expanded = null;
- Exception exception = null;
- try {
-
- expanded = remoteViews.apply(mContext, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (expanded == null) {
- final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
- Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
- return false;
- } else {
-
- content.addView(expanded);
-
- row.setDrawingCacheEnabled(true);
- }
-
- applyLegacyRowBackground(sbn, content);
-
- entry.row = row;
- entry.content = content;
- entry.expanded = expanded;
- entry.largeIcon = largeIcon;
-
- return true;
- }
對于ExpanedView中的Notification設置,,可能這里有點模糊,那請看以下圖1和圖2:
圖 1
圖2
通過圖1和圖2我們可以看到largeIcon以及vetoButton觸發(fā)時間,。對于ExpandedView,,后面會有較為詳細的分析。
上面分析了ExpandedView中的Notification的設置,,在addNotificationViews(key, notification);中就還剩下最后一個步驟了,,即更新圖標,那么查看addNotificationView()中的updateNotificationIcons()方法,,代碼如下:
- private void updateNotificationIcons() {
-
- loadNotificationShade();
-
- final LinearLayout.LayoutParams params
- = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
-
- int N = mNotificationData.size();
-
- if (DEBUG) {
- Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
- }
-
- ArrayList<View> toShow = new ArrayList<View>();
-
- for (int i=0; i<N; i++) {
- toShow.add(mNotificationData.get(N-i-1).icon);
- }
-
- ArrayList<View> toRemove = new ArrayList<View>();
- for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
- View child = mNotificationIcons.getChildAt(i);
- if (!toShow.contains(child)) {
- toRemove.add(child);
- }
- }
-
- for (View remove : toRemove) {
- mNotificationIcons.removeView(remove);
- }
-
- for (int i=0; i<toShow.size(); i++) {
- View v = toShow.get(i);
- if (v.getParent() == null) {
- mNotificationIcons.addView(v, i, params);
- }
- }
- }
通過以上代碼的分析,,我們可以知道updateNotificationIcons()主要做了兩件事:更新ExpanddeView上的通知信息;更新StatusBar上的通知圖標。更新方法都類似,,先查通知看是否有效,,如果不是則刪除,如果是則添加,。以上完成了加載Notification的第一步,,那么我來看第二步tick(notification)。
(2).tick(notification),,跟蹤代碼如下:
- private void tick(StatusBarNotification n) {
-
-
-
-
- if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
- if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
- | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
-
- mTicker.addEntry(n);
- }
- }
- }
跳轉到mTicker.addEntry();方法中,,代碼路徑:SourceCode/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java中,代碼如下:
- public void addEntry(StatusBarNotification n) {
- int initialCount = mSegments.size();
-
-
-
-
- if (initialCount > 0) {
- final Segment seg = mSegments.get(0);
-
- if (n.pkg.equals(seg.notification.pkg)
- && n.notification.icon == seg.notification.notification.icon
- && n.notification.iconLevel == seg.notification.notification.iconLevel
- && CharSequences.equals(seg.notification.notification.tickerText,
- n.notification.tickerText)) {
- return;
- }
- }
-
- final Drawable icon = StatusBarIconView.getIcon(mContext,
- new StatusBarIcon(n.pkg, n.notification.icon, n.notification.iconLevel, 0,
- n.notification.tickerText));
-
-
- final Segment newSegment = new Segment(n, icon, n.notification.tickerText);
-
-
-
- for (int i=0; i<mSegments.size(); i++) {
- Segment seg = mSegments.get(i);
- if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
-
- mSegments.remove(i--);
- }
- }
-
- mSegments.add(newSegment);
-
- if (initialCount == 0 && mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
- seg.first = false;
-
- mIconSwitcher.setAnimateFirstView(false);
- mIconSwitcher.reset();
- mIconSwitcher.setImageDrawable(seg.icon);
-
- mTextSwitcher.setAnimateFirstView(false);
- mTextSwitcher.reset();
- mTextSwitcher.setText(seg.getText());
-
- tickerStarting();
- scheduleAdvance();
- }
- }
通過Open Implementation我們跳轉到PhoneStatusBar中的tickerStatrting()中,代碼如下:
- @Override
- public void tickerStarting() {
- mTicking = true;
-
- mIcons.setVisibility(View.GONE);
-
- mTickerView.setVisibility(View.VISIBLE);
-
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
- }
這里可以發(fā)現ticker開加載動畫并顯示了,,實際上Notification在StatusBar上的顯示效果是加載了動畫的原因。繼續(xù)查看scheduleAdvance()方法:- private void scheduleAdvance() {
- mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
- }
通過Handler的PostDelay方法執(zhí)行mAdvanceTicker這個Runnable中的run方法,,代碼如下:- private Runnable mAdvanceTicker = new Runnable() {
- public void run() {
- while (mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
-
- if (seg.first) {
-
-
-
-
- mIconSwitcher.setImageDrawable(seg.icon);
- }
- CharSequence text = seg.advance();
- if (text == null) {
- mSegments.remove(0);
- continue;
- }
-
- mTextSwitcher.setText(text);
-
- scheduleAdvance();
- break;
- }
- if (mSegments.size() == 0) {
-
- tickerDone();
- }
- }
- };
最后我們再來看看tickerDone()方法,,該方法主要對應于tickerStarting()方法,,代碼如下:
- @Override
- public void tickerDone() {
-
- mIcons.setVisibility(View.VISIBLE);
-
- mTickerView.setVisibility(View.GONE);
-
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
- mTickingDoneListener));
- }
至此,我們完成了Notification加載的前兩步,,分別是addNotificationViews(key, notification)和tick(notification),。剩下最后一步,即:setAreThereNotifications()和updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
(3).setAreThereNotifications()和updateExpandedViewPos().這里先說一下setAreThereNotifications()方法,,代碼如下:
- private void setAreThereNotifications() {
-
- final boolean any = mNotificationData.size() > 0;
-
- final boolean clearable = any && mNotificationData.hasClearableItems();
-
- if (DEBUG) {
- Slog.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
- + " any=" + any + " clearable=" + clearable);
- }
-
- if (mClearButton.isShown()) {
- if (clearable != (mClearButton.getAlpha() == 1.0f)) {
- ObjectAnimator.ofFloat(mClearButton, "alpha",
- clearable ? 1.0f : 0.0f)
- .setDuration(250)
- .start();
- }
- } else {
- mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
- }
- mClearButton.setEnabled(clearable);
-
- ...
- }
繼續(xù)分析updateExpandedViewPos(EXPANDED_LEAVE_ALONE);方法,,代碼如下;
以上代碼主要完成對TrackingView的屬性設置,,ExpandedView實際上是包裹在TrackingView中的,因此這里也附帶進行了設置,,最后更新,完成了整個Notification的加載。
小結
通過對Notification加載流程的分析,,對Notifification工作流程有了大致的了解,。針對上文中的分析可能部分地方還有所偏頗,還需要加強自己的代碼閱讀能力,。以下是簡單的時序圖,,如圖3:
圖3
|