2012/10/27

ClipboardManager で NoClassDefFoundError

Java のパッケージは便利なのですが、同じ名前でパッケージが違うと時々混乱します。
Android の ClipboardManager もそんなクラスの一つです。

テキストを Clipboard にコピーする方法として、以下の様なコードがよく提示されています。
あえて import 文を見せているところが重要。
import android.content.ClipboardManager;

// 省略

private void copyToClipboard(String text) {
    ClipboardManager clipboardManager =
            (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    clipboardManager.setText(text);
}


これ、最近のADTでは、"Class requires API level 11 (current min is 7)" というように怒られます。


このエラー、「API11 未満ではこのクラスは提供されていないよ。」という意味ですから、この時点で何かおかしいとは思うわけですが、とりあえず、@TargetApi(11) をつけて問題を回避してみます。
import android.content.ClipboardManager;

// 省略

@TargetApi(11)
private void copyToClipboard(String text) {
    ClipboardManager clipboardManager =
            (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    clipboardManager.setText(text);
}

これを、API11 未満の端末で動作させると、NoClassDefFoundError が発生します。
E/AndroidRuntime(221): java.lang.NoClassDefFoundError: android.content.ClipboardManager

先ほど Eclipse で警告が出ていたように、Class が存在していないようです。

「本に書いてある通りなのにおかしいなぁ」と思ってよく見てみると、ClipboarManager には以下の2種類があるではないですか。

  • android.content.ClipboardManager
  • android.text.ClipboardManager



こういう時に、IDEは便利だなーって思いますね。
つまり、API11 未満と API11 以上では次のように ClipboardManager を呼び分けてやらないといけないわけです。
(Deprecated なのを承知の上で古い方法だけを使うという方法もあります。)

    // 呼び出し元 (詳細省略)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        copyToClipboard("hoge");
    } else {
        copyToClipboardUnder11("piyo");
    }

// 省略

// API11 未満の時に使用
@SuppressWarnings("deprecation")
private void copyToClipboardUnder11(String text) {
    android.text.ClipboardManager clipboardManager =
            (android.text.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    clipboardManager.setText(text);
}

// API11 以上の時に使用
@TargetApi(11)
private void copyToClipboard(String text) {
    android.content.ClipboardManager clipboardManager =
            (android.content.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    ClipData.Item item = new ClipData.Item(text);
    String[] mimeTypes = new String[] {
            ClipDescription.MIMETYPE_TEXT_PLAIN
    };
    ClipData clip = new ClipData("data", mimeTypes, item);
    clipboardManager.setPrimaryClip(clip);
}

API11 以降では ClipboardManager の使用方法も変わっています。
android.content.ClipboardManager は android.text.ClipboardManager を継承しているので、古い使い方も出来ますが、そのような使い方をするメリットはないでしょう。

1 件のコメント:

匿名 さんのコメント...

参考になりました、ありがとうございます