最近在学习开源中国客户端,整个客户端结构清晰,功能全面,现在开始就一部分慢慢的学习。
Main这个类负责的功能很多,我们首先从onCreate这个方法看起
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 注册广播接收器
tweetReceiver = new TweetReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("net.oschina.app.action.APP_TWEETPUB");
registerReceiver(tweetReceiver, filter);
appContext = (AppContext) getApplication();
// 网络连接判断
if (!appContext.isNetworkConnected())
UIHelper.ToastMessage(this, R.string.network_not_connected);
// 初始化登录
appContext.initLoginInfo();
this.initHeadView();
this.initFootBar();
this.initPageScroll();
this.initFrameButton();
this.initBadgeView();
this.initQuickActionGrid();
this.initFrameListView();
// 检查新版本
if (appContext.isCheckUp()) {
UpdateManager.getUpdateManager().checkAppUpdate(this, false);
}
// 启动轮询通知信息
this.foreachUserNotice();
}
首先注册广播事件,这是动弹发布接收器,然后判断网络连接,然后初始化各种控件并加载listview数据,加载控件我们先不看,先看下
/**
* 初始化所有ListView数据
*/
private void initFrameListViewData() {
// 初始化Handler
lvNewsHandler = this.getLvHandler(lvNews, lvNewsAdapter,
lvNews_foot_more, lvNews_foot_progress, AppContext.PAGE_SIZE);
lvBlogHandler = this.getLvHandler(lvBlog, lvBlogAdapter,
lvBlog_foot_more, lvBlog_foot_progress, AppContext.PAGE_SIZE);
lvQuestionHandler = this.getLvHandler(lvQuestion, lvQuestionAdapter,
lvQuestion_foot_more, lvQuestion_foot_progress,
AppContext.PAGE_SIZE);
lvTweetHandler = this.getLvHandler(lvTweet, lvTweetAdapter,
lvTweet_foot_more, lvTweet_foot_progress, AppContext.PAGE_SIZE);
lvActiveHandler = this.getLvHandler(lvActive, lvActiveAdapter,
lvActive_foot_more, lvActive_foot_progress,
AppContext.PAGE_SIZE);
lvMsgHandler = this.getLvHandler(lvMsg, lvMsgAdapter, lvMsg_foot_more,
lvMsg_foot_progress, AppContext.PAGE_SIZE);
// 加载资讯数据
if (lvNewsData.isEmpty()) {
loadLvNewsData(curNewsCatalog, 0, lvNewsHandler,
UIHelper.LISTVIEW_ACTION_INIT);
}
}
这里先初始化Handler,然后线程加载资讯数据并通知ui
我们先看下loadLvNewsData这个方法
private void loadLvNewsData(final int catalog, final int pageIndex,
final Handler handler, final int action) {
mHeadProgress.setVisibility(ProgressBar.VISIBLE);
new Thread() {
public void run() {
Message msg = new Message();
boolean isRefresh = false;
if (action == UIHelper.LISTVIEW_ACTION_REFRESH
|| action == UIHelper.LISTVIEW_ACTION_SCROLL)
isRefresh = true;
try {
NewsList list = appContext.getNewsList(catalog, pageIndex,
isRefresh);
msg.what = list.getPageSize();
msg.obj = list;
} catch (AppException e) {
e.printStackTrace();
msg.what = -1;
msg.obj = e;
}
msg.arg1 = action;
msg.arg2 = UIHelper.LISTVIEW_DATATYPE_NEWS;
if (curNewsCatalog == catalog)
handler.sendMessage(msg);
}
}.start();
}
这里获取网络数据是在Application中获取的,获取的数据是xml,使用的是NewsList中的public static NewsList parse(InputStream inputStream) 方法解析,这里使用的是pull解析。当调用handler.sendMessage(msg)这个方法后,就会调用lvNewsHandler 这个handler的public void handleMessage(Message msg),lvNewsHandler 是通过getLvHandler获得的。
private Handler getLvHandler(final PullToRefreshListView lv,
final BaseAdapter adapter, final TextView more,
final ProgressBar progress, final int pageSize) {
return new Handler() {
public void handleMessage(Message msg) {
if (msg.what >= 0) {
// listview数据处理
Notice notice = handleLvData(msg.what, msg.obj, msg.arg2,
msg.arg1); // what 数量 ,obj 数据, objtype 数据类型 , actiontype 操作类型 , notice 通知信息
if (msg.what < pageSize) {
lv.setTag(UIHelper.LISTVIEW_DATA_FULL);
adapter.notifyDataSetChanged();
more.setText(R.string.load_full);
} else if (msg.what == pageSize) {
lv.setTag(UIHelper.LISTVIEW_DATA_MORE);
adapter.notifyDataSetChanged();
more.setText(R.string.load_more);
// 特殊处理-热门动弹不能翻页
if (lv == lvTweet) {
TweetList tlist = (TweetList) msg.obj;
if (lvTweetData.size() == tlist.getTweetCount()) {
lv.setTag(UIHelper.LISTVIEW_DATA_FULL);
more.setText(R.string.load_full);
}
}
}
// 发送通知广播
if (notice != null) {
UIHelper.sendBroadCast(lv.getContext(), notice);
}
// 是否清除通知信息
if (isClearNotice) {
ClearNotice(curClearNoticeType);
isClearNotice = false;// 重置
curClearNoticeType = 0;
}
} else if (msg.what == -1) {
// 有异常--显示加载出错 & 弹出错误消息
lv.setTag(UIHelper.LISTVIEW_DATA_MORE);
more.setText(R.string.load_error);
((AppException) msg.obj).makeToast(Main.this);
}
if (adapter.getCount() == 0) {
lv.setTag(UIHelper.LISTVIEW_DATA_EMPTY);
more.setText(R.string.load_empty);
}
progress.setVisibility(ProgressBar.GONE);
mHeadProgress.setVisibility(ProgressBar.GONE);
if (msg.arg1 == UIHelper.LISTVIEW_ACTION_REFRESH) {
lv.onRefreshComplete(getString(R.string.pull_to_refresh_update)
+ new Date().toLocaleString());
lv.setSelection(0);
} else if (msg.arg1 == UIHelper.LISTVIEW_ACTION_CHANGE_CATALOG) {
lv.onRefreshComplete();
lv.setSelection(0);
}
}
};
}
根据接收的what数据对listview的数据进行处理
接着看看handleLvData这个方法
private Notice handleLvData(int what, Object obj, int objtype,
int actiontype) {
Notice notice = null;
switch (actiontype) {
case UIHelper.LISTVIEW_ACTION_INIT:
case UIHelper.LISTVIEW_ACTION_REFRESH:
case UIHelper.LISTVIEW_ACTION_CHANGE_CATALOG:
int newdata = 0;// 新加载数据-只有刷新动作才会使用到
switch (objtype) {
case UIHelper.LISTVIEW_DATATYPE_NEWS:
NewsList nlist = (NewsList) obj;
notice = nlist.getNotice();
lvNewsSumData = what;
if (actiontype == UIHelper.LISTVIEW_ACTION_REFRESH) {
if (lvNewsData.size() > 0) {
for (News news1 : nlist.getNewslist()) {
boolean b = false;
for (News news2 : lvNewsData) {
if (news1.getId() == news2.getId()) {
b = true;
break;
}
}
if (!b)
newdata++;
}
} else {
newdata = what;
}
}
lvNewsData.clear();// 先清除原有数据
lvNewsData.addAll(nlist.getNewslist());
break;
case UIHelper.LISTVIEW_DATATYPE_BLOG:
。。。。省略部分代码
return notice;
}
最后我们看看Application是如何获取数据和存储的
public NewsList getNewsList(int catalog, int pageIndex, boolean isRefresh) throws AppException {
NewsList list = null;
String key = "newslist_"+catalog+"_"+pageIndex+"_"+PAGE_SIZE;
if(isNetworkConnected() && (!isReadDataCache(key) || isRefresh)) {
try{
list = ApiClient.getNewsList(this, catalog, pageIndex, PAGE_SIZE);
if(list != null && pageIndex == 0){
Notice notice = list.getNotice();
list.setNotice(null);
list.setCacheKey(key);
saveObject(list, key);
list.setNotice(notice);
}
}catch(AppException e){
list = (NewsList)readObject(key);
if(list == null)
throw e;
}
} else {
list = (NewsList)readObject(key);
if(list == null)
list = new NewsList();
}
return list;
}
分析一下流程,首先判断如果是有网络连接并且不是从缓存中读取或者是刷新,那么就从ApiClient中获取数据,我们看看list.setCacheKey(key)和saveObject(list, key)这两个方法,这两个是把数据序列化到本地,其中的key每次都不同,这样用于辨别不同数据。我们发现调用loadLvNewsData这个方法后,会置isRefresh为true,因此,只要调用这个方法就会从网络获取数据。
public boolean saveObject(Serializable ser, String file) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try{
fos = openFileOutput(file, MODE_PRIVATE);
oos = new ObjectOutputStream(fos);
oos.writeObject(ser);
oos.flush();
return true;
}catch(Exception e){
e.printStackTrace();
return false;
}finally{
try {
oos.close();
} catch (Exception e) {}
try {
fos.close();
} catch (Exception e) {}
}
}
public Serializable readObject(String file){
if(!isExistDataCache(file))
return null;
FileInputStream fis = null;
ObjectInputStream ois = null;
try{
fis = openFileInput(file);
ois = new ObjectInputStream(fis);
return (Serializable)ois.readObject();
}catch(FileNotFoundException e){
}catch(Exception e){
e.printStackTrace();
//反序列化失败 - 删除缓存文件
if(e instanceof InvalidClassException){
File data = getFileStreamPath(file);
data.delete();
}
}finally{
try {
ois.close();
} catch (Exception e) {}
try {
fis.close();
} catch (Exception e) {}
}
return null;
}
保存和读取的方法比较简单,这里就多说了,大家看一下就懂了。。
好了,今天先到这,下次再继续