从问题出发:捕获监听android 栈顶Activity的resume变化

熟悉Android framework的同学都清楚Activity 进出栈是依靠ActivityStack.java(android/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java)进行管理,但是,对ActivityStack不熟悉的同学或者没有接触的,请参考下面的文章.

    最近做项目,有一个简单的需求就是当手机空间不足时,需要弹出对话框进行提示用户并且当在非删除文件的Activity界面也需要立即弹出对话框提示。换句通俗的话,是当储存空间不足时,只显示可以删除文件的Activity,其他Activity都必须在提示对话框的后面。android 本身自带检测存储检测:frameworks/base/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java 中的
 /**
   * Core logic that checks the storage state of every mounted private volume.
   * Since this can do heavy I/O, callers should invoke indirectly using
   * {@link #MSG_CHECK}.
     */
     @WorkerThread
    private void check() {
          final StorageManager storage = getContext().getSystemService(StorageManager.class);
          final int seq = mSeq.get();

           for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
               final File file = vol.getPath();
               final long fullBytes = storage.getStorageFullBytes(file);
               final long lowBytes = storage.getStorageLowBytes(file);
   
               // Automatically trim cached data when nearing the low threshold;
               // when it's within 150% of the threshold, we try trimming usage
               // back to 200% of the threshold.
               if (file.getUsableSpace() < (lowBytes * 3) / 2) {
                   final PackageManagerService pms = (PackageManagerService) ServiceManager
                          .getService("package");
                  try {
                      pms.freeStorage(vol.getFsUuid(), lowBytes * 2, 0);
                  } catch (IOException e) {
                      Slog.w(TAG, e);
                   }
               }
   
               // Send relevant broadcasts and show notifications based on any
               // recently noticed state transitions.
               final UUID uuid = StorageManager.convert(vol.getFsUuid());
               final State state = findOrCreateState(uuid);
               final long totalBytes = file.getTotalSpace();
               final long usableBytes = file.getUsableSpace();
   
               int oldLevel = state.level;
               int newLevel;
               if (mForceLevel != State.LEVEL_UNKNOWN) {
                   // When in testing mode, use unknown old level to force sending
                   // of any relevant broadcasts.
                   oldLevel = State.LEVEL_UNKNOWN;
                   newLevel = mForceLevel;
               } else if (usableBytes <= fullBytes) {
                   newLevel = State.LEVEL_FULL;
               } else if (usableBytes <= lowBytes) {
                   newLevel = State.LEVEL_LOW;
               } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
                       && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
                   newLevel = State.LEVEL_LOW;
               } else {
                   newLevel = State.LEVEL_NORMAL;
               }
   
               // Log whenever we notice drastic storage changes
               if ((Math.abs(state.lastUsableBytes - usableBytes) > DEFAULT_LOG_DELTA_BYTES)
                       || oldLevel != newLevel) {
                   EventLogTags.writeStorageState(uuid.toString(), oldLevel, newLevel,
                           usableBytes, totalBytes);
                   state.lastUsableBytes = usableBytes;
               }
                //若LEVEL_LOW,则显示Notification.
               updateNotifications(vol, oldLevel, newLevel);
             //进行发各种广播进行通知

               updateBroadcasts(vol, oldLevel, newLevel, seq);
               state.level = newLevel;
           }

          }
      }
  但是,此方法缺陷是周期性检查,而不是立刻可以反馈出来。
  针对此疑问思考和分析:
  [1]根据之前的工作经验,发现event log 可以实时监听那个Activity 是resume的。
    譬如:
      1]使用adb logcat命令并过滤:
          adb logcat -b events | grep "resume"

           (1)从launcher进入Camera界面:
            10-24 03:32:22.631   846  1654 I am_set_resumed_activity: [0,com.mediatek.camera/.CameraLauncher,minimalResumeActivityLocked]
10-24 03:32:23.327  8799  8799 I am_on_resume_called: [0,com.mediatek.camera.CameraLauncher,LAUNCH_ACTIVITY]
           (2)Camera 进入photos:

10-24 03:33:00.272   846  5182 I am_set_resumed_activity: [0,com.google.android.apps.photos/.pager.HostPhotoPagerActivity,minimalResumeActivityLocked]
10-24 03:33:01.124  8908  8908 I am_on_resume_called: [0,com.google.android.apps.photos.pager.HostPhotoPagerActivity,LAUNCH_ACTIVITY]
         (3)从Photos 退出到Camera:
   10-24 03:33:52.308   846  6191 I am_set_resumed_activity: [0,com.mediatek.camera/.CameraLauncher,resumeTopActivityInnerLocked]
10-24 03:33:52.319   846  6191 I am_resume_activity: [0,113921636,107,com.mediatek.camera/.CameraLauncher]
10-24 03:33:52.470  8799  8799 I am_on_resume_called: [0,com.mediatek.camera.CameraLauncher,RESUME_ACTIVITY]
      (4)再退出到launcher:

