2012年9月5日 星期三

[Linux] 使用 Git + Gitolite + Gitweb 架設 Git Server @ Ubuntu 12.04

 


這半年算正式接觸 git 的使用,開始從 svn 轉過去了吧?! :P 接著則架設公司用的 Git Server 服務,就挑選了 Gitolite 套件,以打造像 github.com 服務,並透過 htpasswd 管理,提供簡易的 GitWeb 版,讓公司成員可以透過 Web 進行下載、瀏覽程式碼,而開發成員可以同時且分散式地開發程式。


在這邊就不多說 Git 的使用,而著重在 Gitolite 跟 GitWeb 的部份,其中 Gitolite 則是透過綁定一個系統帳號,而眾人存取都是透過 SSH Key-pair 來進行,只要把 keys 命名及綁定權限,就可以決定哪隻 key 可以讀寫、哪隻 key 只能讀取,達成簡易的權限管理。


Gitolite 的安裝及設定(採用 gitolite 系統帳號):


$ sudo apt-get install gitolite
$ sudo adduser gitolite
$ sudo su gitolite
$ whoami
gitolite


建立管理者(綁在一個 key):


$ cd /path
$ ssh-keygen -t rsa -P '' -f gitolite
$ ls
gitolite gitolite.pub 


初次設定 Gitolite:


$ whoami
gitolite
$ gl-setup /path/gitolite.pub
The default settings in the rc file (/home/gitolite/.gitolite.rc) are fine for most
people but if you wish to make any changes, you can do so now.
hit enter...

