忍者ブログ
  • 2024.10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 2024.12
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

【2024/11/23 02:23 】 |
Android.mkの解読
以下の記述はセットで機能する。

$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/external)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos)

$(call import-module,.)
$(call import-module,Box2D)
$(call import-module,extensions)




import-add-path に書かれたパスから、
import-module に書かれたディレクトリ下のAndroid.mkをインポートする。

上記だと、
cocos2d/external/Box2D/Android.mk
cocos2d/extensions/Android.mk
などがインポートされることになる。

それぞれのAndroid.mkにもまた
読み込むソースファイルやモジュール、ライブラリが記述してあるわけだ






LOCAL_WHOLE_STATIC_LIBRARIES
リンカの–whole-archiveオプションのこと。
これを指定した静的ライブラリは、ライブラリ全体が実行ファイルに含まれることになる(必要な部分だけをstripするということをしなくなる)
参考

最新のcocos2d-xのデフォルトプロジェクトでは、この指定はなくなっている
PR
【2016/02/20 20:48 】 | Android | 有り難いご意見(0)
AndroidStudioでcocos2d-xのプロジェクトを動かした話
iOS版はすでに動いている

cocos2d-x バージョン3.7
AndroidStudio バージョン1.3 (作業中に1.5.1にアップデート)

AndroidStudioに対応したcocos2d-xのバージョンだと、プロジェクトのディレクトリに
proj.android-studio/
が作られている。



まずはAndroidStudioを開いてみる

ディレクトリは前述の場所を選択。

すると以下のエラーが出た


どうやらJDKの指定が悪い様子。

JDKのパスを確認する方法は

を選択。

すると以下のような画面になる


下部にエラー内容が書いてある。
"Please choose a valid JDK directory."

つまり、以下のパスを修正しろということだ
/System/Library/Java/JavaVirtualMachine/1.6.0.jdk/Contents/Home

このパスが間違っているわけではないはずなのだが(Eclipseの時はこれで動いていたので)
変えた方が良さそうだ。

ひとまずjdkの最新をダウンロードしてみる。

ダウンロードページ



ダウンロードされた場所は以下。
/Library/Java/JavaVirtualMachines/jdk1.8.0_73.jdk/Contents/Home

このパスを指定すると、エラーが消えた。


ついでに、Android SDK のパスも変更。
adt-bundle-mac-x86_64-20140321
になっているが、昔のeclipseのダウンロードした時のままなのであまりふさわしくない
(中にAPI level別のディレクトリが入っているし)


変更。
この場合、
~/.bash_profile
の中身の
ANDROID_SDK_ROOT
を書き換え、
$ source ~/.bash_profile
を実行する。

また、ポップアップに従って、AndroidStudioをアップデートした。



次に、
Android.mkの指定をする必要がある。

LOCAL_SRC_FILESの部分は変更が必須。
Classesディレクトリ以下のすべての.cppと.hを正しくビルドに含めるために、以下のように記述。

CPP_FILES := $(shell find $(LOCAL_PATH)/../../../Classes -name *.cpp)
LOCAL_SRC_FILES := hellocpp/main.cpp
LOCAL_SRC_FILES += $(CPP_FILES:$(LOCAL_PATH)/%=%)






また、AndroidStudioからディレクトリ階層が一つ深くなっているので、そこも変更
なおこのコマンドが実行される時のカレントディレクトリ(Android.mkのある場所)は
proj.android-studio/app/jni/
である。


これでビルドしてみる。


cocos compile -s path/to/project -p android --android-studio --debug






コンパイルして実行もする場合は以下のように

cocos run -p android --android-studio



ANDROID PLATFORM(android SDK バージョン)を、AndroidStudioで使用しているのと違うものを指定したい場合は、ap オプションで指定できる

cocos run -p android --ap 20







が、エラーが結構出る。

特に困るのが、stringクラスが使えないこと。

error: 'to_string' is not a member of 'std'
みたいな。

NDKのSTLに含まれていないらしい。なんと不便な
to_stringだけなら自前実装もできるが、stolとか軒並み駄目なので、なんとかしたい。

Application.mkの先頭行を

APP_STL := gnustl_static





から

APP_STL := c++_static





に書き換えると、できるようになる。
参考ページ
指定できるライブラリ一覧


ライブラリを追加する方法は

で選択。
決定すると、自動でダウンロード、ビルドされる。



また、build.gradleにも自動で書き込まれている。




