分類
Uncategorized

ant build error: The SDK Build Tools revision (x.x.x) is too low for project 'MainActivity'. Minimum required is 19.1.0

Description:

Use ant to build android project and show error message like topic
 

Root Caused:

The SDK Build Tools revision (18.0.1) is too low for project ‘MainActivity’. Minimum required is 19.1.0
 

Solution:

1. open eclipse
2. launch Android SDK Manager
3. update to Minimum required version of SDK Build Tools

分類
Uncategorized

ant build error: build.xml does not exist!

Description:

when you created a android project and want to use “ant” to build project
 

Steps to Reproduce:

1.
After Created android project and use terminal to move root of project
2.
type ant in terminal
 

Error:

Buildfile: build.xml does not exist!
Build failed

 
 

Solution:

at root director of android project and type follow command
/your-android-sdk-path/tools/android update project -p .
then some message will show on terminal

Updated local.properties
No project name specified, using Activity name 'MainActivity'.
If you wish to change it, edit the first line of build.xml.
Added file ./build.xml
Updated file ./proguard-project.txt
It seems that there are sub-projects. If you want to update them
please use the --subprojects parameter.

 
 

分類
Uncategorized

使用 ant 手動產生 unsign apk

為了自動化建置的需求, 原先經由 Eclipse 產生 unsign apk 的方式必須改成手動產生
雖然在官網上有教學, 但過程中發生的問題卻隻字未提, 這篇就把發生的問題和解決過程紀錄下來
 
要產生 unsign apk, 根據官網所述, 只要移動到該專案目錄下並輸入

ant release

即可,在專案的bin資料夾就會產生unsign apk, 但沒有ant就不用玩了,所以先安裝 ant 吧, 輸入

sudo apt-get install ant

安裝完成後再輸入 ant release, 會出現

Buildfile: build.xml does not exist!
Build failed

是的, 必須要有 build.xml, 輸入以下指令來產生 build.xml

android update project -p .

注意: android 指的是在sdk/tools/ 下的 android , 必要的話請使用完整路徑來指定吧
完成後在專案目錄下就會產生build.xml
有了build.xml 再輸入 ant release 看看吧,出現

BUILD FAILED
/android-sdk-linux/tools/ant/build.xml:483: The SDK Build Tools revision (18.0.1) is too low for project 'MainActivity'. Minimum required is 19.1.0

果然~沒這麼簡單, 根據錯誤訊息去尋找 sdk/build-tools/ 真的只有18.0.1 資料夾,好吧~開啟eclipse sdk manager 更新 buildtool,
注意:只要勾選 19.0.1 就好其他不必安裝
螢幕擷圖存為 2014-11-27 16:18:36
下載完成後在sdk/build-tools/ 也會出現19.1.0 資料夾,再輸入 ant release 吧,出現

BUILD FAILED
/android-sdk-linux/tools/ant/build.xml:601: Invalid file: /appcompat_v7/build.xml

奇怪~ 怎會跑出 appcompat_v7的build.xml, 檢查專案目錄下的project.properties,發現原專案會參考到appcompat_v7這個專案,再google一下
原因是使用eclipse更新建置版本,會出現”無限重生appcompat_v7的現象”,解決方法就是初始建立專案時,最低SDK版本(Minimum Required SDK)指定為 API 14(即 Android 4.0)
解決之後再輸入ant release
終於

BUILD SUCCESSFUL
Total time: 6 seconds

完成之後在專案目錄下的bin資料夾會出現
xxx-release-unsigned.apk , 就是unsign key 的apk
 
 
 
 
 

分類
Uncategorized

CloneZilla Backup Error: Bitmap free count error

Root Caused:
unknow
Solution:
選擇專家模式後再選擇
-fsck-src-part 存印象檔之前嘗試交談式檢查與修復來源裝置檔案系統
之後會出現詢問 Fix xxx, 選擇y

分類
Design Pattern Refactoring

使用策略模式(Strategy Pattern)取代 switch

一般來說使用switch通常會產生一些缺點,e.g.
1. 同樣的 switch 散佈在不同的位置時, 當新的需求出現而需要修改 switch 時,必須找出所有的 switch 一併修改。
2. 因為 switch 代表做了複數的工作,導致 switch 很難符合開閉原則 (OCP)。
3. switch 中條件區塊擴大時會讓 switch 越來越難以理解。
以上的因素也可套用到 if-else。
因此可考慮使用 Strategy Pattern 取代 switch 或是 if-else
簡單的switch如下

    private static void useSwitchExample(){
        switch(condition){
            case 0:
                System.out.println("audio error");
                break;
            case 1:
                System.out.println("client error");
                break;
            case 2:
                System.out.println("not match");
                break;
        }
    }

 
