人人網(wǎng)官方Android客戶端源碼分析
ContentProvider是不同應(yīng)用程序之間進行數(shù)據(jù)交換的標準API,ContentProvider以某種Uri的形式對外提供數(shù)據(jù),允許其他應(yīng)用訪問或修改數(shù)據(jù);其他應(yīng)用程序使用ContentResolver根據(jù)Uri去訪問操作指定數(shù)據(jù)。
人人網(wǎng)Android客戶端也是使用ContentProvider對需要保存于Android客戶端的數(shù)據(jù)進行管理。
1. renren.db
SQLLiteOpenHelper是Android提供的一個管理數(shù)據(jù)庫的工具類,可用于管理數(shù)據(jù)庫的創(chuàng)建和版本更新。一般的用法是創(chuàng)建SQLiteOpenHelper的子類,并擴展它的onCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)方法。
人人網(wǎng)Android客戶端使用該方法創(chuàng)建及修改用戶手機中的人人網(wǎng)數(shù)據(jù)庫(renren.db)。
下面是RenRenProvider$DatabaseHelper的代碼:
- public class RenRenProvider$DatabaseHelper extends SQLiteOpenHelper {
- public RenRenProvider$DatabaseHelper(Context context) {
- super(context, "renren.db", null, 71);
- }
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE pic (_id INTEGER PRIMARY KEY,url TEXT UNIQUE ON CONFLICT REPLACE,_data TEXT,size INTEGER);");
- db.execSQL("CREATE TABLE account (_id INTEGER PRIMARY KEY,uid INTEGET,account TEXT UNIQUE ON CONFLICT REPLACE,pwd TEXT,proxy INTEGER,sessionkey TEXT,srt_key TEXT,ticket TEXT,name TEXT,headphoto BLOB,isdefault INTEGER,last_login INTEGER,friend_count INTEGER);");
- db.execSQL("CREATE TABLE home (_id INTEGER PRIMARY KEY,item_id INTEGER UNIQUE ON CONFLICT REPLACE,data BLOB);");
- db.execSQL("CREATE TABLE profile (_id INTEGER PRIMARY KEY,type INTEGER UNIQUE ON CONFLICT REPLACE,data BLOB);");
- db.execSQL("CREATE TABLE friends (_id INTEGER PRIMARY KEY,uid INTEGER UNIQUE ON CONFLICT REPLACE,username TEXT,headurl TEXT,doing TEXT,nameindex TEXT,namepinyin TEXT,friendgroup TEXT,network TEXT,gender TEXT,isfriend INTEGER,suggest_text_1 TEXT,suggest_intent_query TEXT);");
- db.execSQL("CREATE TABLE messages (_id INTEGER PRIMARY KEY,messageid INTEGER UNIQUE ON CONFLICT REPLACE,message BLOB);");
- db.execSQL("CREATE TABLE favorites (_id INTEGER PRIMARY KEY,favoriteid BIGINT UNIQUE ON CONFLICT REPLACE,favoriteowner INTEGER,type INTEGER,favorite BLOB);");
- db.execSQL("CREATE TABLE emonticons (_id INTEGER PRIMARY KEY,url TEXT,emotion TEXT UNIQUE ON CONFLICT REPLACE,img BLOB,size INTEGER,_data TEXT);");
- db.execSQL("CREATE TABLE favoritefriends (_id INTEGER PRIMARY KEY,owner INTEGER,uid INTEGER,name TEXT);");
- db.execSQL("CREATE TABLE chathistory (_id INTEGER PRIMARY KEY,uid INTEGER,tochatid INTEGER,chatmessage TEXT,comefrom INTEGER,chatname TEXT,chattime LONG);");
- }
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- db.execSQL("DROP TABLE IF EXISTS pic");
- db.execSQL("DROP TABLE IF EXISTS account");
- db.execSQL("DROP TABLE IF EXISTS home");
- db.execSQL("DROP TABLE IF EXISTS profile");
- db.execSQL("DROP TABLE IF EXISTS friends");
- db.execSQL("DROP TABLE IF EXISTS messages");
- db.execSQL("DROP TABLE IF EXISTS favorites");
- db.execSQL("DROP TABLE IF EXISTS emonticons");
- db.execSQL("DROP TABLE IF EXISTS favoritefriends");
- db.execSQL("DROP TABLE IF EXISTS favoritefriends");
- db.execSQL("DROP TABLE IF EXISTS chathistory");
- onCreate(db);
- }
- }
從代碼中我們可以看到人人網(wǎng)Android客戶端在用戶手機上創(chuàng)建了renren.db數(shù)據(jù)庫,數(shù)據(jù)庫中共有10張表,分別為pic、account、home、profile、friends、messages、favorites、emonticons、favoritefriends、chathistory。#p#
2. RenRenProvider
前面我們已經(jīng)提到過ContentProvider,下面我們來看看人人網(wǎng)Android客戶端是如何開發(fā)ContentProvider的。開發(fā)ContentProvider的兩步:1)開發(fā)一個ContentProvider的子類,該子類需要實現(xiàn)增、刪、改、查等方法。2)在AndroidManifest.xml文件中注冊該ContentProvider。
下面是RenRenProvider核心代碼:
- public class RenRenProvider extends ContentProvider {
- public static final String AUTHORITY = "com.renren.mobile.provider";
- public static final class Account implements BaseColumns {
- public static final Uri ACCOUNT_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/account");
- }
- public static final class ChatHistory implements BaseColumns {
- public static final Uri CHAT_HISTORY_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/chathistory");
- }
- public static final class Emonticons implements BaseColumns {
- public static final Uri EMONTICONS_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/emonticons");
- }
- public static final class Favorite implements BaseColumns {
- public static final Uri FAVORITE_CONTENTURI = Uri
- .parse("content://com.renren.mobile.provider/favorites");
- }
- public static final class FavoriteFriends implements BaseColumns {
- public static final Uri FAVORITE_FRIENDS_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/favoritefriends");
- }
- public static final class Friends implements BaseColumns {
- public static final Uri FRIENDS_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/friends");
- }
- public static final class Home implements BaseColumns {
- public static final Uri HOME_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/home");
- }
- public static final class Messages implements BaseColumns {
- public static final Uri MESSAGES_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/messages");
- }
- public static final class Pic implements BaseColumns {
- public static final Uri PIC_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/pic");
- }
- public static final class Profile implements BaseColumns {
- public static final Uri PROFILE_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/profile");
- }
- private static final UriMatcher URI_MATCHER;
- private RenRenProvider.DatabaseHelper renrenDatabaseHelper;
- static {
- URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
- URI_MATCHER.addURI(AUTHORITY, "pic", 3);
- URI_MATCHER.addURI(AUTHORITY, "pic/#", 4);
- URI_MATCHER.addURI(AUTHORITY, "account", 5);
- URI_MATCHER.addURI(AUTHORITY, "account/#", 6);
- URI_MATCHER.addURI(AUTHORITY, "home", 7);
- URI_MATCHER.addURI(AUTHORITY, "home/#", 8);
- URI_MATCHER.addURI(AUTHORITY, "profile", 9);
- URI_MATCHER.addURI(AUTHORITY, "profile/#", 10);
- URI_MATCHER.addURI(AUTHORITY, "friends", 11);
- URI_MATCHER.addURI(AUTHORITY, "friends/#", 12);
- URI_MATCHER.addURI(AUTHORITY, "search_suggest_query/*", 1);
- URI_MATCHER.addURI(AUTHORITY, "search_suggest_query", 2);
- URI_MATCHER.addURI(AUTHORITY, "messages", 13);
- URI_MATCHER.addURI(AUTHORITY, "messages/#", 14);
- URI_MATCHER.addURI(AUTHORITY, "favorites", 15);
- URI_MATCHER.addURI(AUTHORITY, "favorites/#", 16);
- URI_MATCHER.addURI(AUTHORITY, "emonticons", 17);
- URI_MATCHER.addURI(AUTHORITY, "emonticons/#", 18);
- URI_MATCHER.addURI(AUTHORITY, "favoritefriends", 19);
- URI_MATCHER.addURI(AUTHORITY, "favortiefriends/#", 20);
- URI_MATCHER.addURI(AUTHORITY, "chathistory", 21);
- URI_MATCHER.addURI(AUTHORITY, "chathistory/#", 22);
- URI_MATCHER.addURI(AUTHORITY, "chathistory/*/*", 23);
- }
- public boolean onCreate()
- {
- renrenDatabaseHelper = new RenRenProvider.DatabaseHelper(this.getContext());
- return true;
- }
- //其它代碼省略...
- }
下面是人人網(wǎng)android客戶端在AndroidMantifest.xml中對該ContentProvider的注冊。
- <providerandroid:nameproviderandroid:nameproviderandroid:nameproviderandroid:name=".contentprovider.RenRenProvider" android:permission="com.renren.mobile.android.permission.PERMISSION_ADD_ACCOUNT"
- android:authorities="com.renren.mobile.provider" />
從上面的分析我們了解到只要得到com.renren.mobile.android.permission.PERMISSION_ADD_ACCOUNT權(quán)限我們就可以通過特定Uri訪問人人網(wǎng)Android客戶端在用戶手機上創(chuàng)建的renren.db中特定表了。#p#
3. 開發(fā)Android應(yīng)用訪問renren.db中的數(shù)據(jù)
從上面分析中我們已經(jīng)知道renren.db中表結(jié)構(gòu),及訪問特定表對應(yīng)的Uri,如我們可以通過content://com.renren.mobile.provider/account訪問renren.db中的account表等等。下面我們寫個很簡單的例子來訪問account表中的account和ticket字段。
main.xml根節(jié)點下簡單添加2個TextView,如下:
- <TextView
- android:id="@+id/textView1"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="TextView" />
- <TextView
- android:id="@+id/textView2"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="TextView" />
將account和ticket信息顯示到TextView中,類代碼如下
- public class RenRenExtActivity extends Activity {
- private static final Uri ACCOUNT_CONTENT_URI = Uri
- .parse("content://com.renren.mobile.provider/account");
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- TextView textView1 = (TextView) findViewById(R.id.textView1);
- textView1.setText("Sorry.");
- TextView textView2 = (TextView) findViewById(R.id.textView2);
- textView2.setText("Sorry2.");
- Cursor cursor = getContentResolver().query(ACCOUNT_CONTENT_URI, null,
- null, null, null);
- List<Map<String, String>> resultList = converCursorToList(cursor);
- if (!resultList.isEmpty()) {
- Map<String, String> map = resultList.get(0);
- textView1.setText(map.get("account"));
- textView2.setText(map.get("ticket"));
- }
- }
- private List<Map<String, String>> converCursorToList(Cursor cursor) {
- List<Map<String, String>> result = new ArrayList<Map<String, String>>();
- if (cursor == null) {
- return Collections.emptyList();
- }
- // 遍歷Cursor結(jié)果集
- while (cursor.moveToNext()) {
- // 將結(jié)果集中的數(shù)據(jù)存入ArrayList中
- Map<String, String> map = new HashMap<String, String>();
- map.put("account",
- cursor.getString(cursor.getColumnIndex("account")));
- map.put("ticket", cursor.getString(cursor.getColumnIndex("ticket")));
- result.add(map);
- }
- return result;
- }
- }
需要指出的是,上面的應(yīng)用程序需要操作人人網(wǎng)android客戶端中的數(shù)據(jù)庫,因此要記得在AndroidMantifest.xml文件中為該應(yīng)用程序授權(quán)。也就是在該文件的根元素中添加如下元素:
- <uses-permission android:name="com.renren.mobile.android.permission.PERMISSION_ADD_ACCOUNT" />
如果你android手機中安裝有人人網(wǎng)Android客戶端且曾經(jīng)使用過,那么renren.db中應(yīng)該有數(shù)據(jù)存在,把上面應(yīng)用打包為apk文件安裝到你android手機中,運行它,應(yīng)該能看到屏幕中將顯示你的人人網(wǎng)賬號及一串ticket,該ticket是人人網(wǎng)Andriod客戶端部分功能與人人網(wǎng)服務(wù)器通信的sid。
同理,也可以使用其它特定Uri訪問手機中renre.db中特定的表,如friends表等等,所有Uri詳見RenRenProvider代碼。