12

LocationManager を利用して高度を取得する Android 2.1 を使用するアプリケーションがあります。しかし今、API レベル 9 (2.3) を必要とする SensorManager を使用して高度を取得する必要があります。

SensorManager.getAltitude(float, float)条件を付けて関数名で呼び出すことで、2.1 android アプリケーションにを配置するにはどうすればよいですか (通常の Java で可能)。

前もって感謝します

更新 1 Android 2.1 を使用してアプリケーションをコンパイルする必要があることに気付いた場合。そのため、関数を名前で呼び出す方法、またはコンパイルできる他の方法で呼び出す方法を探しています。

4

6 に答える 6

22

必要な最高の API に対してビルドし、サポートしたい他のレベルの代替コード パスを条件付きでコーディングする必要があります。

実行時に現在の API レベルを確認するには、Android ドキュメントからの最新の推奨事項は、次のようにすることです。

    if(Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD)
    {
        ...

ただし、この複雑さを導入すると、非常に注意する必要があります。minSdkVersion より上のすべての API レベルの呼び出しに、すべてのバージョンをサポートするための代替呼び出しがあることを確認するために、すべてのコード パスを自動的にチェックする方法は現在のところありません。このようなことを行う単体テスト ツールがあれば、誰かが協力してくれるかもしれません。

于 2011-09-14T01:20:31.773 に答える
9

リフレクションを使用してメソッドを呼び出し、エラー(クラスやメソッドの欠落など)が発生した場合に正常に失敗する可能性があります。java.lang.reflectを参照してください

他のオプションは、レベル9でコードをコンパイルすることですが、下位レベルでの実行から発生するエラーをキャッチするためにtry/catchで囲みます。しかし、それはかなりエラーが発生しやすい可能性があり、私はそれを行うことについて2度考えます。


アップデート

これがテストコードです

public void onCreate(Bundle savedInstanceState)
{
    try {
        // First we try reflection approach.
        // Expected result
        //    in 2.3 we print some value in log but no exception
        //    in 2.2 we print NoSuchMethodException
        // In both levels we get our screen displayed after catch
        Method m = SensorManager.class.getMethod("getAltitude",Float.TYPE, Float.TYPE);
        Float a = (Float)m.invoke(null, 0.0f, 0.0f);
        Log.w("test","Result 1: " + a);
    } catch (Throwable e) {
        Log.e("test", "error 1",e);
    }

    try {
        // Now we try compiling against 2.3
        // Expected result
        //    in 2.3 we print some value in log but no exception
        //    in 2.2 we print NoSuchMethodError (Note that it is an error not exception but it's still caught)
        // In both levels we get our screen displayed after catch
        float b = SensorManager.getAltitude(0.0f, 0.0f);
        Log.w("test","Result 2: " + b);

    } catch (Throwable e) {
        Log.e("test", "error 2",e);
    }

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
}

結果:

2.3

09-14 07:04:50.374: DEBUG/dalvikvm(589): Debugger has detached; object registry had 1 entries
09-14 07:04:50.924: WARN/test(597): Result 1: NaN
09-14 07:04:51.014: WARN/test(597): Result 2: NaN
09-14 07:04:51.384: INFO/ActivityManager(75): Displayed com.example/.MyActivity: +1s65ms

2.2

09-14 07:05:48.220: INFO/dalvikvm(382): Could not find method android.hardware.SensorManager.getAltitude, referenced from method com.example.MyActivity.onCreate
09-14 07:05:48.220: WARN/dalvikvm(382): VFY: unable to resolve static method 2: Landroid/hardware/SensorManager;.getAltitude (FF)F
09-14 07:05:48.220: DEBUG/dalvikvm(382): VFY: replacing opcode 0x71 at 0x0049
09-14 07:05:48.220: DEBUG/dalvikvm(382): VFY: dead code 0x004c-0064 in Lcom/example/MyActivity;.onCreate (Landroid/os/Bundle;)V
09-14 07:05:48.300: ERROR/test(382): error 1
        java.lang.NoSuchMethodException: getAltitude
        at java.lang.ClassCache.findMethodByName(ClassCache.java:308)

スキップされたスタックトレース

09-14 07:05:48.300: ERROR/test(382): error 2
    java.lang.NoSuchMethodError: android.hardware.SensorManager.getAltitude
    at com.example.MyActivity.onCreate(MyActivity.java:35)

より多くのスタックトレースをスキップしました

09-14 07:05:48.330: DEBUG/dalvikvm(33): GC_EXPLICIT freed 2 objects / 64 bytes in 180ms
09-14 07:05:48.520: INFO/ActivityManager(59): Displayed activity com.example/.MyActivity: 740 ms (total 740 ms)
于 2011-09-14T01:11:23.843 に答える
4

リフレクションを必要としない簡単な回避策として、アクセスされるまでクラスがロードされない方法を利用できます。新しい API を使用するには、静的メソッドで内部クラスを使用します。簡単な例を次に示します。

public static String getEmail(Context context){
    try{
        if(Build.VERSION.SDK_INT > 4) return COMPATIBILITY_HACK.getEmail(context);
        else return "";
    }catch(SecurityException e){
        Log.w(TAG, "Forgot to ask for account permisisons");
        return "";
    }
}


//Inner class required so incompatibly phones won't through an error when this class is accessed. 
    //this is the island of misfit APIs
    private static class COMPATIBILITY_HACK{

        /**
         * This takes api lvl 5+
         * find first gmail address in account and return it
         * @return
         */
        public static String getEmail(Context context){
            Account[] accounts = AccountManager.get(context).getAccountsByType("com.google");
            if(accounts != null && accounts.length > 0) return accounts[0].name;
            else return "";
        }
     }
于 2011-09-14T01:25:07.107 に答える
3

「このクラスまたはメソッドは現在のAPIレベルにありますか?」という質問の場合。次に、次のような分岐を使用します。

class SomeClass {
    public void someMethod(){
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD)
        {
           //use classes and/or methods that were added in GINGERBREAD
        }
    }
}

