Android TV磁貼類app自動化架構二次改造(基於UiAutomator)

來源:互聯網
上載者:User

標籤:

簡介

項目一直是手工測試為主,加上一直是TV類應用,很多自動化工具都沒有針對TV類項目做很好的適配,所以只有自己動手了。主要針對項目的特殊性進行了部分改造,不一定適用於其他項目。(涉及隱私,就不提供json檔案和軟體名字啦)

痛點

1.非標準控制項的難處

通過uiautomatorviewer擷取到的不一樣的磁貼,屬性全部相同(除了座標點),意味著沒法通過id和class+index方式擷取,text屬性為空白,也就沒有辦法通過byText的方式擷取uiobject,高度定製的磁貼,讓自動化很為難,如果通過座標點,太坑爹,不能跨裝置,還是坑。

2.TV類應用沒有觸摸操作
TV類安卓程式,主要面向的是遙控器,也就是接收的是keyevent,所以touch事件顯得不這麼全面,為了最接近使用者,還是選擇用key來做自動化。

架構模組

解析模組

從伺服器解析json檔案格式,封裝成實體類CellInfo,返回一個包含磁鐵資訊的List。一個磁鐵對應一個CellInfo,一個CellInfo需要提取的資訊有

  • x座標
  • y座標
  • 所屬的Tab分類頁
  • 每個磁鐵的說明標籤

所以對應的定義如下:

package launcherClick.model;public class CellInfo {    private String label;    private String tab;    public String getTab() {        return tab;    }    public void setTab(String tab) {        this.tab = tab;    }    private int x;    private int y;    public String getLabel() {        return label;    }    public void setLabel(String label) {        this.label = label;    }    public int getX() {        return x;    }    public void setX(int x) {        this.x = x;    }    public int getY() {        return y;    }    public void setY(int y) {        this.y = y;    }}

解析用的是第三方開源庫org.json,對應的解析代碼如下:

package launcherClick;import java.util.ArrayList;import java.util.List;import org.json.JSONArray;import org.json.JSONObject;import Utils.IOUtils;import Utils.Println;import launcherClick.model.CellInfo;public class AMetroParse {    public static List<CellInfo> startParse(String url) {        String str = IOUtils.readFromNet(url);        if(str==null){            return null;        }        JSONObject jsonObject = new JSONObject(str);        List<CellInfo> list = new ArrayList<CellInfo>();        JSONArray _tabs = jsonObject.getJSONArray("tabs");        for(int i = 0;i<_tabs.length();i++){            String tab = _tabs.getJSONObject(i).getString("label");            JSONArray _cells = _tabs.getJSONObject(i).getJSONArray("cells");            for(int j=0;j<_cells.length();j++){                CellInfo cellInfo = new CellInfo();                cellInfo.setX(_cells.getJSONObject(j).getJSONObject("location").getInt("x"));                cellInfo.setY(_cells.getJSONObject(j).getJSONObject("location").getInt("y"));                cellInfo.setLabel(_cells.getJSONObject(j).getJSONObject("content").getString("label"));                cellInfo.setTab(tab);                list.add(cellInfo);            }        }        return list;    }}
座標轉換模組

