[轉]截圖原理(一)——Android自動化測試學習歷程(2)
阿新 • • 發佈:2019-01-01
三、Robotium的截圖處理的程式碼分析
步驟:
(1)
程式碼分析:
追本溯源,開始找路。。。
第一步跳轉到的函式:takeScreenshot(String name)
複製程式碼
/**
* Takes a screenshot and saves it with the specified name in "/sdcard/Robotium-Screenshots/".
* Requires write permission (android.permission.WRITE_EXTERNAL_STORAGE) in AndroidManifest.xml of the application under test.
*
* @param name the name to give the screenshot
*
*/
//上面的話翻譯下來就是:儲存的位置確定了,就是在mnt/sdcard/Robotium-Screenshots/目錄下
//但是需要寫sd卡的許可權,需要給under test的application在AndroidManifest.xml中配置permission,那麼這裡也就解釋了我上面的執行過程中第二個問題
public void takeScreenshot(String name){
takeScreenshot(name, 100);
}
複製程式碼
第二步跳轉到的函式:takeScreenshot(String name, int quality)
複製程式碼
/**
* Takes a screenshot and saves the image with the specified name in "/sdcard/Robotium-Screenshots/".
* Requires write permission (android.permission.WRITE_EXTERNAL_STORAGE) in AndroidManifest.xml of the application under test.
*
* @param name the name to give the screenshot
* @param quality the compression rate. From 0 (compress for lowest size) to 100 (compress for maximum quality)
*
*/
//上面的話翻譯下來就是:圖片儲存位置以及讀寫許可權與第一步中相同
//引數分別表示picture的name,以及清晰度(從0到100),預設是100,當然你也可以直接在函式中呼叫這個函式,然後設定這個quality的值
public void takeScreenshot(String name, int quality){
screenshotTaker.takeScreenshot(name, quality);
}
複製程式碼
第三步跳轉到的函式:screenshotTaker.takeScreenshot(String name, int quality)
複製程式碼
/**
* Takes a screenshot and saves it in "/sdcard/Robotium-Screenshots/".
* Requires write permission (android.permission.WRITE_EXTERNAL_STORAGE) in AndroidManifest.xml of the application under test.
*
* @param view the view to take screenshot of
* @param name the name to give the screenshot image
* @param quality the compression rate. From 0 (compress for lowest size) to 100 (compress for maximum quality).
*/
//第三步走到了一個新的類中,是screenShotTaker的類
//這個才是真正的執行Screenshot的函式,這個才是截圖的邏輯
public void takeScreenshot(final String name, final int quality) {
//1、得到目前螢幕所有檢視
View decorView = getScreenshotView();
if(decorView == null)
return;
//2、初始化
initScreenShotSaver();
//3、例項化截圖物件
ScreenshotRunnable runnable = new ScreenshotRunnable(decorView, name, quality);
//4、呼叫截圖物件的run方法
activityUtils.getCurrentActivity(false).runOnUiThread(runnable);
}
複製程式碼
第四步(1 得到螢幕所有檢視)跳轉到的函式:getScreenshotView()
複製程式碼
/**
* Gets the proper view to use for a screenshot.
*/
private View getScreenshotView() {
//獲取到螢幕上的view
View decorView = viewFetcher.getRecentDecorView(viewFetcher.getWindowDecorViews());
final long endTime = SystemClock.uptimeMillis() + Timeout.getSmallTimeout();
while (decorView == null) {
final boolean timedOut = SystemClock.uptimeMillis() > endTime;
if (timedOut){
return null;
}
sleeper.sleepMini();
decorView = viewFetcher.getRecentDecorView(viewFetcher.getWindowDecorViews());
}
wrapAllGLViews(decorView);
return decorView;
}
複製程式碼
第五步跳轉到的函式:viewFetcher.getWindowDecorViews()
複製程式碼
/**
* Returns the WindorDecorViews shown on the screen.
*
* @return the WindorDecorViews shown on the screen
*/
//翻譯下來就是:獲取到展示在screen上的所有WindowDecorViews,是一個View的陣列,然後這個view的陣列返回後,再作為viewFetcher.getRecentDecorView的引數
//用反射方法去獲取 View 檢視陣列
@SuppressWarnings("unchecked")
public View[] getWindowDecorViews()
{
Field viewsField;
Field instanceField;
try {
viewsField = windowManager.getDeclaredField("mViews");
instanceField = windowManager.getDeclaredField(windowManagerString);
viewsField.setAccessible(true);
instanceField.setAccessible(true);
Object instance = instanceField.get(null);
View[] result;
if (android.os.Build.VERSION.SDK_INT >= 19) {
result = ((ArrayList<View>) viewsField.get(instance)).toArray(new View[0]);
} else {
result = (View[]) viewsField.get(instance);
}
return result;
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
複製程式碼
第六步跳轉到的函式:viewFetcher.getRecentDecorView(View[] views)
複製程式碼
/**
* Returns the most recent DecorView
*
* @param views the views to check
* @return the most recent DecorView
*/
//翻譯下來就是:返回最近的DecorView
public final View getRecentDecorView(View[] views) {
if(views == null)
return null;
final View[] decorViews = new View[views.length];
int i = 0;
View view;
//通過遍歷View陣列,來得到most recent DecorView
for (int j = 0; j < views.length; j++) {
view = views[j];
if (view != null && view.getClass().getName()
.equals("com.android.internal.policy.impl.PhoneWindow$DecorView")) {
decorViews[i] = view;
i++;
}
}
return getRecentContainer(decorViews);
}
複製程式碼
第七步:(1中的獲取螢幕已經結束,看2的init操作)
複製程式碼
/**
* This method initializes the aysnc screenshot saving logic
*/
//翻譯下來就是:初始化一個aysnc(非同步)的sreenshot的儲存邏輯
private void initScreenShotSaver() {
if(screenShotSaverThread == null || screenShotSaver == null) {
//宣告一個HandlerThread物件
screenShotSaverThread = new HandlerThread("ScreenShotSaver");
screenShotSaverThread.start();
//把screenShotSaverThread捆綁到handler
screenShotSaver = new ScreenShotSaver(screenShotSaverThread);
}
}
複製程式碼
但是這裡用到了HandlerThread和Handler,看之。。。
第八步跳轉的函式:ScreenShotSaver(HandlerThread thread)
複製程式碼
/**
* This class is a Handler which deals with saving the screenshots on a separate thread.
*
* The screenshot logic by necessity has to run on the ui thread. However, in practice
* it seems that saving a screenshot (with quality 100) takes approx twice as long
* as taking it in the first place.
*
* Saving the screenshots in a separate thread like this will thus make the screenshot
* process approx 3x faster as far as the main thread is concerned.
*
*/
//翻譯下來就是:這是一個繼承自Handler,在一個單獨的thread上處理如何儲存sreenchots的類
//screenshot的邏輯必須要跑在ui執行緒上,然而,事實上,好像這個儲存screenshot反而花費了將近2倍的時間
//儲存這個screenshots在另一個執行緒中,就會使得這個處理能夠快三倍,當然是與跑在主執行緒上相比而言
private class ScreenShotSaver extends Handler {
public ScreenShotSaver(HandlerThread thread) {
super(thread.getLooper());
}
複製程式碼
步驟:
(1)
程式碼分析:
追本溯源,開始找路。。。
第一步跳轉到的函式:takeScreenshot(String name)
複製程式碼
/**
* Takes a screenshot and saves it with the specified name in "/sdcard/Robotium-Screenshots/".
* Requires write permission (android.permission.WRITE_EXTERNAL_STORAGE) in AndroidManifest.xml of the application under test.
*
* @param name the name to give the screenshot
*
*/
//上面的話翻譯下來就是:儲存的位置確定了,就是在mnt/sdcard/Robotium-Screenshots/目錄下
//但是需要寫sd卡的許可權,需要給under test的application在AndroidManifest.xml中配置permission,那麼這裡也就解釋了我上面的執行過程中第二個問題
public void takeScreenshot(String name){
takeScreenshot(name, 100);
}
複製程式碼
第二步跳轉到的函式:takeScreenshot(String name, int quality)
複製程式碼
/**
* Takes a screenshot and saves the image with the specified name in "/sdcard/Robotium-Screenshots/".
* Requires write permission (android.permission.WRITE_EXTERNAL_STORAGE) in AndroidManifest.xml of the application under test.
*
* @param name the name to give the screenshot
* @param quality the compression rate. From 0 (compress for lowest size) to 100 (compress for maximum quality)
*
*/
//上面的話翻譯下來就是:圖片儲存位置以及讀寫許可權與第一步中相同
//引數分別表示picture的name,以及清晰度(從0到100),預設是100,當然你也可以直接在函式中呼叫這個函式,然後設定這個quality的值
public void takeScreenshot(String name, int quality){
screenshotTaker.takeScreenshot(name, quality);
}
複製程式碼
第三步跳轉到的函式:screenshotTaker.takeScreenshot(String name, int quality)
複製程式碼
/**
* Takes a screenshot and saves it in "/sdcard/Robotium-Screenshots/".
* Requires write permission (android.permission.WRITE_EXTERNAL_STORAGE) in AndroidManifest.xml of the application under test.
*
* @param view the view to take screenshot of
* @param name the name to give the screenshot image
* @param quality the compression rate. From 0 (compress for lowest size) to 100 (compress for maximum quality).
*/
//第三步走到了一個新的類中,是screenShotTaker的類
//這個才是真正的執行Screenshot的函式,這個才是截圖的邏輯
public void takeScreenshot(final String name, final int quality) {
//1、得到目前螢幕所有檢視
View decorView = getScreenshotView();
if(decorView == null)
return;
//2、初始化
initScreenShotSaver();
//3、例項化截圖物件
ScreenshotRunnable runnable = new ScreenshotRunnable(decorView, name, quality);
//4、呼叫截圖物件的run方法
activityUtils.getCurrentActivity(false).runOnUiThread(runnable);
}
複製程式碼
第四步(1 得到螢幕所有檢視)跳轉到的函式:getScreenshotView()
複製程式碼
/**
* Gets the proper view to use for a screenshot.
*/
private View getScreenshotView() {
//獲取到螢幕上的view
View decorView = viewFetcher.getRecentDecorView(viewFetcher.getWindowDecorViews());
final long endTime = SystemClock.uptimeMillis() + Timeout.getSmallTimeout();
while (decorView == null) {
final boolean timedOut = SystemClock.uptimeMillis() > endTime;
if (timedOut){
return null;
}
sleeper.sleepMini();
decorView = viewFetcher.getRecentDecorView(viewFetcher.getWindowDecorViews());
}
wrapAllGLViews(decorView);
return decorView;
}
複製程式碼
第五步跳轉到的函式:viewFetcher.getWindowDecorViews()
複製程式碼
/**
* Returns the WindorDecorViews shown on the screen.
*
* @return the WindorDecorViews shown on the screen
*/
//翻譯下來就是:獲取到展示在screen上的所有WindowDecorViews,是一個View的陣列,然後這個view的陣列返回後,再作為viewFetcher.getRecentDecorView的引數
//用反射方法去獲取 View 檢視陣列
@SuppressWarnings("unchecked")
public View[] getWindowDecorViews()
{
Field viewsField;
Field instanceField;
try {
viewsField = windowManager.getDeclaredField("mViews");
instanceField = windowManager.getDeclaredField(windowManagerString);
viewsField.setAccessible(true);
instanceField.setAccessible(true);
Object instance = instanceField.get(null);
View[] result;
if (android.os.Build.VERSION.SDK_INT >= 19) {
result = ((ArrayList<View>) viewsField.get(instance)).toArray(new View[0]);
} else {
result = (View[]) viewsField.get(instance);
}
return result;
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
複製程式碼
第六步跳轉到的函式:viewFetcher.getRecentDecorView(View[] views)
複製程式碼
/**
* Returns the most recent DecorView
*
* @param views the views to check
* @return the most recent DecorView
*/
//翻譯下來就是:返回最近的DecorView
public final View getRecentDecorView(View[] views) {
if(views == null)
return null;
final View[] decorViews = new View[views.length];
int i = 0;
View view;
//通過遍歷View陣列,來得到most recent DecorView
for (int j = 0; j < views.length; j++) {
view = views[j];
if (view != null && view.getClass().getName()
.equals("com.android.internal.policy.impl.PhoneWindow$DecorView")) {
decorViews[i] = view;
i++;
}
}
return getRecentContainer(decorViews);
}
複製程式碼
第七步:(1中的獲取螢幕已經結束,看2的init操作)
複製程式碼
/**
* This method initializes the aysnc screenshot saving logic
*/
//翻譯下來就是:初始化一個aysnc(非同步)的sreenshot的儲存邏輯
private void initScreenShotSaver() {
if(screenShotSaverThread == null || screenShotSaver == null) {
//宣告一個HandlerThread物件
screenShotSaverThread = new HandlerThread("ScreenShotSaver");
screenShotSaverThread.start();
//把screenShotSaverThread捆綁到handler
screenShotSaver = new ScreenShotSaver(screenShotSaverThread);
}
}
複製程式碼
但是這裡用到了HandlerThread和Handler,看之。。。
第八步跳轉的函式:ScreenShotSaver(HandlerThread thread)
複製程式碼
/**
* This class is a Handler which deals with saving the screenshots on a separate thread.
*
* The screenshot logic by necessity has to run on the ui thread. However, in practice
* it seems that saving a screenshot (with quality 100) takes approx twice as long
* as taking it in the first place.
*
* Saving the screenshots in a separate thread like this will thus make the screenshot
* process approx 3x faster as far as the main thread is concerned.
*
*/
//翻譯下來就是:這是一個繼承自Handler,在一個單獨的thread上處理如何儲存sreenchots的類
//screenshot的邏輯必須要跑在ui執行緒上,然而,事實上,好像這個儲存screenshot反而花費了將近2倍的時間
//儲存這個screenshots在另一個執行緒中,就會使得這個處理能夠快三倍,當然是與跑在主執行緒上相比而言
private class ScreenShotSaver extends Handler {
public ScreenShotSaver(HandlerThread thread) {
super(thread.getLooper());
}
複製程式碼