スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。



[Android] AdMob SDK6.2.1をbuild.xmlでビルドするとPorguardエラーが発生する

AndroidでAdMobを使用しているアプリをビルドするとエラーが発生する。
エラー内容は...

Proguard returned with error code 1. See console
Warning: com.google.ads.m: can't find referenced class com.google.ads.internal.state.AdState
Warning: com.google.ads.m: can't find referenced class com.google.ads.internal.state.AdState
You should check if you need to specify additional program jars.
Warning: there were 2 unresolved references to classes or interfaces.
You may need to specify additional library jars (using '-libraryjars').
java.io.IOException: Please correct the above warnings first.
at proguard.Initializer.execute(Initializer.java:321)
at proguard.ProGuard.initialize(ProGuard.java:211)
at proguard.ProGuard.execute(ProGuard.java:86)
at proguard.ProGuard.main(ProGuard.java:492)



このエラーを回避する方法。
(AdMob SDKの公式掲示板)

https://groups.google.com/forum/#!msg/google-admob-ads-sdk/JllLSIXWtBk/ioXzTWKoYgMJ

6.2.1 still has this problem,
I add "-dontwarn com.google.ads.**" in the proguard-project.txt according to Eric's suggestion.
And it seems no problem now.



progurad-project.txtに「-dontwarn com.google.ads.**」を追加することで問題を解消することができる。
スポンサーサイト

テーマ : ゲーム開発
ジャンル : コンピュータ




ここへ来てANR...

ANR(Application Not Responding)が発生した。
アプリが固まったように見えるというものです。

ANRが発生するとtraces.txt(/data/anr/traces.txt)が出力されるので
その内容をみても何が問題かわからない。

内容は

DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0 hwl=0 hwll=0)
"main" prio=5 tid=1 NATIVE
| group="main" sCount=1 dsCount=0 obj=0x400281b8 self=0xd088
| sysTid=15344 nice=0 sched=0/0 cgrp=default handle=-1345006464
| schedstat=( 125016091399 45731889951 132037 )
at android.graphics.Canvas.native_drawBitmap(Native Method)
at android.graphics.Canvas.drawBitmap(Canvas.java:1045)
at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:325)
at android.graphics.drawable.DrawableContainer.draw(DrawableContainer.java:48)
at android.widget.ImageView.onDraw(ImageView.java:854)
at android.view.View.draw(View.java:6933)
at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
at android.view.View.draw(View.java:6936)
at android.widget.FrameLayout.draw(FrameLayout.java:357)
at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
at android.view.ViewGroup.drawChild(ViewGroup.java:1644)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
at android.view.View.draw(View.java:6936)
at android.widget.FrameLayout.draw(FrameLayout.java:357)
at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1905)
at android.view.ViewRoot.draw(ViewRoot.java:1530)
at android.view.ViewRoot.performTraversals(ViewRoot.java:1266)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1868)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:3691)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:864)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:622)
at dalvik.system.NativeStart.main(Native Method)



画像の描画処理で固まったのか???
原因が全くわからない。

やっとリリースだぁと思ってた矢先。
原因がわからないから対策できないなぁ。



Android アプリ内課金でProguardを使用するとき

Androidのアプリ内課金を実装してProguardを使用するとき,
Proguardの設定ファイルに追記しておいてと言われているものがあります。

公式リファレンスには以下のように書かれています。

Note: If you use Proguard to obfuscate your code, you must add the following line to your Proguard configuration file:
-keep class com.android.vending.billing.**



ここでいう設定ファイルはプロジェクト直下にあるproguard-project.txt。
このファイルにリファレンス通りに設定を書いて難読化してみると
普通に難読化されてました。

へ?

リファレンスを信じて検証していませんでしたが,どうやらこの設定ではGoogleが意図していた
aidlから自動生成されたインタフェースなどが難読化から除外されないようです。

とりあえず直書きで以下のように設定して対応しました。

-keep public interface com.android.vending.billing.IMarketBillingService
-keep public class com.android.vending.billing.IMarketBillingService$Stub
-keep class com.android.vending.billing.IMarketBillingService$Stub$Proxy

