Apache Commons HTTP Client のバージョン 3.x には HttpMethod#setFollowRedirects があったのですが、 4.x から綺麗に取り除かれています。
4.0 では以下のように HttpParams に設定してあげるとリダイレクトしなくなりました。
HttpParams params = new BasicHttpParams(); params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, false);
ログイン画面にリダイレクトされたときに、リダイレクト先のコンテンツを取得されるのを抑止し、 302 コードが欲しかったのです。
Android には WebKit ブラウザが内蔵されていて、 SDK にはそのエンジンを利用した WebView というコンポーネントが提供されています。
使い方は layout に WebView コンポーネントを配置して Activity から WebView インスタンスを取得し、以下のメソッドを呼び出すことで描画が行えます。
loadUrl は URL を指定し、 loadData は html 文字列から描画を行うものですが、今回は loadDataWithBaseURL を使いました。というのもローカルに css ファイルを作成して、 html から参照させたかったからです。
[追記]
loadUrl でも assets 連携が行えました。
webView.loadUrl("file:///android_asset/index.html");
[/追記]
まずリソースファイルとなる html ファイルおよび css ファイルを作成します。アプリケーションのルートディレクトリに assets ディレクトリを作成し、その直下に対象のファイルを置きます。
あとやることは Activity から WebView インスタンスを取得し、メソッドで呼び出すだけです。
webView.loadDataWithBaseURL("file:///android_asset/", html, "text/html", "utf-8", null);
第一引数に file:///android_asset を指定することで、アプリケーションルートに作成した assets ディレクトリにマッピングが行われます。最後の引数は baseURL の取得がうまく行えなかったときのエラーページを指定するらしいです。今回は内部ファイルにアクセスするので null にしました。
これで assets ディレクトリを基底ディレクトリとし、 html から assets ディレクトリの中にアクセスできるようになりました。
<link rel="stylesheet" type="text/css" href="./default.css" />
それから assets ディレクトリの資源に直接アクセスしたい場合は、 Context#getAssets (Activity) から AssetManager を取得し、 AssetManager#open メソッドから可能です。
今回は html ファイルをローカルに置いて、テンプレートの中身を書き換える形にしてみました。以下コードです。
public class WebViewActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String template = getTemplateHtml("index.html");
String html = template.replaceAll("%s", "<h1>Hello!</h1>");
WebView webView = (WebView) findViewById(R.id.webView);
webView.loadDataWithBaseURL("file:///android_asset/", html, "text/html", "utf-8", null);
}
private String getTemplateHtml(String path) {
InputStream in = null;
try {
in = getAssets().open(path);
byte[] buffer = IOUtils.read(in); // 自作ユーティリティ
return new String(buffer, "utf-8");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
IOUtils.close(in);
}
}
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="content-script-type" content="text/javascript" />
<meta http-equiv="content-style-type" content="text/css" />
<link rel="stylesheet" type="text/css" href="./default.css" />
</head>
<body>%s</body>
</html>
body {
margin: 0;
padding: 0;
background-color: black;
color: white;
}
WebView は JavaScript との連携も用意されていて、色々できそうです。時間を取って追ってみたいコンポーネントでした。
Android で画面操作を行っているときに電話がかかってくると、処理が停止されてしまいます。しかし停止させず処理を継続させたい場合にはサービスという概念が用意されており、これを利用することで処理をバックエンドで続けてくれるとのこと。
実装方法はいくつかあるのですが、今回は AIDL ファイルに Java インターフェースを定義して、コールバックを受け取れるバインダという種類を選択してみました。なお AIDL ファイルに記述したインターフェースは ADT によって、インターフェースを実装したクラスが自動的に生成されます。
まず AIDL ファイルに定義するインターフェースの引数、戻り値に指定できるものとして以下の制約があるとのことです。
新しく Parcelable インターフェースというものが出てきたのですが、すごく適当にいうと Serializable の Android 用みたいなものです。 Java の Serializable は冗長なので、 Android ではリモート通信間に使うものには Parcelable インターフェースを実装するルールのようです。
で、今回 Parcelable インターフェースを実装した ValueObject を渡そうとしたところ意外にハマってしまったのでメモしておきます。
example.dto.Hoge クラスが Parcelable インタフェースを実装しているものとします。(インターフェース実装は省略します)
Hoge クラスと同じ example.dto パッケージに Hoge.aidl ファイルを作成します。
package example.dto; parcelable Hoge;
ここでは Hoge クラスが parcelable であることを宣言します。
続いてコールバックを受け取るリスナを example.service パッケージに定義します。
package example.service;
import example.dto.Hoge;
interface HogeCallbackListener {
void receive(in Hoge hoge);
}
ここでは先ほど Hoge.aidl で定義した Hoge クラスを import し、インターフェースの引数に in を指定しました。なお public をつけるとおこられます。これで利用するための準備は整いました。
この後に AndroidManifest ファイルに Service を定義したり、 Activity や Service にコードを記述したりと面倒な作業が待っています。
http://developer.android.com/intl/ja/guide/developing/tools/aidl.html
Ruby にある軽量 ORM Sequel を簡単に触ったことはあるのですが、マイグレーションもできるということで試してみました。
$ sudo gem install rake $ sudo gem install sequel $ sudo gem install sqlite3-ruby
+app_root/ +Rakefile +db/ +migrate/ +001_migrate.rb +002_migrate.rb
#!/usr/bin/env ruby
Class.new(Sequel::Migration) do
def up
create_table :users do
primary_key :id
varchar :name, :size => 32, :unique => true
varchar :salt, :size => 32, :null => false
varchar :hashed_password, :size => 64, :null => false
varchar :mail_address, :size => 128, :null => false
timestamp :created_at
timestamp :updated_at
end
add_index :users, :name
end
def down
drop_index :users, :name
drop_table :users
end
end
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
Class.new(Sequel::Migration) do
def up
create_table :tags do
primary_key :id
varchar :name, :size => 32
end
from(:tags).insert(:name => 'あとでやる')
from(:tags).insert(:name => '明日から本気出す')
end
def down
drop_table :tags
end
end
クラスを匿名で生成していますが、意味づけを行う場合は名前をきちんとつけたほうが良いかもしれません。
Rake は引数を渡すと ENV 変数に書き込まれるので、これでバージョンを指定できます。
#!/usr/bin/env ruby
require 'rubygems'
require 'rake'
require 'sequel'
require 'sequel/extensions/migration'
namespace :db do
desc "migrate database"
task :migrate do
target = target.to_i if (target = ENV['target'])
current = current.to_i if (current = ENV['current'])
DB = Sequel.connect("sqlite://db/app.sqlite3")
Sequel::Migrator.apply(DB, './db/migrate', target, current)
end
end
最新版 (version 2) にする
$ rake db:migrate
バージョンを 1 に戻す
$ rake db:migrate target=3
わざわざ rake しなくても、コマンドから migrate できます。 -m でマイグレーションファイルを格納しているディレクトリを指定、 -M でバージョンを指定します。
$ sequel -m ./db/migrate sqlite://db/keyself.db -M 1
マイグレーションとは関係ありませんが、 model をつくる場合は最低限なものであればこれだけです。
class User < Sequel::Model; end
users テーブルと関連づけられ、様々な操作が行えます。手軽にデータベースを操作したい場合に使っていきたいと思う ORM です。
まずサーバーセッションを有効にするには起動ファイルである config.ru で use します。
use Rack::Session::Pool
引数にオプションをつけてセッションの有効期限を変えたりします。
あとはクライアントから呼び出されたアクション(コントローラー)で session_options に :drop オプションを渡してあげると削除されます。
get '/' do (session[:count] = (session[:count] || 0) + 1).to_s end get '/clear' do request.session_options[:drop] = true end
http://localhost:4567/ をたたくとリクエストした数だけカウント数が画面に表示され、 /clear を叩いてから再度 / を叩くと、セッション ID が新たに割り当てられ 1 に戻ります。 :renew を渡すと新たなセッション ID を割り振りつつ、前の情報をマージしてくれます。
session.clear は単純に中身の Hash オブジェクトをクリアするだけで、セッション ID はそのままでした。
Ruby 1.9 上の Sequel で sqlite3 からマルチバイト文字列を含んだレコードを取得し to_json しようとすると
Encoding::UndefinedConversionError: "\xE2" from ASCII-8BIT to UTF-8
というエラーが出力されます。
String#force_encoding で、いちいち utf-8 しないといけないのかと思ったところ、きちんと Sequel の plugin にエンコーディングを変換できるものが提供されていました。
class User < Sequel::Model plugin :force_encoding, 'utf-8' end
カラム名を created_at, updated_at にしておくと、 save メソッドを呼び出したときに自動的に作成、更新時間を更新してくれるみたいです。カラム型は timestamp で確認しています。
さらに追加時にも更新時間を入れたい場合には timestamps プラグインを利用して、オプションを渡してあげるとよいです。
DB = Sequel.connect("sqlite://test.db")
class User < Sequel::Model
plugin :timestamps, :update_on_create => true
end
DB.transaction do
User.new(:name => 'aaa').save
User.new(:name => 'bbb').save
end
既存システムで、すでに別のカラム名が使われている場合には下のオプションを渡してあげると変更できます。
plugin :timestamps, :create => 'created_on', :update => 'updated_on'
Module: Sequel::Plugins::Timestamps [Sequel: The Database Toolkit for Ruby]
モデルすべてに適用したい場合は Sequel::Model で定義してあげるとよいみたいです。
class Sequel::Model plugin :timestamps, :update_on_create => true, :create => 'created_on', :update => 'updated_on' end class User < Sequel::Model; end
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Log.d("display", "w:" + display.getWidth());
Log.d("display", "h:" + display.getHeight());
w:320 h:480
onCreate 時には、まだレイアウトの初期化が行われないため View の width/height からは 0 が返ってきます。調べてみると WindowManager を経由して取得した Display クラスから onCreate 時に取得できました。
こちらは View の onSizeChanged イベントから取得する方法です。