creating gitolite-admin...
Initialized empty Git repository in /home/gitolite/repositories/gitolite-admin.git/
creating testing...
Initialized empty Git repository in /home/gitolite/repositories/testing.git/
[master (root-commit) #######] start
2 files changed, 6 insertions(+)
create mode 100644 conf/gitolite.conf
create mode 100644 keydir/gitolite.pub


新增使用者帳號(請對方產生Key-pair後,把 public key 交給你,此例是 alice.pub):


$ git clone gitolite@localhost:gitolite-admin.git
(需使用當初登記為管理者的 key 來存取) 


$ cd gitolite-admin 
$ ls
conf keydir
$ cp /path/alice.pub keydir
$ git add keydir/alice.pub
$ git commit -m 'add users: alice' 
$ git push 


如此一來,該使用者 (alice.pub) 就能夠透過 git clone gitolite@localhost:testing.git 進行存取測試,且新增使用者可遠端進行


新增專案(proj.git) & 設定權限(alice):


$ cd ~/ && ls
projects.list  repositories
$ ls ~/repositories
gitolite-admin.git  testing.git


$ git clone gitolite@localhost:gitolite-admin.git


(需使用當初登記為管理者的 key 來存取) 


$ cd gitolite-admin
$ vim conf/gitolite.conf
repo gitolite-admin
    RW+ = gitolite

repo testing
    RW+ = @all

repo proj
    RW+ = alice
    R      = @all
$ git commit -a -m 'add projects: proj.git' 
$ git push 
...
...
remote: creating proj...
remote: Initialized empty Git repository in /home/gitolite/repositories/proj.git/

$ ls ~/repositories
gitolite-admin.git proj.git testing.git  


如此一來,Alice 就可以拿著自己的 key 用 git clone gitolite@localhost:proj.git 取出來讀寫了,並提供其他人讀取的權限,且新增專案、設定權限皆可遠端進行


架設 Gitweb:


$ sudo apt-get install apache2 gitweb
$ sudo usermod -a -G gitolite www-data
$ sudo vim /etc/gitweb.conf


$projectroot = "/home/gitolite/repositories"; 
$feature{'highlight'}{'default'} = [1];
# 提供系統 loadavg check,若系統繁忙,逛 gitweb 只會看到 503 - The load average on the server is too high 訊息
$maxload = 500; 


設定 repo 建立權限為 0750,如此一來 www-data 才可以存取:


$ vim /home/gitolite/.gitolite.rc
REPO_UMASK = 0027; 


接著設定 htpasswd 來管理 gitweb 的使用:


$ sudo htpasswd -cb /etc/appach2/gitweb.htpasswd ID PASSWD
$ sudo vim /etc/apache2/conf.d/gitweb
Alias /gitweb /usr/share/gitweb
<Directory /usr/share/gitweb>
  Options FollowSymLinks +ExecCGI
  AddHandler cgi-script .cgi
 
  AuthUserFile /etc/apache2/gitweb.htpasswd
  AuthName "GitWeb"
  AuthType Basic
  require valid-user
  Order allow,deny
  Allow from 127.0.0.0/255.0.0.0 10.0.0.0/8 192.168.0.0/16 ::1/128
  satisfy any
</Directory>


如此一來,公司內部(10.0.0.0/8, 192.168.0.0/16) 可以直接逛 gitweb,而外部連線進來則需要 id/passwd 的確認才能瀏覽。如果不想要把某些 project 被 gitweb 讀取的話,就把它 chmod 700 吧 ($ chmod 700 /home/gitolite/repositories/gitolite-admin.git )


@ 2012-09-06 補充:


Ubuntu 12.04 內建的 Gitolite 版本為 2.2-1,尚不支援 Admin Defined Commands (ADC) ?! 故先從 https://github.com/sitaramc/gitolite 取下後來安裝 Gitolite 3.x ,粗略升級流程:



    1. 移除系統 Gitolite


      • $ sudo apt-get remove gitolite


    2. 備份舊版 Gitolite & admin key-pair


      • $ sudo cp -r /home/gitolite /home/gitolite-2.2-1
        $ sudo chown -R gitolite:gitolite /home/gitolite-2.2-1 
        $ mv /home/gitolite-2.2-1/repositories/gitolite-admin.git /home/gitolite-2.2-1/gitolite-admin.git
        $ mv /home/gitolite-2.2-1/repositories/testing.git /home/gitolite-2.2-1/testing.git 


    3. 下載和安裝最新版 Gitolite (在此換掉原先的 gitolite.pub)


      • $ whoami
        gitolite
        $ rm -rf ~/* ~/.gitolite* ~/.ssh
        $ cd ~/
        $ git clone https://github.com/sitaramc/gitolite.git
        $ mkdir ~/.ssh ~/bin
        $ ssh-keygen -t rsa -P '' -f ~/.ssh/gitolite
        $ ls ~/.ssh
        gitolite    gitolite.pub
        $ mv ~/.ssh/gitolite ~/.ssh/id_rsa
        $ gitolite/install -to $HOME/bin
        $ ~/bin/gitolite setup -pk ~/ssh/gitolite.pub
        ...
        $ ls ~/
        bin  gitolite  projects.list  repositories 


    4. 設定 Gitolite.rc 與 Gitweb 的部份


      • $ vim ~/.gitolite.rc
        UMASK   =>   0027,    # = 0750 


    5. 恢復原先 repositories 跟 keys


      • $ cp -r /home/gitolite-2.2-1/repositories/* ~/repositories/ 
        $ cd ~/ 
        $ git clone /home/gitolite-2.2-1/gitolite-admin.git old-gitolite-admin
        $ git clone gitolite@localhost:gitolite-admin.git
        $ rm ~/old-gitolite-admin/keydir/gitolite.pub
        $ cp ~/old-gitolite-admin/keydir/* ~/gitolite-admin/keydir/
        $ cp ~/old-gitolite-admin/conf/gitolite.conf ~/gitolite-admin/conf/gitolite.conf
        $ cd ~/gitolite-admin
        $ git add .
        $ git commit -a -m 'restore init'
        $ git push 



測試 ADC(需設定 ~/.gitolite.rc 的 COMMANDS 清單,預設只有幾項 commands 可以用):


$ ssh gitolite@localhost help
hello gitolite, this is gitolite3 v3.04-15-gaec8c71 on git 1.7.9.5

list of remote commands available:

D
desc
help
info
perms
writable


遠端指定 repo myproj 進行刪除:


$ ssh gitolite@git-server D unlock myproj
$ ssh gitolite@git-server D rm myproj


遠端建立 repo myproj 方式,有兩種方式:



    1. 編輯 gitolite-admin/conf/gitolite.conf,直接新增未存在的 repo 後,git push 後則會自動建立

    2. 在 gitolite-admin/conf/gitolite.conf 給定 C 權限,如此一來,在 git clone 不存在的專案則會自動建立


      • $ vim gitolite-admin/conf/gitolite.conf

        @admin = user1 user2 user3

        repo [a-zA-Z0-9].*
            C  =  @admin
            RW+D  = CREATOR
            R  = @all 

      • $ git clone gitolite@git-server:myproject.git
        ...
        若 myproject.git 不存在,則會自動建立 myproject.git,並且建立者有 D 的權限,可以透過 ssh gitolite@git-server D rm myproj 來刪除



此外,公司常將一些內部使用的服務擺在防火牆後透,並透過 port forwarding 來處理對外連線,這時候需指定 port 連線,常用的有 ssh, git 跟 scp 指令,用法如下:


$ git clone ssh://gitolite@git-server:port/myproject.git
$ ssh -p port gitolite@git-server help
$ scp -r -P port input user@git-server:output 


@ 2012-09-07 搭配使用者自動管理專案之 ADC 常用方式:


編輯 gitolite-admin/conf/gitolite.conf ,提供使用者自動建立 public projects 和 private projects 功能:


repo priv/CREATOR/[a-zA-Z0-9].*
  C = @RDs
  RW+D = CREATOR
  RW = WRITERS
  R = READERS


# Update bin/Gitolite/Rc.m Or add into ~/.gitolte.rc :
# $REPOPATT_PATT        = qr(^\@?[[0-9a-zA-Z\(^][-0-9a-zA-Z._\@/+\\^$|()[\]*?!={},]*$);
repo ^(?!priv/)[0-9a-zA-Z]+$
  C = @RDs
  RW+D = CREATOR
  RW = WRITERS  
  R = @all


當使用者直接用 $ git clone gitolite@git-server:myproj.git 時,若 myproj.git 不存在則自動建立,此屬性屬於第二個,也就是對全體帳號預設都可以 Read 的;如果是用 $ git gitolite@git-server:priv/UserID/myprivate.git 時,這時 myprivate.git 預設是只有建立者可以讀取跟寫入。此外,這些專案的創立者也可以砍掉專案。


至於要對專案開放權限,則可以使用 perms 指令:


增加讀寫權限:


$ ssh gitolite@git-server perms myproject + WRITERS coworker_name


增加讀取權限:


$ ssh gitolite@git-server perms myproject + READERS coworker_name


移除讀寫(讀取)權限:


$ ssh gitolite@git-server perms myproject - WRITERS coworker_name
$ ssh gitolite@git-server perms myproject - READERS coworker_name 


列出權限清單:


$ ssh gitolite@git-server perms -l myproject


刪除專案:


$ ssh gitolite@git-server D unlock myproject
$ ssh gitolite@git-server D rm myproject


移到垃圾桶和還原操作:


$ ssh gitolite@git-server trash myproject
'myproject' moved to trashcan
$ ssh gitolite@git-server list-trash
myproject/2012-09-07_21:45:25
$ ssh gitolite@git-server restore myproject/2012-09-07_21:45:25
'myproject/2012-09-07_21:45:25' restored to 'myproject' 


2012年9月2日 星期日

使用 iTunes 批次更新歌曲資訊 @ Mac OS X

song_itunes02


用 Mac 有一陣子了,最近整理以前買的日劇音樂合輯共有兩片 CD,弄成 MP3 後,卻發現在 iTunes 上顯示為兩張專輯,仔細確認後,原來 iTunes 對歌曲進行分類專輯時,有用到歌曲的敘述,因此就碰到需要更改歌曲內容來達成擺在同一張專輯,故需要批次整理一堆歌曲的方式。


所幸內建 iTunes 可以處理,可以先把專輯名稱改成一樣,會強制把歌曲擺在同一張專輯,接著再把全選所有歌曲,透過簡介方式可以批次更改。


song_itunes01


2012年8月31日 星期五

iOS 開發筆記 - 使用 Interface Builder 建立專屬的 UITableViewCell

IBStudy--


雖然內建的 UITableViewController 已經有不少還不錯用的 Cell Style (UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle),但客製化自己 Cell 還是一種必學的技能。然而,客製化自己的 layout 大概可以分成兩種方式,一種是純程式碼的撰寫(覆寫 layoutSubViews 或對 contentView 加東西),例如把 ImageView 擺在哪個 (x,y) 座標並設定大小為 (w,h) 等,另一種則是使用 Interface Builder 對元件的拖拉,再跟 Class 進行相對應的設定。由於之前比較熱愛 Coding ,所以一直沒用 Interface Builder,這次就來摸個幾把美美的 UI 操作吧 *誤*


使用 Interface Builder 仍需搭配 Class 物件來使用。例如建立自己的 MyTableViewCell(繼承UITableViewCell) 排版畫面 MyCell.xib 後,需搭配物件使用,當物件初始化時,可以指定採用 MyCell.xib 來初始化,整個過程等同於跟系統要了記憶體來使用,所以有一些人認為透過 Iterface Builder 所建立的 layout 不太好進行資源管理,並且當程式不穩時,很難除錯。


以 UITalbViewController 為例,建立自己的 MyTalbeViewCell,其粗略的使用方式:



  1. 建立 XIB (MyCell.xib),規劃 UI 呈現部分

  2. 建立對應的 UIView Class (MyTableViewCell 並繼承 UITableViewCell),並新增所需的 IBOutlet

  3. 更新 XIB 的 Custom Class 及對 XIB 上的元件進行連結 (outlets)

  4. 在 UITableViewController - (void)viewDidLoad 中,進行 XIB 的註冊

  5. 在 UITableViewController - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 中,進行 cell 的初始化及設定使用

  6. 在 UITableViewController -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 中,回傳 Cell 的高度


用法:[New File] -> [User Interface] -> 選 View or Empty 都行 -> 選擇 iPhone or iPad -> 取名 MyCell (此名稱跟之後XIB使用有關)


IBStudy00


點一下 MyCell.xib 開啟設定頁面,接著可在右下角 Object Library 之下方 Search 欄位中,填入想要的元件並拖拉到畫面進行 layout 安排,如使用 UITableViewCell、UILabel、UIImageView、UITextView 等元件


IBStudy01


IBStudy03


接著建立自己的 UITableViewCell 物件,此例為 MyTableViewCell,並將 XIB 中用到的元件進行宣告及實作(透過 Objective-C @property 語法可以請編譯器處理實作)


@interface MyTableViewCell : UITableViewCell


@property (nonatomic, strong) IBOutlet UIImageView *mImageView;
@property (nonatomic, strong) IBOutlet UILabel *mTitle;
@property (nonatomic, strong) IBOutlet UILabel *mSubtitle;
@property (nonatomic, strong) IBOutlet UILabel *mDescription;


@end


接著再切回 MyCell.XIB 進行元件的連結,先點選左邊 Objects 下的 UITableViewCell 後,在右邊選單上,切到 Identity inspector 可以進行 Custom Class 的更名,請更改成剛剛建立的 Class name (MyTableViewCell),如此一來切到 Connections inspector 可以進行 Outlets 連結,並可以看到 mImageView、mTitle、mSubtitle 和 mDescription 等項目。


IBStudy04 IBStudy05


將滑鼠移至項目旁邊的圓形框中,可以看到 + 號,點選按著後,拖到左邊畫面上的對應元件,放開後則完成連結。


IBStudy06


另外也可以在左邊 Objects 下,按著 Ctrl 點一下 My Table View Cell 後,也會出現 Connections 清單,其連結方式一樣。


IBStudy08


一切的 Connections 設定完後,即可存檔關閉,在此僅 demo 呈現的部份,若有其他點擊互動則需要設定 IBAction。此外,點選 MyTableViewCell.h 中,可以看到左邊都可以看 IBOutlet 都有圓點的連結圖示,亦可用來 debug 觀看自己有沒有漏掉什麼連結。


IBStudy10


最後則是挑一個 UITableViewController 來把玩吧:


- (void)viewDidLoad
{
    [super viewDidLoad];


    [self.tableView registerNib:[UINib nibWithNibName:@"MyCell" bundle:nil]
forCellReuseIdentifier:@"MyCellIdentifier"]; // "MyCell" for MyCell.xib
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return 5;
}


-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 150; // 可在 XIB 檔案,點選 My Talbe View Cell 從 Size inspector 得知
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    //if( cell == nil)
    // cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"] autorelease];
    //cell.imageView.image = [UIImage imageNamed:@"icon_chrome.png"];


    MyTableViewCell *cell = (MyTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"MyCellIdentifier"];
    cell.mTitle.text = [NSString stringWithFormat:@"Row: %d", indexPath.row];
    cell.mSubtitle.text = [NSString stringWithFormat:@"section: %d", indexPath.section];
    cell.mDescription.text = @"My Description";
    switch ((indexPath.row%4)) {
        case 1:
            cell.mImageView.image = [UIImage imageNamed:@"icon_chrome.png"];
            break;
        case 2:
            cell.mImageView.image = [UIImage imageNamed:@"icon_firefox.png"];
            break;
        case 3:
            cell.mImageView.image = [UIImage imageNamed:@"icon_safari.png"];
            break;
         default:
            cell.mImageView.image = [UIImage imageNamed:@"icon_ie.png"];
            break;
    }
    return cell;
}


成果:


IBStudy99


2012年8月29日 星期三

iOS 開發筆記 - UITableViewController 之 dataSource 更新顯示

最近開始複習 iOS SDK,過去使用 UITableViewController 時,每次資料新增變動時,都是透過 [self.tableView reloadData] 來更新畫面,效果就是把整個 UITableView 都重畫一次,只是若碰到資料更新很頻繁時,就會發現有些 touch event 因過度 reloadData 而出現短暫不能被處理。最近看 UITableView 相關書籍時,才發現原來早在 iOS SDK 3.0 時,就可以指定某個 Section 的某個 Row 更新就好!這招當然就可以用在新增資料及更新資料,實在太讚了!不曉得是不是太早接觸 iPhone SDK 哩,真的是太晚發現了 Orz


From UITableView Class Reference



  • reloadData (iOS SDK 2.0)

  • reloadRowsAtIndexPaths:withRowAnimation: (iOS SDK 3.0)

  • reloadSections:withRowAnimation: (iOS SDK 3.0)

  • reloadSectionIndexTitles (iOS SDK 3.0)


早期寫法(iOS 2.0):



  1. 更新 dataSource

  2. 呼叫 [self.tableView reloadData]


更有效率得寫法(iOS 3.0),新增資料(前頭):



  1. [dataSource insertObject:newItem atIndex:0];

  2. [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject: [NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];


更有效率得寫法(iOS 3.0),新增資料(後頭):



  1. [dataSource addObject:newItem];

  2. [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject: [NSIndexPath indexPathForRow:[dataSource count]-1 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];


如果,不幸一開始使用卻會蹦出 Exception:


Invalid update: invalid number of sections.  The number of sections contained in the table view after the update (1) must be equal to the number of sections contained in the table view before the update (0), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).


那可能跟我的情況很像,簡單的說,假設你的 numberOfSectionsInTableView 判斷是 return dataSource && [dataSource count] ? 1 : 0 ; 的話,那一開始因為沒資料而 sections 個數為 0,所以更新指定 section 時會出錯,解法有兩個,一個是一開始 numberOfSectionsInTableView 就會回傳固定數值(非0),這樣就不會出錯;另一個則是第一次新增資料時,呼叫 [self.tableView reloadData] 處理,之後才用 insertRowsAtIndexPaths 處理:


[dataSource addObject:item];
if( [dataSource count] == 1 )
        [self.tableView reloadData];
else
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject: [NSIndexPath indexPathForRow:[dataSource count]-1 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];


此外,在新增資料在前頭後,如果更新太頻繁時將會碰到 'NSInternalInconsistencyException', reason: 'Attempt to create two animations for cell' 而程式 crash,例如:


for( int i=0 ; i<50 ; i++ ) {
         [dataSource insertObject:[NSString stringWithFormat:@"%d",i] atIndex:0]; // 每次塞在 index = 0 的位置
         [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject: [NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic]; // 每次都對 index = 0 的 cell 更新
}


解法:


for( int i=0 ; i<50 ; i++ ) {
        [dataSource insertObject:[NSString stringWithFormat:@"%d",i] atIndex:0];
        [self.tableView beginUpdates];
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject: [NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
        [self.tableView endUpdates];
}


或統一更新:


NSMutableArray *indexPathSet = [[NSMutableArray alloc] init];
for( int i=0 ; i<50 ; i++ ) {
        [dataSource insertObject:[NSString stringWithFormat:@"%d",i] atIndex:0];
        [indexPathSet addObject:[NSIndexPath indexPathForRow:i inSection:0]]; // 依序累積 50 個 rows
}
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPathSet withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
[indexPathSet release];


故比較好得方式就是在對 cell 處理更新特效時(除了reloadData用法外),一律採用 [self.tableView beginUpdates]; 和 [self.tableView endUpdates]; 包起來(有點lock味道),並且在目前很流行 Event-Driven 或 Async 架構更須如此。更多詳情,請參考 UITableView Class Reference 囉。


2012年8月24日 星期五

Android 開發筆記 - APP 的上架與更新

ExportSignedApplicationPackage


嘗試將小試身手的程式上架看看,初步的心得,覺得 Android app 上架比 iOS app 還要快,大概資料填妥後,約兩小時內就可以在 Google Play 搜尋到了!


上架前,需準備的資料:



  • keystore

  • Signed Application Package (apk)

  • ic_Launcher.png

  • 512x512 icon

  • 2 images


軟體更新需準備的資料:



  • 更新 AndroidManifest.xml 的 Version code 跟 Version name 

  • 上次使用的 keystore

  • Signed Application Package (apk)


所謂的 keystore 可以想成對 apk 進行簽名,透過這個 keystore ,可以驗證此 apk 是由同一人所送出的。特別在 Android Developer Console 中,可允許多位開發人員,假設有兩個團隊分別上架兩個 app 時,這時每個團隊各自用自己的 keystore 維護,除了可避免內鬥被人亂換掉 apk 外,也是種不錯的保護機制。當然,如果連 keystore 弄丟的話,似乎…只好砍掉重來了 XD


過去產生 keystone 時,都是透過 command line 來進行,現在 Eclipse+ADT 的環境已經可以透過視窗介面進行啦!並且產生完 keystore 後,連續動作還可以順便產生可以上架的 apk 


在 Eclipse 之 Package Explorer 裡點選 Your App -> 按右鍵選擇 Android Tools -> 選 Export Signed Application -> Create new keystone -> 輸入相關資料及儲存位置 -> 順便輸出簽證後的 apk


CreateKeystore1


CreateKeystore2


接著回到 Android app 上架流程,拿著剛簽好的 apk 往 Android Developer Console 上去後,會蹦出一些表單資料的填寫,其中需要的額外資料:



  • 至少 2 張圖片

  • 一張 512x512 圖片

  • 程式顯示名稱(在Google Play上)

  • 程式簡介

  • 程式分類

  • 程式類型


如此填好後,就可以按儲存或是發佈了,十分快速。至於兩張使用圖片,可以用 Eclipse 之 DDMS 去拍兩張程式在裝置上的使用截圖即可,而 512x512 基本上就是 App icon 的圖,所以做 App icon 時,別忘了先做張 512x512 的圖,之後縮成 72x72 來當 ic_Launcher.png 使用即可。


如果要更新已上架的軟體,姑且不論程式碼有沒更新,至少需更新 AndroidManifest.xml 的 Version code 跟 Version name 即可,其中 Version code 是流水號,讓 Google Play 系統判別用途,而 Version name 是顯示給使用者的版本,如 1.0, 1.0.1, 1.1 等用途。接著一樣用 Android Tools -> Export Signed Application -> 選擇 keystone path -> 輸入密碼 -> 選擇 Alias 及密碼後 -> 輸出成功


UseExistingKeystore1


UseExistingKeystore2


最後到 Android Developer console 上,選擇要更新的 app 後,切換到 APK 分頁,點選 "上載APK" 後,再把前一版本停用,啟用新版本,就算完成更新,更細膩的部份則是切換到"產品詳細資料"裡,填寫此版本更新的項目資訊。約莫兩小時就可以在 Google Play 上搜尋到新版資訊。