1.json解析
写服务端的同学,在Bean里面又设置了Bean,然后我在Android端解析的时候就悲剧了。。我的天,一大串一大串的。不过仔细分析了,其实还好。
public void getQuestionList(){
try {
//get方式连接
HttpGet httpGet = new HttpGet(url);
//相当于一个小型浏览器
HttpClient httpClient = new DefaultHttpClient();
//得到的数据
HttpResponse httpResponse = httpClient.execute(httpGet);
//判断是否连接成功
if( httpResponse.getStatusLine().getStatusCode() == 200){
//得到连接实体(就是把代码从网上拿下来,当然需要一个东西装)
HttpEntity httpEntity = httpResponse.getEntity();
//json方式传递,当然要转成json了
String json = EntityUtils.toString(httpEntity,"utf-8");
//new一个json对象,为什么是array不是object后面会说
JSONArray jsonArray = new JSONArray(json);
JSONArray rows = jsonArray.getJSONObject(0).getJSONArray("rows");
for (int i = 0; i < rows.length(); i++) {
//对照json来看才能看的懂。。只要明白前几步,后面就好懂了
JSONObject question = rows.getJSONObject(i).getJSONObject("questionBean");
JSONObject questioninfo = rows.getJSONObject(i);
QuestionAllInfoBean questionAllInfoBean = new QuestionAllInfoBean();
questionAllInfoBean.setBestAnswer(questioninfo.getString("bestAnswer"));
questionAllInfoBean.setCountOfAnswers(questioninfo.getInt("countOfAnswers"));
questionAllInfoBean.setQuestionUserName(questioninfo.getString("questionUserName"));
questionAllInfoBean.setVpOfQuestion(Integer.parseInt(questioninfo.getString("vpOfQuestion")));
QuestionBean questionBean = new QuestionBean();
questionBean.setQuestion_description(question.getString("question_description"));
questionBean.setQuestion_id(Integer.parseInt(question.getString("question_id")));
questionBean.setQuestion_mark(Integer.parseInt(question.getString("question_mark")));
questionBean.setQuestion_tags(question.getString("question_tags"));
questionBean.setQuestion_time(question.getString("question_time"));
questionBean.setQuestion_title(question.getString("question_title"));
questionBean.setQuestion_user_id(Integer.parseInt(question.getString("question_user_id")));
questionAllInfoBean.setQuestionBean(questionBean);
questionList.add(questionAllInfoBean);
}
}else{
responseStatus = "服务器连接异常,请重试";
Toast.makeText(MainActivity.this,responseStatus,Toast.LENGTH_SHORT).show();
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
先贴代码,然后是json数据
[{"rows":[{"bestAnswer":"暂无","countOfAnswers":1,"questionBean":{"question_description":"我想知道我们究竟认识么、?","question_id":23,"question_mark":34,"question_tags":"情感","question_time":"2014-04-13","question_title":"我们认识么?","question_user_id":1},"questionUserName":"admin","vpOfQuestion":50},
{"bestAnswer":"暂无","countOfAnswers":2,"questionBean":{"question_description":"今天时间...","question_id":22,"question_mark":0,"question_tags":"","question_time":"2014-06-28 08:21:02","question_title":"'几天周几?","question_user_id":0}]}]<span style="font-family: Arial, Helvetica, sans-serif;"></span>
截取了两个,这是在 服务端控制台里输出的json数据。
首先,遵循一个原则,以 [ 开头,要用JSONArray,以 { 开头,要用JSONObject,很明显,array是数组,object是对象。如果错误的话,会报出警告。org.json.JSONException: Value [{"questionBean":{"question_description":"。。。。。。。后面一堆,就是告诉你value解析的方式是错误的。
继续,array解开了第一个[,然后用jsonArray.getJSONObject(0).getJSONArray("rows");解开第一个{,然后用getJSONArray取了rows的值。
接下来是一样的,只不过这里有好多条数据,用for一个一个取出来,放到你自己定义的list里面就好了。questionBean里面的数据也是一样的,不过还是要get一个Object,你自己尝试一下就懂了。
这里要指出,第一个[ ]里,只有一组数据,就是rows,所以直接getObject(0)就可以了,但是第二个里面有两组(推荐用json插件看https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc)所以要用for一个一个取出来。
2.对于Runnable和Handle
直接上代码
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == 1){
questionListView = (ListView) findViewById(R.id.question_listView);
mquestionAdapter = new QuestionAdapter();
questionListView.setAdapter(mquestionAdapter);
Toast.makeText(MainActivity.this,questionList.size() + "" ,Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(MainActivity.this,"没有取到信息(>_<)",Toast.LENGTH_SHORT).show();
}
}
};
Runnable runQuestionList = new Runnable() {
@Override
public void run() {
getQuestionList();
Message msg = new Message();
msg.what = QUESTION_LIST_SUCCESS;
handler.sendMessage(msg);
}
};
起初对这两个类的用处理解不深,不知道是干嘛的,但是在报错之后就懂了。。。
Android的主线程在修改UI的时候是不安全的,而且为了使UI不卡顿,应该把耗时的联网操作放在一个线程中单独处理,所以就有了这两个类。
Runnable一看就知道了,用来联网操作,在create方法中起起来的 new Thread(runQuestionList).start();。这里用message发送给Handler一个信息,(不能让人家无缘无故的给你改东西吧,总得有的约定什么的吧,于是就用起了Message)处理完之后hanler把message传过去,如果符合条件,就开始更新UI,这里我是更新的ListView,其他的也是一样的,像TextView.setText()什么的,总之是这个意思。(还有就是我在原来专门写一个listview的demo的时候是不需要用这个的,不知道这次怎么就报错的了,求大神解释)。
3.slidingMenu
//设置抽屉菜单
public void slidingMenu(){
slidingMenu = new SlidingMenu(MainActivity.this);
slidingMenu.setMode(SlidingMenu.LEFT);
slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
slidingMenu.setMenu(R.layout.sliding_menu);
slidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);
//与主界面绑定
slidingMenu.attachToActivity(MainActivity.this,SlidingMenu.SLIDING_CONTENT);
slidingAdapter = new mSlidingAdapter();
slidingListview = (ListView) findViewById(R.id.slidingListView);
slidingListview.setAdapter(slidingAdapter);
}
记住就好。。。
有一点就是,slidingAdapter一定要放在最后,不然得不到数据。
还有这个setBehindOffsetRes方法,别用错了,少了后面的Res就惨了,会出现主界面闪动,slidingmenu不出现的现象。
4.再按一次退出程序
@Override
public void onBackPressed() {
if(isExit){
finish();
}else{
isExit = true;
Toast.makeText(MainActivity.this,"再按一次退出应用",Toast.LENGTH_SHORT).show();
timerTask = new TimerTask() {
@Override
public void run() {
isExit = false;
}
};
timer.schedule(timerTask,2000);//Schedule a task for single execution after a specified delay.
}
}
记住就好。。。
好了,第一天结束
明天加入下拉刷新,和点击item进入question展示界面。
nice的一天,明天继续加油↖(^ω^)↗
第二天
被UI折磨死了。。。还是得多练才行,不过也知道不少。
margin一般是具体的数值,比如离边框或者其他组件。algin一般是相对的布局,相对一个组件把带有这个参数的组件放在哪。
1.严苛模式 严苛模式讲解
我不明白,我已经把下拉刷新重新写在一个线程里了,还是会报android.os.NetworkOnMainThreadException的错。所以只能用这个模式。o(╯□╰)o
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
//严苛模式
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDeath()
.build());
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
2.OnItemClickListener
listview菜单监听,打开单个listview中的数据
class questionItemListener implements AdapterView.OnItemClickListener{
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
int position = i - 1;
Intent intent = new Intent(MainActivity.this,QuestionDetail.class);
//传递一些必要的数据
intent.putExtra("question_id",questionList.get(position).getQuestionBean().getQuestion_id());
intent.putExtra("question_title",questionList.get(position).getQuestionBean().getQuestion_title());
intent.putExtra("question_summary",questionList.get(position).getQuestionBean().getQuestion_description());
intent.putExtra("question_username",questionList.get(position).getQuestionUserName());
startActivity(intent);
}
}
用的时候只要set一下就可以了 questionListView.setOnItemClickListener(new questionItemListener());
3.下拉刷新,上拉加载(改进)
Runnable pullRunnable = new Runnable() {
@Override
public void run() {
questionListView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2<ListView>() {
//下拉
@Override
public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) {
currentPage = 0;
questionList.removeAll(questionList);
getQuestionList();
Message msg = new Message();
msg.what = 2;
pullHandler.sendMessage(msg);
}
//上拉
@Override
public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {
String lable = DateUtils.formatDateTime(getApplicationContext(), System.currentTimeMillis(),
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL);
refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(lable);
currentPage = currentPage + 1;
getQuestionList();
Message msg = new Message();
msg.what = 2;
pullHandler.sendMessage(msg);
}
});
}
};
Handler pullHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 2) {
<span style="white-space:pre"> </span>//更新listview
mquestionAdapter.notifyDataSetChanged();
questionListView.onRefreshComplete();
} else {
Toast.makeText(MainActivity.this, "没有取到信息(>_<)", Toast.LENGTH_SHORT).show();
}
}
};
questionListView.setMode(PullToRefreshBase.Mode.BOTH);
今天就这样吧。。好累。。不想多写了
7月30日
最近迷上了《神探夏洛克》进度都拖慢了。。。
今天最大的收获就是在客户端取得了Session,就是一个cookie的值。不过为了方便,一直没有用网上的方法,最近研究了半天,还有就是ASyncTask的使用,简单的来总结ASyncTaskd话,就是该做什么的时候就做什么,不然人家会报错。
1.为了得到session纯粹是同学写的服务端好多session,没办法。然后就百度了一大堆东西(好像是google的)。
以下摘自:session取得
在解决问题前先回顾下Session与Cookie:
Cookie和Session都为了用来保存状态信息,都是保存客户端状态的机制,它们都是为了解决HTTP无状态的问题而所做的努力。
Session可以用Cookie来实现,也可以用URL回写的机制来实现。
Cookie和Session有以下明显的不同点:
1)Cookie将状态保存在客户端,Session将状态保存在服务器端;
2)Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies。
3)Session是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分是不同用户session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器;
4)就安全性来说:当你访问一个使用session 的站点,同时在自己机器上建立一个cookie,建议在服务器端的SESSION机制更安全些.因为它不会任意读取客户存储的信息。
Session机制
Session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。
当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。
Session的实现方式
1 ) 使用Cookie来实现
服务器给每个Session分配一个唯一的JSESSIONID,并通过Cookie发送给客户端。
当客户端发起新的请求的时候,将在Cookie头中携带这个JSESSIONID。这样服务器能够找到这个客户端对应的Session。
2 )使用URL回显来实现
URL回写是指服务器在发送给浏览器页面的所有链接中都携带JSESSIONID的参数,这样客户端点击任何一个链接都会把JSESSIONID带给服务器。
如果直接在浏览器中输入url来请求资源,Session是匹配不到的。
HttpURLConnection con= (HttpURLConnection) url.openConnection();
// 取得sessionid.
String cookieval = con.getHeaderField("set-cookie");
String sessionid;
if(cookieval != null) {
sessionid = cookieval.substring(0, cookieval.indexOf(";"));
}
HttpURLConnectioncon= (HttpURLConnection) url.openConnection();
if(sessionid != null) {
con.setRequestProperty("cookie", sessionid);
}
只要设置了sessionID,这样web服务器在接受请求的时候就会自动搜索对应的session了,从而保证了在同一会话Session。
Tomcat对 Session的实现,是一开始同时使用Cookie和URL回写机制,如果发现客户端支持Cookie,就继续使用Cookie,停止使用URL回写。如果发现Cookie被禁用,就一直使用URL回写。jsp开发处理到Session的时候,对页面中的链接记得使用 response.encodeURL() 。
回顾完Session和Cookie,我们来说说为什么手机端与服务器交互没有实现在同一session下?
1)原因很简单,就是因为android手机端在访问web服务器时,没有给http请求头部设置sessionID,而使用web浏览器作为客户端访问服务器时,在客户端每次发起请求的时候,都会将交互中的sessionID:JSESSIONID设置在Cookie头中携带过去,服务器根据这个sessionID获取对应的Session,而不是重新创建一个新Session(除了这个Session失效)。
以java.net.HttpURLConnection发起请求为例:
获取Cookie:
URL url = new URL(requrl);
//sessionid值格式:JSESSIONID=AD5F5C9EEB16C71EC3725DBF209F6178,是键值对,不是单指值
发送设置cookie:
URL url = new URL(requrl);
今天只得到了。。还没来得及用,所以不知道能不能行。以下是我自己的代码
class URLConnect extends AsyncTask<String, Integer, String> {
String message;
String sessionid;
@Override
protected String doInBackground(String... strings) {
try {
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(reurl);
List<NameValuePair> valuePairs = new ArrayList<NameValuePair>();
valuePairs.add(new BasicNameValuePair("user_name", username));
valuePairs.add(new BasicNameValuePair("user_password", password));
httpPost.setEntity(new UrlEncodedFormEntity(valuePairs));
HttpResponse httpResponse = httpClient.execute(httpPost);
if( httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK ){
//获取sessionid
String session = httpResponse.getFirstHeader("Set-Cookie").toString();
sessionid = session.substring(13,session.indexOf(";"));
Log.e("session",session);
Log.e("sessionid",sessionid);
HttpEntity httpEntity = httpResponse.getEntity();
String json = EntityUtils.toString(httpEntity, "utf-8");
Log.e("json",json);
JSONObject object = new JSONObject(json);
message = object.getString("message");
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if(message.equals("true")){
Intent intent = new Intent(LoginActivity.this,MainActivity.class);
intent.putExtra("sessionid",sessionid);
intent.putExtra("username",username);
startActivity(intent);
finish();
}else{
AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);
builder.setTitle("提示");
builder.setMessage("用户名或密码出错了哦(⊙o⊙)");
builder.setPositiveButton("确定",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
}).create().show();
}
}
}
实际上就是获取请求头里面的set-cookie,因为你会发现,在那个网站的页面里面,cookie的值都是一样的。。。(废话,不一样怎么保证session能用)
2.上面的代码就是写在AsyncTask中的,很方便,也不容易出错(比较懒,只重写了两个方法)。比较好的解释:AsyncTask的运用。其实只要记住,该干嘛的时候干嘛,doInBackground()就是用来做前期准备的,不能用来更新UI,否则会报Loop的错,就是没有加载到队列中,当然你也可以把它用 Looper.prepare();放进去,但是强扭的代码,迟早会出你意想不到的问题的。很明显,onPostExecute()我用来更新UI。这里还有很多能做的东西,以后还会碰到,具体请参见大神的博客。
这么多应该可以凑一篇了吧,先发表了吧,不然有点太长了。。