Googleの設定ではclassをキープしたかったように見えますが
多分インタフェース名だと思います。
自動生成されたIMarcketBillingServiceの内部クラスStubのメンバにはDESCRIPTORが設定されていて,
ここにインターフェースのパッケージ名が書かれています。

サービス接続時(onServiceConnected)に渡される引数IBinderからインターフェースを検索取得して
IMarketBillingServiceへキャストしている。
難読化しているときはキャスト前の型チェック(instanceof)で失敗していると考えられる。

ただそのときはaidlによって自動生成されたIMarketBillingService.javaに定義されている
Proxyクラスによって購入リクエストが実行できるようになっている...と考えられる。

難読化しても実行できるように組まれているんだけど,
リモート側の実装が変わったときに柔軟に対応できないんじゃないかな。

そのために難読化するときはIMarketBillingService.javaに定義されているクラスは除外してね
と言っているに違いない。
(ただIMarketBillingServiceへキャストできればよいので難読化対象外にするのはIMarketBillingServiceインタフェースだけで良さそう)

aidlで自動生成されたIMarketBillingService.java
/*
 * This file is auto-generated. DO NOT MODIFY. Original file:
 * /Users/junji/Develop
 * /workspace/BillingLibrary/src/com/android/vending/billing/
 * IMarketBillingService.aidl
 */
package com.android.vending.billing;

public interface IMarketBillingService extends android.os.IInterface
{
	/** Local-side IPC implementation stub class. */
	public static abstract class Stub extends android.os.Binder implements com.android.vending.billing.IMarketBillingService
	{
		private static final java.lang.String DESCRIPTOR = "com.android.vending.billing.IMarketBillingService";

		/** Construct the stub at attach it to the interface. */
		public Stub()
		{
			this.attachInterface(this, DESCRIPTOR);
		}

		/**
		 * Cast an IBinder object into an
		 * com.android.vending.billing.IMarketBillingService interface,
		 * generating a proxy if needed.
		 */
		public static com.android.vending.billing.IMarketBillingService asInterface(android.os.IBinder obj)
		{
			if((obj == null))
			{
				return null;
			}
			android.os.IInterface iin = (android.os.IInterface) obj.queryLocalInterface(DESCRIPTOR);

			// ここでリモート・クライアント間共通インターフェースで型チェックの後キャストしている。
			if(((iin != null) && (iin instanceof com.android.vending.billing.IMarketBillingService)))
			{
				return ((com.android.vending.billing.IMarketBillingService) iin);
			}
			return new com.android.vending.billing.IMarketBillingService.Stub.Proxy(obj);
		}

		public android.os.IBinder asBinder()
		{
			return this;
		}

		@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
		{
			switch(code)
			{
				case INTERFACE_TRANSACTION:
				{
					reply.writeString(DESCRIPTOR);
					return true;
				}
				case TRANSACTION_sendBillingRequest:
				{
					data.enforceInterface(DESCRIPTOR);
					android.os.Bundle _arg0;
					if((0 != data.readInt()))
					{
						_arg0 = android.os.Bundle.CREATOR.createFromParcel(data);
					}
					else
					{
						_arg0 = null;
					}
					android.os.Bundle _result = this.sendBillingRequest(_arg0);
					reply.writeNoException();
					if((_result != null))
					{
						reply.writeInt(1);
						_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
					}
					else
					{
						reply.writeInt(0);
					}
					return true;
				}
			}
			return super.onTransact(code, data, reply, flags);
		}

		private static class Proxy implements com.android.vending.billing.IMarketBillingService
		{
			private android.os.IBinder mRemote;

			Proxy(android.os.IBinder remote)
			{
				mRemote = remote;
			}

			public android.os.IBinder asBinder()
			{
				return mRemote;
			}

			public java.lang.String getInterfaceDescriptor()
			{
				return DESCRIPTOR;
			}

