編輯:關於Android編程
Android5.0新增了一個重啟後可恢復Task功能。在正常的Activity切換使用過程中AMS會將Task和對應截圖進行保存,重啟後會將Task和截圖恢復到最近任務欄中。開機恢復Task沒什麼好說的,我們重點研究下Task和截圖的保存邏輯,如下。
我們重點分析下screenshotApplications()、notifyTaskPersisterLocked()、LazyTaskWriterThread線程。
1、screenshotApplications()
public Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height, boolean force565) { if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER, screenshotApplications())) { throw new SecurityException(Requires READ_FRAME_BUFFER permission); } final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent == null) { if (DEBUG_SCREENSHOT) Slog.i(TAG, Screenshot of + appToken + : returning null. No Display for displayId= + displayId); return null; } final DisplayInfo displayInfo = displayContent.getDisplayInfo(); int dw = displayInfo.logicalWidth; int dh = displayInfo.logicalHeight; if (dw == 0 || dh == 0) { if (DEBUG_SCREENSHOT) Slog.i(TAG, Screenshot of + appToken + : returning null. logical widthxheight= + dw + x + dh); return null; } Bitmap bm = null; int maxLayer = 0; final Rect frame = new Rect(); final Rect stackBounds = new Rect(); float scale = 0; int rot = Surface.ROTATION_0; boolean screenshotReady; int minLayer; if (appToken == null) { screenshotReady = true; minLayer = 0; } else { screenshotReady = false; minLayer = Integer.MAX_VALUE; } int retryCount = 0; WindowState appWin = null; final boolean appIsImTarget = mInputMethodTarget != null && mInputMethodTarget.mAppToken != null && mInputMethodTarget.mAppToken.appToken != null && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken; final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; while (true) { if (retryCount++ > 0) { // Reset max/min layers on retries so we don't accidentally take a screenshot of a // layer based on the previous try. maxLayer = 0; minLayer = Integer.MAX_VALUE; try { Thread.sleep(100); } catch (InterruptedException e) { } } synchronized(mWindowMap) { // Figure out the part of the screen that is actually the app. appWin = null; final WindowList windows = displayContent.getWindowList(); for (int i = windows.size() - 1; i >= 0; i--) { WindowState ws = windows.get(i); if (!ws.mHasSurface) { continue; } if (ws.mLayer >= aboveAppLayer) { continue; } if (ws.mIsImWindow) { if (!appIsImTarget) { continue; } } else if (ws.mIsWallpaper) { if (appWin == null) { // We have not ran across the target window yet, so it is probably // behind the wallpaper. This can happen when the keyguard is up and // all windows are moved behind the wallpaper. We don't want to // include the wallpaper layer in the screenshot as it will coverup // the layer of the target window. continue; } // Fall through. The target window is in front of the wallpaper. For this // case we want to include the wallpaper layer in the screenshot because // the target window might have some transparent areas. } else if (appToken != null) { if (ws.mAppToken == null || ws.mAppToken.token != appToken) { // This app window is of no interest if it is not associated with the // screenshot app. continue; } appWin = ws; } // Include this window. final WindowStateAnimator winAnim = ws.mWinAnimator; if (maxLayer < winAnim.mSurfaceLayer) { maxLayer = winAnim.mSurfaceLayer; } if (minLayer > winAnim.mSurfaceLayer) { minLayer = winAnim.mSurfaceLayer; } // Don't include wallpaper in bounds calculation if (!ws.mIsWallpaper) { final Rect wf = ws.mFrame; final Rect cr = ws.mContentInsets; int left = wf.left + cr.left; int top = wf.top + cr.top; int right = wf.right - cr.right; int bottom = wf.bottom - cr.bottom; frame.union(left, top, right, bottom); ws.getStackBounds(stackBounds); frame.intersect(stackBounds); } if (ws.mAppToken != null && ws.mAppToken.token == appToken && ws.isDisplayedLw()) { screenshotReady = true; } } if (appToken != null && appWin == null) { // Can't find a window to snapshot. if (DEBUG_SCREENSHOT) Slog.i(TAG, Screenshot: Couldn't find a surface matching + appToken); return null; } if (!screenshotReady) { if (retryCount > MAX_SCREENSHOT_RETRIES) { Slog.i(TAG, Screenshot max retries + retryCount + of + appToken + appWin= + (appWin == null ? null : (appWin + drawState= + appWin.mWinAnimator.mDrawState))); return null; } // Delay and hope that window gets drawn. if (DEBUG_SCREENSHOT) Slog.i(TAG, Screenshot: No image ready for + appToken + , + appWin + drawState= + appWin.mWinAnimator.mDrawState); continue; } // Screenshot is ready to be taken. Everything from here below will continue // through the bottom of the loop and return a value. We only stay in the loop // because we don't want to release the mWindowMap lock until the screenshot is // taken. if (maxLayer == 0) { if (DEBUG_SCREENSHOT) Slog.i(TAG, Screenshot of + appToken + : returning null maxLayer= + maxLayer); return null; } // Constrain frame to the screen size. frame.intersect(0, 0, dw, dh); // Tell surface flinger what part of the image to crop. Take the top // right part of the application, and crop the larger dimension to fit. Rect crop = new Rect(frame); if (width / (float) frame.width() < height / (float) frame.height()) { int cropWidth = (int)((float)width / (float)height * frame.height()); crop.right = crop.left + cropWidth; } else { int cropHeight = (int)((float)height / (float)width * frame.width()); crop.bottom = crop.top + cropHeight; } // The screenshot API does not apply the current screen rotation. rot = getDefaultDisplayContentLocked().getDisplay().getRotation(); if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) { rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90; } // Surfaceflinger is not aware of orientation, so convert our logical // crop to surfaceflinger's portrait orientation. convertCropForSurfaceFlinger(crop, rot, dw, dh); if (DEBUG_SCREENSHOT) { Slog.i(TAG, Screenshot: + dw + x + dh + from + minLayer + to + maxLayer + appToken= + appToken); for (int i = 0; i < windows.size(); i++) { WindowState win = windows.get(i); Slog.i(TAG, win + : + win.mLayer + animLayer= + win.mWinAnimator.mAnimLayer + surfaceLayer= + win.mWinAnimator.mSurfaceLayer); } } ScreenRotationAnimation screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); final boolean inRotation = screenRotationAnimation != null && screenRotationAnimation.isAnimating(); if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG, Taking screenshot while rotating); Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, wmScreenshot); bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer, inRotation, rot); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); if (bm == null) { Slog.w(TAG, Screenshot failure taking screenshot for ( + dw + x + dh + ) to layer + maxLayer); return null; } } break; } if (DEBUG_SCREENSHOT) { // TEST IF IT's ALL BLACK int[] buffer = new int[bm.getWidth() * bm.getHeight()]; bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight()); boolean allBlack = true; final int firstColor = buffer[0]; for (int i = 0; i < buffer.length; i++) { if (buffer[i] != firstColor) { allBlack = false; break; } } if (allBlack) { Slog.i(TAG, Screenshot + appWin + was monochrome( + Integer.toHexString(firstColor) + )! mSurfaceLayer= + (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : null) + minLayer= + minLayer + maxLayer= + maxLayer); } } // Copy the screenshot bitmap to another buffer so that the gralloc backed // bitmap will not have a long lifetime. Gralloc memory can be pinned or // duplicated and might have a higher cost than a skia backed buffer. Bitmap ret = bm.copy(bm.getConfig(),true); bm.recycle(); return ret; }
2、notifyTaskPersisterLocked()
void wakeup(TaskRecord task, boolean flush) { synchronized (this) { if (task != null) { int queueNdx; for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) { final WriteQueueItem item = mWriteQueue.get(queueNdx); if (item instanceof TaskWriteQueueItem && ((TaskWriteQueueItem) item).mTask == task) { if (!task.inRecents) { // This task is being removed. removeThumbnails(task); } break; } } if (queueNdx < 0 && task.isPersistable) { mWriteQueue.add(new TaskWriteQueueItem(task)); } } else { // Dummy. mWriteQueue.add(new WriteQueueItem()); } if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) { mNextWriteTime = FLUSH_QUEUE; } else if (mNextWriteTime == 0) { mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS; } if (DEBUG_PERSISTER) Slog.d(TAG, wakeup: task= + task + flush= + flush + mNextWriteTime= + mNextWriteTime + mWriteQueue.size= + mWriteQueue.size() + Callers= + Debug.getCallers(4)); notifyAll(); } yieldIfQueueTooDeep(); }
3、LazyTaskWriterThread線程
public void run() { ArraySetpersistentTaskIds = new ArraySet (); while (true) { // We can't lock mService while holding TaskPersister.this, but we don't want to // call removeObsoleteFiles every time through the loop, only the last time before // going to sleep. The risk is that we call removeObsoleteFiles() successively. final boolean probablyDone; synchronized (TaskPersister.this) { probablyDone = mWriteQueue.isEmpty(); } if (probablyDone) { if (DEBUG_PERSISTER) Slog.d(TAG, Looking for obsolete files.); persistentTaskIds.clear(); synchronized (mService) { final ArrayList tasks = mService.mRecentTasks; if (DEBUG_PERSISTER) Slog.d(TAG, mRecents= + tasks); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = tasks.get(taskNdx); if (DEBUG_PERSISTER) Slog.d(TAG, LazyTaskWriter: task= + task + persistable= + task.isPersistable); if ((task.isPersistable || task.inRecents) && (task.stack == null || !task.stack.isHomeStack())) { if (DEBUG_PERSISTER) Slog.d(TAG, adding to persistentTaskIds task= + task); persistentTaskIds.add(task.taskId); } else { if (DEBUG_PERSISTER) Slog.d(TAG, omitting from persistentTaskIds task= + task); } } } removeObsoleteFiles(persistentTaskIds); } // If mNextWriteTime, then don't delay between each call to saveToXml(). final WriteQueueItem item; synchronized (TaskPersister.this) { if (mNextWriteTime != FLUSH_QUEUE) { // The next write we don't have to wait so long. mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS; if (DEBUG_PERSISTER) Slog.d(TAG, Next write time may be in + INTER_WRITE_DELAY_MS + msec. ( + mNextWriteTime + )); } while (mWriteQueue.isEmpty()) { if (mNextWriteTime != 0) { mNextWriteTime = 0; // idle. TaskPersister.this.notifyAll(); // wake up flush() if needed. } // See if we need to remove any expired back-up tasks before waiting. removeExpiredTasksIfNeeded(); try { if (DEBUG_PERSISTER) Slog.d(TAG, LazyTaskWriter: waiting indefinitely.); TaskPersister.this.wait(); } catch (InterruptedException e) { } // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS // from now. } item = mWriteQueue.remove(0); long now = SystemClock.uptimeMillis(); if (DEBUG_PERSISTER) Slog.d(TAG, LazyTaskWriter: now= + now + mNextWriteTime= + mNextWriteTime + mWriteQueue.size= + mWriteQueue.size()); while (now < mNextWriteTime) { try { if (DEBUG_PERSISTER) Slog.d(TAG, LazyTaskWriter: waiting + (mNextWriteTime - now)); TaskPersister.this.wait(mNextWriteTime - now); } catch (InterruptedException e) { } now = SystemClock.uptimeMillis(); } // Got something to do. } if (item instanceof ImageWriteQueueItem) { ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item; final String filename = imageWriteQueueItem.mFilename; final Bitmap bitmap = imageWriteQueueItem.mImage; if (DEBUG_PERSISTER) Slog.d(TAG, writing bitmap: filename= + filename); FileOutputStream imageFile = null; try { imageFile = new FileOutputStream(new File(sImagesDir, filename)); bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile); } catch (Exception e) { Slog.e(TAG, saveImage: unable to save + filename, e); } finally { IoUtils.closeQuietly(imageFile); } } else if (item instanceof TaskWriteQueueItem) { // Write out one task. StringWriter stringWriter = null; TaskRecord task = ((TaskWriteQueueItem) item).mTask; if (DEBUG_PERSISTER) Slog.d(TAG, Writing task= + task); synchronized (mService) { if (task.inRecents) { // Still there. try { if (DEBUG_PERSISTER) Slog.d(TAG, Saving task= + task); stringWriter = saveToXml(task); } catch (IOException e) { } catch (XmlPullParserException e) { } } } if (stringWriter != null) { // Write out xml file while not holding mService lock. FileOutputStream file = null; AtomicFile atomicFile = null; try { atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf( task.taskId) + RECENTS_FILENAME + TASK_EXTENSION)); file = atomicFile.startWrite(); file.write(stringWriter.toString().getBytes()); file.write(' '); atomicFile.finishWrite(file); } catch (IOException e) { if (file != null) { atomicFile.failWrite(file); } Slog.e(TAG, Unable to open + atomicFile + for persisting. + e); } } } } }
本文實例講述了Android編程之藍牙測試。分享給大家供大家參考。具體分析如下:一、軟件平台:win7 + eclipse + sdk二、設計思路:配合倒計時定時器實現藍
本文實例講述了Android實現Service獲取當前位置(GPS+基站)的方法。分享給大家供大家參考。具體如下:需求詳情:1)、Service中每隔1秒執行一次定位操作
Usage xml android:background= ?attr/zzbackground app:backgroundAttr= zzbackground
魅藍Max和小米Max哪個好?在小米Max發布了四個月了,魅族旗下的子系列魅藍Max也跟隨腳步發布了,那麼對於兩款都是“MAX”的手