TraceView

Traceview 用來追蹤程式執行效能,主要可運用2種方式來產生效能分析圖(.trace)

A-1.使用插入code方式
在想分析的程式碼區塊插入Debug.startMethodTracing()及Debug.stopMethodTracing();
其中Debug.startMethodTracing()可指定分析圖檔名如Debug.startMethodTracing("trace_report"),在/sdcard會產生trace_report.trace檔

A-2.加入<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
否則A-1的方法會丟出Exception

A-3.執行程式

A-4.將產生的分析圖從/sdcard中複製到本機的資料夾中以方便查看,使用以下指令
./adb pull /sdcard/trace_report.trace /資料夾名稱

A-5.使用android-sdk tools的 traceview來開啟.trace檔,使用以下指令
android-sdk-linux/tools$ ./traceview /資料夾名稱/trace_report.trace

B-1.使用DDMS來產生分析圖,執行程式並切換到DDMS

B-2.點擊Start Method Profiling,開始分析

B-3.點擊Stop Method Profiling,結束分析

B-4.自動產生分析檔

以下提供簡單的分析範例,尋找程式效能熱點(HotPoint)
首先這是MainActivity
MainActivity.java
public class MainActivity extends Activity
{
   
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);      
        Debug.startMethodTracing("trace_report");
        TestString testStr = new TestString();
        Debug.stopMethodTracing();
    }

}
相當簡單,只專注在 TestString 物件的建立
接著是TestString
TestString.java
public class TestString
{
    private String mStr = new String();
   
    public TestString(){
        initStr();
    }
   
    private void initStr(){
        for(int i=0; i<10000; ++i){
            mStr = mStr.concat(""+i);
        }
    }
}
在initStr方法內為相當消耗效能的熱點,執行程式產生分析圖如下














在Incl cpu time 佔用 89.6% 為String.concat()方法,總共佔用了9.66秒,針對這裡來修改熱點
修改後的TestString.java如下
public class TestString
{
    private StringBuffer mStr = new StringBuffer();
   
    public TestString(){
        initStr();
    }
   
    private void initStr(){
        for(int i=0; i<10000; ++i){
            mStr = mStr.append(i);
        }
    }
}
採用StringBuffer取代原來的String,重新產生分析圖















可以看到StringBuffer.append()方法仍然為熱點(95.7%),佔用時間卻降到了0.73秒


XmlParser

網路上一堆範例不如自己作一遍還來的了解
直接切入主題,這裡使用DOM來轉換xml,以步驟來說明

Step1.
首先要有xml檔放到assets資料夾中,為了方便整理在assets 資料夾中另外開了一個xmlfiles資料夾,路徑如下

Project/assets/xmlfiles/command_simple_formatter.xml
<?xml version="1.0" encoding="utf-8"?>
<commands>
    <command
        id="1"
        enable="true"
        function_name="function1"
        type="FLOAT" />
    <command
        id="2"
        enable="false"
        function_name="function2"
        type="FIXED" />
</commands>

Step2.
接著建立對應的資料結構,根據xml中的tag來建立

Command.java
public class Command
{
    private int mIndex;
    private String mFunction;
    private boolean mEnable;
    private Type mType;
 
    public enum Type {
        FIXED, FLOAT
    }
 
    public void setType(Type type){
        mType = type;
    }
 
    public Type getType(){
        return mType;
    }
 
    public void setIndex(int index)
    {
        mIndex = index;
    }

    public void setFunction(String function)
    {
        mFunction = function;
    }

    public int getIndex()
    {
        return mIndex;
    }

    public String getFunction()
    {
        return mFunction;
    }
 
    public boolean getEnable(){
        return mEnable;
    }
 
    public void setEnable(boolean enable){
        mEnable = enable;
    }
}

Step3.
接著開始撰寫轉換的本體,這裡我帶入Template Method,
XmlParser.java
public abstract class XmlParser
{
    private Context mContext;

    public XmlParser(Context context) {
        mContext = context;
    }

    public final List<?> parserProcess(String xmlFileName)
    {
        Document document = prepareParseComponent(xmlFileName);
        Element root = getXmlBasicRootElement(document);
        List<?> javaDatas = parseXmlContentToJavaObjects(root);

        return javaDatas;
    }
 
