今天练习android例子,例子请求新浪网站地址获取内容,结果遇到android.os.NetworkOnMainThreadException异常。网上查资料说是android4.0+后,在主线程里不能直接访问外部网络。
相关说法见:
http://hi.baidu.com/duqingming/item/8e9504c39d74bd25a0b50aaf
http://blog.csdn.net/haqer0825/article/details/18223793
折腾一番后初步解决问题:
总的思路: MainActivity的onCreate方法中通过新的线程实现对网页内容的访问(采用HttpClient方式),线程中访问到数据后通过消息返回到MainActivity,MainActivity实现Handler接收消息
1.布局文件
activity_main.xml
---------------------------
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/rssNews"></ListView>
</RelativeLayout>
---------------------------
shownews.xml
---------------------------
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/titleView" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:id="@+id/linkView" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/pubDateView" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/descriptionView" />
</LinearLayout>
---------------------------
2.java文件
News.java
---------------------------
package com.sharpandroid.pullrssdemo;
public class News {
private String title;
private String link;
private String author;
private String guid;
private String category;
private String pubDate;
private String comments;
private String description;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getPubDate() {
return pubDate;
}
public void setPubDate(String pubDate) {
this.pubDate = pubDate;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getGuid() {
return guid;
}
public void setGuid(String guid) {
this.guid = guid;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
}
---------------------------
MainActivity.java
---------------------------
package com.sharpandroid.pullrssdemo;
import java.io.InputStream;
import java.util.List;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView contentView;
private String[] content;//记录标题
private int count = 0;//计数器
private List<News> list=null;//新闻
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//一种说法是android 4.0+后不能直接在主进程中进行网络访问, 要通过新开线程来访问,获取的数据通过Message来发送,主进程通过实现Handler来获取返回数据
new Thread(rssRunnable).start();
}
/**
* 渲染列表
*/
public void renderList(){
if(null != list && list.size()>0){
content = new String[list.size()];
for(News ne : list){
content[count] = ne.getTitle().trim();
count++;
}
contentView =(ListView)findViewById(R.id.rssNews);
//绑定列表项单击事件监听器
contentView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
News temp = list.get(position);
Intent intent = new Intent(MainActivity.this, ShowNewsActivity.class);
intent.putExtra("title",temp.getTitle());
intent.putExtra("pubDate", temp.getPubDate());
intent.putExtra("description", temp.getDescription());
intent.putExtra("link", temp.getLink());
startActivity(intent);
}
});
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_list_item_1, content);
//列表展示适配器
contentView.setAdapter(adapter);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
menu.add(0, 0, 0, "继续");
menu.add(0, 1, 1, "退出");
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case 1:
MainActivity.this.finish();
break;
}
return super.onOptionsItemSelected(item);
}
Runnable rssRunnable = new Runnable(){
@Override
public void run() {
try {
String path = "http://rss.sina.com.cn/sports/global/focus.xml";
//访问网络
InputStream is = NetTool.getStream(path,"UTF-8");
if(null != is){
List<News> list2 = PullNewsService.readXML(is);
Log.i("MainActivity", "list2 size = "+list2.size());
Message msg = new Message();
msg.what=0;
msg.obj = list2;
//发送消息
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
//接收消息
private Handler handler = new Handler(){
@SuppressWarnings("unchecked")
public void handleMessage(Message msg) {
if(0 == msg.what){
list = (List<News>)msg.obj;
Log.i("MainActivity", "list3 size = "+list.size());
renderList();
}
}
};
}
---------------------------
NetTool.java
---------------------------
package com.sharpandroid.pullrssdemo;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.util.Log;
public class NetTool {
/**
* 从指定地址获取RSS输入流
* @param urlpath
* @param encoding
* @return
* @throws ClientProtocolException
* @throws IOException
* @throws Exception
*/
public static InputStream getStream(String urlpath, String encoding){
//此处采用HttpClient来请求外网内容
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(urlpath);
HttpResponse httpResponse;
try {
httpResponse = httpClient.execute(httpGet);
HttpEntity entity = httpResponse.getEntity();
InputStream inputStream = entity.getContent();
Log.i("NetTool", "inputStream="+inputStream.toString());
return inputStream;
} catch (ClientProtocolException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
---------------------------
PullNewsService.java
---------------------------
package com.sharpandroid.pullrssdemo;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import android.util.Xml;
public class PullNewsService {
/**
* 从RSS输入流中提取新闻信息
* @param inputStream
* @return List<News>
* @throws Exception
*/
public static List<News> readXML(InputStream inputStream) throws Exception{
List<News> news_list=null;
News news = null;
boolean flog = false;
//解析器
XmlPullParser parser = Xml.newPullParser();
//设置输入流和编码
parser.setInput(inputStream, "UTF-8");
//读取初始事件编码
int eventCode = parser.getEventType();
//判断文档读取是否结束
while(eventCode != XmlPullParser.END_DOCUMENT){
switch(eventCode){
case XmlPullParser.START_DOCUMENT:{//文档开始事件
news_list =new ArrayList<News>();
break;
}
case XmlPullParser.START_TAG:{
if("item".equals(parser.getName())){
flog = true;//开始标志
news = new News();
}
if(flog){
if("title".equals(parser.getName())){
news.setTitle(parser.nextText());
}else if("link".equals(parser.getName())){
news.setLink(parser.nextText());
}else if("pubDate".equals(parser.getName())){
news.setPubDate(parser.nextText());
}else if("description".equals(parser.getName())){
news.setDescription(parser.nextText());
}else if("author".equals(parser.getName())){
news.setAuthor(parser.nextText());
}else if("guid".equals(parser.getName())){
news.setGuid(parser.nextText());
}else if("category".equals(parser.getName())){
news.setCategory(parser.nextText());
}else if("comments".equals(parser.getName())){
news.setComments(parser.nextText());
}
}
break;
}
case XmlPullParser.END_TAG:{
if("item".equals(parser.getName())){
flog = false;
news_list.add(news);
news = null;
}
break;
}
}
//获取下一事件编码
eventCode = parser.next();
}
//返回处理结果
return news_list;
}
}
---------------------------
ShowNewsActivity.java
---------------------------
package com.sharpandroid.pullrssdemo;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
public class ShowNewsActivity extends Activity {
private TextView titleView, linkView, pubDateView, descriptionView;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.shownews);
titleView = (TextView)findViewById(R.id.titleView);
linkView = (TextView)findViewById(R.id.linkView);
pubDateView = (TextView)findViewById(R.id.pubDateView);
descriptionView = (TextView)findViewById(R.id.descriptionView);
Intent intent = getIntent();
//获取相应数据
String title = intent.getStringExtra("title");
String pubDate = intent.getStringExtra("pubDate");
String description = intent.getStringExtra("description");
String link = intent.getStringExtra("link");
titleView.setText(formatString(title));
linkView.setText(link);
pubDateView.setText(pubDate);
descriptionView.setText(formatString(description));
}
/**
* 利用正则表达式, 格式化字符串
* @param content
* @return
*/
public String formatString(String content){
Pattern p = Pattern.compile("\\s*|\t|\n");
Matcher m = p.matcher(content);
return content.trim();
}
}
---------------------------
AndroidManifest.xml
---------------------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sharpandroid.pullrssdemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<!-- 访问网络权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<uses-library android:name="android.test.runner" />
<activity
android:name="com.sharpandroid.pullrssdemo.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.sharpandroid.pullrssdemo.ShowNewsActivity"
android:label="@string/app_name"></activity>
</application>
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.sharpandroid.pullrssdemo"
android:label="Test For My App"></instrumentation>
</manifest>
---------------------------