Android 5.0重启恢复Task功能分析

81 篇文章 3 订阅

转自:原文 

    Android5.0新增了一个重启后可恢复Task功能。在正常的Activity切换使用过程中AMS会将Task和对应截图进行保存,重启后会将Task和截图恢复到最近任务栏中。开机恢复Task没什么好说的,我们重点研究下Task和截图的保存逻辑,如下。

我们重点分析下screenshotApplications()、notifyTaskPersisterLocked()、LazyTaskWriterThread线程。

1、screenshotApplications()

[html]  view plain  copy
  1. public Bitmap screenshotApplications(IBinder appToken, int displayId, int width,  
  2.         int height, boolean force565) {  
  3.     if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,  
  4.             "screenshotApplications()")) {  
  5.         throw new SecurityException("Requires READ_FRAME_BUFFER permission");  
  6.     }  
  7.   
  8.     final DisplayContent displayContent = getDisplayContentLocked(displayId);  
  9.     if (displayContent == null) {  
  10.         if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken  
  11.                 + ": returning null. No Display for displayId=" + displayId);  
  12.         return null;  
  13.     }  
  14.     final DisplayInfo displayInfo = displayContent.getDisplayInfo();  
  15.     int dw = displayInfo.logicalWidth;  
  16.     int dh = displayInfo.logicalHeight;  
  17.     if (dw == 0 || dh == 0) {  
  18.         if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken  
  19.                 + ": returning null. logical widthxheight=" + dw + "x" + dh);  
  20.         return null;  
  21.     }  
  22.   
  23.     Bitmap bm = null;  
  24.   
  25.     int maxLayer = 0;  
  26.     final Rect frame = new Rect();  
  27.     final Rect stackBounds = new Rect();  
  28.   
  29.     float scale = 0;  
  30.     int rot = Surface.ROTATION_0;  
  31.   
  32.     boolean screenshotReady;  
  33.     int minLayer;  
  34.     if (appToken == null) {  
  35.         screenshotReady = true;  
  36.         minLayer = 0;  
  37.     } else {  
  38.         screenshotReady = false;  
  39.         minLayer = Integer.MAX_VALUE;  
  40.     }  
  41.   
  42.     int retryCount = 0;  
  43.     WindowState appWin = null;  
  44.   
  45.     final boolean appIsImTarget = mInputMethodTarget != null  
  46.             && mInputMethodTarget.mAppToken != null  
  47.             && mInputMethodTarget.mAppToken.appToken != null  
  48.             && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;  
  49.   
  50.     final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)  
  51.             * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;  
  52.   
  53.     while (true) {  
  54.         if (retryCount++ > 0) {  
  55.             // Reset max/min layers on retries so we don't accidentally take a screenshot of a  
  56.             // layer based on the previous try.  
  57.             maxLayer = 0;  
  58.             minLayer = Integer.MAX_VALUE;  
  59.             try {  
  60.                 Thread.sleep(100);  
  61.             } catch (InterruptedException e) {  
  62.             }  
  63.         }  
  64.         synchronized(mWindowMap) {  
  65.             // Figure out the part of the screen that is actually the app.  
  66.             appWin = null;  
  67.             final WindowList windows = displayContent.getWindowList();  
  68.             for (int i = windows.size() - 1; i >= 0; i--) {  
  69.                 WindowState ws = windows.get(i);  
  70.                 if (!ws.mHasSurface) {  
  71.                     continue;  
  72.                 }  
  73.                 if (ws.mLayer >= aboveAppLayer) {  
  74.                     continue;  
  75.                 }  
  76.                 if (ws.mIsImWindow) {  
  77.                     if (!appIsImTarget) {  
  78.                         continue;  
  79.                     }  
  80.                 } else if (ws.mIsWallpaper) {  
  81.                     if (appWin == null) {  
  82.                         // We have not ran across the target window yet, so it is probably  
  83.                         // behind the wallpaper. This can happen when the keyguard is up and  
  84.                         // all windows are moved behind the wallpaper. We don't want to  
  85.                         // include the wallpaper layer in the screenshot as it will coverup  
  86.                         // the layer of the target window.  
  87.                         continue;  
  88.                     }  
  89.                     // Fall through. The target window is in front of the wallpaper. For this  
  90.                     // case we want to include the wallpaper layer in the screenshot because  
  91.                     // the target window might have some transparent areas.  
  92.                 } else if (appToken != null) {  
  93.                     if (ws.mAppToken == null || ws.mAppToken.token != appToken) {  
  94.                         // This app window is of no interest if it is not associated with the  
  95.                         // screenshot app.  
  96.                         continue;  
  97.                     }  
  98.                     appWin = ws;  
  99.                 }  
  100.   
  101.                 // Include this window.  
  102.   
  103.                 final WindowStateAnimator winAnim = ws.mWinAnimator;  
  104.                 if (maxLayer < winAnim.mSurfaceLayer) {  
  105.                     maxLayer = winAnim.mSurfaceLayer;  
  106.                 }  
  107.                 if (minLayer > winAnim.mSurfaceLayer) {  
  108.                     minLayer = winAnim.mSurfaceLayer;  
  109.                 }  
  110.   
  111.                 // Don't include wallpaper in bounds calculation  
  112.                 if (!ws.mIsWallpaper) {  
  113.                     final Rect wf = ws.mFrame;  
  114.                     final Rect cr = ws.mContentInsets;  
  115.                     int left = wf.left + cr.left;  
  116.                     int top = wf.top + cr.top;  
  117.                     int right = wf.right - cr.right;  
  118.                     int bottom = wf.bottom - cr.bottom;  
  119.                     frame.union(left, top, right, bottom);  
  120.                     ws.getStackBounds(stackBounds);  
  121.                     frame.intersect(stackBounds);  
  122.                 }  
  123.   
  124.                 if (ws.mAppToken != null && ws.mAppToken.token == appToken &&  
  125.                         ws.isDisplayedLw()) {  
  126.                     screenshotReady = true;  
  127.                 }  
  128.             }  
  129.   
  130.             if (appToken != null && appWin == null) {  
  131.                 // Can't find a window to snapshot.  
  132.                 if (DEBUG_SCREENSHOT) Slog.i(TAG,  
  133.                         "Screenshot: Couldn't find a surface matching " + appToken);  
  134.                 return null;  
  135.             }  
  136.   
  137.             if (!screenshotReady) {  
  138.                 if (retryCount > MAX_SCREENSHOT_RETRIES) {  
  139.                     Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken +  
  140.                             " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +  
  141.                             appWin.mWinAnimator.mDrawState)));  
  142.                     return null;  
  143.                 }  
  144.   
  145.                 // Delay and hope that window gets drawn.  
  146.                 if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot: No image ready for " + appToken  
  147.                         + ", " + appWin + " drawState=" + appWin.mWinAnimator.mDrawState);  
  148.                 continue;  
  149.             }  
  150.   
  151.             // Screenshot is ready to be taken. Everything from here below will continue  
  152.             // through the bottom of the loop and return a value. We only stay in the loop  
  153.             // because we don't want to release the mWindowMap lock until the screenshot is  
  154.             // taken.  
  155.   
  156.             if (maxLayer == 0) {  
  157.                 if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken  
  158.                         + ": returning null maxLayer=" + maxLayer);  
  159.                 return null;  
  160.             }  
  161.   
  162.             // Constrain frame to the screen size.  
  163.             frame.intersect(0, 0, dw, dh);  
  164.   
  165.             // Tell surface flinger what part of the image to crop. Take the top  
  166.             // right part of the application, and crop the larger dimension to fit.  
  167.             Rect crop = new Rect(frame);  
  168.             if (width / (float) frame.width() < height / (float) frame.height()) {  
  169.                 int cropWidth = (int)((float)width / (float)height * frame.height());  
  170.                 crop.right = crop.left + cropWidth;  
  171.             } else {  
  172.                 int cropHeight = (int)((float)height / (float)width * frame.width());  
  173.                 crop.bottom = crop.top + cropHeight;  
  174.             }  
  175.   
  176.             // The screenshot API does not apply the current screen rotation.  
  177.             rot = getDefaultDisplayContentLocked().getDisplay().getRotation();  
  178.   
  179.             if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {  
  180.                 rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;  
  181.             }  
  182.   
  183.             // Surfaceflinger is not aware of orientation, so convert our logical  
  184.             // crop to surfaceflinger's portrait orientation.  
  185.             convertCropForSurfaceFlinger(crop, rot, dw, dh);  
  186.   
  187.             if (DEBUG_SCREENSHOT) {  
  188.                 Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "  
  189.                         + maxLayer + " appToken=" + appToken);  
  190.                 for (int i = 0; i < windows.size(); i++) {  
  191.                     WindowState win = windows.get(i);  
  192.                     Slog.i(TAG, win + ": " + win.mLayer  
  193.                             + " animLayer=" + win.mWinAnimator.mAnimLayer  
  194.                             + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);  
  195.                 }  
  196.             }  
  197.   
  198.             ScreenRotationAnimation screenRotationAnimation =  
  199.                     mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);  
  200.             final boolean inRotation = screenRotationAnimation != null &&  
  201.                     screenRotationAnimation.isAnimating();  
  202.             if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG,  
  203.                     "Taking screenshot while rotating");  
  204.             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmScreenshot");  
  205.             bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,  
  206.                     inRotation, rot);  
  207.             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);  
  208.             if (bm == null) {  
  209.                 Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh  
  210.                         + ") to layer " + maxLayer);  
  211.                 return null;  
  212.             }  
  213.         }  
  214.   
  215.         break;  
  216.     }  
  217.   
  218.     if (DEBUG_SCREENSHOT) {  
  219.         // TEST IF IT's ALL BLACK  
  220.         int[] buffer = new int[bm.getWidth() * bm.getHeight()];  
  221.         bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());  
  222.         boolean allBlack = true;  
  223.         final int firstColor = buffer[0];  
  224.         for (int i = 0; i < buffer.length; i++) {  
  225.             if (buffer[i] != firstColor) {  
  226.                 allBlack = false;  
  227.                 break;  
  228.             }  
  229.         }  
  230.         if (allBlack) {  
  231.             Slog.i(TAG, "Screenshot " + appWin + " was monochrome(" +  
  232.                     Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +  
  233.                     (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null") +  
  234.                     " minLayer=" + minLayer + " maxLayer=" + maxLayer);  
  235.         }  
  236.     }  
  237.   
  238.     // Copy the screenshot bitmap to another buffer so that the gralloc backed  
  239.     // bitmap will not have a long lifetime. Gralloc memory can be pinned or  
  240.     // duplicated and might have a higher cost than a skia backed buffer.  
  241.     Bitmap ret = bm.copy(bm.getConfig(),true);  
  242.     bm.recycle();  
  243.     return ret;  
  244. }  


