Twitterのタイムラインをgzip転送の効用

勉強中

この本で勉強中
Androidプログラミングバイブル―SDK3.0/2.3/2.2/2.1対応 (smart phone programming bible)

Androidプログラミングバイブル―SDK3.0/2.3/2.2/2.1対応 (smart phone programming bible)

参考

DefaultHttpClientのgzip転送の効用
http://d.hatena.ne.jp/Tackn1977/20120414/1334373350

Twitterのタイムラインでgzip転送の効用を見る

タイムラインは常に容量が変化しますがgzip転送の効果は十分に見て取れる結果が出ました。

アプリケーションソース

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="jp.tackn.defaulthttpclientex"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name"
                  android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="myapp" android:host="httpclient" />
            </intent-filter>
        </activity>
    </application>

    <uses-sdk android:minSdkVersion="4" />
    <supports-screens android:resizeable="true" />
    
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest> 

MainActivity.java

package jp.tackn.defaulthttpclientex;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.zip.GZIPInputStream;
import oauth.signpost.OAuthProvider;
import oauth.signpost.basic.DefaultOAuthProvider;
import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.http.HttpRequest;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

/**
 * DefaultHttpClientを使ったサンプルプログラム
 *
 * @author Tackn
 */
public class MainActivity extends Activity
        implements View.OnClickListener {
    //定数
    /** OAuth CONSUMER KEY */
    private String CONSUMER_KEY="";
    /** OAuth CONSUMER SECRET */
    private String CONSUMER_SECRET="";
    /** OAuth REQUEST_TOKEN URL */
    private String REQUEST_TOKEN_URL="";
    /** OAuth AUTHORIZE URL */
    private String AUTHORIZE_URL="";
    /** OAuth ACCESS TOKEN URL */
    private String ACCESS_TOKEN_URL="";
    /** ブラウザのコールバックURL */
    private final static String CALLBACKURL="myapp://httpclient";

    /** Log用tag */
    private final static String LOG_TAG = "tackn";
    /** 内容に合わせる */
    private final static int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
    /** 親コンテンツに合わせる */
    private final static int FP = LinearLayout.LayoutParams.FILL_PARENT;
    /** URL入力用エディットボックス */
    private EditText url_input;
    /** 結果表示用テキストビュー */
    private TextView result_view;
    /**gzip追加有無 */
    private CheckBox cb_Gzipx;

    //認証
    private CommonsHttpOAuthConsumer consumer;
    private OAuthProvider provider;
    
    /** アクティビティ起動時に呼ばれる */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //TwitterのOAuth関連の読み込み
        CONSUMER_KEY     =getResources().getString(R.string.Consumer_key);
        CONSUMER_SECRET  =getResources().getString(R.string.Consumer_secret);
        REQUEST_TOKEN_URL=getResources().getString(R.string.Request_token_URL);
        AUTHORIZE_URL    =getResources().getString(R.string.Authorize_URL);
        ACCESS_TOKEN_URL =getResources().getString(R.string.Access_token_URL);
        
        //コンポーネントの垂直方向に配置
        LinearLayout mainLayout = new LinearLayout(this);
        mainLayout.setBackgroundColor(Color.WHITE);
        mainLayout.setOrientation(LinearLayout.VERTICAL);

        // URL入力欄の生成
        url_input = new EditText(this);
//        url_input.setText("http://www.ugtop.com/spill.shtml", TextView.BufferType.NORMAL);//確認くん
//        url_input.setText("http://www.kojikoji.net/", TextView.BufferType.NORMAL);//GET/POST 確認くん
        url_input.setText("https://api.twitter.com/1/statuses/public_timeline.xml", TextView.BufferType.NORMAL);//GET/POST 確認くん
        url_input.setLayoutParams(new LinearLayout.LayoutParams(FP, WC));
        mainLayout.addView(url_input);

        // gzipするかのチェックボックスの生成
        cb_Gzipx = new CheckBox(this);
        cb_Gzipx.setText("ヘッダーにgzipを追加");
        cb_Gzipx.setTextColor(Color.BLACK);
        cb_Gzipx.setChecked(false);
        cb_Gzipx.setLayoutParams(new LinearLayout.LayoutParams(WC, WC));
        mainLayout.addView(cb_Gzipx);

        //実行するボタンの生成
        mainLayout.addView(makeButton("取得", "get"));

        //結果を表示するテキストビューの生成
        result_view = new TextView(this);
        result_view.setTextSize(16f);
        result_view.setTextColor(Color.BLACK);
        result_view.setLayoutParams(new LinearLayout.LayoutParams(FP, FP));
        mainLayout.addView(result_view);

        //ビューのセット
        setContentView(mainLayout);
        
        //認証
        doOauth(false);
    }

    /**
     * ボタンを押したときの動作
     * 
     * @param v クリックされたView
     */
    public void onClick(View v) {
        if (v.getTag().equals("get")) {
            getFile(url_input.getText().toString());
        } else {
            Toast.makeText(this, "何事!", Toast.LENGTH_SHORT);
        }
    }

    /**
     * ボタンを作成するサブクラス
     *
     * @param text ボタンのラベル
     * @param tag ボタンの識別タグ
     * @return 作成したButtonオブジェクト
     */
    private Button makeButton(String text, String tag) {
        Button button = new Button(this);
        button.setText(text);
        button.setTag(tag);
        button.setOnClickListener(this);
        button.setLayoutParams(new LinearLayout.LayoutParams(FP, WC));
        return button;
    }

    /**
     * URLのパスからSDカード直下にファイルを取得する
     *
     * @param url 取得するURL
     */
    private void getFile(String url) {
        try {
            URL path = new URL(url);
        } catch (MalformedURLException ex) {
            result_view.setText(result_view.getText() + "URLが不正です\n");
            return;
        }
        
        HttpRequest rq = null;        
        
        /**
         * HTTP GETリクエスト
         */
        HttpGet httpGet = new HttpGet(url);
        try {
            rq= consumer.sign(httpGet);
        } catch (OAuthMessageSignerException ex) {
            Log.e(LOG_TAG,"OAuthMessageSignerException: "+ex.getMessage());
        } catch (OAuthExpectationFailedException ex) {
            Log.e(LOG_TAG,"OAuthExpectationFailedException: "+ex.getMessage());
        } catch (OAuthCommunicationException ex) {
            Log.e(LOG_TAG,"OAuthCommunicationException: "+ex.getMessage());
        }
        if (cb_Gzipx.isChecked()) {
            result_view.setText(result_view.getText() + "gzip圧縮\n");
            httpGet.setHeader("Accept-Encoding", "gzip, deflate");
        } else {
            result_view.setText(result_view.getText() + "gzip非圧縮\n");
        }

        /** 読み込みサイズ */
        int size = 0;
        /** 読み込みバッファ */
        byte[] w = new byte[1024];
        /** 入力ストリーム */
        InputStream in = null;
        /** 受信用ストリーム */
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        /**ファイル保存用ストリーム */
        FileOutputStream writefile = null;


        /** コンテンツ取得用HTTPクライアント */
        DefaultHttpClient httpClient = new DefaultHttpClient();
        /**取得結果 */
        HttpResponse execute = null;

        try {//URLへリクエスト
            execute = httpClient.execute(httpGet);

            switch (execute.getStatusLine().getStatusCode()) {
                case HttpStatus.SC_OK:
                    result_view.setText(result_view.getText() + "Status 200 OK (HTTP/1.0 - RFC 1945)\n");
                    break;
                case HttpStatus.SC_MOVED_PERMANENTLY:
                    result_view.setText(result_view.getText() + "Status 301 Moved Permanently (HTTP/1.0 - RFC 1945)\n");
                    return;
                case HttpStatus.SC_MOVED_TEMPORARILY:
                    result_view.setText(result_view.getText() + "Status 302 Moved Temporarily (Sometimes Found) (HTTP/1.0 - RFC 1945)\n");
                    return;
                case HttpStatus.SC_NOT_FOUND:
                    result_view.setText(result_view.getText() + "Status 404 Not Found (HTTP/1.0 - RFC 1945)\n");
                    return;
                case HttpStatus.SC_INTERNAL_SERVER_ERROR:
                    result_view.setText(result_view.getText() + "Status 500 Server Error (HTTP/1.0 - RFC 1945)\n");
                    return;
                case HttpStatus.SC_SERVICE_UNAVAILABLE:
                    result_view.setText(result_view.getText() + "Status 503 Service Unavailable (HTTP/1.0 - RFC 1945)\n");
                    return;
                default:
                    result_view.setText(result_view.getText() + "Status " + execute.getStatusLine().getStatusCode() + "\n");
                    return;
            }

        } catch (ClientProtocolException ex) {
            Log.e(LOG_TAG, "ClientProtocolException: " + ex.getMessage());
        } catch (IOException ex) {
            Log.e(LOG_TAG, "IOException: " + ex.getMessage());
        }
        try {//HttpStatus.SC_OKの場合取得開始
            /** 取得開始時刻 */
            Long stratTime = System.currentTimeMillis();

            //gzip転送の有無で切り替え
            if (isGZipHttpResponse(execute)) {
                in = new GZIPInputStream(execute.getEntity().getContent());
            } else {
                in = execute.getEntity().getContent();
            }

            //読み込み処理
            while (true) {
                size = in.read(w);
                if (size <= 0) {
                    break;
                }
                out.write(w, 0, size);
            }
            in.close();
            /**
             * 取得終了時刻
             */
            Long endTime = System.currentTimeMillis();

            result_view.setText(result_view.getText() + "取得時間:" + (endTime - stratTime) + "ms\n");
            Log.i(LOG_TAG, "取得時間:"+(endTime - stratTime) + "ms");

            //ファイルに保存
            if(checkSDCard()){
                /** 出力ファイルパスの取得 */
                File dir = Environment.getExternalStorageDirectory();
                File file = null;
                if(url.length()-1==url.lastIndexOf("/")){
                    file=File.createTempFile("test", ".html", dir);
                }else{
                    file=new File(dir.getAbsolutePath()+url.substring(url.lastIndexOf("/")));
                }
                Log.i(LOG_TAG, url);
                writefile = new FileOutputStream(file);
                writefile.write(out.toByteArray());
                writefile.flush();
                writefile.close();
            }
    
        } catch (IOException ex) {
            Log.e(LOG_TAG,"IOException: "+ex.getMessage());
        } catch (IllegalStateException ex) {
            Log.e(LOG_TAG,"IllegalStateException: "+ex.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                    writefile.close();
                } catch (IOException ex) {
                }
            }
            httpClient.getConnectionManager().shutdown();
        }

    }

    /**
     * gzipが有効か判定する処理
     *
     * @param response HTTPレスポンス
     * @return gzip圧縮されているかの判定
     */
    private boolean isGZipHttpResponse(HttpResponse response) {
        Header header = response.getEntity().getContentEncoding();
        if (header == null) {
            return false;
        }
        String value = header.getValue();
        return (!TextUtils.isEmpty(value) && value.contains("gzip"));
    }

    /**
     * SDカードの状態をチェック
     * @return SDカードが着込み可かどうか
     */
    private boolean checkSDCard() {
        String status = Environment.getExternalStorageState();

        if (status.equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
//            Toast.makeText(MainActivity.this,
//                    "SDカードが装着されている",
//                    Toast.LENGTH_LONG).show();
            //この状態が返ってきた場合は、読み書きが可能です。
            return true;
        } else if (status.equalsIgnoreCase(Environment.MEDIA_MOUNTED_READ_ONLY)) {
            Toast.makeText(MainActivity.this,
                    "SDカードが装着されていますが、読み取り専用・書き込み不可です",
                    Toast.LENGTH_LONG).show();
            return false;
        } else if (status.equalsIgnoreCase(Environment.MEDIA_REMOVED)) {
            Toast.makeText(MainActivity.this,
                    "SDカードが装着されていません",
                    Toast.LENGTH_LONG).show();
            return false;
        } else if (status.equalsIgnoreCase(Environment.MEDIA_SHARED)) {
            Toast.makeText(MainActivity.this,
                    "SDカードが装着されていますが、USBストレージとしてPCなどに"
                    + "マウント中です", Toast.LENGTH_LONG).show();
            return false;
        } else if (status.equalsIgnoreCase(Environment.MEDIA_BAD_REMOVAL)) {
            Toast.makeText(MainActivity.this,
                    "SDカードのアンマウントをする前に、取り外しました",
                    Toast.LENGTH_LONG).show();
            return false;
        } else if (status.equalsIgnoreCase(Environment.MEDIA_CHECKING)) {
            Toast.makeText(MainActivity.this,
                    "SDカードのチェック中です",
                    Toast.LENGTH_LONG).show();
            return false;
        } else if (status.equalsIgnoreCase(Environment.MEDIA_NOFS)) {
            Toast.makeText(MainActivity.this,
                    "SDカードは装着されていますが、ブランクであるか、"
                    + "またはサポートされていないファイルシステムを利用しています",
                    Toast.LENGTH_LONG).show();
            return false;
        } else if (status.equalsIgnoreCase(Environment.MEDIA_UNMOUNTABLE)) {
            Toast.makeText(MainActivity.this,
                    "SDカードは装着されていますが、マウントすることができません",
                    Toast.LENGTH_LONG).show();
            return false;
        } else if (status.equalsIgnoreCase(Environment.MEDIA_UNMOUNTED)) {
            Toast.makeText(MainActivity.this,
                    "SDカードは存在していますが、マウントすることができません",
                    Toast.LENGTH_LONG).show();
            return false;
        } else {
            Toast.makeText(MainActivity.this,
                    "その他の要因で利用不可能",
                    Toast.LENGTH_LONG).show();
            return false;
        }
    }
    /**
     * Oauth認証
     *
     * @param setup
     */
    private void doOauth(boolean setup) {
        consumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
        provider = new DefaultOAuthProvider(REQUEST_TOKEN_URL, ACCESS_TOKEN_URL, AUTHORIZE_URL);
        try {
            //トークンの読み込み
            SharedPreferences pref = getSharedPreferences("token", MODE_PRIVATE);
            String token = pref.getString("token", "");
            String tokenSecret = pref.getString("tokenSecret", "");

            //認証済み
            if (!setup && token.length() > 0 && tokenSecret.length() > 0) {
                consumer.setTokenWithSecret(token, tokenSecret);
            } //認証処理のためブラウザ起動
            else {
                String url = provider.retrieveRequestToken(consumer, CALLBACKURL);
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                this.startActivity(intent);
            }
            
        } catch (Exception e) {
            Toast.makeText(this, "doOauth: "+e.getMessage(), Toast.LENGTH_LONG).show();
            Log.e(LOG_TAG, "doOauth: "+e.getMessage());
        }
    }

    /**
     * トークン情報の取得 設定終了時に呼ばれる
     * AndroidManifestのactivityにandroid:launchMode="singleInstance"
     * の記述が必要
     * @param intent
     */
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Uri uri = intent.getData();
        if (uri != null && uri.toString().startsWith(CALLBACKURL)) {
            String verier = uri.getQueryParameter(oauth.signpost.OAuth.OAUTH_VERIFIER);
            try {
                provider.retrieveAccessToken(consumer, verier);
                //トークン書き込み
                SharedPreferences prof = getSharedPreferences("token", MODE_PRIVATE);
                SharedPreferences.Editor editor = prof.edit();
                editor.putString("token", consumer.getToken());
                editor.putString("tokenSecret", consumer.getTokenSecret());
                editor.commit();

            } catch (Exception e) {
                Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
                Log.e(LOG_TAG, e.getMessage());
            }
        }
    }


}