しかし、これもエラーが。
No resource found the matches the given name 'android:TextAppearance.Material.Widget.Button.Inverse'

原因は、APIレベルがあっていないことらしい

以下の対応をしたら直った
・Project Structureウィザードで、プロジェクトのBuildToolVersionを23.0以上にする
・build.gradleに以下を記述
compile 'com.android.support:support-v4:23.1.1'

参考




これらを対応してようやっとビルドに成功。

しかしまだ気になるワーニングがある


The NDK version is not r10c or above.
Your application may crash or freeze on Android L(5.0) when using BMFont and HttpClient.
For More information:
https://github.com/cocos2d/cocos2d-x/issues/9114
https://github.com/cocos2d/cocos2d-x/issues/9138

Your application may crash when using c++ 11 regular expression with NDK_TOOLCHAIN_VERSION 4.8




Project Structureを見ると、ご丁寧にNDKのダウンロードができるようになっている。
クリックして、ダウンロードする(10分ほどかかる)

すると、自動的にディレクトリがセットされる
(ダウンロードディレクトリは、ANDROID_SDK_ROOT/ndk-bundle になる)

このディレクトリをbash_profileのNDK_ROOTに書き、
$ source ~/.bash_profile
を実行すれば、ワーニングは消えた


【2016/02/20 19:03 】 | Android | 有り難いご意見(0)
admobのアプリ内課金向け広告のフォーマット
以前admobから告知があった(そして本社に行った)、
アプリ内課金と広告をユーザに応じて出しわける例の技術。
名前は「アプリ内課金向け広告」というらしいが。

公式

現時点でまだベータ版なのと、
Android版だけのサービスなので(GooglePlayで検索して出てくるアプリにしか使えないと明記されている)、とりあえず実装は見送り
【2016/01/18 18:06 】 | Android | 有り難いご意見(0)
LG-D620Jをデバッグに使う
後から思い出しながらなので、抜けがあるかもしれないが。
なお結論としては、直接Macとつないでのデバッグは不可。ログを見たりはできない。
ネットワークからapkをダウンロードして動作確認しかできない。


1.
端末を初期化して、Gmailアカウントを設定。
このアカウントは、テスト用(主に課金テスト)に使う物。
自分の普段使うアカウントではない。

2.
設定アプリ→端末情報→ソフトウェア情報→ビルド番号を連打。これで開発者オプションを有効にする

3.
設定アプリ→PC接続→USB接続方法の選択→充電(気持ち的にはテザリングのような気がするが)

4.
http://www.lg.com/jp/support-mobile/lg-D620J
からUSBドライバーをインストール。再起動

5.
USBケーブルで繋いだら、あとは出来る


はずだったのだが、ドライバーがMac OS 10.11 (El Capitan)に対応していない!
そのため

$ adb devices
で表示されない…

$ adb install XXX.apk
ももちろんできない…



○繋いでいるはずのandroid端末がadbから見えない時の対処

ここからは、以下のページを見てやってみる
参考ページ


まずは端末をmacに繋ぐ
左上のリンゴ→このMacについて→システムレポートをクリック
USBの項目を見る
ここの、製造元ID(Vendor ID)をチェック。


この16進数を、以下のファイルに書き込む
$ echo 0x1001 >> ~/.android/adb_usb.ini

これで、adbを再起動し、再度チェック
$ adb kill-server
$ adb devices

これで出る!

はずだが、やはり出なかった

ドライバーが古いせいなのか…


【2016/01/08 16:01 】 | Android | 有り難いご意見(0)
gradleの書き方メモ
jarファイルはこの位置に書く



dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile 'com.android.support:support-v4:22.2.0'
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.google.android.gms:play-services:7.5.0'
}



【2015/12/11 22:17 】 | Android | 有り難いご意見(0)
AndroidStudio
Android Studioを導入。
ずっとEclipse+ADTでやってきたが、これを機にやっとこさAndroidStudioに入る。
こういうきっかけがないとなかなか始めない(今まで作ってきたゲームの改良続きだと、環境を変えないでも十分すぐに作れてしまうし)

というわけでまずはダウンロード

パッケージが展開されるところまでは問題ないだろう。

Android Studioを起動するとWelcome画面になる。

今回は、gradleを使用したサンプルプロジェクトが用意されていたので、
以下のように、gradleプロジェクトをインポートする。



