最近やっていたAndroid案件で、メモリリーク対策についてまた理解が進んだのでメモ。
主要な参考先:
http://tomokey.blogspot.jp/2011/05/android.html
コツ:
■内部クラスは static にする。
■「弱参照」という仕組みについて理解して、アクティビティやフラグメントといったライフサイクルのあるインスタンスを他のクラスに保持させるときはこの弱参照を使う。
■内部クラスから外側クラスにアクセスする必要がある場合、内部クラスのプロパティに外側クラスのインスタンスを持たせて、内部クラスのコンストラクタで外側クラスのインスタンスを渡す。この時、弱参照インスタンスとして保持しておく。
■処理の効率化のために作成した色々なクラスでも、プロパティにアクティビティやフラグメントを保持するときは弱参照経由にしておく。
■あとは、とにかくフラグメントやアクティビティを遷移するときは古いインスタンスを初期化していく。onResume()で準備したインスタンスはonPause()で片付けて、onStart()で準備したインスタンスはonStop()で片付ける。
---------------------------------
弱参照について。
通常、あるインスタンスへの参照を作成すると、それは「強参照」という強い参照になるらしい。
弱参照というのは弱い参照で、ガベージコレクションの対象になりやすい。
あるインスタンスについて、参照がひとつでも残っているとガベージコレクションの対象にならないという。
しかし、あるインスタンスについて、そこに残っている参照が弱参照のみだったら、それはGCの対象になるという。
アクティビティやフラグメントなど、ライフサイクルを持つものは、開発者が意図してないタイミングで破棄されたり再生成されたりするらしい。
しかしそうやってこっそり破棄された時、そのアクティビティやフラグメントへの参照(強参照)が残っていると、きちんと破棄されないでメモリを圧迫するという。
なので、これらへの参照は弱参照がいいのだということだ。
例:フラグメントクラスでアクティビティのインスタンスを持っておこうという時
//メンバ変数
private Activity activity;
//onStart() あたりで
× this.activity = getActivity();
○ this.activity = new WeakReference<Activity>(getActivity()).get();
弱参照とは、参照インスタンスである。
WeakReference クラスのインスタンス。
new WeakReference<型指定>(参照されるインスタンス) というコンストラクタで作成する。
このインスタンス自体は参照であってお目当てのインスタンスではない。
get() メソッドによって参照されているインスタンスを取り出す。
---------------------------------
インナークラスを持つフラグメントの見本
---------------------------------
public class MyFragment extends AbsFragmentWithViewFlipper{
///////////////////////////////////////////////////////////
//フィールド
///////////////////////////////////////////////////////////
//ボタン
private Button btn;
//ボタンのクリックリスナー
private MyOnClickListener listener;
///////////////////////////////////////////////////////////
/**
* ライフサイクル:onStart()
* フラグメントがユーザーに見えるように生成された
* タイミングで呼び出される。
*
* アニメーションの開始など、表示に関わる初期化処理を行う。
*/
@Override
public void onStart(){
super.onStart();
//ボタン
this.btn = (Button)getActivity().findViewById(R.id.btn);
//クリックリスナー作成
this.listener = new MyOnClickListener(this);
//クリックリスナーをセット
this.btn.setOnClickListener(listener);
}//function
///////////////////////////////////////////////////////////
//クラス内クラス
///////////////////////////////////////////////////////////
/**
* スイッチ操作時のリスナー
*/
private static class MyOnClickListener implements OnClickListener{
//外側クラスのインスタンス
private MyFragment parent;
//////////////////////////////
/**
* コンストラクタ
*/
public MyOnClickListener(MyFragment fragment){
//弱参照経由で外側クラスを受け取る
parent = new WeakReference<MyFragment>(fragment).get();
}//function
//////////////////////////////
/**
* クリック時
*/
@Override
public void onClick(
View view
){
//外側クラスにアクセスする時は「parent」を使う
}//function
}//class
}//class
仕事中に気づいたプログラミング上のTipsや備忘録をメモしておくための場所です。
[PHP][Android]など
ブログ主:いしいたける
メインのWebサイト(マンガ) : http://loveandcomic.com/
2015年10月29日木曜日
2015年8月13日木曜日
【cakePHP】ヴァリデーション時、UPDATEの場合であればプライマリキーのセットを忘れずに
cakePHPではデータベースを扱うMODELクラスにバリデーションの設定を書いておくことで入力内容の妥当性のチェックが容易にできる。
今回、バリデーションでしくじったケースがあったのでメモ。
モデルクラスによるバリデーションが行われるタイミングは主にふたつ。
■保存系:例・モデルクラスのインスタンスの save() が実行されるとき。
■明示的ヴァリデート系:例・モデルクラスのインスタンスの validates() を実行したとき。
save()の挙動は、新規レコードの追加(INSERT)と既存レコードの更新(UPDATE)とを兼ねるので、今書いている処理がINSERTなのかUPDATEなのかをしっかり意識して制御する必要がある。
主要な区別は、
■INSERT:モデルに渡される配列が、そのテーブルのプライマリキーの値を含んでいない。
■UPDATE:モデルに渡される配列が、そのテーブルのプライマリキーの値を含んでいる。
今回やってしまったエラーは以下のケース。
あるテーブルについて、レコードを新規追加する処理を追加した。
その際、そのテーブルを扱うモデルクラスについて、ヴァリデーションルールを追加した。
そのヴァリデーションルールは「isUnique」。そのテーブル内で値が重複していたらエラー。
そのモデルでのヴァリデーションを使用している箇所は、今回作った新規追加だけではなく、既存の処理にもあった。
過去に作った既存の処理においては、UPDATEにおけるヴァリデーションであるのにも関わらず、プライマリキーを配列に含めないでチェックさせていた。これまではそれで問題なかった。
しかし、「isUnique」のヴァリデーションルールは、INSERT時とUPDATE時とで挙動が違うため、既存の処理側でエラーを起こしてしまった(エラーとしたくないケースの入力においてもエラーになってしまった)。
今回の教訓:
ヴァリデーションが必要な場合、INSERTかUPDATEかを意識して、UPDATEであればプライマリキーを忘れずにモデルに渡すこと。
今回、バリデーションでしくじったケースがあったのでメモ。
モデルクラスによるバリデーションが行われるタイミングは主にふたつ。
■保存系:例・モデルクラスのインスタンスの save() が実行されるとき。
■明示的ヴァリデート系:例・モデルクラスのインスタンスの validates() を実行したとき。
save()の挙動は、新規レコードの追加(INSERT)と既存レコードの更新(UPDATE)とを兼ねるので、今書いている処理がINSERTなのかUPDATEなのかをしっかり意識して制御する必要がある。
主要な区別は、
■INSERT:モデルに渡される配列が、そのテーブルのプライマリキーの値を含んでいない。
■UPDATE:モデルに渡される配列が、そのテーブルのプライマリキーの値を含んでいる。
今回やってしまったエラーは以下のケース。
あるテーブルについて、レコードを新規追加する処理を追加した。
その際、そのテーブルを扱うモデルクラスについて、ヴァリデーションルールを追加した。
そのヴァリデーションルールは「isUnique」。そのテーブル内で値が重複していたらエラー。
そのモデルでのヴァリデーションを使用している箇所は、今回作った新規追加だけではなく、既存の処理にもあった。
過去に作った既存の処理においては、UPDATEにおけるヴァリデーションであるのにも関わらず、プライマリキーを配列に含めないでチェックさせていた。これまではそれで問題なかった。
しかし、「isUnique」のヴァリデーションルールは、INSERT時とUPDATE時とで挙動が違うため、既存の処理側でエラーを起こしてしまった(エラーとしたくないケースの入力においてもエラーになってしまった)。
今回の教訓:
ヴァリデーションが必要な場合、INSERTかUPDATEかを意識して、UPDATEであればプライマリキーを忘れずにモデルに渡すこと。
【cakePHP】半角・全角・カタカナ・ひらがななどの混じった文字列でのあいまい検索
cakePHPで、データベースからの検索をする場合。
キーワード入力欄などで、いい加減な入力にも柔軟に対応したいという要望がある場合。
MySQLの機能をうまく使うと、以下の曖昧入力での検索が簡単にできるようになる。
■アルファベットが、半角/全角、大文字/小文字にかかわらず、検索可能
■数字が、半角/全角にかかわらず、検索可能
■平仮名、カタカナ、半角カナにかかわらず、検索可能
参考URL:
http://tutty.info/527
http://qiita.com/kazu56/items/108075fcb5e98c0d637a
https://www.softel.co.jp/blogs/tech/archives/1877
要点:
■「collate utf8_unicode_ci like」を使う
select * from item where item_name collate utf8_unicode_ci like '%りんご%';
■cakePHPの場合、テーブル名やコラム名にバッククオーテーションがつくので、カッコで囲む。
$this->find('all',[
'conditions'=>['(name) collate utf8_unicode_ci like'=>'%hoge%'
]);
■データベースがUTF8でない場合はもう一段階変換を挟む。「convert(コラム名 using utf8)」という書式。
select * from member where convert(namae using utf8) collate utf8_unicode_ci like '%サトウ%';
現在の案件だと、データベースの文字コードがUTF8ではなかったので、以下のような感じになった。
コントローラーにおける記述:
---------------------------------------------------
//検索結果
$items = array();
if(empty($this->request->data) == false){
//検索条件のセット
$condition = array();
//会員名あいまい検索
$condition['and'][] = array('(convert(Member.name using utf8)) collate utf8_unicode_ci like'=>'%'.$this->request->data['Member']['name'].'%');
//~ここにさらに検索条件を追加する処理~
//ページネイターを使っての検索実行
$items = $this->paginate('Member', $condition);
}//if
//検索結果をセット
$this->set("items", $items);
---------------------------------------------------
キーワード入力欄などで、いい加減な入力にも柔軟に対応したいという要望がある場合。
MySQLの機能をうまく使うと、以下の曖昧入力での検索が簡単にできるようになる。
■アルファベットが、半角/全角、大文字/小文字にかかわらず、検索可能
■数字が、半角/全角にかかわらず、検索可能
■平仮名、カタカナ、半角カナにかかわらず、検索可能
参考URL:
http://tutty.info/527
http://qiita.com/kazu56/items/108075fcb5e98c0d637a
https://www.softel.co.jp/blogs/tech/archives/1877
要点:
■「collate utf8_unicode_ci like」を使う
select * from item where item_name collate utf8_unicode_ci like '%りんご%';
■cakePHPの場合、テーブル名やコラム名にバッククオーテーションがつくので、カッコで囲む。
$this->find('all',[
'conditions'=>['(name) collate utf8_unicode_ci like'=>'%hoge%'
]);
■データベースがUTF8でない場合はもう一段階変換を挟む。「convert(コラム名 using utf8)」という書式。
select * from member where convert(namae using utf8) collate utf8_unicode_ci like '%サトウ%';
現在の案件だと、データベースの文字コードがUTF8ではなかったので、以下のような感じになった。
コントローラーにおける記述:
---------------------------------------------------
//検索結果
$items = array();
if(empty($this->request->data) == false){
//検索条件のセット
$condition = array();
//会員名あいまい検索
$condition['and'][] = array('(convert(Member.name using utf8)) collate utf8_unicode_ci like'=>'%'.$this->request->data['Member']['name'].'%');
//~ここにさらに検索条件を追加する処理~
//ページネイターを使っての検索実行
$items = $this->paginate('Member', $condition);
}//if
//検索結果をセット
$this->set("items", $items);
---------------------------------------------------
【cakePHP】Paginator を使っての検索結果並び替え
cakePHPで作るシステムはたいていデータベースと連動する。
基本的な構造として、ひとつのDBテーブルあたり、以下の機能がワンセットで作られる。
■一覧:検索フォームと検索結果
■新規登録
■詳細&編集
■削除
検索結果ページにおいて、検索結果にページネーション機能をもたせたりいろいろな条件での並び替えをさせたかったりする。
cakePHPには「Paginator」という便利な機能があるので、一覧画面のためのDB検索にはこの機能を使う。
今回、ページネイターを使ってのレコードの並び替えをはじめて使ってみて便利だったのでメモを残す。
Viewにおいて、
<?php echo($this->Paginator->sort("並び替え対象モデル名.並び替え対象コラム名", "リンク文字列")); ?>
これが基本。
これで、並び替えのためのURLが仕込まれたリンクを作成することができる。
この場合、指定されたコラムでの並び替えになり、昇順・降順がトグルする(クリックするたびに昇順降順が入れ替わる)。
トグルするのが気色悪いという場合、昇順降順指定込みのリンクをワンセットで作っておくとよい。sort() の第三引数にオプションを設定できるのでここに書く。
こんな感じ。たいてい検索結果はテーブルで出すだろうからテーブルの一部を抜き出した感じのソースで。
------------------------
<th>
会員ID
<?php echo($this->Paginator->sort("Member.id", "▲", array('direction' => 'asc' , 'lock' => true))); ?>
<?php echo($this->Paginator->sort("Member.id", "▼", array('direction' => 'desc', 'lock' => true))); ?><br>
</th>
------------------------
基本的な構造として、ひとつのDBテーブルあたり、以下の機能がワンセットで作られる。
■一覧:検索フォームと検索結果
■新規登録
■詳細&編集
■削除
検索結果ページにおいて、検索結果にページネーション機能をもたせたりいろいろな条件での並び替えをさせたかったりする。
cakePHPには「Paginator」という便利な機能があるので、一覧画面のためのDB検索にはこの機能を使う。
今回、ページネイターを使ってのレコードの並び替えをはじめて使ってみて便利だったのでメモを残す。
Viewにおいて、
<?php echo($this->Paginator->sort("並び替え対象モデル名.並び替え対象コラム名", "リンク文字列")); ?>
これが基本。
これで、並び替えのためのURLが仕込まれたリンクを作成することができる。
この場合、指定されたコラムでの並び替えになり、昇順・降順がトグルする(クリックするたびに昇順降順が入れ替わる)。
トグルするのが気色悪いという場合、昇順降順指定込みのリンクをワンセットで作っておくとよい。sort() の第三引数にオプションを設定できるのでここに書く。
こんな感じ。たいてい検索結果はテーブルで出すだろうからテーブルの一部を抜き出した感じのソースで。
------------------------
<th>
会員ID
<?php echo($this->Paginator->sort("Member.id", "▲", array('direction' => 'asc' , 'lock' => true))); ?>
<?php echo($this->Paginator->sort("Member.id", "▼", array('direction' => 'desc', 'lock' => true))); ?><br>
</th>
------------------------
2015年7月17日金曜日
【cakePHP】/プレフィックス/クラス/メソッド ルーティングプレフィックスの話
コントローラーのメソッドに、アンダーバーを持ったプレフィックスを設定して、変則的なURLを生成する技がある。
class TestsController extends AppController{
public function prefix_index(){
}//function
}//class
こういうクラスがあるとき、
/tests/prefix_index
というのが本来のURL
これに、ルーティングプレフィックスという設定を組み合わせる。
参考:
https://24nwakahana.wordpress.com/2013/02/13/cakephp-2-0-routing-prefixes-%E3%82%92%E4%BD%BF%E3%81%86%E3%80%82%E3%81%9D%E3%81%97%E3%81%A6%E4%BD%BF%E3%81%86%E3%81%AE%E3%82%92%E6%AD%A2%E3%82%81%E3%82%8B%E3%80%82/
Configure::write('Routing.prefixes', array('prefix'));
このコンフィグは、/app/Config/core.php に書かないといけないらしい。
この設定を有効にすると、上記メソッドによるURLは以下の形になる。
/prefix/tests/index
注意しなくてはならないのは、
/クラス/プレフィックス/メソッド
というURLにはならず、
/プレフィックス/クラス/メソッド
というURLになること。
prefix_index()
↓
prefix/index
とはならないので注意。
2015年6月11日木曜日
【cakePHP】ヴァリデーションエラーをDIVで囲わせたくない
cakePHP で、ヴァリデーションエラーを明示的に表示したい場合、以下のようなコードをViewに書く。
DIVで囲わせたくない時、以下のように書く。第2引数、第3引数(配列)が必要。
[Viewに書くコード]
--------------<?php echo $this->Form->error("モデル名.フィールド名"); ?>
--------------すると、実際は以下のようなHTMLが出力される。
--------------
<div class="error-message">
郵便番号の形式が不正です。(半角数字と半角ハイフンのみ有効)
</div>
--------------しかし、DIVが邪魔であると。
DIVで囲わせたくない時、以下のように書く。第2引数、第3引数(配列)が必要。
[Viewに書くコード]
--------------<?php echo $this->Form->error("モデル名.フィールド名", null, array("wrap"=>false,)); ?>
--------------
2015年6月10日水曜日
【PHP】Cookieを書き込むときはパス情報を意識する
Webサイトの機能拡張の案件。
既存のログインシステムがあるのでそれに合わせないといけない。
そのサイト自体はcakePHPで作るようなデータベースを持っておらず、外部のAPIでログイン機能を実装している。
調べてみると特定の名前のCookieが書き込まれていればログインであるという判断をしている。
こちらでもログインを制御するためにCookieを見たり書き込んだりしていた。
ところがうまく動かないことがある。
ブラウザでCookieを見てみると、CookieにはCookie名と有効期限だけでなく、「パス情報」が付属している。
既存のログインシステムでは、そのドメインでの一番浅い階層で Cookieを書き込んでいた。
なので、こちらでCookieを書き込むときも、パス情報を合わせる必要がある。
PHPでは、setcookie()関数の第四引数がパス指定となっている。
------------------------------------
setcookie(
"name", //クッキー名
"value", //値
0, /クッキーの有効期限
"/" //パス
);
------------------------------------
参考:http://php.net/manual/ja/function.setcookie.php
JavaScriptの場合:パスを指定しつつCookieを書き込む
------------------------------------
document.cookie = "name=value; path=/";
------------------------------------
既存のログインシステムがあるのでそれに合わせないといけない。
そのサイト自体はcakePHPで作るようなデータベースを持っておらず、外部のAPIでログイン機能を実装している。
調べてみると特定の名前のCookieが書き込まれていればログインであるという判断をしている。
こちらでもログインを制御するためにCookieを見たり書き込んだりしていた。
ところがうまく動かないことがある。
ブラウザでCookieを見てみると、CookieにはCookie名と有効期限だけでなく、「パス情報」が付属している。
既存のログインシステムでは、そのドメインでの一番浅い階層で Cookieを書き込んでいた。
なので、こちらでCookieを書き込むときも、パス情報を合わせる必要がある。
PHPでは、setcookie()関数の第四引数がパス指定となっている。
------------------------------------
setcookie(
"name", //クッキー名
"value", //値
0, /クッキーの有効期限
"/" //パス
);
------------------------------------
参考:http://php.net/manual/ja/function.setcookie.php
JavaScriptの場合:パスを指定しつつCookieを書き込む
------------------------------------
document.cookie = "name=value; path=/";
------------------------------------
2015年4月24日金曜日
【Android】Googleデベロッパーコンソールからアプリ内アイテムをCSVで一括アップロードする
Androidでアプリ内課金の仕組みを実装しなくてはならなくなりました。
アプリ側の実装も必要ですが、デベロッパーコンソールから有料アイテムを登録する作業も必要です。
コンソールから登録しないといけないアイテム数が多いのでCSVで一括登録したいんですがこれがうまくいかない。特に価格のところがどんな値を入れてもエラーになり、しかも正しい書式をGoogle側が示してくれない。これはエラー表示するときに合わせて正しい表記を出力すべきですねGoogleさんは。
クソだと思いました。
で、いろいろやっていたところ、やっとうまくいきました。
ポイント:
■CSVファイルの保存コードは「UTF-8(BOMなし)」。
■日本円で99円の表記は、「JP; 99000000」。
■つまり、日本円を示すために「JP; 」。
■99という数字を示すために「99000000」(ゼロが6つ)。
■1行目をタイトル行にせずいきなりデータ行から始める。
見本データ一行分(ブログ記事では改行が挟まるかもしれませんが一行です):
a0105,published,managed_by_android,false,ja_JP; アイテム名; アイテム詳細記述,false,JP; 99000000
----------
ちなみにダウンロードCSVの一行目(タイトル行)は以下のような感じでした(アップロードCSVファイルには一行目のタイトル行は不要)。
Product ID,Published State,Purchase Type,Auto Translate,Locale; Title; Description,Auto Fill Prices,Price
アプリ側の実装も必要ですが、デベロッパーコンソールから有料アイテムを登録する作業も必要です。
コンソールから登録しないといけないアイテム数が多いのでCSVで一括登録したいんですがこれがうまくいかない。特に価格のところがどんな値を入れてもエラーになり、しかも正しい書式をGoogle側が示してくれない。これはエラー表示するときに合わせて正しい表記を出力すべきですねGoogleさんは。
クソだと思いました。
で、いろいろやっていたところ、やっとうまくいきました。
ポイント:
■CSVファイルの保存コードは「UTF-8(BOMなし)」。
■日本円で99円の表記は、「JP; 99000000」。
■つまり、日本円を示すために「JP; 」。
■99という数字を示すために「99000000」(ゼロが6つ)。
■1行目をタイトル行にせずいきなりデータ行から始める。
見本データ一行分(ブログ記事では改行が挟まるかもしれませんが一行です):
a0105,published,managed_by_android,false,ja_JP; アイテム名; アイテム詳細記述,false,JP; 99000000
----------
ちなみにダウンロードCSVの一行目(タイトル行)は以下のような感じでした(アップロードCSVファイルには一行目のタイトル行は不要)。
Product ID,Published State,Purchase Type,Auto Translate,Locale; Title; Description,Auto Fill Prices,Price
2015年4月6日月曜日
【Android】メールアプリを暗黙的インテントで呼び出し、送信後に元のアプリに戻る
ネット上のニュースなどを集めるアプリで、ニュース記事を共有する機能があるとする。
■ツイッターに投稿
■LINEに投稿
■フェイスブックに投稿
■メールで送信
メールで送信させるときは、メールアプリを暗黙的インテントで呼び出していた。
しかし、メール送信後、画面がメールアプリのままなので元のアプリまでユーザーに操作して戻ってもらわねばならなかった。
これを修正。
こうしたい:
■メール編集画面から戻るボタン→元のアプリに戻る。
■メール送信後→元のアプリに戻る。
意外と簡単に出来た。
※String url_for_mailto はmailtoのリンク文字列
この処理は「メール送信ボタン」のオンクリックリスナーなどに書かれる。
----------------------------
修正前
----------------------------
uri = Uri.parse(url_for_mailto);
//インテント作成
Intent intent = new Intent(
Intent.ACTION_SENDTO, //メールソフト起動
uri //URIインスタンス
);
//メールアプリは新たに立ち上げさせる
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//スタートアクティビティの主語はアクティビティインスタンス
activity.startActivity(intent);
----------------------------
修正前
----------------------------
uri = Uri.parse(url_for_mailto);
//インテント作成
Intent intent = new Intent(
Intent.ACTION_SENDTO, //メールソフト起動
uri //URIインスタンス
);
//メールアプリは新たに立ち上げさせる
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//スタートアクティビティの主語はアクティビティインスタンス
//インテントの開始をstartActivityForResult メソッドで行えば、メール送信後(または戻るボタンを押した時)このアプリに戻る
activity.startActivityForResult(intent, 1);
つまり、startActivityForResultメソッドを使うということですね。
■ツイッターに投稿
■LINEに投稿
■フェイスブックに投稿
■メールで送信
メールで送信させるときは、メールアプリを暗黙的インテントで呼び出していた。
しかし、メール送信後、画面がメールアプリのままなので元のアプリまでユーザーに操作して戻ってもらわねばならなかった。
これを修正。
こうしたい:
■メール編集画面から戻るボタン→元のアプリに戻る。
■メール送信後→元のアプリに戻る。
意外と簡単に出来た。
※String url_for_mailto はmailtoのリンク文字列
この処理は「メール送信ボタン」のオンクリックリスナーなどに書かれる。
----------------------------
修正前
----------------------------
uri = Uri.parse(url_for_mailto);
//インテント作成
Intent intent = new Intent(
Intent.ACTION_SENDTO, //メールソフト起動
uri //URIインスタンス
);
//メールアプリは新たに立ち上げさせる
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//スタートアクティビティの主語はアクティビティインスタンス
activity.startActivity(intent);
----------------------------
修正前
----------------------------
uri = Uri.parse(url_for_mailto);
//インテント作成
Intent intent = new Intent(
Intent.ACTION_SENDTO, //メールソフト起動
uri //URIインスタンス
);
//メールアプリは新たに立ち上げさせる
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//スタートアクティビティの主語はアクティビティインスタンス
//インテントの開始をstartActivityForResult メソッドで行えば、メール送信後(または戻るボタンを押した時)このアプリに戻る
activity.startActivityForResult(intent, 1);
つまり、startActivityForResultメソッドを使うということですね。
2015年1月23日金曜日
【PHP】【Android】プッシュ通知 Google Cloud Messaging に連携する Webシステム側から送信のテスト
Web上のシステムから送信し、Androidでそれを受信し、ノーティフィケーションで通知したいような場合。
いわゆるアンドロイドのプッシュ通知の機能。
Web上のシステムとAndroidとをつなぐシステムがGoogleから提供されている。
Google Cloud Messaging という。
参考:
http://www.techdoctranslator.com/android/guide/google/gcm/gs
http://www.techdoctranslator.com/android/guide/google/gcm/gcm
http://dev.classmethod.jp/smartphone/android/gcm/
Android側の実装は上記のそれぞれの記事に色々書いてあるのですが、Web側からの送信のサンプルが少ないので、自作しました。
共有します。
下記ふたつのPHPファイルを、下のソースからコピペして作成し、Webサーバーの同じディレクトリに設置して使ってください。私はXAMPP環境でテストしていました。
◆index.php
◆ClassSenderDataToGoogleCloudMessaging.php
保存文字コードはUTF-8(ボム無し)です。
ClassSenderDataToGoogleCloudMessaging.php のほうは、書き換える場所があります。
◆サーバーアプリAPIのキー:Googleのデベロッパーコンソールから取得します。上のURL参照。
◆端末の登録ID:これはAndroidの側の処理が出来てからじゃないと取得できない値なのでAndroid側の実装を先に頑張る必要があります。Google Cloud Messaging のAPIに端末がアクセスした時得られる値です。
「message」と「url」というふたつのキーの情報が送信される想定です。
///////////////////////////////////////////////////
[1]index.php
///////////////////////////////////////////////////
///////////////////////////////////////////////////
[2]ClassSenderDataToGoogleCloudMessaging.php
///////////////////////////////////////////////////
いわゆるアンドロイドのプッシュ通知の機能。
Web上のシステムとAndroidとをつなぐシステムがGoogleから提供されている。
Google Cloud Messaging という。
参考:
http://www.techdoctranslator.com/android/guide/google/gcm/gs
http://www.techdoctranslator.com/android/guide/google/gcm/gcm
http://dev.classmethod.jp/smartphone/android/gcm/
Android側の実装は上記のそれぞれの記事に色々書いてあるのですが、Web側からの送信のサンプルが少ないので、自作しました。
共有します。
下記ふたつのPHPファイルを、下のソースからコピペして作成し、Webサーバーの同じディレクトリに設置して使ってください。私はXAMPP環境でテストしていました。
◆index.php
◆ClassSenderDataToGoogleCloudMessaging.php
保存文字コードはUTF-8(ボム無し)です。
ClassSenderDataToGoogleCloudMessaging.php のほうは、書き換える場所があります。
◆サーバーアプリAPIのキー:Googleのデベロッパーコンソールから取得します。上のURL参照。
◆端末の登録ID:これはAndroidの側の処理が出来てからじゃないと取得できない値なのでAndroid側の実装を先に頑張る必要があります。Google Cloud Messaging のAPIに端末がアクセスした時得られる値です。
「message」と「url」というふたつのキーの情報が送信される想定です。
///////////////////////////////////////////////////
[1]index.php
///////////////////////////////////////////////////
<?php
require_once("ClassSenderDataToGoogleCloudMessaging.php");
//処理結果通知
$notice_message = "";
//モード取得
if(isset($_REQUEST["mode"])){
$mode = $_REQUEST["mode"];
}else{
$mode = "default";
}//if
//送信モードなら送信クラスに処理させる
if($mode == "send"){
$sender = new ClassSenderDataToGoogleCloudMessaging();
$result = $sender->sendDataToApi($_REQUEST);
$notice_message = "<p style='color:red;'>".$result."</p>";
}//if
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Google Cloud Messaging for Android 送信テスト</title>
</head>
<body>
<div>
<h1>Google Cloud Messaging for Android 送信テスト</h1>
<!--送信先はこのスクリプト APIへの送信は受け取った後の処理で実行される-->
<form method="POST" action="index.php">
<fieldset>
<p>
<label>Message</label>
<input type="text" size="20" name="message">
</p>
<p>
<label>URL</label>
<input type="text" size="50" name="url">
</p>
</fieldset>
<input type="hidden" name="mode" value="send">
<input type="submit" value="送信">
</form>
</div>
<!--実行結果-->
<?php echo("$notice_message") ?>
</body>
</html>
require_once("ClassSenderDataToGoogleCloudMessaging.php");
//処理結果通知
$notice_message = "";
//モード取得
if(isset($_REQUEST["mode"])){
$mode = $_REQUEST["mode"];
}else{
$mode = "default";
}//if
//送信モードなら送信クラスに処理させる
if($mode == "send"){
$sender = new ClassSenderDataToGoogleCloudMessaging();
$result = $sender->sendDataToApi($_REQUEST);
$notice_message = "<p style='color:red;'>".$result."</p>";
}//if
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Google Cloud Messaging for Android 送信テスト</title>
</head>
<body>
<div>
<h1>Google Cloud Messaging for Android 送信テスト</h1>
<!--送信先はこのスクリプト APIへの送信は受け取った後の処理で実行される-->
<form method="POST" action="index.php">
<fieldset>
<p>
<label>Message</label>
<input type="text" size="20" name="message">
</p>
<p>
<label>URL</label>
<input type="text" size="50" name="url">
</p>
</fieldset>
<input type="hidden" name="mode" value="send">
<input type="submit" value="送信">
</form>
</div>
<!--実行結果-->
<?php echo("$notice_message") ?>
</body>
</html>
///////////////////////////////////////////////////
[2]ClassSenderDataToGoogleCloudMessaging.php
///////////////////////////////////////////////////
<?php
/**
* Google Cloud Messaging APIにデータを送信するクラス。
*
* Android連携のプッシュ通知の挙動テスト用。
*
*/
class ClassSenderDataToGoogleCloudMessaging{
//Google Cloud Messaging API のURL
private $google_api_url = "https://android.googleapis.com/gcm/send";
//APIのキー(Googleのデベロッパーコンソールで取得できる「Server API Key(サーバー アプリケーションのAPIキー)」)
private $api_key = "ここにサーバーアプリケーションのAPIキーを記述";
//AndroidデバイスIDの配列(まずAndroidからWebAPIに登録してもらう動作をさせ、この値を取得する必要がある)
//「APA91bEglOye6b4CRq0aACO1ENKHm-V3sOCBgSwIvdSn_UzNioF8jlRftjgR1XSYWk0c8DfOGLcbVuBps3v-PAOsfnvkB4A3ItCDNxlPslI_5BosUNuuZtVGRiZf1Nu1yXa5sGzXuTG0OtjDGu8xcIyHqCPf1Uyi3w」みたいな長い文字列になる
private $registration_ids = array(
"ここに端末の登録IDを記述"
,"ここに端末の登録IDを記述"
);
///////////////////////////////
/**
* APIにデータ送信
*
* @param array フォームの送信データ($_REQUEST)
*/
public function sendDataToApi(
$request
){
////////////////////////////
//送信データ受け取り
$send_data = $request;
////////////////////////////
//ヘッダーの文字列作成
$header_string =
"Content-Type:application/json"."\r\n"
."Authorization:key=".$this->api_key."\r\n";
////////////////////////////
//APIへの送信コンテント作成
//送信コンテント JSON形式
//参考 : http://dev.classmethod.jp/smartphone/android-tips-14-gcm/
/*
* [送信データ]
* data : 送りたい文字列(JSON形式で複数可能) ※ 4KBまで
* registration_ids : 端末ごとの登録IDのリスト(JSON形式) ※一度に1~1000のidを登録可能 ※必須
* collapse_key : 送信するメッセージのグループ(任意の文字列)
* time_to_live : デバイスがオフラインの場合にメッセージを保持しておく期間(秒で指定)
*/
$content_array = array(
'data' => $send_data
,'registration_ids' => $this->registration_ids
,'collapse_key' => 'google_cloud_messaging_test'
);
//【重要】JSON形式に変換
$content_json = json_encode($content_array);
////////////////////////////
//コンテキストインスタンスにセットするオプションの配列を作成
//options は、 $arr['wrapper']['option'] = $value のような形式の、連想配列の連想配列である必要がある
//参照 : http://jp2.php.net/manual/ja/context.http.php
$options_array = array();
//送信メソッド
$options_array["http"]["method"] = "POST";
//ヘッダー
$options_array["http"]["header"] = $header_string;
//コンテント
$options_array["http"]["content"] = $content_json;
////////////////////////////
//コンテキストインスタンスを作成
$context = stream_context_create();
//コンテキストにオプション情報(ヘッダー情報、コンテントの内容、送信メソッドなど)をセット
stream_context_set_option(
$context,
$options_array
);
try{
//APIへのクエリ送信、戻ってきたデータの受取
$return_data = file_get_contents(
$this->google_api_url, //接続先のURL
false, //パス検索
$context //送信する内容(リソースコンテクスト)
);
}catch(Exception $e){
$return_data = "エラー : APIからのデータ取得に失敗しました。<br>\n".$e;
}//try
//データを戻す
return $return_data;
}//function
}//class
/**
* Google Cloud Messaging APIにデータを送信するクラス。
*
* Android連携のプッシュ通知の挙動テスト用。
*
*/
class ClassSenderDataToGoogleCloudMessaging{
//Google Cloud Messaging API のURL
private $google_api_url = "https://android.googleapis.com/gcm/send";
//APIのキー(Googleのデベロッパーコンソールで取得できる「Server API Key(サーバー アプリケーションのAPIキー)」)
private $api_key = "ここにサーバーアプリケーションのAPIキーを記述";
//AndroidデバイスIDの配列(まずAndroidからWebAPIに登録してもらう動作をさせ、この値を取得する必要がある)
//「APA91bEglOye6b4CRq0aACO1ENKHm-V3sOCBgSwIvdSn_UzNioF8jlRftjgR1XSYWk0c8DfOGLcbVuBps3v-PAOsfnvkB4A3ItCDNxlPslI_5BosUNuuZtVGRiZf1Nu1yXa5sGzXuTG0OtjDGu8xcIyHqCPf1Uyi3w」みたいな長い文字列になる
private $registration_ids = array(
"ここに端末の登録IDを記述"
,"ここに端末の登録IDを記述"
);
///////////////////////////////
/**
* APIにデータ送信
*
* @param array フォームの送信データ($_REQUEST)
*/
public function sendDataToApi(
$request
){
////////////////////////////
//送信データ受け取り
$send_data = $request;
////////////////////////////
//ヘッダーの文字列作成
$header_string =
"Content-Type:application/json"."\r\n"
."Authorization:key=".$this->api_key."\r\n";
////////////////////////////
//APIへの送信コンテント作成
//送信コンテント JSON形式
//参考 : http://dev.classmethod.jp/smartphone/android-tips-14-gcm/
/*
* [送信データ]
* data : 送りたい文字列(JSON形式で複数可能) ※ 4KBまで
* registration_ids : 端末ごとの登録IDのリスト(JSON形式) ※一度に1~1000のidを登録可能 ※必須
* collapse_key : 送信するメッセージのグループ(任意の文字列)
* time_to_live : デバイスがオフラインの場合にメッセージを保持しておく期間(秒で指定)
*/
$content_array = array(
'data' => $send_data
,'registration_ids' => $this->registration_ids
,'collapse_key' => 'google_cloud_messaging_test'
);
//【重要】JSON形式に変換
$content_json = json_encode($content_array);
////////////////////////////
//コンテキストインスタンスにセットするオプションの配列を作成
//options は、 $arr['wrapper']['option'] = $value のような形式の、連想配列の連想配列である必要がある
//参照 : http://jp2.php.net/manual/ja/context.http.php
$options_array = array();
//送信メソッド
$options_array["http"]["method"] = "POST";
//ヘッダー
$options_array["http"]["header"] = $header_string;
//コンテント
$options_array["http"]["content"] = $content_json;
////////////////////////////
//コンテキストインスタンスを作成
$context = stream_context_create();
//コンテキストにオプション情報(ヘッダー情報、コンテントの内容、送信メソッドなど)をセット
stream_context_set_option(
$context,
$options_array
);
try{
//APIへのクエリ送信、戻ってきたデータの受取
$return_data = file_get_contents(
$this->google_api_url, //接続先のURL
false, //パス検索
$context //送信する内容(リソースコンテクスト)
);
}catch(Exception $e){
$return_data = "エラー : APIからのデータ取得に失敗しました。<br>\n".$e;
}//try
//データを戻す
return $return_data;
}//function
}//class
2015年1月22日木曜日
【ヴァージョン管理システム】コミット→プル→マージ→→プッシュ
Gitやサブバージョンといったヴァージョン管理システムを使って多人数でひとつのプロジェクトをいじる際、よく巻き戻りとかを起こしてしまう。
原因は私にあったようだ。
最近学んだことをメモしておく。
システム:サブヴァージョン
使用ソフト:Tortoise HG
【理解が進んだのでメモ】
コミット:ローカルで自分が行った変更の一覧を作成する。
プル :リモートで他人によって行われた変更のリストを取得する。
ここで樹形が枝分かれする。
マージ :コミットとプルとのふたつの変更リストを突き合わせながら、ふたつを結合する。
結合した結果、ローカルのレポジトリにそれが反映される。
ここで樹形が統合される。
----------/ここまでローカルレポジトリの世界/-----------
----------/ここからリモートレポジトリの世界/-----------
プッシュ:マージされた変更を、リモートのリポジトリにアップロードする。
【重要】
枝分かれしてもそれはまだローカルの世界。
枝分かれはプルによって生じる。
プルしてきてもまだそれがいきなりローカルに反映されるわけではない。
コミットもプルも、「候補」である。
マージが重要。
マージによってコミットの候補とプルの候補とを結合する。
マージの結果、樹形が統合される。
マージが終わってもまだそれはローカルの話である。
プッシュによってはじめてリモートに影響が出る。
【Android】ノーティフィケーションにアイコンは必須
Androidで、以前作ったノーティフィケーション通知クラスをコピペで使いまわそうと思ったら、以前は問題なく動いていたものが動かなくてはまってしまったので、メモ。
結論:アイコンは必須 。セットしないと動かないしエラーも出ない。
備考:ノーティフィケーションビルダーにノーティフィケーションインスタンスを作成させる。
------------------------------------------------------
パッケージ名
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
/**
* ノーティフィケーション通知を行うクラス
*/
public class ClassNotificationSender{
//Singletonデザインパターン : このクラスの唯一のインスタンス
private static ClassNotificationSender notification_sender = new ClassNotificationSender();
//ノーティフィケーションのID
private int notification_id = 0;
//クラス名
private static String class_name = "ClassNotificationSender";
///////////////////////////////////////////////////////////
//コンストラクタ
///////////////////////////////////////////////////////////
/**
* 同じインスタンスはひとつしか作れないようにする
* Singletonデザインパターン
*/
private ClassNotificationSender(){
System.out.println("ClassNotificationSender インスタンスを生成しました。(Singletonデザインパターン)");
}//function
///////////////////////////////////////////////////////////
/**
* インスタンス取得
*/
public static ClassNotificationSender getInstance(){
return notification_sender;
}//function
///////////////////////////////////////////////////////////
//メソッド
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/**
* プッシュ通知ノーティフィケーション表示。
*
* メインアプリ非稼働時にも動作する。
* インスタンス作成時にコンテキストが得られないようなので
* このメソッド引数にコンテキストを必要とすることにした。
*
* @param String 通知すべきテキスト
* @param Context コンテキストインスタンス
* @return void
*/
public void sendNotification(
String content_text,
Context context
){
//nullチェック
if(
(content_text == null)||
(context == null)
){
return;
}//if
////////////////////////////////////
//ノーティフィケーションインスタンス作成
//ノーティフィケーションインスタンスを作る際は Notification.Builder を使うのが推奨 APIレベル11以降
//ノーティフィケーションビルダーインスタンスを作成
Notification.Builder notification_builder = new Notification.Builder(context);
//ノーティフィケーションの各種内容をセット
notification_builder.setTicker("通知あり"); //メインの画面の上部の狭い領域に表示される文字列
notification_builder.setContentTitle("通知タイトル"); //通知一覧に表示されるタイトル
notification_builder.setContentText(content_text); //通知一覧に表示される本文
notification_builder.setNumber(this.notification_id); //通し番号
notification_builder.setSmallIcon(R.drawable.ic_launcher); //注意! アイコンは必須で、無いと表示されないしエラーも出ない
//ビルダーにノーティフィケーションインスタンスを作成させる
Notification notification = notification_builder.build();
//通知表示実行
//ノーティフィケーションマネジャーインスタンス
NotificationManager notification_manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notification_manager.notify(
context.getPackageName(), //ノーティフィケーション識別タグ
this.notification_id, //ノーティフィケーションに割り振ったID
notification //ノーティフィケーションインスタンス
);
System.out.println(class_name + "[通過]ノーティフィケーション発行 : " + notification.toString());
//ノーティフィケーションのIDをカウントアップ
this.notification_id++;
}//function
}//class
結論:アイコンは必須 。セットしないと動かないしエラーも出ない。
備考:ノーティフィケーションビルダーにノーティフィケーションインスタンスを作成させる。
------------------------------------------------------
パッケージ名
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
/**
* ノーティフィケーション通知を行うクラス
*/
public class ClassNotificationSender{
//Singletonデザインパターン : このクラスの唯一のインスタンス
private static ClassNotificationSender notification_sender = new ClassNotificationSender();
//ノーティフィケーションのID
private int notification_id = 0;
//クラス名
private static String class_name = "ClassNotificationSender";
///////////////////////////////////////////////////////////
//コンストラクタ
///////////////////////////////////////////////////////////
/**
* 同じインスタンスはひとつしか作れないようにする
* Singletonデザインパターン
*/
private ClassNotificationSender(){
System.out.println("ClassNotificationSender インスタンスを生成しました。(Singletonデザインパターン)");
}//function
///////////////////////////////////////////////////////////
/**
* インスタンス取得
*/
public static ClassNotificationSender getInstance(){
return notification_sender;
}//function
///////////////////////////////////////////////////////////
//メソッド
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/**
* プッシュ通知ノーティフィケーション表示。
*
* メインアプリ非稼働時にも動作する。
* インスタンス作成時にコンテキストが得られないようなので
* このメソッド引数にコンテキストを必要とすることにした。
*
* @param String 通知すべきテキスト
* @param Context コンテキストインスタンス
* @return void
*/
public void sendNotification(
String content_text,
Context context
){
//nullチェック
if(
(content_text == null)||
(context == null)
){
return;
}//if
////////////////////////////////////
//ノーティフィケーションインスタンス作成
//ノーティフィケーションインスタンスを作る際は Notification.Builder を使うのが推奨 APIレベル11以降
//ノーティフィケーションビルダーインスタンスを作成
Notification.Builder notification_builder = new Notification.Builder(context);
//ノーティフィケーションの各種内容をセット
notification_builder.setTicker("通知あり"); //メインの画面の上部の狭い領域に表示される文字列
notification_builder.setContentTitle("通知タイトル"); //通知一覧に表示されるタイトル
notification_builder.setContentText(content_text); //通知一覧に表示される本文
notification_builder.setNumber(this.notification_id); //通し番号
notification_builder.setSmallIcon(R.drawable.ic_launcher); //注意! アイコンは必須で、無いと表示されないしエラーも出ない
//ビルダーにノーティフィケーションインスタンスを作成させる
Notification notification = notification_builder.build();
//通知表示実行
//ノーティフィケーションマネジャーインスタンス
NotificationManager notification_manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notification_manager.notify(
context.getPackageName(), //ノーティフィケーション識別タグ
this.notification_id, //ノーティフィケーションに割り振ったID
notification //ノーティフィケーションインスタンス
);
System.out.println(class_name + "[通過]ノーティフィケーション発行 : " + notification.toString());
//ノーティフィケーションのIDをカウントアップ
this.notification_id++;
}//function
}//class
2015年1月21日水曜日
【cakePHP】データベース構造を変更したらキャッシュを殺せ
cakePHPを使ってのシステム開発。
データベーステーブルを追加する必要があったのでPHPMyAdminからテーブルを追加。
そのテーブルを受け持つモデルクラスを作成し、find()で動作テスト。
…
動かん!!!
3時間苦しんだ挙句、原因がわかりました。
キャッシュです。
参考
http://oneday.ter.jp/php/cakephp-php/983.html
\app\tmp\cache\models
に、データベーステーブルとモデルクラスとをつなぐファイルのキャッシュがあるようです。
テーブル構造を変更したら(カラムやテーブルを追加したり名前を変えたりしたら)キャッシュをバシバシ皆殺しにしよう!!!
データベーステーブルを追加する必要があったのでPHPMyAdminからテーブルを追加。
そのテーブルを受け持つモデルクラスを作成し、find()で動作テスト。
…
動かん!!!
3時間苦しんだ挙句、原因がわかりました。
キャッシュです。
参考
http://oneday.ter.jp/php/cakephp-php/983.html
\app\tmp\cache\models
に、データベーステーブルとモデルクラスとをつなぐファイルのキャッシュがあるようです。
テーブル構造を変更したら(カラムやテーブルを追加したり名前を変えたりしたら)キャッシュをバシバシ皆殺しにしよう!!!
登録:
投稿 (Atom)