sola's note -5ページ目

sola's note

日ごろのメモを書いていきます。何か新しいものを作るために。

全てのサーバに言える事かわかりませんが、
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モードで動かせば何とかなるケースでしたので、
互換モードでいきましたが、
ブラウザのバージョンが変わると、表示の違い等様々な弊害が出るので、
気を付けてやって下さい。
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があるというだけなので、
特筆して内容は載せず、省略致します。