選択するディレクトリはココ。
appやgradleディレクトリ、settings.gradleの1つ上。


すると、こんな感じでプロジェクトが開く。
ちなみに赤丸囲みのところ、初めは「Android」になっているが、
「Project」に変えておくと、実際のディレクトリ構造と同じように表示される。慣れるまではこちらのほうがよさそうだ。ディレクトリ追加などもここからできるようになる。



さて、さっそくビルドしようとすると(というかバックグラウンドで勝手にビルドしてる)
いくつかエラーが発生。



うむ。SDKのandroid-22がないと。
リンクを踏むと、勝手にインストールし始める。



簡単だ!
Eclipseのときとは大違い。


再度ビルドするが、またエラー。


なるほど。ツールのほうもないと。
ではリンクを踏みましょう。



で完了。
うう〜ん、べんり!

こんな感じで、エラーを解決させていけば、ビルドできた。


ちなみに、ビルド設定などは build.gradle に書く。
依存関係など。


見て分かるように、SDKバージョンもbuild.gradleに書くようになっている。
AndroidManifest.xml には書かない!

とりあえず、いろいろ分かりやすくなっているぞ!


【2015/08/25 11:28 】 | Android | 有り難いご意見(0)
Androidで、コード上でビューを座標指定して表示させる方法
Androidで、ビューをpx指定で表示したいことがある。

当たり前のやり方のような気がするが、意外と方法が用意されていない。
iOSなら、view.frameやview.boundsで矩形を指定することができるのだが、Androidはそれがない。


Androidは、ビューに加えて、レイアウト(FrameLayout,LinearLayout,RelativeLayoutなど)という概念がある。
(正確にはレイアウトもビューなのだが。)
ビューはレイアウトの中に配置される。

そのビューを、レイアウトから上下左右のマージンいくつ離すか、という指定をすることになる。

本来これでいいはずなのだが、
実際にビューを配置してみると、狙った位置と大きさも位置もずれていることがある。


なぜか。

マージンを指定することによって、矩形の大きさが決まる。
例えば、幅640,高さ240のLayoutの中に、左右マージン各20,上下マージン各10を取ると、
ビューのRect(x,y,w,h)=(20,10,600,220)になる。

しかし、ビューが実際に正確にその大きさで配置されるとは限らない。

たとえば、ビューの幅、高さの指定がWRAP_CONTENTだと、矩形のサイズはあまり関係なくビューの「見た目の大きさ」は決まってしまう。

また、MATCH_PARENTだとしても、
たとえばImageViewなら、元画像の縦横比を崩さない範囲で最大サイズ表示、のようなことになるからだ。
また、広告SDKで表示するアイコンなど、実際の表示サイズが不定になるものの場合もある。

そのようなとき、ビューは大抵左上寄せの位置に表示される。

これは嫌だ。指定座標を中心とする位置に収まってほしい。


じゃあ、gravityをCENTERに指定すれば…?
としたいが、gravity指定はビューにはできず、Layoutに指定する。
gravityは、そのレイアウト全体の重力場を指定するためのものだからだ。

ビューを中央寄せにする指定をビュー自体に対してできないか…となる。
もちろんその指定はあって、xmlでいうと
centerHorizontal
などの指定がそれにあたる。これは、
「自分の親のレイアウトの左右方向中心に配置」
ということだ。

……ということは、自分の親のレイアウトが必要になる。
何も考えずそのままcenterHorizontal,centerVerticalを指定したら、大本のレイアウト(おそらく画面全体を覆うRelativeLayoutになっているだろう)の中心に行ってしまう。それはスクリーンの中心だ。
そうじゃない。

さっき指定した矩形の中心にいてほしいのだ!


…………ということは、さっきの指定の矩形のレイアウトを作成すればいいんだ。


大本のRelativeLayout
+-- 矩形Layout
+-- ビュー(中央寄せで配置)




という二重構造にすればよい。




private void addChildView(View parent, View view, Rect rect) {
int x = rect.left;
int y = rect.top;
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;

// 中間レイアウトを作成。正確にrectの範囲を覆うRelativeLayout。
RelativeLayout positioner = new RelativeLayout(getApplicationContext());

// 中間レイアウトのレイアウトパラメータ
RelativeLayout.LayoutParams lp1 = new RelativeLayout.LayoutParams(width, height);
lp1.leftMargin = x;
lp1.topMargin = y;

// 中間レイアウトを親ビューの子にする
parent.addView(positioner, lp1);

// 目的のビューのレイアウトパラメータを作成。今作った中間ビューの中心に配置
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp2.addRule(RelativeLayout.CENTER_VERTICAL);
lp2.addRule(RelativeLayout.CENTER_HORIZONTAL);

// 目的のビューを中間レイアウトの子にする
positioner.addView(view, lp2);
}