			/**
			 * Given the arguments in bundle form, returns a bundle for results.
			 */
			public android.os.Bundle sendBillingRequest(android.os.Bundle bundle) throws android.os.RemoteException
			{
				android.os.Parcel _data = android.os.Parcel.obtain();
				android.os.Parcel _reply = android.os.Parcel.obtain();
				android.os.Bundle _result;
				try
				{
					_data.writeInterfaceToken(DESCRIPTOR);
					if((bundle != null))
					{
						_data.writeInt(1);
						bundle.writeToParcel(_data, 0);
					}
					else
					{
						_data.writeInt(0);
					}
					mRemote.transact(Stub.TRANSACTION_sendBillingRequest, _data, _reply, 0);
					_reply.readException();
					if((0 != _reply.readInt()))
					{
						_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
					}
					else
					{
						_result = null;
					}
				}
				finally
				{
					_reply.recycle();
					_data.recycle();
				}
				return _result;
			}
		}

		static final int TRANSACTION_sendBillingRequest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
	}

	/** Given the arguments in bundle form, returns a bundle for results. */
	public android.os.Bundle sendBillingRequest(android.os.Bundle bundle) throws android.os.RemoteException;
}

難読化したときのclassファイルをリバースエンジニアリングしたとき,以下のとおり。

package a.a.a.a;

import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.Parcelable.Creator;

public abstract class b extends Binder
  implements a
{
  public static a a(IBinder paramIBinder)
  {
    Object localObject;
    if (paramIBinder == null)
      localObject = null;
    while (true)
    {
      return localObject;
      IInterface localIInterface = paramIBinder.queryLocalInterface("com.android.vending.billing.IMarketBillingService");

      // ここでリモート・クライアント間共通インターフェースで型チェックの後キャストしていたが難読化で
	  // クラス名が変更されている。
	  if ((localIInterface != null) && ((localIInterface instanceof a)))
        localObject = (a)localIInterface;
      else
        localObject = new c(paramIBinder);
    }
  }

  public boolean onTransact(int paramInt1, Parcel paramParcel1, Parcel paramParcel2, int paramInt2)
  {
    boolean bool;
    switch (paramInt1)
    {
    default:
    case 1598968902:
      for (bool = super.onTransact(paramInt1, paramParcel1, paramParcel2, paramInt2); ; bool = true)
      {
        return bool;
        paramParcel2.writeString("com.android.vending.billing.IMarketBillingService");
      }
    case 1:
    }
    paramParcel1.enforceInterface("com.android.vending.billing.IMarketBillingService");
    Bundle localBundle1;
    if (paramParcel1.readInt() != 0)
    {
      localBundle1 = (Bundle)Bundle.CREATOR.createFromParcel(paramParcel1);
      label81: Bundle localBundle2 = a(localBundle1);
      paramParcel2.writeNoException();
      if (localBundle2 == null)
        break label122;
      paramParcel2.writeInt(1);
      localBundle2.writeToParcel(paramParcel2, 1);
    }
    while (true)
    {
      bool = true;
      break;
      localBundle1 = null;
      break label81;
      label122: paramParcel2.writeInt(0);
    }
  }
}
ただ外部のサービスからクラス情報までロードされる気がしない。 開発環境でデバッグしてみると結局,難読化されていようがいまいが クラスはキャストされず代理クラスで実行されている。 googleドキュメント側の間違いじゃないか!?



AndroidのHandlerを使用した遅延実行

Javaで遅延実行するときはjava.util.concurrentパッケージのものを使用していけば良いと思うのですが,
せっかくAndroidなのでHandlerで遅延実行できないか検証してみました。

Handlerにも〜Delayed()メソッドがあり,指定時刻に実行できます。
さらにhasMessages()がありキューに既に追加されているかも確認することができます。

以下のようなコードになりました。
注意点はMessage.obtain()でRunnableを渡すとHandler.CallbackのhandleMessage()がコールされません。


public class HandlerCallBackActivity extends Activity implements Handler.Callback, OnClickListener
{
	static final private String TAG = "HandlerCallBackActivity";
	
	private Handler mHandler = new Handler(this);
	private Button mButton = null;
	private Message mMessage = null;
	
	// テキストを設定する
	static final private int HANDLER_SET_TEXT = 1;
	
