AndroidQtの環境作成
以前一度試した時には、まだきちんとリリースされてなかったので実行までたどり着けなかった。
2月中旬に発表されて、手をつけないまま6月になってしまってるんだけれど、、
TechBoosterの記事にでも、と手をつけたらハマってしまったので覚書き。
※未解決です。
まず、本家のLink
necessitas
続いて、Ministro
Ministro
※Ministroは、Android上でQtアプリを実行するためのライブラリのインストーラー(だよね?)
※Qtアプリの初回起動時に、 Qtほげ.soファイルを取得してくれる。
手順
手順としては、本家のLink先から
「Download it」→「necessitas-0.2-online-sdk-installer-linux」
をダウンロードし実行。
実行した際のスクリーンショットを順次貼っていく。
SDK/NDK共に持っていたのでINSTALLはせず。
どちらも必須(特にNDK)っぽいので、持ってない場合はここで入れること。
INSTALLの開始。
結構な時間がかかるので、覚悟しておくこと。
INSTALL完了。
チェックボックスにチェックを入れておけば、Creatorが起動する。
Creatorの起動。
二回目以降は、
/QtCreator/bin/necessitas
TOOL -> Optionにて、SDK/NDKのPATH設定をする。
あとは、サンプルプロジェクトを走らせて起動確認すればいいだけ。
と思いきや。。。
問題点
■ ant debug でこける。
Androidのbuildバージョンにも寄るが、ant1.8以降が求められている様子。
※手動Buildで確認するとエラーメッセージ詳細が得られます。
※Build.xmlのあるところで、 ant debug してみましょう。
しかし、Ubuntu環境でのStatableなantは1.7.4(2011/06/18)
ココからバイナリを拾ってきました。
無事にBuildが通ることを確認。
※後で気づいたのですが、どうやらAndroidQtの動作確認環境はAndroid2.2r2みたい。
■ 実行直後に落ちる
antの問題が解決すれば、実機/Emulator問わず、アプリケーションの実行のところまではこぎつけれる。
しかし、起動直後に落ちる。
エラー内容は、
I/Qt JAVA ( 6232): java.lang.UnsatisfiedLinkError: Cannot load library: link_image[1962]: 75 could not load needed library 'libQtDeclarative.so' for 'libsample3.so' (load_library[1104]: Library 'libQtDeclarative.so' not found)
というもの。
と、ここでログを出そうとサンプルを起動してると、
2つ作ってたサンプルの片方が動くようになってるし。なんだこれww
どこか設定に依存してるのかもしれませんね。要調査。
Android3.0と3.1とRenderScriptと。
Android3.0 HoneyCombには、新しく(実は昔からあるそうな?)RenderScriptという3D描画用のエンジンが載っていたりするのですが、、、
先日のAndroid3.1の発表と共に、RenderScriptを使ったアプリケーションが動かなくなってしまいました。
動作的には、アプリ起動直後にエラー終了。
動作ログはというと、
E/AndroidRuntime(24361): FATAL EXCEPTION: main E/AndroidRuntime(24361): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.samples/com.android.samples.RsList}: android.renderscript.RSRuntimeException: Loading of ScriptC script failed. E/AndroidRuntime(24361): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1736) E/AndroidRuntime(24361): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1752) E/AndroidRuntime(24361): at android.app.ActivityThread.access$1500(ActivityThread.java:123) E/AndroidRuntime(24361): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:993) E/AndroidRuntime(24361): at android.os.Handler.dispatchMessage(Handler.java:99) E/AndroidRuntime(24361): at android.os.Looper.loop(Looper.java:126) E/AndroidRuntime(24361): at android.app.ActivityThread.main(ActivityThread.java:3997) E/AndroidRuntime(24361): at java.lang.reflect.Method.invokeNative(Native Method) E/AndroidRuntime(24361): at java.lang.reflect.Method.invoke(Method.java:491) E/AndroidRuntime(24361): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) E/AndroidRuntime(24361): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) E/AndroidRuntime(24361): at dalvik.system.NativeStart.main(Native Method) E/AndroidRuntime(24361): Caused by: android.renderscript.RSRuntimeException: Loading of ScriptC script failed. E/AndroidRuntime(24361): at android.renderscript.ScriptC.(ScriptC.java:60) E/AndroidRuntime(24361): at com.android.samples.ScriptC_rslist. (ScriptC_rslist.java:32) E/AndroidRuntime(24361): at com.android.samples.RsListRS.initRS(RsListRS.java:119) E/AndroidRuntime(24361): at com.android.samples.RsListRS.init(RsListRS.java:82) E/AndroidRuntime(24361): at com.android.samples.RsListView.ensureRenderScript(RsListView.java:39) E/AndroidRuntime(24361): at com.android.samples.RsListView. (RsListView.java:28) E/AndroidRuntime(24361): at com.android.samples.RsList.onCreate(RsList.java:32) E/AndroidRuntime(24361): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1048) E/AndroidRuntime(24361): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1700) E/AndroidRuntime(24361): ... 11 more
こんな感じ。
initializeの部分で死んでるとか言われても、手の打ちようが無いわけで。
現象を纏めると。
・元々、Android3.1のSDKが来るまで動いていた
・3.1のSDKが来てからは、手元の3.0XOOMでは動かないAPKも、3.1XOOMでは動いてた。
・RenderScriptのサンプル全て同じ現象
・自分で作ってたのも同じ用に動かない
で、原因は、、
Android3.1の発表と共に配られた「platform-tools revision4」の問題。
これをrevision3に落とすと、この問題は解決しました。
以下を残したいがために記事を書いたんだけど。
現在SDKのダウンロードは(platform-tools含む)
/tools/android
を使用して最新を落としてくる。
で、下位バージョンを落とすためには、
参考リンク:Android SDK ライブラリ強制インストール
以下にブラウザからアクセスすればいい。(platform-toolsに限る)
http://dl-ssl.google.com/android/repository/platform-tools_r◯-△.zip
◯ → revision 今回は 「03」を指定。
△ → OSの種類 今回は 「macosx」と記載。
これで無事に、手持ちの3.0XOOMでもRenderScriptで遊べるようになりましたとさっ!
C2DM方式を考えてみる
C2DM(プッシュ)方式について。(サーバサイド寄り)
ブログには直接書いてませんが、GoodMoorningTweet(以下、GMT)というアプリを出してます。
簡単に説明すると、twitterで誰かが自分に対し
ping
と打てば設定した音が鳴る
というアプリです。
で、この現在はアプリ、Twitterサーバに対し一定間隔でTLの取得
を行ってるわけですが…
例えば1分間隔で毎回TL取得しに行くと電池の消費量がどうしても増えちゃうわけです。
そこで今回、ポーリング方式からC2DM(プッシュ)方式
に変えてみようと考えてます。(まだ作ってません!)
最初はGoogleののC2DMサーバを使用する方法を検討していたわけですが
Sign in - Google Accounts
・googleのアカウント情報が必須
・Android2.2以上必須
・定期的に登録IDがリフレッシュされる
・Android Marketが必要
という制限があったのでthrow Lifeさんの記事
http://www.adamrocker.com/blog/311/ore-ore-c2dm.html
に載っているオレオレC2DMで行くことに。
現状の動きはこんな感じです。
改造後はこんな感じ。
C2DM方式の場合、pingを確認する処理がサーバの処理となり、GMTは
ただpingの通知を待つだけになります。
GMTサーバからGMTへデータを投げる方法はthrow Lifeさんの記事の通り
Channel API
を使います。
このChannel APIはサーバから「Webページ」へプッシュするためのAPIです。
なのでGMTで一定時間ごとにTLを確認していたサービスに
Webページを持たせておきます。
これで、C2DM方式が実現でき(るはず!)ます。
とりあえずGAEがどこまで無料で使えるのか、どの程度のユーザまで耐えれるのか
などの調査も兼ねて、作ってみたいと思います。
「UDGりくえすと」はじめました。【雑記】
本日は、少しいつもとは志向を変えたお話。
「UDGりくえすと」の配信を始めたきっかけとかについて書いておく。
きっかけ自体はすごい単純で、ふとしたことでお目にかかったアプリに
「すげー。」
って思ったこと。
すげー。でも全然知らなかった。こんなに面白いのに。
↓
マーケットの宣伝って出来ないのかな?
↓
動いてる所見せてくれるのが、一番宣伝になるよね?
↓
なんか出来ないかな。
という、ごにょごにょしたのを、わいわい話し合ってたら出てきたのが
それなら、自分たちで紹介番組つくっちゃえばいいじゃん!
→「UDGりくえすと」
でした。
きっと、Marketにはすごい良い物が実は埋もれてたりすると思うんだ。
下手な放送で、上手く説明出来ないかもしれないけれど、
紹介に一役買わせてください。
そんなきっかけになればいいなって思ってます。
■□■□■□■□■□■□■□
日時:毎週日曜日 20時45分〜21時ぐらいまで
URL: http://p.tl/d-oo
内容:AndroidApplicationを @kobashinG と @seit がUSTで実況紹介します。
随時Twitterで紹介アプリ募集中。
開発者様も、ユーザー様も、どしどしりくえすとをお待ちしてます。
※録画も置いておりますので、いつでもご覧になってくださいっ
さくらVPSのUbuntuにRedmineをほげほげ。。
久しぶりに書くわけだけど、お久しぶりです。
ちょっと前から、チケットドリブンな開発に興味を惹かれていて、
身内で回してるプロジェクトに導入できないかと考えてた。
そんなこんなで、さくらVPSのUbuntu-10.04 amd64 にRedmineを入れた時のメモ。
参考にしたサイト
⇛http://risin.jp/archives/119
ほとんどリンク先の通りで大丈夫なんだけど、
空っぽのUbuntuからじゃ詰まる所があったので勝手補足メモ。
◯readlineのインストールには gcc/g++ が必要。
$ sudo apt-get install gcc g++
◯reeのインストールには build-essential libreadline5-dev libssl-dev zlib1g-dev が必要。
$ sudo apt-get install build-essential libreadline5-dev libssl-dev zlib1g-dev
◯passenger-install-apache2-module の実行に足りないのは
・libcurl4-openssl-dev(リンク先にも記載)
・apache2-mpm-prefork
・apache2-prefork-dev
・libapr1-dev
・libaprutil1-dev
$ sudo apt-get install libcurl4-openssl-dev apache2-mpm-prefork apache2-prefork-dev libapr1-dev libaprutil1-dev
◯SVNのインストール
$ sudo apt-get install subversion
。。。mysqlがインストールされてる前提だと?
お勉強します。ちょっと寝かすです。
設定→端末情報→電池使用量がいかがなものか調べてみた
先日のAndroid関西支部「情報交換会」に参加してきました。
LTにも参加したんだけど、私は下っ端扱いだったので触れません。
きっとseitさんが書いてくれるんじゃないかな?w
さてさて、勉強会の中で、電源管理のお話がでました。
タスク切れば電源が持つってのは違うんだよ?
アプリいっぱい眠ってると電池が減る訳じゃないんだよ?
ってお話があって、
kobashinG「あれ?設定→端末情報→電池使用量ってアテにできないのかな?」
と疑問が湧いたので調べてみました。
設定アプリは
$ANDROID-HEAD/package/apps/Settings
にありました。
まずはAndroid端末で問題の画面に行って、
画面上の表示を確認。
それをgrep
$ cd ./package/apps/Settings $ find . -type f -print | xargs grep --color -n "電池使用量" /dev/null
結果からぽいのを探す。
./res/values-ja/strings.xml:864:"電池使用量"
次はAndroidManifest.xmlからこれを使ってるActivityを探す。
面倒なのでgrepした。
./AndroidManifest.xml:766: android:label="@string/power_usage_summary_title"
ほしい結果が得られたので、見に行く。
$ vim AndroidManifest.xml
765 <activity android:name=".fuelgauge.PowerUsageSummary" 766 android:label="@string/power_usage_summary_title" 767 android:clearTaskOnLaunch="true" 768 > 769 <intent-filter> 770 <action android:name="android.intent.action.MAIN" /> 771 <action android:name="android.intent.action.POWER_USAGE_SUMMARY" /> 772 <category android:name="android.intent.category.DEFAULT" /> 773 <category android:name="com.android.settings.SHORTCUT" /> 774 </intent-filter> 775 </activity>
どうやら、PowerUsageSummaryってActivityの様子。
早速追っかける。
onCreate()から怪しいとこを読みあさって、
見つけたのがこの処理。
private void processAppUsage() { SensorManager sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); final int which = mStatsType; final int speedSteps = mPowerProfile.getNumSpeedSteps(); final double[] powerCpuNormal = new double[speedSteps]; final long[] cpuSpeedStepTimes = new long[speedSteps]; for (int p = 0; p < speedSteps; p++) { powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p); } final double averageCostPerByte = getAverageDataCost(); long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which); mStatsPeriod = uSecTime; updateStatsPeriod(uSecTime); SparseArray<? extends Uid> uidStats = mStats.getUidStats(); final int NU = uidStats.size(); for (int iu = 0; iu < NU; iu++) { Uid u = uidStats.valueAt(iu); double power = 0; double highestDrain = 0; String packageWithHighestDrain = null; //mUsageList.add(new AppUsage(u.getUid(), new double[] {power})); Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); long cpuTime = 0; long cpuFgTime = 0; long gpsTime = 0; if (processStats.size() > 0) { // Process CPU time for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent : processStats.entrySet()) { if (DEBUG) Log.i(TAG, "Process name = " + ent.getKey()); Uid.Proc ps = ent.getValue(); final long userTime = ps.getUserTime(which); final long systemTime = ps.getSystemTime(which); final long foregroundTime = ps.getForegroundTime(which); cpuFgTime += foregroundTime * 10; // convert to millis final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis int totalTimeAtSpeeds = 0; // Get the total first for (int step = 0; step < speedSteps; step++) { cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which); totalTimeAtSpeeds += cpuSpeedStepTimes[step]; } if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1; // Then compute the ratio of time spent at each speed double processPower = 0; for (int step = 0; step < speedSteps; step++) { double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds; processPower += ratio * tmpCpuTime * powerCpuNormal[step]; } cpuTime += tmpCpuTime; power += processPower; if (highestDrain < processPower) { highestDrain = processPower; packageWithHighestDrain = ent.getKey(); } } if (DEBUG) Log.i(TAG, "Max drain of " + highestDrain + " by " + packageWithHighestDrain); } if (cpuFgTime > cpuTime) { if (DEBUG && cpuFgTime > cpuTime + 10000) { Log.i(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); } cpuTime = cpuFgTime; // Statistics may not have been gathered yet. } power /= 1000; // Add cost of data traffic power += (u.getTcpBytesReceived(mStatsType) + u.getTcpBytesSent(mStatsType)) * averageCostPerByte; // Process Sensor usage Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats(); for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry : sensorStats.entrySet()) { Uid.Sensor sensor = sensorEntry.getValue(); int sensorType = sensor.getHandle(); BatteryStats.Timer timer = sensor.getSensorTime(); long sensorTime = timer.getTotalTimeLocked(uSecTime, which) / 1000; double multiplier = 0; switch (sensorType) { case Uid.Sensor.GPS: multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON); gpsTime = sensorTime; break; default: android.hardware.Sensor sensorData = sensorManager.getDefaultSensor(sensorType); if (sensorData != null) { multiplier = sensorData.getPower(); if (DEBUG) { Log.i(TAG, "Got sensor " + sensorData.getName() + " with power = " + multiplier); } } } power += (multiplier * sensorTime) / 1000; } // Add the app to the list if it is consuming power if (power != 0) { BatterySipper app = new BatterySipper(packageWithHighestDrain, DrainType.APP, 0, u, new double[] {power}); app.cpuTime = cpuTime; app.gpsTime = gpsTime; app.cpuFgTime = cpuFgTime; mUsageList.add(app); } if (power > mMaxPower) mMaxPower = power; mTotalPower += power; if (DEBUG) Log.i(TAG, "Added power = " + power); } }
ちょっと長ったらしいんだけど、上から順に
・CPU使用時間
・TCPのrecv/sendの量
・sensor使用時間
を計算してる。
計上されていくのは「power」って子。
そいつを最後にBatterySipperってのに入れてリスト化。
そいつはどこにいくのかっていうと、
private void refreshStats() { ....省略 processAppUsage(); processMiscUsage(); mAppListGroup.setOrderingAsAdded(false); Collections.sort(mUsageList); for (BatterySipper sipper : mUsageList) { if (sipper.getSortValue() < MIN_POWER_THRESHOLD) continue; final double percentOfTotal = ((sipper.getSortValue() / mTotalPower) * 100); if (percentOfTotal < 1) continue; PowerGaugePreference pref = new PowerGaugePreference(this, ....省略 if (DEBUG) setTitle("Battery total uAh = " + ((mTotalPower * 1000) / 3600));
sipperっていうのが、BatterySipperのインスタンスで、
sipper.getSortValue()で突っ込んだ「power」が取得できる。
DEBUGで切られてるけど、mTotalPowerが総Ahとすると、
final double percentOfTotal = ((sipper.getSortValue() / mTotalPower) * 100);
ここでパーセント計算してるかな。
とすると、
結局「電池使用量」さんは
・CPU使用時間
・TCPのrecv/sendの量
・sensor使用時間
をしっかり見てることになる。
ここを目安に、アプリのお行儀を調べるのもありじゃないかなー?
ブラウザの「ページを共有」からURLを取り出してみる
Tech Boosterに投稿するつもりだったが、あえなくボツとなってしまったネタ。このままお蔵入りおもったいないので、ここに残す。
Androidでは、暗黙的Intentを用いて、アプリ間でのデータの受け渡しを行うことが可能です。
ここではその一例として、ブラウザのページの共有機能を用いて、ウェブページのURLを取得する方法をご紹介します。
早速、まずはIntentの送信側を見てみましょう。
//Ex.)1
public static final void sharePage(Context c, String title, String url,
Bitmap favicon, Bitmap screenshot) {
Intent send = new Intent(Intent.ACTION_SEND);・・・①
send.setType("text/plain");
send.putExtra(Intent.EXTRA_TEXT, url);・・・②
send.putExtra(Intent.EXTRA_SUBJECT, title);
send.putExtra(Browser.EXTRA_SHARE_FAVICON, favicon);
send.putExtra(Browser.EXTRA_SHARE_SCREENSHOT, screenshot);
try {
c.startActivity(Intent.createChooser(send, c.getString(
R.string.choosertitle_sharevia)));
} catch(android.content.ActivityNotFoundException ex) {
// if no app handles it, do nothing
}
}
こちらは標準ブラウザのソースコードの一部で、メニューから「ページを共有」を選択した際に呼び出される処理です。
②でIntentにURLを格納しているのがわかります。
今回、受信側では、ここで格納されているURLを取り出します。
では、受信側の処理を見てみましょう。
まず、「ページを共有」から起動できるようにするために、AndroidManifest.xmlを以下の記述を追加します。
//Ex.)2
これで、ページを共有の一覧に自分のアプリのアイコンが表示されます。
暗黙的Intent(送信側アプリ)では、受信側アプリの
今回は、送信側(Ex.)1)で、actionにACTION_SENDを、mimeTypeにtext/plainをそれぞれ設定していますので、受信側でも同様に、actionにACTION_SEND、mimeTypeにtext/plainを設定しています。
ちなみに、暗黙的Intentを用いて画像のギャラリーアプリケーションを呼び出したい場合は、actionにACTION_PICK、mimeTypeにimage/*を設定することで呼びだすことができました。
では、受信したintentからURLを取り出します。その前にex.)3を見てみましょう。
//Ex.)3
ここでは、Activityが2つ定義されていますが、アプリケーションを通常起動した場合は「Sample01Activity」に、ページの共有からアプリケーションを起動した場合は「Sample02Activity」が、それぞれ起動するようになっています。
によって、どのタイミングで起動させたいかを定義しているのです。
さて、今回はページの共有からの暗黙的Intentによる呼び出しなので、Sample02Activityが起動します。このActivity内でIntentからURLを取り出し、取り出したURLをTextViewで表示してみます。
以下がその一部です。
//Ex.)4
if(Intent.ACTION_SEND.equals(getIntent().getAction())){
//URLを取り出す
CharSequence uri = getIntent().getExtras().getCharSequence(Intent.EXTRA_TEXT); ・・・①
if(uri != null){
TextView textView = new TextView(this);
textView.setText((String)uri);・・・②
layout.addView(textView);
}
}
①でEXTRA_TEXTをキーにURLを取得しています。ここで取得したURLはオブジェクト型なので、今回はTextViewで文字列として表示するため、②でString型でキャストしています。
今回は例として、「ブラウザアプリ」からページを共有機能を用い、「自作アプリケーション」でURLを受け取ってみました。このように、暗黙的Intentを用いれば、同アプリ内でのActivity間だけでなく、アプリケーション間でのオブジェクトの受け渡しを行うことが可能です。