・ContentProviderとは?
ContentProviderを利用すると、異なるアプリケーション間でデータを共有できる。
COntentProviderを利用することで、特定のデータタイプを他のアプリケーションが利用できる。
例えば、Androidで利用されている正式なContentProviderには、電話帳がある。
電話帳には、名前、住所、電話番号がリストで表示されている。
content://contacts/people/
という特定のURIを利用することで、どのアプリケーションであってもこのデータにアクセスすることができる。
androidURIの構成は以下の通り。
スキーム | content:// |
オーソリティ | contacts |
パス | people |
WEBのURIとほぼ同じですね。オーソリティがホストにあたります。ということで、実装して試してみます。
開発用os(jar)は現在メインで開発している2.1、エミュレータは2.3で確認
1、まずはエミュレーターを立ち上げて電話帳にデータを投入
2、では、実装していきます。
2-1、
AndroidManifest.xmlに
<uses-permission android:name="android.permission.READ_CONTACTS" />
を記述。
2-2、
Activityクラスを作成します。
以下ソースコードです。
public class ProviderTest extends ListActivity { public static final String TAG = "ProviderTest"; private ArrayList<ContactPojo> list; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.provider); } @Override public void onStart() { super.onStart(); Log.d(TAG, "onStart start "); ContentResolver resolver = this.getContentResolver(); Cursor c = resolver.query(Contacts.CONTENT_URI, new String [] {Contacts.DISPLAY_NAME} , null, null, null); this.list = new ArrayList<ContactPojo>(); while(c.moveToNext()) { ContactPojo contactPojo = new ContactPojo(); contactPojo.display_name = c.getString(0); this.list.add(contactPojo); } if (c != null) { c.close(); } Log.d(TAG, "onStart end "); } @Override public void onResume() { super.onResume(); setListAdapter(new ProviderTestList(this, R.layout.provider_list, this.list)); } class ProviderTestList extends ArrayAdapter<ArrayList<ContactPojo>> { private Context mContext; private ArrayList<ContactPojo> list; public ProviderTestList(Context context, int textViewResourceId, ArrayList<ContactPojo> list) { super(context, textViewResourceId); this.mContext = context; this.list = list; } @Override public int getCount() { return this.list.size(); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; ViewWapper viewWapper = null; if (row == null) { LayoutInflater inflater = getLayoutInflater(); row = inflater.inflate(R.layout.provider_list, parent, false); viewWapper = new ViewWapper(row); row.setTag(viewWapper); } else { viewWapper = (ViewWapper)row.getTag(); } ContactPojo contactPojo = (ContactPojo)this.list.get(position); viewWapper.getDispName().setText(contactPojo.display_name); contactPojo = null; // release return row; } } /** * ホルダーパターンView */ class ViewWapper { View base; TextView disp_name; ViewWapper (View base) { this.base = base; } TextView getDispName() { if (disp_name == null) { disp_name = (TextView)base.findViewById(R.id.disp_name); } return disp_name; } } }
一覧を表示するように、ListActivityで作成してます。
特に重要なのは
- getContentResolverでContentResolverを取得していること。
- Cursorオブジェクトを取得していること。
続いて表示xmlのprovider.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@id/android:list" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <TextView android:id="@id/android:empty" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="データはありません" /> </RelativeLayout>
加えて、リストxmlのprovider_list.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/disp_name" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </RelativeLayout>
上記のソースをビルドして実行します。
上記のような感じで出力されます。
ContentResolverのqueryで指定してる「Contacts.CONTENT_URI」が電話帳のデータにアクセスする為のURIです。Contacts.CONTENT_URIのURIの形式を取得してみましょう。以下が結果。
- URIスキーム(Contacts.CONTENT_URI.getScheme()):content
- ホスト名(Contacts.CONTENT_URI.getHost()):com.android.contacts
- パス(Contacts.CONTENT_URI.getPath()):/contacts
確認のために、自分でURIを作成してアクセスしてみます。
getContentResolverの後ろのコードに以下の処理を追加します。
ContentResolver resolver = this.getContentResolver(); Uri.Builder builder = new Uri.Builder(); builder.scheme("content"); builder.authority("com.android.contacts"); builder.path("contacts"); //Cursor c = resolver.query(Contacts.CONTENT_URI, new String [] {Contacts.DISPLAY_NAME} , null, null, null); Cursor c = resolver.query(builder.build(), new String [] {Contacts.DISPLAY_NAME} , null, null, null);
ビルドして実行します。
同じ結果になりましたね。
以上の処理から理解できるのは、URIで電話帳にアクセスすることができ、結果をCursorオブジェクトで取得していることですね。
気になるのは、ContentProviderがCursorを返す仕組みになっていることです。
データベースを通さない処理の場合はどうするのでしょうか…。また、intentを利用する仕組みとどう関連があるのか…。
とにかく、ContentProviderは理解したのでLinkifyとintentを関連付けるサンプルを作成していきます。
3、Linkfyサンプルを十分に読み込んで実装していきます。
参照するのは
http://android-developers.blogspot.com/2008/03/wikinotes-for-android-routing-intents.html
です。
まずは文字列リンクを作成します。
getViewメソッドのContactPojoオブジェクトを取り出している下に、以下のコード処理を追加します。
ContactPojo contactPojo = (ContactPojo)this.list.get(position); // viewWapper.getDispName().setText(contactPojo.display_name); Pattern matcher = Pattern.compile("テスト"); viewWapper.getDispName().setText(contactPojo.display_name); String viewURL = "content://jp.co.sample.android.muzukashii/name/"; Linkify.addLinks(viewWapper.getDispName(), matcher, viewURL);
ビルドして実行します。
おお、リンクが作成されましたね。
次はAndroidManifest.xmlに
<provider android:name="jp.co.sample.LinkTestProvider" android:authorities="jp.co.sample.android.muzukashii" />
を追記します。
- android:authoritiesは、URIのホストと同じ意味です。URIのROOTとして扱われます。
- android:nameはjavaクラスのフルパスを指定します。
では、呼び出されるLinkTestProviderクラスを作成していきましょう。このクラスでは、ContentProviderクラスを継承します。
注意すべき点は、updateやinsertをOverrideする必要があるということです。なんかずさんな抽象化ですね。DB使わない人もいるでしょうに。
気持ち悪いけどここは無視して作成します。で、作成したのが以下のコードです。
public class LinkTestProvider extends ContentProvider { public static final String TAG = "LinkTestProvider"; public static final String AUTHORITY = "jp.co.sample.android.muzukashii"; private static final int NOTE_NAME = 2; private static final UriMatcher URI_MATCHER; @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public String getType(Uri uri) { switch (URI_MATCHER.match(uri)) { case NOTE_NAME: return "hatoyama.kan.owawa/minsyu.kiero"; default: throw new IllegalArgumentException("Unknown URL " + uri); } } @Override public Uri insert(Uri uri, ContentValues values) { return null; } @Override public boolean onCreate() { return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } static { URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); URI_MATCHER.addURI(AUTHORITY, "name/*", NOTE_NAME); } }
上記のコードで重要なのは、staticとgetTypeメソッドです。
- staticでUriMatcherにURIを作成しています。
- 投げるURIに応じたUriMatcherを全て作成します。
Pattern matcher = Pattern.compile("テスト"); viewWapper.getDispName().setText(contactPojo.display_name); String viewURL = "content://jp.co.sample.android.muzukashii/name/"; Linkify.addLinks(viewWapper.getDispName(), matcher, viewURL);
getTypeメソッドで、Uriを振り分ける処理を行っています。
色々なパターンで分岐可能なので、以下の公式サイトのソースを参考にしてみてください。
http://developer.android.com/reference/android/content/UriMatcher.html
上記のソースではcase NOTE_NAME:に処理が入り、「hatoyama.kan.owawa/minsyu.kiero」文字列が返却されます。
この文字列はMIMEタイプを表しています。なので、このMIMEタイプによって呼び出されるActivityクラスを作成するようにします。
作成したのが以下のクラスです。
public class GoalActivity extends Activity { private TextView sendparam; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.goal); this.sendparam = (TextView)findViewById(R.id.sendparam); Intent intent = getIntent(); Uri uri = intent.getData(); if (uri != null) { this.sendparam.setText(uri.getPath()); } } @Override public void onStart() { super.onStart(); } @Override public void onResume() { super.onResume(); } }
上記の処理では、URIのパスを取得して画面に表示しています。
続いて表示xmlのgoal.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/sendparam" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
最後にandroidManifest.xmlにGoalActivityのintentを設定します。
<activity android:name=".GoalActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:mimeType="hatoyama.kan.owawa/minsyu.kiero"/> </intent-filter> </activity>
intent-filterを設定します。android:mimeTypeは、LinkTestProviderクラスのgetTypeメソッドで返すMIMEタイプ文字列に対応します。
では、上記のソースをビルドして実行します。
続いて、リンク文字をタップします。
おお。見事にActvityが呼び出されて画面遷移できましたね。
URIのpathもきちんと取得できています。
上記の処理から理解できるのは、
- 文字リンクのパターンを利用して特定のURIを作成することで、activityの呼び出しを制御できる。
- Providerクラスで受けとったURIをgetTypeメソッドで分岐し、任意のactivityを呼び出すことが出来る。様々なパラメーターも付加出来る。
かなり長くなってしまいましたが、これで終了です。お疲れ様でした。
android開発にそれなりに通じていないと、内容的に少し難しかったかもしれませんが、すごく役に立つパターンだと思うので、是非勉強してみてください。
みなさんが素晴らしいアプリを開発して、僕を唸らせてくれることを期待しています。
でわ。
追伸
URIの概念がよくわからないという人は以下の書籍がオススメです。WEB開発に携わるエンジニアは是非是非読んでもらいたい名著です。
0 件のコメント:
コメントを投稿