Google Cloud Source Repositories 試用紀錄

GCSR(Google Cloud Source Repositories) 是 google 推出以 git 為基礎的程式碼代管服務。

原先我的side project是自己建git server來放置source code,後來因為硬體故障等等

自己維護需要花費不少時間和成本,加上本身也是google 的愛好者,最後選擇GCSR來試試。

首先談談GCSR的缺點,基本上最大麻煩的點為google 提供的repository必須依附於google cloud platform(GCP)上的專案。

也就是說你必須先了解基本的GCP使用方式才能開始用GCSR。

第2個麻煩的點在於create, clone repository在官方教學中必須透過 google cloud sdk才能使用(也許可以透過別的方式但還未試出),也就是說你必須先安裝 google cloud sdk才能開始使用GCSR。

第3個麻煩的點在於免費版的限制,如下







免費方案
超量


最多 5 位使用者
每位專案使用者每月 $1 美元 (專案使用者達 5 位以上時)

50 GB 儲存空間
每 GB 儲存空間 每月 $0.10 美元

50 GB 輸出
每 GB 輸出每月 $0.10 美元



若以上3個限制您都還可以接受,那就繼續往下看吧。

首先提供官網GCSR的說明。若想直接參考使用方式請看quick start。

1.在Google Cloud Platform建立專案
建議新增獨立的GCP專案來放置repository避免和其他的專案混淆。

2.安裝Google Cloud SDK
安裝SDK的目的就是用來新增,複製專案(應該是可以透過其他方式來做到但還未試出)。
注意安裝完成之後還需要進行初始化的動作,參考這裡。

3.安裝git
Windows 官方推薦使用git-scm

Linux就用command line安裝吧

4.使用Google Cloud SDK(gcloud command line) 建立 repository
gcloud source repos create hello-world

clone repo 到本地端
gcloud source repos clone hello-world

從官網看起來似乎只有 create和clone需要使用 gcloud

其它的操作就可以使用一般的git指令,如 git add, git commit, git push等等

5. GCSR UI
當然就像github,gitlab等等,GCSR也有提供repository的UI操作。
以下為repository的介面,存放區即為repository


以下是搶先試用版的畫面


Orignal From: Google Cloud Source Repositories 試用紀錄

使用 RingtoneManager 播放提示音(Notification),鬧鐘(Alarm),來電鈴聲(Ringtone)

1.播放預設的提示音。
  public static void playDefaultNotification(Context context) {
    Uri defaultNotificationURI = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    Ringtone soundOfDefaultNotification = RingtoneManager.getRingtone(context, defaultNotificationURI);
    soundOfDefaultNotification.play();
  }
其中RingtoneManager.TYPE_NOTIFICATION為提示音的設定。

若想播放預設的鬧鐘只要改為RingtoneManager.TYPE_ALARM
播放預設的鬧鐘只要改為RingtoneManager.TYPE_RINGTONE

2.播放裝置內其他的提示音。
首先取得裝置內的提示音清單。

  public static Map<String, String> getSoundTitleAndUri(Context context, int type) {
    RingtoneManager manager = new RingtoneManager(context);
    manager.setType(type);
    Cursor cursor = manager.getCursor();

    Map<String, String> titleAndUri = new HashMap<>();
    while (cursor.moveToNext()) {
      String title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
      String id = cursor.getString(RingtoneManager.ID_COLUMN_INDEX);
      String uri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX);
      titleAndUri.put(title, uri+"/"+id);
    }

    return titleAndUri;
  }
 
使用的方式如下(取得提示音的清單)

    getSoundTitleAndUri(context, RingtoneManager.TYPE_NOTIFICATION);
回傳的Map其key值即為提示音的名稱,value值為提示音的Uri。

若想取得的是鬧鐘清單,只要改為RingtoneManager.TYPE_ALARM即可
若想取得的是來電鈴聲清單,只要改為RingtoneManager.TYPE_RINGTONE即可

若把該Map印出來如下。
key:Alloy value:content://media/internal/audio/media/89
key:Arc value:content://media/internal/audio/media/90
key:Bezel value:content://media/internal/audio/media/91
...
Alloy為提示音的名稱,其URI為 content://media/internal/audio/media/89

最後要播放指定的提示音如下

  public static void playSound(Context context, String uri) {
    Uri soundUri = Uri.parse(uri);
    Ringtone sound = RingtoneManager.getRingtone(context, soundUri);
    sound.play();
  }
使用的方法為

  playSound(context,"content://media/internal/audio/media/89");//play Alloy

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組合(package和activity)
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上,再把
目前branch的commit接到最後面(最新)
當然在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