このためには、Gingerbread以上のAndroidライブラリを使用する必要があります。そうしないと、Gingerbreadで追加されたクラスでコードがコンパイルされません。

このソリューションは、嫌な反射のものよりもはるかにクリーンです。dalvikは、SomeClassを読み込もうとしたときに、GINGERBREADに追加されたクラスが見つからないことを示す(致命的ではない)エラーをログに記録しますが、アプリはクラッシュしないことに注意してください。その特定のクラスを使用してIFブランチに入ろうとした場合にのみクラッシュしますが、それは行いません(GINGERBREAD以降を使用している場合を除く)。

このソリューションは、永遠に存在するクラスがあるが、Gingerbreadに新しいメソッドが追加された場合にも機能することに注意してください。実行時に、ジンジャーブレッド以前で実行している場合は、IFブランチに入らず、そのメソッドを呼び出さないため、アプリがクラッシュすることはありません。

于 2012-10-25T12:49:14.027 に答える
0

私はそれを試していませんが、いくつかのコード生成を使用して、ネイティブ android.jar 全体をラップし、その実装が android.jar からメソッドを呼び出そうとするプロキシ ライブラリを (API レベルごとに) 作成することは可能です。 .

このプロキシ lib は、上記の internal-static-class 方法またはリフレクションのいずれかを使用して、dalvikvm を要求されたメソッドに遅延リンクさせます。

これにより、ユーザーは必要なすべての API にアクセスできるようになり (正しい API レベルを確認すると仮定して)、不快な dalvikvm ログ メッセージが表示されるのを防ぐことができます。各メソッドの API レベルを埋め込んで、使用可能な例外 (BadApiLevelException など) をスローすることもできます。

(Google/Androidがまだそのようなことをしていない理由を知っている人はいますか?)

于 2011-10-10T18:21:39.023 に答える
0

ここでは、リフレクションを使用してそれを行う方法を示します (使用できないレベルから StrictMode クラスを呼び出す:

  try {
          Class<?> strictmode = Class.forName("android.os.StrictMode");
          Method enableDefaults = strictmode.getMethod("enableDefaults");
          enableDefaults.invoke(null, new Object[] {});
  } catch (Exception e) {
          Log.i(TAG, e.getMessage());
  }
于 2011-09-14T02:51:36.517 に答える