野良アプリに自動更新のような機能を実装する
アプリ起動中に更新チェックとapkファイルのダウンロードを行い、インストール画面を表示する方法。
公式マーケットのようにユーザーの操作を一切必要とせずアプリを更新するものではありません。
動作としてはAdobe AIRの自動更新に近いものです。
まずはバージョンチェック用のxml
バージョン情報と最新版apkファイルのurlを記載します 。
<?xml version="1.0" encoding="utf-8"?>
<update>
<versionCode>4</versionCode>
<versionName>1.2</versionName>
<url>http://myserver.net/update.apk</url>
<description><![CDATA[
test
]]></description>
</update>
自動更新したいアプリ本体のソースはだいたい以下のようになります。最新版apkと上記xmlをサーバにアップロードしてupdateXmlUrlに指定。
public class AutoUpdateTestActivity extends Activity implements AutoUpdateEventListener {
private final String updateXmlUrl = "http://myserver.net/update.xml";
private Handler handler;
private Updater updater;
public void onCreate(Bundle savedInstanceState) {
handler = new Handler();
//1.更新チェック
updater = new Updater(this, updateXmlUrl);
updater.updateCheck();
}
@Override
public void onUpdateAvailable() {
//2.アップデートダイアログの表示
handler.post(new Runnable() {
@Override
public void run() {
showUpdateDialog();
}
});
}
private void showUpdateDialog(){
new AlertDialog.Builder(this)
.setTitle("Update")
.setIcon(R.drawable.ic_launcher)
.setMessage("Update available")
.setPositiveButton("Update",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//3.ダウンロード開始
updater.downloadApk();
}
})
.setNegativeButton("Cancel", null)
.show();
}
@Override
public void onUpdateApkDownloadComplete() {
//4.インストール
handler.post(new Runnable() {
@Override
public void run() {
updater.installApk();
}
});
}
public void onDestroy() {
//5.テンポラリファイルの削除
updater.deleteTempApkFile();
}
}
Updaterは長いので一部抜粋。
アプリのversionCodeとversionNameを取得
PackageManager pm = activity.getPackageManager(); String packageName = activity.getPackageName(); PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_META_DATA); int versionCode = packageInfo.versionCode; String versionName = packageInfo.versionName;
更新用xmlからversionCodeとversionName、apkファイルのurlを取得
HashMap<String, String> map = new HashMap<String, String>();
InputStream is = new URL(updateXmlUrl).openConnection().getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
DocumentBuilderFactory document_builder_factory = DocumentBuilderFactory.newInstance();
DocumentBuilder document_builder = document_builder_factory.newDocumentBuilder();
Document document = document_builder.parse(bis);
Element root = document.getDocumentElement();
if(root.getTagName().equals("update")){
NodeList nodelist = root.getChildNodes();
for (int j=0;j<nodelist.getLength();j++){
Node node = nodelist.item(j);
if(node.getNodeType()==Node.ELEMENT_NODE){
Element element = (Element) node;
String name = element.getTagName();
String value = element.getTextContent().trim();
map.put(name, value);
}
}
int versionCode = Integer.parseInt(map.get("versionCode"));
String versionName = map.get("versionName");
String updateApkUrl = map.get("url");
}
apkファイルのダウンロード
URL url = new URL(updateApkUrl);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
InputStream is = urlConnection.getInputStream();
File cacheDir = getExternalCacheDir();
File tempApkFile = File.createTempFile("apk", ".apk", cacheDir);
FileOutputStream os = new FileOutputStream(tempApkFile);
byte[] buffer = new byte[1024];
int bufferLength = 0;
while ( (bufferLength = is.read(buffer)) > 0 ) {
os.write(buffer, 0, bufferLength);
}
os.close();
is.close();
インストール画面の表示
Intent intent = new Intent(Intent.ACTION_VIEW); Uri dataUri = Uri.fromFile(tempApkFile); intent.setDataAndType(dataUri, "application/vnd.android.package-archive"); startActivity(intent);
AutoUpdateEventListenerやその他細かい部分はソースをダウンロードしてください。エラー処理などはかなり適当です。それとインストール用のapkファイルを本体側に保存するとなぜかインストールできませんでした。AndroidManifest.xmlが読み込めないとか何とか・・・。
ソースダウンロード: AutoUpdateTest_src.zip
参考にさせていただきました。
UtilのurlToOutputStream関数の、
urlConnection.setDoOutput(true); を削除したらうまくいきました。
SDカードがないNexus7でテストしましたが、インストーラーも起動しました。
コメントありがとうございます。setDoOutputはPOSTのときだけ必要でしたか。
インストーラが起動しないのは何かの設定ミスか、あるいはAndroid2.3の頃のSDKが原因かもしれませんね。