本人对Java可谓是一窍不通,工作主要是用C,C++,但最近工作需要,分析了一下ICS关于打开最近应用的程序如何获得程序的缩略图,要说详细步骤可能没有看仔细,现将分析过程稍作总结如下,供大家参考:
通过logcat分析找打了一个突破口:
1:frameworks/base/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java中
public void refreshRecentTasksList() { refreshRecentTasksList(null); } private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) { if (mRecentTasksDirty) { if (recentTasksList != null) { mRecentTaskDescriptions = recentTasksList; } else { mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks(); //注意mRecentTasksLoader.getRecentTasks } mListAdapter.notifyDataSetInvalidated(); updateUiElements(getResources().getConfiguration()); mRecentTasksDirty = false; } }
2:frameworks/base/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java中
ArrayList<TaskDescription> getRecentTasks() { cancelLoadingThumbnails(); ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>(); final PackageManager pm = mContext.getPackageManager(); final ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); final List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE); ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) .resolveActivityInfo(pm, 0); HashSet<Integer> recentTasksToKeepInCache = new HashSet<Integer>(); 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);////分析一下createTaskDescription 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<TaskDescription>(tasks)); ////个人认为这个函数就是加载缩略图的 return tasks; }
======
TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent, ComponentName origActivity, CharSequence description, ActivityInfo homeInfo) { Intent intent = new Intent(baseIntent); if (origActivity != null) { intent.setComponent(origActivity); } final PackageManager pm = mContext.getPackageManager(); if (homeInfo == null) { homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) .resolveActivityInfo(pm, 0); } intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) | Intent.FLAG_ACTIVITY_NEW_TASK); final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); if (resolveInfo != null) { final ActivityInfo info = resolveInfo.activityInfo; final String title = info.loadLabel(pm).toString(); Drawable icon = getFullResIcon(resolveInfo, pm); if (title != null && title.length() > 0 && icon != null) { if (DEBUG) Log.v(TAG, "creating activity desc for id=" + persistentTaskId + ", label=" + title); TaskDescription item = new TaskDescription(taskId, persistentTaskId, resolveInfo, baseIntent, info.packageName, description); item.setLabel(title); //设置标题 item.setIcon(icon); //设置icon // Don't load the current home activity. if (homeInfo != null && homeInfo.packageName.equals(intent.getComponent().getPackageName()) && homeInfo.name.equals(intent.getComponent().getClassName())) { return null; } return item; } else { if (DEBUG) Log.v(TAG, "SKIPPING item " + persistentTaskId); } } return null; }
======
void loadThumbnail(TaskDescription td) { final ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(td.persistentTaskId); //going into ActivityManager.java if (DEBUG) Log.v(TAG, "Loaded bitmap for task " + td + ": " + thumbs.mainThumbnail); synchronized (td) { if (thumbs != null && thumbs.mainThumbnail != null) { td.setThumbnail(thumbs.mainThumbnail); } else { td.setThumbnail(mDefaultThumbnailBackground); } } }
3:frameworks/base/core/java/android/app/ActivityManager.java
public TaskThumbnails getTaskThumbnails(int id) throws SecurityException { try { return ActivityManagerNative.getDefault().getTaskThumbnails(id); ////注意此返回值 } catch (RemoteException e) { // System dead, we will be dead too soon! return null; } }
4:frameworks/base/core/java/android/app/ActivityManagerNative.java
1:static public IActivityManager getDefault() { return gDefault.get(); } ////IActivityManager java牛人能否告知IActivityManager什么意思啊,查找android源码没有发现对应的java文件 2: 同时跟踪代码还发现执行了以下的程序,本人所知有限,无法解释: case GET_TASK_THUMBNAILS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int id = data.readInt(); ActivityManager.TaskThumbnails bm = getTaskThumbnails(id); reply.writeNoException(); if (bm != null) { reply.writeInt(1); bm.writeToParcel(reply, 0); } else { reply.writeInt(0); } return true; }
5:frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
关于如何跳到这个函数中执行,还请高手不吝指教
public ActivityManager.TaskThumbnails getTaskThumbnails(int id) { Log.d(TAG,"++++sam debug ActivityManagerService.java----getTaskThumbnails id:" + id ); synchronized (this) { enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, "getTaskThumbnails()"); TaskRecord tr = taskForIdLocked(id); if (tr != null) { return mMainStack.getTaskThumbnailsLocked(tr); ////注意此函数 } } return null; }
6:frameworks/base/services/java/com/android/server/am/ActivityStack.java
public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) { TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true); ActivityRecord resumed = mResumedActivity; if (resumed != null && resumed.thumbHolder == tr) { info.mainThumbnail = resumed.stack.screenshotActivities(resumed); } else { info.mainThumbnail = tr.lastThumbnail; //// ****通过分析 程序走的是else分支,tr.lastThumbnail从何而来??????
} return info; }
7:frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
public Bitmap screenshotApplications(IBinder appToken, int width, int height) { if (!checkCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, "screenshotApplications()")) { throw new SecurityException("Requires READ_FRAME_BUFFER permission"); } Bitmap rawss; int maxLayer = 0; final Rect frame = new Rect(); float scale; int dw, dh; int rot; synchronized(mWindowMap) { long ident = Binder.clearCallingIdentity(); dw = mAppDisplayWidth; dh = mAppDisplayHeight; int aboveAppLayer = mPolicy.windowTypeToLayerLw( WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; aboveAppLayer += TYPE_LAYER_MULTIPLIER; boolean isImeTarget = mInputMethodTarget != null && mInputMethodTarget.mAppToken != null && mInputMethodTarget.mAppToken.appToken != null && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken; // Figure out the part of the screen that is actually the app. boolean including = false; for (int i=mWindows.size()-1; i>=0; i--) { WindowState ws = mWindows.get(i); if (ws.mSurface == null) { continue; } if (ws.mLayer >= aboveAppLayer) { continue; } // When we will skip windows: when we are not including // ones behind a window we didn't skip, and we are actually // taking a screenshot of a specific app. if (!including && appToken != null) { // Also, we can possibly skip this window if it is not // an IME target or the application for the screenshot // is not the current IME target. if (!ws.mIsImWindow || !isImeTarget) { // And finally, this window is of no interest if it // is not associated with the screenshot app. if (ws.mAppToken == null || ws.mAppToken.token != appToken) { continue; } } } // We keep on including windows until we go past a full-screen // window. including = !ws.mIsImWindow && !ws.isFullscreen(dw, dh); if (maxLayer < ws.mAnimLayer) { maxLayer = ws.mAnimLayer; } // 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); } } Binder.restoreCallingIdentity(ident); // Constrain frame to the screen size. frame.intersect(0, 0, dw, dh); if (frame.isEmpty() || maxLayer == 0) { return null; } // The screenshot API does not apply the current screen rotation. rot = mDisplay.getRotation(); int fw = frame.width(); int fh = frame.height(); // Constrain thumbnail to smaller of screen width or height. Assumes aspect // of thumbnail is the same as the screen (in landscape) or square. float targetWidthScale = width / (float) fw; float targetHeightScale = height / (float) fh; if (dw <= dh) { scale = targetWidthScale; // If aspect of thumbnail is the same as the screen (in landscape), // select the slightly larger value so we fill the entire bitmap if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) { scale = targetHeightScale; } } else { scale = targetHeightScale; // If aspect of thumbnail is the same as the screen (in landscape), // select the slightly larger value so we fill the entire bitmap if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) { scale = targetWidthScale; } } // The screen shot will contain the entire screen. dw = (int)(dw*scale); dh = (int)(dh*scale); if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) { int tmp = dw; dw = dh; dh = tmp; rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90; } if (DEBUG_SCREENSHOT) { Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer); for (int i=0; i<mWindows.size(); i++) { Slog.i(TAG, mWindows.get(i) + ": " + mWindows.get(i).mLayer + " animLayer=" + mWindows.get(i).mAnimLayer + " surfaceLayer=" + mWindows.get(i).mSurfaceLayer); } } rawss = Surface.screenshot(dw, dh, 0, maxLayer); } if (rawss == null) { Slog.w(TAG, "Failure taking screenshot for (" + dw + "x" + dh + ") to layer " + maxLayer); return null; } Bitmap bm = Bitmap.createBitmap(width, height, rawss.getConfig()); Matrix matrix = new Matrix(); ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix); matrix.postTranslate(-(int)(frame.left*scale), -(int)(frame.top*scale)); Canvas canvas = new Canvas(bm); canvas.drawBitmap(rawss, matrix, null); canvas.setBitmap(null); rawss.recycle(); return bm; }
由于我需要的是将缩略图翻转相应的度数,修改7中rot的值即可。