前言
在Android的開發上,每當建置完apk後,在版本控制或是將apk釋出給測試人員或專案管理者測試時,有時相關測試人員會來向開發者尋求apk檔,倘若該情形是很頻繁的釋出,那這件事情就會變得很擾人。我們可以透過Jenkins或其他持續整合(Continuous Integration, CI)環境來將我們的apk釋出至DropBox環境或其他空間,當然,若你沒有建立CI,也不想每次建置完apk後,又要手動複製檔案至某個儲存空間,或許可以利用Google Drive API來將你的檔案自動上傳,這篇文章便是教你如何開發一個apk檔案上傳器。
Java實作
在Android: 如何在Gradle建立Build Variants及更換APK檔名這篇文章中,我們在使用"gradle assembleDebug/assembleRelease"的指令時,會同時建置出很多apk,所以我會利用這個專案來解釋實作。- 申請憑證
- 由於要在程式內使用Google Drive API,我們需要至Google Developer Console申請一個應用程式內用來識別的憑證。
- 先在Google Developer Console建立新專案,這裡我命名為GDUSample
- 點選憑證
- 選擇OAuth用戶端ID
- 選擇其他,並輸入一個名稱,這邊我一樣命名為GDUSample
- 建立後,就能看見憑證的資料,這時選擇下載Json
- 至資訊主頁點選啟用API,並搜尋Google Drive API
- 撰寫程式碼
- 待憑證和API啟用後,我們則要使用Google Drive API進行開發。由於這個檔案上傳器是利用Java程式來開發,就使用你習慣的IDE即可,這裡我是使用Eclipse,並開啟一個新的JAVA專案。
- 匯入Drive API
- 選擇Drive API v2,由於我已經匯入,所以下圖會顯示"installed"
- 匯入下列lib
- 將先前下載憑證的Json,命名為"client_secret.json",並放置於專案下。
- 初始HttpTransport與FileDataStoreFactory物件
public static final String APPLICATION_NAME = "Drive API Java Quickstart";
public static final java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("user.home"), getJarPath());
public static FileDataStoreFactory DATA_STORE_FACTORY;
public static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
public static HttpTransport HTTP_TRANSPORT;
public static final List<String> SCOPES = Arrays.asList(DriveScopes.DRIVE);
static {
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
} catch (Throwable t) {
t.printStackTrace();
System.exit(1);
}
}
- 取得Credential物件
- 在程式內使用Google Drive API時,我們需要先利用先前下載的憑證,藉由FileInputStream來產生一個GoogleClientSecrets,並且藉由這個物件,取得Credential物件,程式碼片段下。
public static Credential authorize() throws IOException {
System.out.println("path: " + DATA_STORE_DIR.getPath());
FileInputStream fileInputStream = new FileInputStream(getJarPath() + "/client_secret.json");
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
new InputStreamReader(fileInputStream));
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
clientSecrets, SCOPES).setDataStoreFactory(DATA_STORE_FACTORY).setAccessType("offline").build();
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
System.out.println("Credentials saved to " + DATA_STORE_DIR.getAbsolutePath());
return credential;
}
- 利用Credential取得Drive物件
public static Drive getDriveService() throws IOException {
Credential credential = authorize();
return new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();
}
- 利用Drive物件的Insert方法,在Google Drive上新增資料夾與檔案
- 定義欲在Google Drive新增資料夾或檔案的ID。例如我想在名為GoogleDriveUploader這個資料夾中,新增我的apk資料夾和檔案,所以我需要先取得該資料夾的ID,並將該ID定義於程式當中,如下圖所示。
public static final String apksLocationId = "0B-0iN5m8YqglMHJxdy13Z0JkU3c";
- 使用Insert方法新增資料夾。
- 這邊資料夾的命名方式是以當天的年月日命名,所以會使用到Date物件。
- Insert方法的使用很簡單,只要先產生一個Google Drive的File物件後,接著設定Title與MimeType即可。
- parentId則是上述定義的Id
public File createRemoteApkFolder(String parentId) throws IOException {
File fileMetadata = new File();
StringBuffer folderName = new StringBuffer();
String fileTitle = folderName.append("gduSampleFolder").append(this.util.getDateTime()).toString();
fileMetadata.setTitle(fileTitle);
fileMetadata.setMimeType("application/vnd.google-apps.folder");
if (parentId != null && parentId.length() > 0) {
fileMetadata.setParents(Arrays.asList(new ParentReference().setId(parentId)));
}
File remoteFileFolder = this.service.files().insert(fileMetadata).setFields("id").execute();
return remoteFileFolder;
}
- 使用Insert方法新增檔案
- 新增檔案方式與資料夾無異,只是我們需要指定欲上傳檔案的路徑(filePath)。
- 新增檔案的MimeType,由於我是將所有apk打包成zip檔,所以我將MimeType指定為"application/octet-stream"。
public File createRemoteApkFiles(String fileTitle, String parentId, String filePath, String mediaContentMimeType)
throws IOException {
File fileMetadata = new File();
fileMetadata.setTitle(fileTitle);
fileMetadata.setMimeType(mediaContentMimeType);
if (parentId != null && parentId.length() > 0) {
fileMetadata.setParents(Arrays.asList(new ParentReference().setId(parentId)));
}
java.io.File localFile = new java.io.File(filePath);
FileContent mediaContent = new FileContent(mediaContentMimeType, localFile);
File remoteFile = this.service.files().insert(fileMetadata, mediaContent).setFields("id").execute();
return remoteFile;
}
- 產生出來的Runnable Jar File需要和你下載的“client_secret.json”放在同一個目錄下,基本上就可以執行。可以在Terminal使用"java -jar GoogleDriveUploader.jar"執行,如下圖所示。
- 第一次會需要使用者驗證,所以當你執行該檔案,則會跳出要求使用者允許的頁面,按下允許就可以,之後也無需驗證。
撰寫簡單的Shell Script
其實撰寫完Java程式已經算完成,但我們可以撰寫一個Shell Script,讓我們每次執行完"gradle assebmleDebug/assembleRelease"指令後,將所有apk打包成zip,並上傳至Google Drive,甚至可以再利用CI環境整合,當上傳完成後,則發送信件通知相關測試人員。CI部分若有時間,我則會在之後的文章加入。
#!/bin/sh
echo Current file path: $(pwd)/${0}
gradle clean assembleDebug
zip -r $(pwd)/app/build/outputs/apk/ReleaseApks.zip $(pwd)/app/build/outputs/apk/*.apk
java -jar $(pwd)/GDUSample.jar
sleep 90
- 撰寫完後,我們就可以執行該檔案,就可以直接為我們建置apk,最後直接上傳至Google Drive了,如下圖所示。