根據 condition 執行不同的 case 條件,會印出不同的錯誤訊息,為了取代 switch ,先建立 Strategy 如下

public interface IErrorStrategy
{
    public void showErrorMessage();
   
    class AudioError implements IErrorStrategy{
        @Override
        public void showErrorMessage()
        {
            System.out.println("audio error");
        }
    }
   
    class ClientError implements IErrorStrategy{
        @Override
        public void showErrorMessage()
        {
            System.out.println("client error");
        }
    }
   
    class NoMatchError implements IErrorStrategy{
        @Override
        public void showErrorMessage()
        {
            System.out.println("no match");
        }
    }
}

 
在 showErrorMessage 方法即是印出各個錯誤代碼,接著建立 Manager 來控制 Strategy e.g.,

public class ErrorStrategyManager
{
    public static final int AUDIO_ERROR = 0;
    public static final int CLIENT_ERROR = 1;
    public static final int NOMATCH_ERROR = 2;
    private Map<Integer, IErrorStrategy> mErrors = new HashMap<Integer, IErrorStrategy>();
    public ErrorStrategyManager() {
        initErrors();
    }
    private void initErrors()
    {
        mErrors.put(CLIENT_ERROR, new IErrorStrategy.ClientError());
        mErrors.put(AUDIO_ERROR, new IErrorStrategy.AudioError());
        mErrors.put(NOMATCH_ERROR, new IErrorStrategy.NoMatchError());
    }
    public void showErrorMessage(int errorMessage)
    {
        IErrorStrategy error = mErrors.get(errorMessage);
        error.showErrorMessage();
    }
}

 
其中使用 map 來對應不同的情況對應不同的 Strategy ,便可取代原本的 switch

    private static void useStrategyInsteadSwitchExample(){
        new ErrorStrategyManager().showErrorMessage(condition);
    }

 
以上的處理方式有個缺點,如果傳入的數值沒有在對應的項目之中就會出現 NullPointerException. e.g.,

new ErrorStrategyManager().showErrorMessage(100);

 
因此我們必須考慮 “例外” 的情況發生,另外判斷 null 的狀態, e.g.,

    public void showErrorMessage(int errorMessage) {
        IErrorStrategy error = mErrors.get(errorMessage);
        if (error != null) {
            error.showErrorMessage();
        } else {
            System.out.println("no this error message");
        }
    }

 
或是確實使用 “例外” 來處理,端看是否把該情況視為例外。

    public void showErrorMessage(int errorMessage) {
        try {
            IErrorStrategy error = mErrors.get(errorMessage);
            error.showErrorMessage();
        } catch (NullPointerException e) {
            System.out.println("no this error message");
        }
    }

 
以下是另外一個範例。
對字串做檢查,判斷字串是否符合某些規則。
一般來說最簡單直覺的方式就是以 if-else 來判斷 e.g.,

        public boolean checkData(String data)
        {
            if(data == null){
                return false;
            }else if(data.equals("")){
                return false;
            }
            return true;
        }

 
當然也具有在開頭提到的種種缺點,因此我們一樣使用 Strategy 來取代 if-else

public interface DataFormatChecker
{
    public static final String TAG = DataFormatChecker.class.getSimpleName();
    public boolean checkData(String data);
    class CheckAllRules implements DataFormatChecker
    {
        private Collection<DataFormatChecker> mRules = new ArrayList<DataFormatChecker>();
        public CheckAllRules() {
            mRules.add(new CheckNull());
            mRules.add(new CheckEmpty());
        }
        @Override
        public boolean checkData(String data)
        {
            for (DataFormatChecker checker : mRules) {
                if (checker.checkData(data)) {
                    Log.d(TAG, "which checker:" + checker.getClass().getSimpleName());
                    return false;
                }
            }
            return true;
        }
    }
    class CheckNull implements DataFormatChecker
    {
        @Override
        public boolean checkData(String data)
        {
            return null == data;
        }
    }
    class CheckEmpty implements DataFormatChecker
    {
        @Override
        public boolean checkData(String data)
        {
            return data.equals("");
        }
    }
}

