野良アプリに自動更新のような機能を実装する
アプリ起動中に更新チェックと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が原因かもしれませんね。