這篇要示範的是, 如何寫一個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應該可以組合出其他不同的作法跟應用…

晚上快速翻了一變 memcached , 想了一些想法, 趁還沒忘之前先寫下來再去睡好了

memcahced的設計概念說實在的簡單到不行, 說簡單不是說他不好或是沒什麼, 反而這麼一個簡單的設計, 幫助真的會不小

memcached簡單說了就是一個分散式key-value based data cache, 資料是分散放在很多台電腦的RAM裡面, 因為是key-value based, 所以說起來就是一個大的hash table, 並不需要複雜的query, 只需要O(1)就可以取得資料, 而且資料就在RAM中, 可以是相當的快

我們都知道一個事實: “IO costs”, 如果每次都需要從database query資料, 那所花的時間, 包含SQL處理的時間, 以及IO的時間會是相當長的, 這點不管是Web application或是在mobile上(尤其現在SQLite被大量使用)都通用

因此使用RAM來cache query result as long as possible會是tuning這類的performance的其中一步, 這觀念應該是很多人都想的到

不過這樣得pattern可能得考慮的:

  1. 怎樣把一個常用且複雜的query對應到一個key-value pair
  2. cache的存續時間
  3. How to efficiently invalidate or update cache after data set changed

或許在Android的ContentProvider中可以引入memory caching的機制來減少IO的量