こういう感じになる。

中間レイアウトはRelativeLayoutでなければダメで、他のレイアウトだとマージン指定が効かない。
FrameLayoutやLinearLayoutにすると表示位置が左上固定になってしまう。

RelativeLayoutなので、レイアウトパラメータもRelativeLayout.LayoutParamsにする。



ビューのレイアウトパラメータもRelativeLayout.LayoutParamsにする。
でないとaddRule()メソッドが使えない。(だからCENTER_HORIZONTAL/VERTICAL指定ができない)

FrameLayoutには
lp2.gravity = Gravity.CENTER;
のような指定もできるが、これは自分の子孫に対して効かせるパラメータなので、ビュー自身を中央寄せしたい場合は意味が無い。




というわけで、
=================================
中間レイアウトはRelativeLayout
中間レイアウトのパラメータはRelativeLayout.LayoutParams
ビューのパラメータもRelativeLayout.LayoutParams
=================================
となる。



ちなみにレイアウトパラメータ類の子孫関係は以下

ViewGroup.LayoutParams
+-- ViewGroup.MarginLayoutParams
+-- FrameLayout.LayoutParams
+-- RelativeLayout.LayoutParams
+-- LinearLayout.LayoutParams



どのレイアウトパラメータも、コンストラクタの基本はwidthとheightの指定(int)である。
lp1のように、幅と高さを直に設定することもできるが、
多くの場合はlp2のように MATCH_PARENT(-1) か WRAP_CONTENT(-2) という特殊な値を入れて使うことになる。

【2015/05/25 20:25 】 | Android | 有り難いご意見(0)
Androidでローカル通知
こんな感じ。


private int localNotificationTag = 0;
private int cancelledLocalNotificationTag = 0;
private final String LOCAL_NOTIFICATION_TAG_PREFERENCE_KEY = "local_notification_tag";
private final String CANCELLED_LOCAL_NOTIFICATION_TAG_PREFERENCE_KEY = "cancelled_local_notification_tag";

// ローカル通知レシーブ用のクラス
// マニフェストにも記述のこと。また、必ずstaticをつけて宣言すること
public static class LocalNotificationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {

int notificationId = intent.getIntExtra("notification_id", 0);
String message = intent.getStringExtra("message");

Intent intent2 = new Intent(context, Cocos2dxActivity.class);
intent2.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent2,
PendingIntent.FLAG_UPDATE_CURRENT);

Bitmap largeIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle(context.getString(R.string.app_name));
builder.setContentText(message);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setLargeIcon(largeIcon);
builder.setTicker(message);
builder.setAutoCancel(true);
builder.setDefaults(Notification.DEFAULT_ALL);
builder.setContentIntent(pendingIntent);

NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(notificationId, builder.build());
}
}

// 指定秒後の時刻をミリ秒で返す
private long getTimeInMillisSinceNow(int secSinceNow) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, secSinceNow);
return calendar.getTimeInMillis();
}