2、notifyTaskPersisterLocked()

[html]  view plain  copy
  1. void wakeup(TaskRecord task, boolean flush) {  
  2.     synchronized (this) {  
  3.         if (task != null) {  
  4.             int queueNdx;  
  5.             for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {  
  6.                 final WriteQueueItem item = mWriteQueue.get(queueNdx);  
  7.                 if (item instanceof TaskWriteQueueItem &&  
  8.                         ((TaskWriteQueueItem) item).mTask == task) {  
  9.                     if (!task.inRecents) {  
  10.                         // This task is being removed.  
  11.                         removeThumbnails(task);  
  12.                     }  
  13.                     break;  
  14.                 }  
  15.             }  
  16.             if (queueNdx < 0 && task.isPersistable) {  
  17.                 mWriteQueue.add(new TaskWriteQueueItem(task));  
  18.             }  
  19.         } else {  
  20.             // Dummy.  
  21.             mWriteQueue.add(new WriteQueueItem());  
  22.         }  
  23.         if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {  
  24.             mNextWriteTime = FLUSH_QUEUE;  
  25.         } else if (mNextWriteTime == 0) {  
  26.             mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;  
  27.         }  
  28.         if (DEBUG_PERSISTER) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush  
  29.                 + " mNextWriteTime=" + mNextWriteTime + " mWriteQueue.size="  
  30.                 + mWriteQueue.size() + " Callers=" + Debug.getCallers(4));  
  31.         notifyAll();  
  32.     }  
  33.   
  34.     yieldIfQueueTooDeep();  
  35. }  


