Blog -
画面の解像度毎にランチャアイコンや通知アイコンを用意しますが
少し前のAndroidならそれほど解像度がずば抜けているという事は無かったんですが
最近のタブレットや4.0搭載している物などは今までの余り気に留めなかったサイズがもろに
出てしまいますそこで意外と忘れがち、な各解像度で必要なサイズをまとめました
(若干サイズが違うのは私の主観もあります)
drawable-hdpi | drawable-mdpi | drawable-ldpi | drawable-xhdpi | |
ランチャー |
72*72 | 48*48 | 32*32 | 98*98 |
通知 |
38*38 | 25*25 | 19*19 | 50*50 |
drawable-nodpi はそれぞれ解像度判断されない画像を格納する(ただしストレッチさせると画面に合わせて縮小拡大する)
開発しているとXMLをいじる事が多い、特にAndroidはレイアウトや言語定数など
XMLで記述する、またその専用エディタもSDKに含まれている。
ただ、XMLをいじった後に、デバッグの実行や実行などを行うとコンソールに
Error in an XML file: aborting build.
が発生して、XXXXXXX.out.xmlというファイルが作成されて中止されてしまう。
これは、Eclipse側のバグではないかという話ですが、対応策をメモ代わりに記事にします。
簡単な話ですが、XXXXXXX.out.xmlを消して(すぐさま消せないのでバックグラウンドの処理が終わったら)
プロジェクトのクリーンを行ってパッケージエクスプローラーで右クリック、実行またはデバッグから
Androidアプリケーション()を実行すると、エラーにならずに実行される。
Eclipse側のXMLへのチェック機構の問題か稀に発生するのでちょっと面倒ですがこの方法で回避可能です。
文字列の操作、連結でとてもスピードの速いStringBuilderですが
私がハマったところ(ただのおっちょこちょいですが)の原因と注意を記事にします。
StringBuilderをメンバーで持って使いまわす場合に初期化の方法として、setLength(0)を使います
こうする事で、初期化と同じ行為(ポインタを初期化)する事が出来ます。
private StringBuilder mStringBuilder = new StringBuilder(); //初期化 mStringBuilder.setLength(0);
これを分岐に使用してifで比較します。
private StringBuilder mStringBuilder = new StringBuilder(); //初期化 mStringBuilder.setLength(0); int ret = 0; //1-3の乱数 ret = (int)Math.floor(Math.random()*(3+1-1))+1; switch(ret){ case 1: mStringBuilder.append("A"); break; case 2: mStringBuilder.append("BB"); break; case 3: mStringBuilder.append("CCC"); break; default: mStringBuilder.append("A"); break; } if(mStringBuilder.equals("A")){ Log.d(TAG,mStringBuilder.toString); }
これを、何度か繰り返します。
A → BB → CCC → A と発生した場合最後のAではFALSEとして評価されます。
これは何故かというと、mStringBuilderはポインタをリセットしましたが中身のメモリがクリア
された訳では無いからです。
実際、最後のif分の評価は、ACC == A を評価していることになります。
if(mStringBuilder.toString().equals("A")){ Log.d(TAG,mStringBuilder.toString); }
この様に比較すると想定通り評価出来ます。
これにハマって余分にデバッグしてしまった^^;
・リソースにある画像を順番に入れ替える
・特定のコードに当てはまる、リソース内に定義してあるArrayを呼び出したい。
リソースID(一部) + 変数でリソースを取得したいという処理があります。
こんな時に便利なのが
i = 1; picture_id = "pict_" + (i);
pict_res_id = getResources().getIdentifier(Picture_id,"drawable", getPackageName());
Arrayの場合は
i = 1; Array_id = "Array_" + (i);
Array_res_id = getResources().getIdentifier(Array_id,"array", getPackageName());
ExpandableListなどで択ばれた親のコードを元に子供のArrayなどを呼び出すなどに応用出来ます。
これで、その4ですがやっと接続後の処理への理解になります。
前回までに接続の確立まで行いましたが
個人的に注意点として
・接続した後に、有効範囲からどちらかが居なくなる。
・どちらかの電源が切れる。
いつ切れるか解らない処理も考えながら作らないと実用的にはならない。
まだまだ、先は長いですが今回は、接続以降にどの様な処理があるか理解します。 いつもの様に、Threadで実現します。 宣言部
private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; //中略 }
中で使うメンバーを定義します、見た事がある奴も居ます。コンストラクタですが、BluetoothSocketからStreamを取得します。
以降、Streamとして処理が出来ます。
public ConnectedThread(BluetoothSocket socket) { mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; // Get the input and output streams, using temp objects because // member streams are final try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { } mmInStream = tmpIn; mmOutStream = tmpOut; }
次にrun()メソッドが実行されます。
先に、Handlerについてですが、ThreadからUIに対して処理が行えません
これは非同期処理中に直接ThreadからUIへ更新を掛ける為です。考えれば非同期処理からUIを触りたい処理ばかりなのですが、やり方があります。
それは、Handlerを使う方法です。Handlerは送られてきたメッセージを受け取り、処理します非同期処理から非同期にHandlerに渡して
HandlerはそれをUIへ更新します。(正確には、postかmessageを使ってBundleなどとやり取りをするみたいです)
他に非同期処理はAsynctaskなどもあります。 今回は、HandlerのMessageを利用してUIとのやり取りを行っています。
(他にも方法はありますがobtainMessageでにやり取りの効率が良いらしいです)
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
対象のメソッドと引数は obtainMessage(int what, int arg1, int arg2, Object obj) ・MESSAGE_READは、Handlerで受け取った時に
どの処理に対してか識別します。・int bytesは第一引数・bufferは、InputStream(送られて来たデータ)
これを受け取って、UIへHandlerがMessageオブジェクトとして渡しています。 と話を戻しますが。
bytes = mmInStream.read(buffer);
のメソッドでストリームからデータが送られて来るまで待機します。
送られて来たら、UIへHandlerがMessageオブジェクトとして渡しています。
public void run() { byte[] buffer = new byte[1024]; // buffer store for the stream int bytes; // bytes returned from read() // Keep listening to the InputStream until an exception occurs while (true) { try { // Read from the InputStream bytes = mmInStream.read(buffer); // Send the obtained bytes to the UI Activity mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); } catch (IOException e) { break; } } }
また、こちらからデータを書き込む場合の処理を準備します。これは、メインの処理から呼ばれます。
/* Call this from the main Activity to send data to the remote device */ public void write(byte[] bytes) { try { mmOutStream.write(bytes); } catch (IOException e) { } }
キャンセル時の処理を準備しておきます。
/* Call this from the main Activity to shutdown the connection */ public void cancel() { try { mmSocket.close(); } catch (IOException e) { } }
今回までで、接続からデータの受信(正確にはサーバ側でデータ書き込みは解説してませんが)まで理解出来たかと思います。
もう少し深く理解して、後の処理も考えたいと思っています。
※GoogleDevelopers チュートリアルを使用しています。
次にクライアント側へ理解を広げたいと思います。
クライアント側はどうなっているのか
【クライアントとしての接続】
クライアントとしては、接続相手を探したおく必要があります。
接続相手は、発見可能な状態であってBluetoothServerSocketがリスニング状態である必要があります。
ここでは、相手を見つけた後の処理について書きます。
この処理も、Threadで実現するべきです。まず定義から
private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; //中略 }
BluetoothSocketとBluetoothDeviceをメンバーに持たせています。
public ConnectThread(BluetoothDevice device) { // Use a temporary object that is later assigned to mmSocket, // because mmSocket is final BluetoothSocket tmp = null; mmDevice = device; // Get a BluetoothSocket to connect with the given BluetoothDevice try { // MY_UUID is the app's UUID string, also used by the server code tmp = device.createRfcommSocketToServiceRecord(UUID); } catch (IOException e) { } mmSocket = tmp; }
コンストラクタでは、BluetoothSocketを初期化してBluetoothDeviceを受け取ります。
BluetoothDeviceからcreateRfcommSocketToServiceRecord(MY_UUID)を発行する事で BluetoothSocketが相手のBluetoothServerSocketから戻されます。
その2で説明したBluetoothServerSocket開始時に指定したMY_UUIDと同じ事を前提にします。
tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
成功すれば、BluetoothSocketが返されます。続いて、run()メソッドが開始されます。
public void run() { // Cancel discovery because it will slow down the connection mBluetoothAdapter.cancelDiscovery(); try { // Connect the device through the socket. This will block // until it succeeds or throws an exception mmSocket.connect(); } catch (IOException connectException) { // Unable to connect; close the socket and get out try { mmSocket.close(); } catch (IOException closeException) { } return; } // Do work to manage the connection (in a separate thread) manageConnectedSocket(mmSocket); }
cancelDiscovery()を最初に行っていますが、Discovery()(接続相手の検索)中である事が予測できる為接続前に中止します、
BluetoothAdapterは一つであればどのインスタンスからでもキャンセル出来ます。 その後、取得したBluetoothSocketに対してconnect()を行います。
Thread処理のキャンセルに対応して処理を作っておきます。
/** Will cancel an in-progress connection, and close the socket */ public void cancel() { try { mmSocket.close(); } catch (IOException e) { } }
これで、サーバ、クライアント両方の接続までは理解出来たかと思います。
なぜ、Threadなのか?に関しては、後日時間が有れば解説します。
※GoogleDevelopers チュートリアルを使用しています。
前回は、設計に際しての概要まで纏めましたが 今回はちょっと具体的に
【サーバとしての接続】
private class AcceptThread extends Thread { private final BluetoothServerSocket mmServerSocket; //中略 }
AcceptThreadというThreadを作って、そのコンストラクタで
public AcceptThread() { // Use a temporary object that is later assigned to mmServerSocket, // because mmServerSocket is final BluetoothServerSocket tmp = null; try { // MY_UUID is the app's UUID string, also used by the client code tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (IOException e) { } mmServerSocket = tmp; }
BluetoothServerSocketをの有無を確認して無ければ(無いというのは、接続中か接続後か)
BluetoothServerSocketを用意する、Threadなのでrun()メソッドが開始される。
public void run() { BluetoothSocket socket = null; // Keep listening until exception occurs or a socket is returned while (true) { try { socket = mmServerSocket.accept(); } catch (IOException e) { break; } // If a connection was accepted if (socket != null) { // Do work to manage the connection (in a separate thread) manageConnectedSocket(socket); mmServerSocket.close(); break; } } }
run()の中では、BluetoothSocketを初期化してaccept()を発行しリスニングを開始します。
クライアントがBluetoothSocketを要求するとBluetoothSocketがsocketへソケットを用意します。
socket(接続以降)の処理を開始して、BluetoothSocketをclose()して終了します。 また、処理を中断した時の処理も記述します。
public void cancel() { try { mmServerSocket.close(); } catch (IOException e) { } }
これで、サーバソケットでの待機が理解出来ました。
注意点として、accept() が BluetoothSocket を返すときは、ソケットがすでに接続済みでなので
connect() メソッドを呼び出す必要はありません ( クライアントサイドでしている想定 )
※GoogleDevelopers チュートリアルを使用しています。
ただ、余り文献が無いという事で、調べた事をメモ的に記載します。
(基本的に2.1以降を対象とします)
【Android内で抽象化されたBluetoothのクラス】
BluetoothAdapter・・・ハードその物を表します。
BluetoothDevice・・・リモートのデバイスを表します、いわゆる相手。
BluetoothSocket・・・相手との通信にStreamを利用する場合のソケット。
BluetoothServerSocket・・・リクエストを待ち受けしているサーバソケット
BluetoothClass・・・Bluetoothのプロパティセット(読み込み専用)
大きくはこんな所、ここでちょっと解りににくいポイントのまとめ
・サーバは待ち受けるリスナー。
・クライアントは、サーバへ接続に来る。
・接続には一般的にSocketを利用する。
【パーミッション】
Bluetoothを利用するには
BLUETOOTH
BLUETOOTH_ADMIN
が必要になる。
【発見機能】
通常、BluetoothはON/OFFですが最近ではむやみに発見されない様に
発見させるかさせないかのON/OFFが機能として実装されています。
【デバイスの接続】
サーバサイドとクライアントサイドの両方の機構で実装が必要になる。
(まあ当然ですが)
同一の RFCOMM チャネル上で、それぞれが接続済みの BluetoothSocket
を受け取ったときに接続されたとみなされます。
ポイント
・ペアリングされたデバイスの場合はこの限りではない
・ペアリングされていないデバイスは検索してBluetoothDeviceを得る必要がある
(そのMACアドレス)
【設計のポイント】
2つの端末で通信をする場合に、どちらかがサーバで一方がクライアントの動きをする
と言う感じになる、先にサーバでSocketを獲得した方がサーバとして振る舞い接続を確立する。
サーチ
発見
BluetoothDeviceを獲得
Sockeを要求、取得
通信開始
と同時に
発見可能へ
サーバソケットリスニング開始(accept())
接続要求受領
Socketを提供
*サーバソケットリスニング停止(破棄)
通信開始
通信終了
発見可能へ
サーバソケットリスニング開始
とこうなる訳ですが、重要なのは※の部分
サーバソケットを停止(破棄していますが)RFCOMMでは同時通信はありません(チャネルあたりひとつ)ですので
接続を待つ必要性がなくなります。よって高負荷の処理は破棄します。
破棄してもSocketは破棄されない為、通信は可能です。
また、acceptはブロックされた呼び出しである為、AndroidのUI(シングル)では高負荷ですのでActivityのUIで
実行するのは現実的ではありません、別スレッドで処理します。
その2へ続く
魔法の五行を編集して利用しているが
ro.ril.oem.ecclist=110,118,119
ro.ril.enable.a52=0
ro.ril.enable.a53=1
ro.ril.fast.dormancy.timeout=3
ro.ril.enable.sbm.feature=1
バッテリー満タン時には、juicePlotterで40時間近い稼動予定になる。
実際は、ちょこちょこ(稀にヘビーに)使っても2日は持つ感じになる。
音楽再生などで使うと1日くらい、夜中0時から朝6時のスリープ時消費は
大体、2〜3%というところ、グラフで倍率が高いと殆ど横線に見える。
ただ、Sense系以外のROMを使った場合、丁度45度の角度で消費されて行く
満タンからだと8時間くらい使っていれば5時間持つかどうか、以前の
バッテリー大量消費に見舞われてからの現象。
ハードは同じだし、SDかデバイス関連でしょうか。
同じ様な症状の方は、Sense系ROM入れて魔法の5行追加してみては?
追記
→libhtc_ril.soを入れ替えるとSense系以外もなかなか行けます。
AuraxTSenseのlibhtc_ril.soを愛用していますがなかなかです。
(/system/lib以下にあります。)