10-24 03:34:47.892   846  2056 I am_set_resumed_activity: [0,com.android.launcher3/.Launcher,resumeTopActivityInnerLocked]
10-24 03:34:47.901   846  2056 I am_resume_activity: [0,159438434,89,com.android.launcher3/.Launcher]
10-24 03:34:47.919  1663  1663 I am_on_resume_called: [0,com.android.launcher3.Launcher,RESUME_ACTIVITY]

[2]发现每次activity 栈顶切换时,都会出现 am_set_resumed_activity 和 am_resume_activity 的log,
     根据这两个关键字在源码中搜索:
     发现只有一个文件中显示:
     android/frameworks/base/services/core/java/com/android/server/am/EventLogTags.logtags
     

# Activity set to resumed
30043 am_set_resumed_activity (User|1|5),(Component Name|3),(Reason|3)
# An activity has been resumed and is now in the foreground:
30007 am_resume_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3)
[3]去掉关键字的"_"在源码中搜索出现的地方:
      很遗憾"amresumeactivity"没有搜索到,有可能是打包到其他文件中。
      但是,在中android/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 搜索到amsetresumedactivity关键字.
        /**
        * Update AMS states when an activity is resumed. This should only be called by
        * {@link ActivityStack#onActivityStateChanged(ActivityRecord, ActivityState, String)} when an
        * activity is resumed.
        */
       @GuardedBy("this")
       void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
           final TaskRecord task = r.getTask();
           if (task.isActivityTypeStandard()) {
               if (mCurAppTimeTracker != r.appTimeTracker) {
                   // We are switching app tracking.  Complete the current one.
                   if (mCurAppTimeTracker != null) {
                       mCurAppTimeTracker.stop();
                       mHandler.obtainMessage(
                               REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
                       mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
                       mCurAppTimeTracker = null;
                   }
                   if (r.appTimeTracker != null) {
                       mCurAppTimeTracker = r.appTimeTracker;
                       startTimeTrackingFocusedActivityLocked();
                   }
               } else {
                   startTimeTrackingFocusedActivityLocked();
               }
           } else {
               r.appTimeTracker = null;
           }
           // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null
           // TODO: Probably not, because we don't want to resume voice on switching
           // back to this activity
           if (task.voiceInteractor != null) {
               startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid);
           } else {
               finishRunningVoiceLocked();
   
               if (mLastResumedActivity != null) {
                   final IVoiceInteractionSession session;
   
                   final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
                   if (lastResumedActivityTask != null
                           && lastResumedActivityTask.voiceSession != null) {
                       session = lastResumedActivityTask.voiceSession;
                   } else {
                       session = mLastResumedActivity.voiceSession;
                   }
   
                   if (session != null) {
                       // We had been in a voice interaction session, but now focused has
                       // move to something different.  Just finish the session, we can't
                       // return to it and retain the proper state and synchronization with
                       // the voice interaction service.
                       finishVoiceTask(session);
                   }
               }
           }
   
           if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
               mUserController.sendForegroundProfileChanged(r.userId);
           }
           updateResumedAppTrace(r);
           mLastResumedActivity = r;
   
           mWindowManager.setFocusedApp(r.appToken, true);
   
           applyUpdateLockStateLocked(r);
           applyUpdateVrModeLocked(r);
   
           EventLogTags.writeAmSetResumedActivity(
                   r == null ? -1 : r.userId,
                   r == null ? "NULL" : r.shortComponentName,
                   reason);
       }
[4]我们找到打印log的地方,其实,我们只要打印log的下方写上自己逻辑即可,但是,我们想再深究一下。搜索是谁调用setResumedActivityUncheckLocked方法,发现是:
android/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
/**
        * This should be called when an activity in a child task changes state. This should only
        * be called from
        * {@link TaskRecord#onActivityStateChanged(ActivityRecord, ActivityState, String)}.
        * @param record The {@link ActivityRecord} whose state has changed.
        * @param state The new state.
        * @param reason The reason for the change.
        */
       void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
           if (record == mResumedActivity && state != RESUMED) {
               setResumedActivity(null, reason + " - onActivityStateChanged");
           }
   
           if (state == RESUMED) {
               if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
                       + reason);
               setResumedActivity(record, reason + " - onActivityStateChanged");
               mService.setResumedActivityUncheckLocked(record, reason);
               mStackSupervisor.mRecentTasks.add(record.getTask());
           }
       }
     此方法注释写的比较清楚,当Activity的栈发生改变的时候,就触发此方法。若继续深入,必须了解ActivityRecord、TaskRecord、ActivityStack 之间的关系,网上介绍这块的内容比较多,可以耐心的查找阅读。
      本文仅仅介绍遇到问题顺藤摸瓜的定位和解决方案.

你可能感兴趣的:(Activity,ActivityStack)