3、LazyTaskWriterThread线程

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void run() {  
  2.     ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();  
  3.     while (true) {  
  4.         // We can't lock mService while holding TaskPersister.this, but we don't want to  
  5.         // call removeObsoleteFiles every time through the loop, only the last time before  
  6.         // going to sleep. The risk is that we call removeObsoleteFiles() successively.  
  7.         final boolean probablyDone;  
  8.         synchronized (TaskPersister.this) {  
  9.             probablyDone = mWriteQueue.isEmpty();  
  10.         }  
  11.         if (probablyDone) {  
  12.             if (DEBUG_PERSISTER) Slog.d(TAG, "Looking for obsolete files.");  
  13.             persistentTaskIds.clear();  
  14.             synchronized (mService) {  
  15.                 final ArrayList<TaskRecord> tasks = mService.mRecentTasks;  
  16.                 if (DEBUG_PERSISTER) Slog.d(TAG, "mRecents=" + tasks);  
  17.                 for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {  
  18.                     final TaskRecord task = tasks.get(taskNdx);  
  19.                     if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: task=" + task +  
  20.                             " persistable=" + task.isPersistable);  
  21.                     if ((task.isPersistable || task.inRecents)  
  22.                             && (task.stack == null || !task.stack.isHomeStack())) {  
  23.                         if (DEBUG_PERSISTER)  
  24.                                 Slog.d(TAG, "adding to persistentTaskIds task=" + task);  
  25.                         persistentTaskIds.add(task.taskId);  
  26.                     } else {  
  27.                         if (DEBUG_PERSISTER) Slog.d(TAG,  
  28.                                 "omitting from persistentTaskIds task=" + task);  
  29.                     }  
  30.                 }  
  31.             }  
  32.             removeObsoleteFiles(persistentTaskIds);  
  33.         }  
  34.   
  35.         // If mNextWriteTime, then don't delay between each call to saveToXml().  
  36.         final WriteQueueItem item;  
  37.         synchronized (TaskPersister.this) {  
  38.             if (mNextWriteTime != FLUSH_QUEUE) {  
  39.                 // The next write we don't have to wait so long.  
  40.                 mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;  
  41.                 if (DEBUG_PERSISTER) Slog.d(TAG, "Next write time may be in " +  
  42.                         INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");  
  43.             }  
  44.   
  45.   
  46.             while (mWriteQueue.isEmpty()) {  
  47.                 if (mNextWriteTime != 0) {  
  48.                     mNextWriteTime = 0; // idle.  
  49.                     TaskPersister.this.notifyAll(); // wake up flush() if needed.  
  50.                 }  
  51.   
  52.                 // See if we need to remove any expired back-up tasks before waiting.  
  53.                 removeExpiredTasksIfNeeded();  
  54.   
  55.                 try {  
  56.                     if (DEBUG_PERSISTER)  
  57.                             Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");  
  58.                     TaskPersister.this.wait();  
  59.                 } catch (InterruptedException e) {  
  60.                 }  
  61.                 // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS  
  62.                 // from now.  
  63.             }  
  64.             item = mWriteQueue.remove(0);  
  65.   
  66.             long now = SystemClock.uptimeMillis();  
  67.             if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: now=" + now  
  68.                         + " mNextWriteTime=" + mNextWriteTime + " mWriteQueue.size="  
  69.                         + mWriteQueue.size());  
  70.             while (now < mNextWriteTime) {  
  71.                 try {  
  72.                     if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: waiting " +  
  73.                             (mNextWriteTime - now));  
  74.                     TaskPersister.this.wait(mNextWriteTime - now);  
  75.                 } catch (InterruptedException e) {  
  76.                 }  
  77.                 now = SystemClock.uptimeMillis();  
  78.             }  
  79.   
  80.             // Got something to do.  
  81.         }  
  82.   
  83.         if (item instanceof ImageWriteQueueItem) {  
  84.             ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;  
  85.             final String filename = imageWriteQueueItem.mFilename;  
  86.             final Bitmap bitmap = imageWriteQueueItem.mImage;  
  87.             if (DEBUG_PERSISTER) Slog.d(TAG, "writing bitmap: filename=" + filename);  
  88.             FileOutputStream imageFile = null;  
  89.             try {  
  90.                 imageFile = new FileOutputStream(new File(sImagesDir, filename));  
  91.                 bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);  
  92.             } catch (Exception e) {  
  93.                 Slog.e(TAG, "saveImage: unable to save " + filename, e);  
  94.             } finally {  
  95.                 IoUtils.closeQuietly(imageFile);  
  96.             }  
  97.         } else if (item instanceof TaskWriteQueueItem) {  
  98.             // Write out one task.  
  99.             StringWriter stringWriter = null;  
  100.             TaskRecord task = ((TaskWriteQueueItem) item).mTask;  
  101.             if (DEBUG_PERSISTER) Slog.d(TAG, "Writing task=" + task);  
  102.             synchronized (mService) {  
  103.                 if (task.inRecents) {  
  104.                     // Still there.  
  105.                     try {  
  106.                         if (DEBUG_PERSISTER) Slog.d(TAG, "Saving task=" + task);  
  107.                         stringWriter = saveToXml(task);  
  108.                     } catch (IOException e) {  
  109.                     } catch (XmlPullParserException e) {  
  110.                     }  
  111.                 }  
  112.             }  
  113.             if (stringWriter != null) {  
  114.                 // Write out xml file while not holding mService lock.  
  115.                 FileOutputStream file = null;  
  116.                 AtomicFile atomicFile = null;  
  117.                 try {  
  118.                     atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf(  
  119.                             task.taskId) + RECENTS_FILENAME + TASK_EXTENSION));  
  120.                     file = atomicFile.startWrite();  
  121.                     file.write(stringWriter.toString().getBytes());  
  122.                     file.write('\n');  
  123.                     atomicFile.finishWrite(file);  
  124.                 } catch (IOException e) {  
  125.                     if (file != null) {  
  126.                         atomicFile.failWrite(file);  
  127.                     }  
  128.                     Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " +  
  129.                             e);  
  130.                 }  
  131.             }  
  132.         }  
  133.     }  
  134. }  


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值