第7行的 CheckAllRules 會將所有的規則物件放到 list 中,在其 checkData 方法(第18行)將這些規則物件取出並比對字串與規則。
之後若有新的規則只要新增 CheckXXX 類別 implements DataFormatChecker,再加入到 CheckAllRules 的 list 中。
外部呼叫 e.g.,

String data = "1234";
new DataFormatChecker.CheckAllRules().checkData(data);

 
另外也可使用 enum 取代 CheckAllRules e.g,

public interface DataFormatChecker
{
    ...
    public enum CheckAllRules {
        CHECK_NULL(new DataFormatChecker.CheckNull()),
        CHECK_EMPTY(new DataFormatChecker.CheckEmpty());
        private DataFormatChecker mChecker;
        private CheckAllRules(DataFormatChecker checker) {
            mChecker = checker;
        }
        public static boolean checkData(String data)
        {
            for (CheckAllRules type : CheckAllRules.values()) {
                if (type.mChecker.checkData(data)) {
                    Log.d(TAG, "check data error:" + type.mChecker.getClass().getSimpleName());
                    return false;
                }
            }
            Log.d(TAG, "check data valid");
            return true;
        }
    }
    ...
}

 
若有新的規則需求只要新增 CHECK_XXX(new DataFormatChecker.CheckXXX()) 在 enum 中,
並實作 DataFormatChecker.CheckXXX 內容 e.g.。

    public enum CheckAllRules {
        CHECK_NULL(new DataFormatChecker.CheckNull()),
        CHECK_EMPTY(new DataFormatChecker.CheckEmpty()),
        CHECK_LENGTH_MAX(new DataFormatChecker.CheckLengthMax());
        ...
    }
    ...
    class CheckLengthMax implements DataFormatChecker
    {
        @Override
        public boolean checkData(String data)
        {
            return data.length() > 100;
        }
    }

 
外部呼叫不需要修改(符合 OCP),但我們已經新增了另一項檢查規則(Check Length Max)了。

String data = "1234";
new DataFormatChecker.CheckAllRules().checkData(data);

 

分類
Uncategorized

TraceView

Traceview 用來追蹤程式執行效能,主要可運用2種方式來產生效能分析圖(.trace)
A-1.使用插入code方式
在想分析的程式碼區塊的開頭及結尾個別加入

Debug.startMethodTracing()
//Analysis code....
//Analysis code....
//Analysis code....
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

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

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秒

分類
Uncategorized

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來建立

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 Pattern, XmlParser為SuperClass,轉換的必要步驟都在此實作,其餘部份在個別SubClass實作

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方法

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.
使用轉換

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);
    }
}

 

分類
Uncategorized

MonkeyRunner 紀錄

Description:
首先MonkeyRunner主要是調用
/android-sdk-linux/tools/monkeyrunner 來自動化執行腳本
Step1.
/android-sdk-linux/tools/中建立專屬的測試腳本資料夾,其中放入測試用腳本,
假設專案名稱為Project1,建立/android-sdk-linux/tools/Project1_script資料夾
Step2.
把專案apk(Project1.apk)放入步驟1建立的資料夾中並建立測試腳本(Project1_script.py),
測試腳本內容可參考以下範例
# -*- coding: utf-8 -*-
# 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組合(package和activity)
runComponent = package + '/' + activity
 
# Runs the component
device.startActivity(component=runComponent)
 
# Do something u want to do...
第10行為安裝 package(app), 若 app 已安裝且沒有任何變動,這行可以刪除。
第22行為啟動 activity,其中參數 component= 為固定用法。
2.如何執行測試腳本:
使用Terminal移動到/android sdk/tools/Project1_script資料夾,再輸入
../monkeyrunner Project1_script.py
3.相關的內容在android api 中都有使用說明
http://developer.android.com/tools/help/monkeyrunner_concepts.html#APIClasses
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 '顯示訊息'

5. MonkeyRunner 可以獨立於app測試,大部分使用 MonkeyRunner 的情況都是測試app內部功能,但MonkeyRunner可以獨立於app以外,如下腳本並沒有啟動任何activity,而是按下home key。

# -*- coding: utf-8 -*-
# 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()
# do anything "OUT" of app
device.press('KEYCODE_HOME','DOWN_AND_UP')

 
 

分類
git 錯誤紀錄

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

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