這部分的工作是把上一步驟解析後的實體類進行提取和處理,主要處理的內容是根據x y座標計算磁鐵移動量,更具x的極大值和分類頁做跨分類移動的位移計算以及一些其他處理。
通過x y計算位移比較簡單,只是簡單計算距離:

        for (CellInfo cellInfo : list) {            if (cellInfo.getLabel().contains(label)                    && cellInfo.getTab().contains(tab)) {                int x = cellInfo.getX();                int y = cellInfo.getY();                new Println("x:" + x + "  " + "y:" + y + "  " + "位移量" + _offset);                // x方向                for (int i = x + _offset; i > 1; i--) {                    new Println("→" + " count is " + (x + _offset));                    keyRight();                }                // y方向                for (int i = y; i > 1; i--) {                    new Println("↓");                    keyDown();                }            }        }

恩,裡面有個位移量,跨分類用的,位移量是通過每個分類下磁鐵最大值得出來的,每一個分類最後一個磁鐵的x值即為最大值,遍曆當前分類下的所有磁鐵的x,如果大於後面一個,則放在前面,下次再用這個值去比較下面的x值。忘記這是什麼排序演算法了…囧…當然,也可以用Collections的內建的演算法。

    private int togicOffset(String tab) {        int lineLenth = 0;        List<CellInfo> list = AMetroParse                .startParse("我是隱藏的介面");        for (CellInfo cellInfo : list) {            if (cellInfo.getTab().contains(tab)) {                int x = cellInfo.getX();                lineLenth = lineLenth >= x ? lineLenth : x;            }        }        return lineLenth;    }

那麼通過上面的排序之後,就可以得到每個分類的位移量,這樣對於後面的分類磁貼,就可以知道磁貼在整體的真正位置啦,於是就可以開始移動了。

        for (CellInfo cellInfo : list) {            if (cellInfo.getLabel().contains(label) && cellInfo.getTab().contains(tab)) {                int x = cellInfo.getX();                int y = cellInfo.getY();                new Println("x:" + x + "  " + "y:" + y + "  " + "位移量" + _offset);                // x方向                for (int i = x + _offset; i > 1; i--) {                    new Println("→" + " count is " + (x + _offset));                    keyRight();                }                // y方向                for (int i = y; i > 1; i--) {                    new Println("↓");                    keyDown();                }            }        }
圖片比較模組

來源於monkeyrunner的思路,原理是計算兩個bitmap的長寬,然後提取出裡面每個像素的像素值,如果相同,相似性+1,最後再除以總像素值(比如1280x720),這樣就可以把相似程度轉為一個一個可以衡量的具體值了,那麼判斷這個介面是不是我需要點擊的時候,只需要截屏當前圖片和預期的圖片對比,相似性達到100的時候就認為是正確的。當然也提供了局部比較的功能,比如擷取不到資料的異常提示。那麼代碼如下,提供了多種重載的方法以及兩個assert判斷。

package Utils;import junit.framework.Assert;import android.graphics.Bitmap;import android.graphics.BitmapFactory;public class ImageCompare {    public  static void AssertBitmapEqual(Bitmap bitmap0, Bitmap bitmap1){        /**         * 百分百圖片相同斷言-.-         */        Assert.assertEquals(100, ImageCompare(bitmap0,bitmap1));    }    public  static void AssertBitmapNotEqual(Bitmap bitmap0, Bitmap bitmap1){        /**         * 百分之零圖片相同斷言         */        Assert.assertEquals(0, ImageCompare(bitmap0,bitmap1));    }    public static int ImageCompare(String path0,String path1) {        /**         * 提供根據路徑直接比較         */        Bitmap bitmap0 = BitmapFactory.decodeFile(path0);         Bitmap bitmap1 = BitmapFactory.decodeFile(path1);         return ImageCompare(bitmap0,bitmap1);    }    public static int ImageCompareChild(Bitmap bitmap0, Bitmap bitmap1,int x,int y,int width,int height) {        /**         * 裁剪子圖並比較,主要是為瞭解決拉取動態資料不同,但是局部提示不變的比較情境。         */        Bitmap bitmap00 = bitmap0.createBitmap(bitmap0, x, y, width, height);        Bitmap bitmap01 = bitmap1.createBitmap(bitmap1, x, y, width, height);        return ImageCompare(bitmap00,bitmap01);    }    public static int ImageCompare(Bitmap bitmap0, Bitmap bitmap1) {        /**         * 比較的主函數         * 只能比較相同長寬的圖片,不相等返回-1失敗         * 相似性為1~100         * 原理是提取每一個像素點比較,整張圖相似性取決於像素點相同個數,所以還是比較準確的         */        int picPct = 0;        int picCount = 0;        int picCountAll = 0;        new Println("begin to compare");        if (bitmap0 == null || bitmap1 == null) {            new Println("null bitmap");            return -1;        }        if (bitmap0.getWidth() != bitmap1.getWidth()                || bitmap0.getHeight() != bitmap1.getHeight()) {            return -1;        }        new Println("寬度為:" + bitmap1.getWidth() + "高度為:" + bitmap1.getHeight());        for (int j = 0; j < bitmap1.getWidth(); j++) {            for (int i = 0; i < bitmap0.getHeight(); i++) {                if (bitmap0.getPixel(j, i) == bitmap1.getPixel(j, i)) {                    picCount++;                }                picCountAll++;            }        }        int result = (int) (((float) picCount) / picCountAll * 100);        new Println(picCount + "/" + picCountAll);        new Println("相似性為:" + result);        return result;    }}
異常&&其他模組

定義了一些異常類,主要功能用於提示,這個提示的作用後面會用到。
增加了啟動應用和關閉應用的方法,原理是用到了shell命令。

Runtime.getRuntime().exec("am start -n 我是包名隱藏者");

初始化方法,主要用於異常時能夠一鍵重新開始,以及磁貼複原功能,還有其他一些小的處理就不多說啦,下面開始持續構建。

自動化的持續整合

還是那句話,不持續整合的自動化不是自動化,所以這裡介紹的是基於jenkins的自動化整合,其實UiAutomator做整合還是很容易的,只要把jar包放在一個固定的目錄,然後shell命令執行就完事了。

然後定時任務自己選吧,構建失敗的寄件提醒這裡設定的是,上面我自訂的異常類,當控制台輸出我的異常類,那麼會認為不通過,然後觸發寄件提醒。

那麼至此,一個較為完整的流程就完成啦,那麼在此架構上組員們(就我一個)就可以更進一部去完善二級頁面的自動化用例了。

Android TV磁貼類app自動化架構二次改造(基於UiAutomator)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.