    private Document prepareParseComponent(String xmlFileName)
    {
        DocumentBuilder documentBuilder = getDocumentBuilder();
        InputStream inputStream = getXmlInputStream(xmlFileName);
        Document document = null;
        try {
            document = documentBuilder.parse(inputStream);
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return document;
    }

    protected abstract List<?> parseXmlContentToJavaObjects(Element root);
 
    private DocumentBuilder getDocumentBuilder()
    {
        DocumentBuilder documentBuilder = null;
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
        try {
            documentBuilder = builderFactory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }

        return documentBuilder;
    }

    /**
     *
     * @param xmlFileName : xml file need put in assets folder
     * @return InputStream
     */
    private InputStream getXmlInputStream(String xmlFileName)
    {
        AssetManager assetManager = mContext.getAssets();
        InputStream inputStream = null;
        try {
            inputStream = assetManager.open(xmlFileName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return inputStream;
    }

    private Element getXmlBasicRootElement(Document document)
    {
        Element root = document.getDocumentElement();
        return root;
    }
}

Step4.
建立繼承XmlParser的類別,主要複寫parseXmlContentToJavaObjects方法
CommandSimpelXmlParser.java
public class CommandSimpleXmlParser extends XmlParser
{
    private String mElementName = "command";
    private String mId = "id";
    private String mFunctionName = "function_name";
    private String mEnable = "enable";
    private String mType = "type";
 
    public CommandSimpleXmlParser(Context context) {
        super(context);
    }

    @Override
    protected List<Command> parseXmlContentToJavaObjects(Element root)
    {
        NodeList commandNode = root.getElementsByTagName(mElementName);
        int commandsNodeSize = commandNode.getLength();
        return transXmlToCommand(commandNode,commandsNodeSize);
    }
 
    private List<Command> transXmlToCommand(NodeList nodeList,int size){
        List<Command> result = new ArrayList<Command>();
        for (int i = 0; i < size; ++i) {
            Command command = new Command();
            Element commandElement = (Element) nodeList.item(i);
            command.setIndex(transXmlIdTagToCommand(
                    commandElement, mId));
            command.setFunction(transXmlFunctionNameTagToCommand(
                    commandElement, mFunctionName));
            command.setEnable(transXmlEnableTagToCommand(
                    commandElement, mEnable));
            command.setUIType(trnasXmlTypeToCommand(
                    commandElement, mType));
            result.add(command);
            command = null;
        }
        return result;
    }
 
    private UIType trnasXmlTypeToCommand(Element element,String type){
        return Command.Type.valueOf(element.getAttribute(type));
    }
 
    private int transXmlIdTagToCommand(Element element,String id){
        return Integer.valueOf(element.getAttribute(id));
    }
 
    private String transXmlFunctionNameTagToCommand(Element element,String functionName){
        return element.getAttribute(functionName);
    }
 
    private boolean transXmlEnableTagToCommand(Element element,String enable){
        return Boolean.valueOf(element.getAttribute(enable));
    }
}

Step5.
使用轉換
XmlParserMainActivity.java
public class XmlParserMainActivity extends Activity
{
    private static final String TAG = "XmlParserMainActivity";
    private static final String COMMAND_SIMPLE_XML_FILE_PATH = "xmlfiles/command_simple_formatter.xml";
 
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        examParseSimpleXml(COMMAND_SIMPLE_XML_FILE_PATH);
    }

    private void examParseSimpleXml(String xmlFilePath)
    {
        XmlParser simpleCommandXmlParser = new CommandSimpleXmlParser(this);
        List<Command> commands = (List<Command>) simpleCommandXmlParser
                .parserProcess(xmlFilePath);
    }

}

MonkeyRunner 紀錄

紀錄:
首先MonkeyRunner主要是調用
/android-sdk-linux/tools/monkeyrunner 來自動化執行腳本

步驟1
/android-sdk-linux/tools/中建立專屬的測試腳本資料夾,其中放入測試用腳本,
假設專案名稱為Project1,建立/android-sdk-linux/tools/Project1_script

步驟2
把專案apk(Project1.apk)放入步驟1建立的資料夾中並建立測試腳本(Project1_script.py),
測試腳本內容可參考以下範例,必須修改
device.installPackage('../Project1_script/Project1.apk') ->路徑必須對應apk位置
package = 'abc.efg.hij' -> package name
activity = 'abc.efg.hij.xxxActivity' -> activity name


範例如下
Project1_script.py

# Imports the monkeyrunner modules used by this program
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage

# Connects to the current device, returning a MonkeyDevice object
device = MonkeyRunner.waitForConnection()

# Installs the Android package. Notice that this method returns a boolean, so you can test
# to see if the installation worked.
device.installPackage('../Project1_script/Project1.apk')

# sets a variable with the package's internal name
package = 'abc.efg.hij'

# sets a variable with the name of an Activity in the package
activity = 'abc.efg.hij.xxxActivity'

# sets the name of the component to start組合(packageactivity)
runComponent = package + '/' + activity

# Runs the component
device.startActivity(component=runComponent)

# Do something u want to do....





2.如何執行測試腳本:
使用Terminal移動到/android sdk/tools/Project1_script資料夾,再輸入
../monkeyrunner Project1_script.py

3.相關的內容在android api 中都有使用說明

4.常用方法紀錄
MonkeyRunner.sleep(n) ->等待n

device = MonkeyRunner.waitForConnection()
device.press('KEYCODE_NUMPAD_1','DOWN_AND_UP') -> 模擬虛擬鍵盤輸入1
device.press('KEYCODE_NUMPAD_7','DOWN_AND_UP') -> 模擬虛擬鍵盤輸入7
device.press('KEYCODE_NUMPAD_2','DOWN_AND_UP') -> 模擬虛擬鍵盤輸入2
device.press('KEYCODE_NUMPAD_DOT','DOWN_AND_UP') -> 模擬虛擬鍵盤輸入 .
以上指令可用
device.type('172.') 取代

device.touch(384,199,'DOWN_AND_UP') -> 點擊螢幕x:384 y:199座標

start = (100,100) -> 拖曳起點
end = (100,120) -> 拖曳終點
device.drag(start,end,1,10) -> 開始拖曳

device.getProperty() -> 可獲得系統資訊,
參考http://developer.android.com/tools/help/MonkeyDevice.html#table1
width = int(device,getProperty('display.width') -> 螢幕寬度
height = int(device.getProperty('display.height') -> 螢幕高度

#註解

print '顯示訊息'

Git error: 解決在Gerrit網頁上submit patch set 出現衝突的情況

Cause:
在Gerrit等待code review通過期間,若有協同開發者先merge commit,且該commit和自己的commit
發生衝突的情況

Solution:

1.
在發生conflict branch上 執行
git pull --rebase origin master
此指令說明如下
This command will fetch new commits from the remote and then rebase your local commits on top of them. It will temporarily set aside the changes you've made in your branch, apply all of the changes that have happend in master to your working branch, then merge (recommit) all of the changes you've made back into the branch. Doing this will help avoid future merge conflicts. Plus, it gives you an opportunity to test your changes against the latest code in master.
Once you are satisfied with your change set and you've rebased against master, you are ready to push your code to Gerrit for review.

簡單的說:把master上已合併的commit(但在目前branch不存在),接到目前的branch,再把
目前branchcommit接到最後面(最新)
當然在Gerrit網頁上合併會發生衝突,所以執行這個指令之後在本地端也會發生衝突,把執行後的提示紀錄下來如下

When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To check out the original branch and stop rebasing run "git rebase –abort".
第一行為當修改衝突後,要執行的指令

2.修改發生衝突的地方,使用vim修改
3.把修改後的檔案全部 git add
4.git add 所有的檔案之後執行 git rebase --continue
5.最後 git push origin HEAD:refs/for/master

6.必須重新等待code review

Eclipse Error: Indexer runs out of memory

Cause:
編譯jni會產生大量的檔案,特別是 C/C++ indexer 會建立索引 若eclipse初始記憶體太小會導致
out of memory 的錯誤

Solution:
在 eclipse.ini檔案中可設定記憶體限制,主要調整以下幾個數值

初始設定
--launcher.XXMaxPermSize
256m
-XX:MaxPermSize=256m
-Xms40m
-Xmx512m

調整後
--launcher.XXMaxPermSize
512m
-XX:MaxPermSize=512m
-Xms80m
-Xmx1000m



細節參考
http://wiki.eclipse.org/Eclipse.ini

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Affiliate Network Reviews