2010年12月7日 星期二

[Android] Access remote data on another process using ContentProvider and Cursor

這篇要示範的是, 如何寫一個ContentProvider讓其他的應用程式透過這個Provider去存取網路上的資料

這不是正統用法, 不過的確可以這樣用, 那為啥要這樣呢? 只是好玩, 還沒想到特別的應用

這邊用的範例是Twitter search (search keyword): MLB

首先要建立一個放Provider的Package, 不過這個Provider不是拿來存取DB的, 傳統的Android ContentProvider通常是用來存取資料庫, 但那並不代表那就是它的全部, "Content"Provider不就是拿來提供"Content"的嘛?管他Content哪來

建立一個Provider, 姑且叫它做TwitterSearchProvider, Authorities定為twitter.my.search好了, 裡面, 先只實作一個"query" 

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco} span.s1 {color: #9a1867} span.Apple-tab-span {white-space:pre}

public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder) {

return new TwitterSearchCursor();

}

這Query啥事都不用作, 回傳個TwitterSearchCursor就好了

從這看出玄機了嘛? 我們就是要利用Cursor, 在Cursor裡面實作連接網路取得資料的動作

後面我懶得寫解釋了, MBP快沒電了, 以下就是TwitterSearchCursor的Source:

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco} p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; min-height: 15.0px} p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #9a1867} p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #777777} p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #429073} p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #382ffa} p.p7 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #0023c7} span.s1 {color: #9a1867} span.s2 {color: #000000} span.s3 {color: #0023c7} span.s4 {color: #382ffa} span.s5 {color: #8dafc9} span.Apple-tab-span {white-space:pre}

public class TwitterSearchCursor extends AbstractCursor {

 

static class Tweet {

String created_at = "";

String id_str = "";

String text = "";

}

ArrayList<Tweet> tweets = new ArrayList<Tweet>();

@Override

public String[] getColumnNames() {

// TODO Auto-generated method stub

return new String[] {"_id", "created_at","id_str","text"};

}

 

@Override

public int getCount() {

if(tweets.size() == 0) {

loadData();

}

Log.d("AAAA", "cnt="+tweets.size());

return tweets.size();

}

 

@Override

public double getDouble(int arg0) {

// TODO Auto-generated method stub

return 0;

}

 

@Override

public float getFloat(int arg0) {

// TODO Auto-generated method stub

return 0;

}

 

@Override

public int getInt(int index) {

return index;

}

 

@Override

public long getLong(int index) {

return index;

}

 

@Override

public short getShort(int index) {

return (short)index;

}

 

@Override

public String getString(int index) {

Tweet t = tweets.get(mPos);

switch(index) {

case 1:

return ""+t.created_at;

case 2:

return ""+t.id_str;

case 3:

return ""+t.text;

}

return null;

}

 

@Override

public boolean isNull(int arg0) {

// TODO Auto-generated method stub

return false;

}

 

private void loadData() {

 

HttpClient client = AndroidHttpClient.newInstance("TwiAndroid");

        HttpGet get = new HttpGet("http://search.twitter.com/search.json?q=mlb");

 

        try {

HttpResponse resp = client.execute(get);

BufferedReader reader = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));

String line = null;

StringBuilder r = new StringBuilder();

while((line = reader.readLine())!=null) {

Log.d("AAAA", line);

r.append(line);

}

JSONObject obj = new JSONObject(r.toString());

JSONArray ary = obj.getJSONArray("results");

for(int i=0;i<ary.length();i++) {

Tweet t = new Tweet();

t.created_at = ary.getJSONObject(i).getString("created_at");

t.id_str = ary.getJSONObject(i).getString("id_str");

t.text = ary.getJSONObject(i).getString("text");

 

tweets.add(t);

}

        } catch (ClientProtocolException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (JSONException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

其實就是利用getCount()時去存取Twitter search API

為什麼要在這時候呢? 主要是我在remote端用的是Cursor來bind這些資料, 第一個會被呼叫到的是getCount()

由於整個Cursor會被跑在Provider所在的process並非跟caller同一個process, 所以heap也是吃在provider

這種利用Custom cursor應該可以組合出其他不同的作法跟應用...