	@Override public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_handler_call_back);
		
		mButton = (Button)findViewById(R.id.button);
		mButton.setOnClickListener(this);
	}

	@Override public boolean handleMessage(Message msg)
	{
		Log.d(TAG, "handleMessage() msg:" + msg);

		if(msg==null)
		{
			Log.e(TAG, "Invalid param. Message is null.");
			return false;
		}
		
		if(msg.what==HANDLER_SET_TEXT)
		{
			Log.d(TAG, "Message is HANDLER_SET_TEXT. " + msg.obj);
			setTextButton();
		}
		else
		{
			Log.d(TAG, "Not HANDLER_SET_TEXT message.");
		}
		
		return false;
	}

	@Override public void onClick(View v)
	{
		if(v==mButton)
		{
			if(mMessage!=null)
			{
				// 既にメッセージを追加しているとき削除する
				if(mHandler.hasMessages(HANDLER_SET_TEXT, this))
				{
					Log.d(TAG, "remove message.");
					mHandler.removeMessages(HANDLER_SET_TEXT, this);
				}
			}

			// Memo) Callbackが設定されているとhandleMessage()に通知されない。
			//       Handler.dispatchMessage()みるとわかる。
//			mMessage = Message.obtain(mHandler, new Runnable());
			mMessage = mHandler.obtainMessage(HANDLER_SET_TEXT, this);
			
			if(!mHandler.sendMessageDelayed(mMessage, 2000))
			{
				Log.e(TAG, "Failed send message.");
			}
			else
			{
				Log.i(TAG, "Success send message.");
			}
			
			return;
		}
	}
	
	public void setTextButton()
	{
		final long time = System.currentTimeMillis();
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
		mButton.setText(sdf.format(new Date(time)));
	}
}

// MessageQueueの削除条件はobjectを等価でなくて等値として評価している
public class MessageQueue {
	final boolean hasMessages(Handler h, int what, Object object) {
        if (h == null) {
            return false;
        }

        synchronized (this) {
            Message p = mMessages;
            while (p != null) {
                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
                    return true;
                }
                p = p.next;
            }
            return false;
        }
    }
}

これでHandlerを使用して遅延実行ができます。

テーマ : ゲーム開発
ジャンル : コンピュータ




Add Native Supportを使用すると「unresolved inclusion」のエラーがでる

ADT r20からNDKの準備にポップアップメニューから選択して自動で実行することができる。
(Add Native Support)
しかし不具合があって「unresolved inclusion jni.h」のようなエラーがでてしまう。

プロジェクトを閉じてから,再度開くと直ることもあるようですが,
こちらに報告されているように不具合のようです。
(英語が正しく読めていないかもしれないので間違っているかもしれません)

どうもCDT 8.1.x以降とADT r20の組み合わせが悪いようです。
リンク先のコメント#5にも書かれていたように以下の方法で一時的に回避することができるようです。

1.プロジェクトの設定 [C/C++ ビルド]-[ツール・チェーン・エディター]の[現在のツールチェーン]を
 「Android GCC」から「Cygwin GCC」へ変更する。
2.[C/C++ 一般]-[パスおよびシンボル]の[インクルード]にNDKのインクルードパスを設定する。

これでエラーが消える。
リンク先にも書かれているが,既に問題は修正されているようで次のADT r21がリリースされれば
[Add Native Support]は問題なく使えるはず。

ちなみに[Add Native Support]を使ったときに追加されるNDKのパスは以下のとおり。(Macの場合)

/sources/cxx-stl/system/include
jni
/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/lib/gcc/arm-linux-androideabi/4.6.x-google/include
/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/lib/gcc/arm-linux-androideabi/4.6.x-google/include-fixed
/platforms/android-14/arch-arm/usr/include


Androidの開発環境とEclipseの開発環境が平行して進んでいるから
こういった問題がおきるということだろうか...。

Visual Studioのようにワンパッケージで提供してくれないかなw

テーマ : ゲーム開発
ジャンル : コンピュータ




アクセス
あなたは
キーワード
カテゴリー
最近の記事
リンク
月別アーカイブ
ブロとも申請フォーム

この人とブロともになる

WEB検索
Google

RSSフィード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。