2012年12月10日月曜日
Facebookアプリの作成
新しくアプリを作ってマーケットにアップしました。
https://play.google.com/store/apps/details?id=com.matsuhiro.android.share
起動するとカメラが立ち上がって、それをFacebookに投稿するだけです。撮った写真をそのままアップロードしてしまうので、使うときはお気をつけて。
そもそもこのアプリを作った目的がFacebookのSDKを使い勝手を見てみたかったからなので、その使用感を軽くまとめてみようかと。
SDKの良い点、悪い点などを箇条書きでまとめてみる
良い点
・サンプルを起動して機能を試すまでが速い。ものの10分くらいで試すとこまで行ける
・ログイン部分の実装が楽。ActivityをFacebookの用意したものにして、その中で任意の場所にFacebookのログインボタンを入れたげればいいので、簡単。
・認証の仕組みが簡単。Androidのsignatureの情報からアプリを特定していて、それを入力するだけなので簡単。
・アップロードが簡単。ファイルを指定してあげるだけでアップロードできるのでよい。Videoなども簡単にできそう
悪い点
・ドキュメントがしょぼい。ログイン済みかどうかを判断するためにSessionを使うわけだがドキュメントからはすぐにわからなくてコードを見た。
・permissionのモデルについての説明がない。あるにはあるが探しにくくて一覧になってないのでよくわからない。
・コメント付きの写真の投稿とかどうすればいいかの説明がない。GraphAPIたたけばいいのだろうけど、リンクくらいあってもよいと思う。
・いろいろ機能が入っているけど、ほとんど使わないのでライブラリをもっと小さくしてほしい。
・Q&A方式の機能説明とかほしい。Javadocだけだと使い方がよくわからない。
とりあえずは良いものだという印象だ。
Facebookのアプリを作るデベロッパーは、結局はログインできれば良いので、それが簡単に実装できるのは大きいと思う。
ただ、permissionに関しては説明がほとんどない。どんな種類があるのかなどがないし、認証した際のステートの変化などがないので、どうやってアプリをハンドルすればいいのかいまいちはっきりしないので、サンプルから予想して書いている感じになってしまう。
・簡単にログイン実装ができる
・permissionの説明とそれを使うときのユースケース含めた説明がある
・他機能が使いたかったら追加でライブラリをインポートできる
みたいになっていれば理想かと思う。
ちなみに今回作ったアプリのソースは
https://github.com/matsuhiro/Share4F
にあります。FacebookSDKは入れていないし、app_idとかは抜いてあります。
ちなみにこれ、半日ちょっとで作れました。FacebookSDKとdeveloperコンソールが優秀な証拠かなとか思う。
2012年8月27日月曜日
Life cycle of view inside Fragment
I could not clearly understand life cycle of fragments which is added to back stack .
So, I created sample program at github.
Fragmentを使おうと思ったのであるけど、Fragment自体をstackさせた時の挙動がどうにも理解しにくかった。
Activityもそうなのであるけど、stackした時のlife cycleについてのドキュメントが無いから、自分で調べないとなんとも言えないのだよなー。
特に、よく分からなかったのはFragmentを含んだActivityがメモリ不足等の理由でシステムによりdestroyされた際にFragment内で作成したViewはdestroyされるっぽいんだけど、Fragmentのインスタンス自体は削除されないし、View自体も実態は削除されていないことが非常に不思議だ。Activityに紐付いたまま。
なので、メモリ不足等でActivityが削除されて、またonCreateがコールされた際にFragmentをnewしてaddするとFragmentがどんどん増えてしまう。
1: public class MainActivity extends FragmentActivity {
2: private static final String TAG = "FragmentTest";
3:
4: @Override
5: public void onCreate(Bundle savedInstanceState) {
6: Log.v(TAG, "Activity Enter onCreate");
7: super.onCreate(savedInstanceState);
8: setContentView(R.layout.activity_main);
9:
10: if (savedInstanceState == null) {
11: FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
12: Fragment newFragment = FirstFragment.newInstance("http://www.google.co.jp");
13: ft.add(R.id.fragment_container, newFragment, "first");
14: ft.commit();
15: }
16: Button btn = (Button) findViewById(R.id.button);
17: btn.setOnClickListener(new OnClickListener() {
18: @Override
19: public void onClick(View v) {
20: FragmentManager fm = MainActivity.this.getSupportFragmentManager();
21: FragmentTransaction ft = fm.beginTransaction();
22: Fragment newFragment = SecondFragment.newInstance("http://www.goo.ne.jp");
23: ft.replace(R.id.fragment_container, newFragment, "second");
24: ft.addToBackStack(null);
25: ft.commit();
26: }
27: });
28: Log.v(TAG, "Activity Exit onCreate");
29: }
なので、上記の10行目みたいにonCreateが再生成でコールされたと判断できたら、Fragmentを作らないようにしなければいけない。
このようにしておけば、Fragmentが余計に生成されるのを避ける事が出来て納得の行くlife cycleになると思う。
もう一つ分かりにくかったのは、Fragmentをreplace等でstackさせた時にもonDestroyViewがコールされるのだ。それでbackキーで元のFragmentに戻った際に、新しくViewを作りなおさなければいけないところがなんとも釈然としなかった。stackしているだけなのだからonStopまでで良いと思うのだが。。。
しかも、ViewのInstance自体はRootのViewに紐付けられたままで削除されているわけではないので、元々あったViewのインスタンスをActivityは保持したままであるし。
1: public class FirstFragment extends Fragment {
2: private static final String TAG = "FragmentTest";
3: private WebView mWebview = null;
4:
5: @Override
6: public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
7: Log.d(TAG, "FirstFragment Enter onCreateView");
8: if (mWebview != null) return mWebview;
9: mWebview = (WebView) inflater.inflate(R.layout.first_fragment, container, false);
10: mWebview.setWebViewClient(new WebViewClient());
11: String url = "http://www.yahoo.co.jp";
12: Bundle args = getArguments();
13: if (mLastUrl != null) {
14: url = mLastUrl;
15: } else if (savedInstanceState != null && savedInstanceState.containsKey("url")) {
16: url = savedInstanceState.getString("url");
17: } else if (args != null && args.containsKey("url")) {
18: url = args.getString("url");
19: }
20: mWebview.loadUrl(url);
21: Log.d(TAG, "FirstFragment Exit onCreateView view = " + mWebview.toString());
22: return mWebview;
23: }
などのように、8行目にある感じで、すでにFragment内で保持しているViewを返すとエラーで落ちるし。
FragmentをStackさせる際にreplaceを利用するとonDestroyViewがコールされるの前提でコードを書かなければいけないようだ。どこにもその辺の作法とか無いけれども、確かにサンプルでは必ずViewを新しく生成していますしね。
でも、そうするとWebViewを使った時にとても困る。WebViewを生成してonCreateViewで渡してユーザが操作した後に新しくFragmentを重ねるとViewが破棄されてしまうので、historyが消えてしまう。
historyの仕組みを自分で実装しなければいけなくって非常に面倒くさい。
Stack
2012年2月27日月曜日
SMSの送受信
AndroidにおいてSMSの送信をしようと思ったらpreinstallされたアプリにIntentを投げるのが一番簡単ではあるが、アプリ内で送信してしまうのもありで、それは結構簡単でした。
こんなかんじ
public class SMSSenderDialog extends Dialog {
public SMSSenderDialog(Context context) {
super(context);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sms_sender_dialog);
this.setTitle("SMS sender");
final EditText phoneNumber = (EditText) findViewById(R.id.phoneNumber);
final EditText message = (EditText) findViewById(R.id.sms_message);
Button btn = (Button)findViewById(R.id.sendSMS);
btn.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
SmsManager smsManager = SmsManager.getDefault();
String destinationAddress = phoneNumber.getText().toString();
String text = message.getText().toString();
smsManager.sendTextMessage(destinationAddress, null, text, null, null);
dismiss();
}
});
}
}
sendTextMessageを書けばいいのだかららくなもんである
受信に関してはAPIdemosに既に記述があって
./ApiDemos/src/com/example/android/apis/os/SmsMessageReceiver.java
./ApiDemos/src/com/example/android/apis/os/SmsReceivedDialog.java
をパクれば一発であった。
SMSについてはかなり好きなように出来そうで、メッセージの内容も好きに見られるので、そこからUIの処理を走らせて通知とかもふつうに出来そうだった。
2012年2月26日日曜日
AndroidでModalなDialog
AndroidでModalなDialogがほしいー。と思ったことがある人も少なくないと思う。
ModalDialogってMFCとかではあったけどAndroidにはない。AndroidだとAlertDialogとかを使ってYES/NOのDialogを表示したとしても、結果を取得するためにListenerを書かなければいけないし、必ず非同期になる。非同期で取得した結果から処理を分岐させたいときには、そのListenerの中で処理を記述しなければならない。こうすると関数のネストが深くなりがちになる。まあ、関数分ければいいだけの話だけど、いちいち他の関数に飛ぶのもなー。ということでModalなDialogが欲しくなる。
ModalDialog dialog = new ModalDialog();
int result = dialog.show();
のように書いたら結果が同期で帰ってくるようなやつ。
で、作ってみようかと思た。
結論だけ書くと無理だった。。。
java.util.concurrent.CountDownLatch
を使ってthread止めればいけんじゃね?とかおもったけど、
mStartSignal.await();
でUIのThread止めちゃうとイベントリスナーのコールバックが帰ってこないのだ。。。
まあ、ListenerもUIスレッドで動いているから当たり前ですよね。。。
やっぱAndroidって動きがシングルスレッドっぽいよね。
ただ、かなりインチキ臭いけど回避策もある。ModalDialogの呼び出しをUIスレッド以外で行えばいいのだ!そうすれば同期でコールできる。
でも呼び出し元はこうなる。
public class ModalDialogActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button) findViewById(R.id.start);
btn.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
final Context c = v.getContext();
new Thread(new Runnable() {
@Override
public void run() {
final int result = ModalDialog.show(c);
ModalDialogActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast toast;
switch (result) {
case ModalDialog.YES:
toast = Toast.makeText(c, "YES", Toast.LENGTH_SHORT);
break;
case ModalDialog.NO:
toast = Toast.makeText(c, "NO", Toast.LENGTH_SHORT);
break;
case ModalDialog.CANCEL:
default:
toast = Toast.makeText(c, "CANCEL", Toast.LENGTH_SHORT);
break;
}
toast.show();
}
});
}
}).start();
}
});
}
コールした結果をToastで出すだけなのに、すげー深くなったww
まあ、教訓としてはAndroidではCountDownLatchを極力使わないほうがいいし、もし使うならLooperが絡んでいるThreadでは呼んじゃだめってことくらいか。
まあ、Androidの場合はCountDownLatchを使いたくなる機会ってほぼないとは思うけど。
2012年1月2日月曜日
あけましておめでとうございます。
今年もよろしくお願いします。
2011年12月30日金曜日
今年はいろいろあった
日本に住んでいる人のすべてがそうであったと思うが、自分も今年はいろいろあった。
今後の人生に大きく影響するだろう出来事が7〜8はあったと思う。
地震はあるし、そんとき海外にいたし、会社はリストラ始まるし、転職先でも粉飾が起こるし、1年で2回転職するし。
思い返せば毎年、何かしらその後の人生に影響するだろうことが起こっている訳だが、今年ほど様々なことが起こるのは珍しいだろう。
地震があり原発に対する考え方が揺らいだ。ただ、政治やテレビ番組や新聞などが、それらに対して驚くほど鈍感であった。もっと大きな変化があってもよさそうなものであるが、ほとんど何も変わっていない。今までも別に変わらなかったのだとは思うが、自分がいる業界や生活の変化と比べて、あまりにも変わらなすぎて今まで以上に遠い存在になってしまったように思える。
Androidが興味の中心だった。twitterとかbookmarkとかを1年分さらっと見返してみたが、自分にとってはAndroidが関心の中心となった一年だったようだ。今も仕事で関わっているので、たぶん来年もだろう。端末のスペックの進化は、もう少し続くかもしれないけど来年くらいからモバイルのみのソフトウェアの競争が始まるのであろうか?
個人的にはWindowsPhone7が年末にヒットして来年に普及し始めるだろうくらいに思っていたのだが、日本のみならず海外でも鳴かず飛ばずのようで非常に残念だった。
情報の取得の方法も去年までとは様変わりしたように思う。
twitterはもう少し前からやっていたけど、facebookも始めて、知り合いが調べたことや興味のあることがTL上に並んで、RSSやGoogleAlertで情報を取得していた頃よりも、情報の幅とか深さが増して、尚かつ情報にたどり着くまでの時間が減ったように思える。IT業界以外の人たちも同じような体験をし始めているだろうか?だとしたらとても面白い。
来年は自分に何が起こって、世の中がどう変わるのか、元々予想など出来ないけれども、予想不可能であるということを実感したのは今年が始めてであった気がする。
今後の人生に大きく影響するだろう出来事が7〜8はあったと思う。
地震はあるし、そんとき海外にいたし、会社はリストラ始まるし、転職先でも粉飾が起こるし、1年で2回転職するし。
思い返せば毎年、何かしらその後の人生に影響するだろうことが起こっている訳だが、今年ほど様々なことが起こるのは珍しいだろう。
地震があり原発に対する考え方が揺らいだ。ただ、政治やテレビ番組や新聞などが、それらに対して驚くほど鈍感であった。もっと大きな変化があってもよさそうなものであるが、ほとんど何も変わっていない。今までも別に変わらなかったのだとは思うが、自分がいる業界や生活の変化と比べて、あまりにも変わらなすぎて今まで以上に遠い存在になってしまったように思える。
Androidが興味の中心だった。twitterとかbookmarkとかを1年分さらっと見返してみたが、自分にとってはAndroidが関心の中心となった一年だったようだ。今も仕事で関わっているので、たぶん来年もだろう。端末のスペックの進化は、もう少し続くかもしれないけど来年くらいからモバイルのみのソフトウェアの競争が始まるのであろうか?
個人的にはWindowsPhone7が年末にヒットして来年に普及し始めるだろうくらいに思っていたのだが、日本のみならず海外でも鳴かず飛ばずのようで非常に残念だった。
情報の取得の方法も去年までとは様変わりしたように思う。
twitterはもう少し前からやっていたけど、facebookも始めて、知り合いが調べたことや興味のあることがTL上に並んで、RSSやGoogleAlertで情報を取得していた頃よりも、情報の幅とか深さが増して、尚かつ情報にたどり着くまでの時間が減ったように思える。IT業界以外の人たちも同じような体験をし始めているだろうか?だとしたらとても面白い。
来年は自分に何が起こって、世の中がどう変わるのか、元々予想など出来ないけれども、予想不可能であるということを実感したのは今年が始めてであった気がする。
2011年12月11日日曜日
WebViewからJavaScriptをコールしたり
AndroidとかiPhoneのアプリ開発はWebViewを使ったWebアプリが増えていく感じがあるが、
WebViewだけだと読み込みが遅いとかレスポンスが悪いとかまだまだNativeで作った方がユーザとしてはストレスが少ないものが作れるような気がする。
HTML5でアプリがWebアプリになったとしてもなかなかレスポンスの部分は解決が難しいのではないかな?というのが個人的な印象。
そんなときにはNativeとWebViewの組み合わせでアプリを作ろうとする場合があると思う。
両者を橋渡しすることになるのはJavaScriptになりそうだけど、それぞれのプラットフォームでコールの仕方とか違うみたいなので少し調べてみた。
AndroidとiPhoneを比べると
NativeからJSを呼び出すのはiPhoneのが整っていて
JSからNativeを呼び出すのはAndroidの方が整っている印象。
Androidの場合は、
・NativeからJSの呼び出し
mWeb.loadUrl("javascript:コールしたいJSの関数");
のようにWebViewのインスタンスからmethodをコールして
JSを呼び出すような感じになっている。
ただ、この関数戻り値がvoidのなので結果が分からない。。。
結果を知ろうと思ったら、関数コールの引数にcallbackを指定するような文字列を
入れるなどして結果を取得しなければならない。サンプルではconsole.logをフックして結果を表示しているけど、ほめられた方法ではない。
・JSからNativeの呼び出し
mWeb.addJavascriptInterface(new NativeClass(), "Android");
のようにするとNativeClassが"Android"がJS内における名前空間にように扱えるので
Android.callNative("arg");
のようにJS側からコールスことが出来る。callNativeはJavaのNativeClassのmethodになっている。
この関数は戻り値をとることが出来るので同期でコールすることが出来そうだ。
iPhoneの場合は、
・NativeからJSの呼び出し
[_webView stringByEvaluatingJavaScriptFromString:@"IPHONE.CallJSfromNative('hoge')"];
のように、WebViewのメソッドをコールスことでJSを呼び出すことが出来る。
ここでは、IPHONE.CallJSFromNativeがJS側で宣言されている関数になる。
ここでは、IPHONE.CallJSFromNativeがJS側で宣言されている関数になる。
stringByEvaluatingJavaScriptFromStringは戻り値があるので同期でコールすることが出来そうだ。
・JSからNativeの呼び出し
iPhoneの場合はJSからNativeをコールする方法はきちんと用意されていないようで、少し調べた限りでは、いろんな人がそれぞれ工夫しながら実現しているような印象だ。
具体的には独自のスキームを利用してコンテンツをJSからロードしたときにshouldStartLoadWithRequestがコールされるので、独自のスキームをフックして、処理を行うような感じにするのが一般的のようだ。- (BOOL) webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
NSString *requestString = [[request URL] absoluteString];
if (![requestString hasPrefix:@"callobjc:"]) return YES;
NSArray *components = [requestString componentsSeparatedByString:@":"];
NSString *function = [components objectAtIndex:1];
[self performSelector:NSSelectorFromString(function)];
return NO;
}
上のような感じで、関数名を取り出した後にself performSelector:NSSelectorFromString()で内部に宣言した関数をコールしている。サンプルをここにおいておいた。
WebがわはDjandoで作っているが、アプリではなく静的なファイルを戻すだけなので、
必要なのは、それぞれのディレクトリ内にあるindex.htmlとsite_templeteの中にあるJSのファイルだけだ。AndroidについてはeclipseにPydevさえ入れてあればwebサーバとかたてなくても試せるはず。
少し調べてみた感じだと、すべてのフレームワークに共通のIFを用意するのはなかなか難しそうで、用意するとしたら全部非同期になりそうだ。なぜなら戻り値がなかったりするから。
https://github.com/callback/callback-ios.git
にPhoneGapのソースがあって、これは結構面白かった。
JS側からNativeをコールするときにはPhoneGap.exec()を利用する。
内部では、gap:のスキームをloadするとWebViewのコールバックが呼ばれて、その中でJS側でキューイングされている文字列を取得して、Native側の関数をキューイングされている分だけすべて関数をコールする感じになっている。
独自にJSのNativeの連携部分を作るよりもこういったコードを流用する方がよいかもしれない。
./PhoneGapLib/javascripts/core/phonegap.js.base
は拡張子が微妙だけどJS部分だろうと思う。
./PhoneGapLib/Classes/PhoneGapDelegate.m
の中にJSからのコールバック部分があったりした。
実際にWebViewとNativeの組み合わせのアプリ作ってみて感じたけど、やっぱりWebだと連打はつらいなー。というのが現状のようだ。
それと思いのほかWebViewとNativeの組み合わせでやり取りしても、画面表示のレスポンスは結構よかった。
登録:
投稿 (Atom)