// ローカル通知用のPendingIntentを作成する
private PendingIntent makeLocalNotificationPendingIntent(String message, int tag) {
Intent intent = new Intent(getApplicationContext(), LocalNotificationReceiver.class);
intent.putExtra("notification_id", tag);
intent.putExtra("message", message);
PendingIntent sender = PendingIntent.getBroadcast(this, tag, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return sender;
}

// ローカル通知用のPendingIntentを返す(データは入れない)
private PendingIntent getLocalNotificationPendingIntent(int tag) {
Intent intent = new Intent(getApplicationContext(), LocalNotificationReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(this, tag, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return sender;
}

private static void InitLocalNotification() {
instance.localNotificationTag = LoadInt(instance.LOCAL_NOTIFICATION_TAG_PREFERENCE_KEY, 0);
instance.cancelledLocalNotificationTag = LoadInt(instance.CANCELLED_LOCAL_NOTIFICATION_TAG_PREFERENCE_KEY, 0);
}

// 指定秒後に指定の文字列でローカル通知を行う
public static void Native_scheduleLocalNotification(int secSinceNow, String text) {
PendingIntent sender = instance.makeLocalNotificationPendingIntent(text, ++instance.localNotificationTag);

long time = instance.getTimeInMillisSinceNow(secSinceNow);
AlarmManager am = (AlarmManager) instance.getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, time, sender);

SaveInt(instance.LOCAL_NOTIFICATION_TAG_PREFERENCE_KEY, instance.localNotificationTag);
}

// 登録されているローカル通知をすべて削除する
public static void Native_unscheduleAllLocalNotifications() {
AlarmManager am = (AlarmManager) instance.getSystemService(ALARM_SERVICE);

for (int tag = instance.cancelledLocalNotificationTag + 1; tag <= instance.localNotificationTag; ++tag) {
PendingIntent sender = instance.getLocalNotificationPendingIntent(tag);
am.cancel(sender);
}
instance.cancelledLocalNotificationTag = instance.localNotificationTag;
SaveInt(instance.CANCELLED_LOCAL_NOTIFICATION_TAG_PREFERENCE_KEY, instance.cancelledLocalNotificationTag);
}





AndroidManifest.xmlには

<receiver
android:name="***.***.MainActivity$LocalNotificationReceiver"
android:process=":remote" />


と記述のこと。


◯内部クラスを使う場合の注意

・内部クラスはstaticをつけて定義する
Javaでは、内部クラスは基本的にstaticをつけて宣言することが推奨。
staticにしていないと、「no empty constructor」という実行時エラーになる。
なぜなら、staticでない内部クラスは、それを包含するクラスからしかインスタンス化できないからだ。

ちなみに、staticでない内部クラスは、自身を包含するクラスへの参照を暗黙的に持つ。アクセスできて便利だが、当然gcを阻害することになる


・AndroidManifest.xmlでは、内部クラスは $ で繋ぐ。
これもJava的な決まり事。

【2015/05/25 19:54 】 | Android | 有り難いご意見(0)
JNIでattempt to use stale local reference 0x1
JNIで

attempt to use stale local reference 0x1


というエラーが出た。

ググってみると、無効になったクラスにアクセスしてるとか、解放された変数に参照しているみたいなことが書いてある。
参考
参考

でも、そんな単純ミスはしていない。
おかしい。

その少し前のワーニングで、
trying to work around app JNI bugs, but didn't find 0xXXXXXXXX in table!
DeleteLocalRef(0xXXXXXXXX) failed to find entry
というようなのが出ている。

で、このポインタが示す変数はなんぞや、というのを出力して見てみたところ、
C++変数から変換したJNIオブジェクトだった。
特に何の問題もなさそう。


さらに発見したのが、

jobject objResult = methodInfo.env->CallStaticObjectMethod(methodInfo.classID , methodInfo.methodID, jarg);


の戻り値のobjResultが0x1になっている。
エラーはこの変数にアクセスした瞬間起きたようだ。


しかし、メソッドシグネチャーも合っているし、なぜ戻り値がこんなことになるのか。



このコードのどこに問題があるのか。


こういう不可解なことが起きるときはスレッドが絡んでいるもの。
そうすると真の原因はここでないまったく別の場所にある可能性がある。

ということで、ともかく例外を出力させてみる。

if (methodInfo.env->ExceptionCheck()) {
methodInfo.env->ExceptionDescribe();
}



すると…

05-25 13:04:16.466: W/System.err(15337): java.lang.IllegalStateException: isLoaded must be called on the main UI thread.



案の定、スレッドのエラーだった。
発生箇所とはこことは無関係の場所。


ここを直したら、無事に動いた。
【2015/05/25 13:28 】 | Android | 有り難いご意見(0)
JNIで不具合起きたら
以下のようにして例外を出力できる

if (methodInfo.env->ExceptionCheck()) {
methodInfo.env->ExceptionDescribe();
}



また、ポインタ変数を確認したいときは以下のようにすればよい。

jstring jarg = methodInfo.env->NewStringUTF(arg.c_str());
jobject objResult = methodInfo.env->CallStaticObjectMethod(methodInfo.classID , methodInfo.methodID, jarg);
printf("classId=%p, jarg=%p, objResult=%p", methodInfo.classID, jarg, objResult);



辿ってみるとわかるが、jstringとかjobjectは全部
_jobject
クラスの子孫になっている。



【2015/05/25 13:19 】 | Android | 有り難いご意見(0)
<<前ページ | ホーム | 次ページ>>