全てのサーバに言える事かわかりませんが、
Google App Engine for JavaのBlobstoreにアップロードしたapkファイルをダウンロードした時に、
apkファイルがインストール出来たり、出来なかったりしたので、簡単に調査。
(当然ですが、問題となりやすい
レスポンスヘッダのContent-Typeにapplication/vnd.android.package-archiveが必要というのは
クリアしていることが条件の調査です。)
操作としては大きく次の二つがありますが、
挙動が安定しなかったのは、後者の「ダウンロード→インストール」の場合。
「ダウンロード→展開」の場合は、どれも問題なく出来ました。
・apkファイルダウンロード→展開
・apkファイルダウンロード→インストール
インストール出来ない場合というのは、ダウンロードしたapkファイルを展開する際に
「パッケージの解析中に問題が発生しました」
というエラーが出て、Android端末にインストール出来ない状況です。
今回調査した内容について、
サーバに問題があるのか、クライアントに問題があるのかという事について
googleから公式に発表されていないので、あくまで結果のみの掲載となります。
ではまず前提条件。
クライアント側の設定とサーバ側の設定。
■クライアント側
・formのpostでダウンロード用サーブレットの呼び出し。
・formのgetでダウンロード用サーブレットの呼び出し。
・aタグでダウンロード用サーブレットの呼び出し。
■サーバ側
・リクエストパラメータを解析して、BlobstoreServiceのserveメソッドを利用してダウンロード処理。
プログラムで書くと次のような感じです。
■クライアント側
formの場合の例:
<form action="/hoge/fuga" method="post">
<!-- 必要に応じてパラメータ設定 -->
<input type="submit" value="post"/>
</form>
aタグの場合の例:
<a href="/hoge/fuga?key=${key}">aタグ</a>
■サーバ側(Slim3フレームワークでのプログラムとなります)
BlobstoreService blobstoreService = BlobstoreServiceFactory
.getBlobstoreService();
BlobInfoFactory blobInfoFactory = new BlobInfoFactory();
//BlobKeyを取得
String blobKeyStr = asString("blob-key");
// ブロブキー生成
BlobKey blobKey = new BlobKey(blobKeyStr);
BlobInfo blobInfo = blobInfoFactory.loadBlobInfo(blobKey);
// ファイル名を指定
response.setHeader("Content-Type",
"application/vnd.android.package-archive");
response.setHeader("Content-Disposition", "attachment; filename="
+ blobInfo.getFilename() + "");
blobstoreService.serve(blobKey, response);
return null;
結果はというと、
・formのpostでダウンロード用サーブレットの呼び出し。→×インストール失敗
・formのgetでダウンロード用サーブレットの呼び出し。→○インストール成功
・aタグでダウンロード用サーブレットの呼び出し。→○インストール成功
でした。
冒頭でも記載しましたが、ダメだった理由が、
blobstore特有の挙動によるものなのか、Androidの端末の個体差に何かあるのかというのは不明です。
追伸
formのmethodをgetにしても、サーバ側でリクエストのリファラをいじくると
インストール時にエラーが発生する模様です。
原因はわかりませんが、事象は発生率100%で確認されました。
IE8だとfadeInが効かない。
jQueryにfadeIn(ふわっと表示する機能)
http://semooh.jp/jquery/api/effects/fadeIn/%5Bspeed%5D,+%5Bcallback%5D/
というものがあるのだけれど、
こいつがIE8だと効かない。(正確に言うと正常に動かない。)
例えば、こんな感じに書くと、
---ヘッダ---
<script type="text/javascript" src="./jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("div.fuga", this).hide();
$("div.hoge").hover(
function(){
$("div.fuga", this).fadeIn("normal");
},
function() {
$("div.fuga", this).hide();
}
);
});
</script>
---ボディ---
<div class="hoge">
<p class="top_hoge">hoge</p>
<div class="fuga">
fadeInさせるもの
</div>
</div>
IE7とかChromeなら
<p class="top_hoge">hoge</p>
にカーソルを合わせれば、
<div class="fuga">
fadeInさせるもの
</div>
がfadeInされる。
ところが、IE8だと、
ただ表示されるだけ。(ふわっとしない)
fadeInメソッドの引数(normalとか)が意味無くなっている。
そんな時、有効にさせたい場合の一つの方法として、
ブラウザを互換モードで動作させるというのがありました。
htmlのヘッダ部分に↓のmetaタグを追加すれば出来ます。
<meta http-equiv="X-UA-Compatible" content="IE=7">
参考:現在のサイトを解決する方法
http://msdn.microsoft.com/ja-jp/library/cc817570.aspx
↑Windows IE8→IE7の場合
今回はIE7モードで動かせば何とかなるケースでしたので、
互換モードでいきましたが、
ブラウザのバージョンが変わると、表示の違い等様々な弊害が出るので、
気を付けてやって下さい。
jQueryにfadeIn(ふわっと表示する機能)
http://semooh.jp/jquery/api/effects/fadeIn/%5Bspeed%5D,+%5Bcallback%5D/
というものがあるのだけれど、
こいつがIE8だと効かない。(正確に言うと正常に動かない。)
例えば、こんな感じに書くと、
---ヘッダ---
<script type="text/javascript" src="./jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("div.fuga", this).hide();
$("div.hoge").hover(
function(){
$("div.fuga", this).fadeIn("normal");
},
function() {
$("div.fuga", this).hide();
}
);
});
</script>
---ボディ---
<div class="hoge">
<p class="top_hoge">hoge</p>
<div class="fuga">
fadeInさせるもの
</div>
</div>
IE7とかChromeなら
<p class="top_hoge">hoge</p>
にカーソルを合わせれば、
<div class="fuga">
fadeInさせるもの
</div>
がfadeInされる。
ところが、IE8だと、
ただ表示されるだけ。(ふわっとしない)
fadeInメソッドの引数(normalとか)が意味無くなっている。
そんな時、有効にさせたい場合の一つの方法として、
ブラウザを互換モードで動作させるというのがありました。
htmlのヘッダ部分に↓のmetaタグを追加すれば出来ます。
<meta http-equiv="X-UA-Compatible" content="IE=7">
参考:現在のサイトを解決する方法
http://msdn.microsoft.com/ja-jp/library/cc817570.aspx
↑Windows IE8→IE7の場合
今回はIE7モードで動かせば何とかなるケースでしたので、
互換モードでいきましたが、
ブラウザのバージョンが変わると、表示の違い等様々な弊害が出るので、
気を付けてやって下さい。
Androidでリスト表示をする場合は
ListViewを使うのは当然なのだけれど、
テキストや画像を表示する場合は一筋縄ではいかない。
ちなみに文字をリスト表示させるだけであれば
こんな感じで終わる。
"Activity"を拡張したクラスで次の処理を行う。
//ListViewのインスタンスを生成
ListView listView = (ListView) findViewById(R.id.listView1);
//ArrayAdapterのインスタンスを生成
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1);
//adapterに要素を追加する。
adapter.add("hoge");
// adapterをリストに追加
listView.setAdapter(adapter);
行数増やしたい場合は
adapter.add();を繰り返せば良い。
しかし、リストの項目を少し複雑にする場合は別。
まず、継承するクラスをActivityから"ListActivity"に変更する。
public class TestActivity extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//表示する要素をリストで用意する。
ArrayList<Fuga> list = new ArrayList<Fuga>();
setApplicationList(list);
HogeAdapter hogeAdp = new HogeAdapter(this, R.layout.main, list);
setListAdapter(hogeAdp);
}
private void setApplicationList(ArrayList<Fuga> fugaList){
//fugaリストを生成する
Fuga fuga = new Fuga();
fuga.setName("tarou");
fuga.setMessage("Hello!");
fugaList.add(fuga);
}
}
上のクラスでやることといえば、
1.表示したい項目をArrayList型で用意すること。
2.用意したリストを設定するAdapterクラス(後述)を用意すること。
3.リストを設定したAdapterクラスを設定(setListAdapter)すること。
という感じです。
次にArrayAdapterクラスを継承したクラスを用意する。
public class HogeAdapter extends ArrayAdapter {
private ArrayList<Fuga> items;
private LayoutInflater inflater;
public HogeAdapter(Context context, int textViewResourceId,
ArrayList<Fuga> items) {
super(context, textViewResourceId, items);
this.items = items;
this.inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// ビューを受け取る
View view = convertView;
if (view == null) {
// 受け取ったビューがnullなら新しくビューを生成
view = inflater.inflate(R.layout.main, null);
}
//viewから必要な要素を取得して値を設定する。
TextView name = (TextView) view.findViewById(R.id.textView1);
TextView message = (TextView) view.findViewById(R.id.textView2);
ImageView image = (ImageView) view.findViewById(R.id.imageView1);
if (items != null && items.size() >= position) {
name.setText(items.get(position).getName());
message.setText(items.get(position).getMessage());
image.setImageResource(R.drawable.ic_launcher);
}
return view;
}
}
Adapterクラスでやることは、
コンストラクタの設定と、getViewでの表示設定。
コンストラクタはActivityから受け取るリストを設定する必要がある。
またgetViewではViewの設定と、Viewに配置されている各項目の設定。
今回はリスト表示される項目をFugaクラスを使って
ActivityとAdapterクラス間でやりとりしたけれど、
ArrayListを使えば、オブジェクトは何でも良いと思われる。
Fugaクラスの内容はDTOやEntityのような構成で、
単に変数とgetter、setterがあるというだけなので、
特筆して内容は載せず、省略致します。
ListViewを使うのは当然なのだけれど、
テキストや画像を表示する場合は一筋縄ではいかない。
ちなみに文字をリスト表示させるだけであれば
こんな感じで終わる。
"Activity"を拡張したクラスで次の処理を行う。
//ListViewのインスタンスを生成
ListView listView = (ListView) findViewById(R.id.listView1);
//ArrayAdapterのインスタンスを生成
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1);
//adapterに要素を追加する。
adapter.add("hoge");
// adapterをリストに追加
listView.setAdapter(adapter);
行数増やしたい場合は
adapter.add();を繰り返せば良い。
しかし、リストの項目を少し複雑にする場合は別。
まず、継承するクラスをActivityから"ListActivity"に変更する。
public class TestActivity extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//表示する要素をリストで用意する。
ArrayList<Fuga> list = new ArrayList<Fuga>();
setApplicationList(list);
HogeAdapter hogeAdp = new HogeAdapter(this, R.layout.main, list);
setListAdapter(hogeAdp);
}
private void setApplicationList(ArrayList<Fuga> fugaList){
//fugaリストを生成する
Fuga fuga = new Fuga();
fuga.setName("tarou");
fuga.setMessage("Hello!");
fugaList.add(fuga);
}
}
上のクラスでやることといえば、
1.表示したい項目をArrayList型で用意すること。
2.用意したリストを設定するAdapterクラス(後述)を用意すること。
3.リストを設定したAdapterクラスを設定(setListAdapter)すること。
という感じです。
次にArrayAdapterクラスを継承したクラスを用意する。
public class HogeAdapter extends ArrayAdapter {
private ArrayList<Fuga> items;
private LayoutInflater inflater;
public HogeAdapter(Context context, int textViewResourceId,
ArrayList<Fuga> items) {
super(context, textViewResourceId, items);
this.items = items;
this.inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// ビューを受け取る
View view = convertView;
if (view == null) {
// 受け取ったビューがnullなら新しくビューを生成
view = inflater.inflate(R.layout.main, null);
}
//viewから必要な要素を取得して値を設定する。
TextView name = (TextView) view.findViewById(R.id.textView1);
TextView message = (TextView) view.findViewById(R.id.textView2);
ImageView image = (ImageView) view.findViewById(R.id.imageView1);
if (items != null && items.size() >= position) {
name.setText(items.get(position).getName());
message.setText(items.get(position).getMessage());
image.setImageResource(R.drawable.ic_launcher);
}
return view;
}
}
Adapterクラスでやることは、
コンストラクタの設定と、getViewでの表示設定。
コンストラクタはActivityから受け取るリストを設定する必要がある。
またgetViewではViewの設定と、Viewに配置されている各項目の設定。
今回はリスト表示される項目をFugaクラスを使って
ActivityとAdapterクラス間でやりとりしたけれど、
ArrayListを使えば、オブジェクトは何でも良いと思われる。
Fugaクラスの内容はDTOやEntityのような構成で、
単に変数とgetter、setterがあるというだけなので、
特筆して内容は載せず、省略致します。