ListView联网:多线程、硬盘缓存、容错机制

URLURLConnection 类共同提供与用户所选的 Web 站点的连接。

BufferedReader 的一个实例负责从 Web 站点连接中读取传入的数据。每读取一行代码,文本就被附加到一个 TextView。数据并没有直接指定给 TextView,而是引入了一种设计模式,即创建一个消息对象并将该对象发送到一个处理程序的实例。这是更新 UI 的一种比较可取的方法,对可能需要同时运行多个线程的应用程序而言尤其如此。 实际上,在前面的Snake游戏深入解析篇幅中,我们已经介绍了Android SDK提供的Handle类:A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue,这句话简单明了,相信大家一看就完全明白了。

tView.setText(””);
// 获取输入的URL地址
URL url = new URL(eText.getText().toString());
URLConnection conn = url.openConnection();
//获取网页数据流
BufferedReader rd =new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = “”;
//读取数据
while ((line = rd.readLine()) != null) {
Message lmsg;
lmsg = new Message();
lmsg.obj = line;
lmsg.what = 0;
//将数据显示在界面上
h.sendMessage(lmsg);
}
}
catch (Exception e)
{
//输出异常信息
Log.v(”Browse”, e.toString());
}
}

catch(Exception e)

以上程序,在实际运行中可能会导致异常catch (Exception e) 输出异常信息:
java.net.SocketException: Permission denied(maybe missing INTERNET permission)需要在AndroidManifest.xml中定义相应的权限,注意在也可以定义INTERNET权限,这种权限似乎还是会导致异常,看来两种权限除了作用的范围有所不同,具体的作用也是有些不同的,以后开发中需要注意两者的区别。

关于Java I/O:对数据流的操作都是阻塞的,在一般情况下,我们是不需要考虑这个问题的,但是在Android 实现联网的时候,我们必须考虑到这个问题。比如:从网络上下载一张图片:

public Bitmap returnBitmap(String url){
URL myFileUrl = null;
Bitmap bitmap = null;
try{
myFileUrl = new URL(url);
}catch(MalformedURLException e){
e.printStackTrace();
return null;
};
try{
HttpURLConnection conn = (HttpURLConnection)myFileUrl.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bitmap = BitmapFactroy.decodeStream(is);
is.close();
}catch(IOException e){
e.printStackTrace();
}
return bitmap;
}

由于网络连接需要很长的时间,需要3-5秒,甚至更长的时间才能返回页面的内容。如果此连接动作直接在主线程,也就是UI线程中处理,会发生什么情况呢?整个程序处于等待状态,界面似乎是“死”掉了。为了解决这个问题,必须把这个任务放置到单独线程中运行,避免阻塞UI线程,这样就不会对主线程有任何影响。举个例子如下:

private void connect(String strURL){
new Thread() {
     public void run() {
try {
HttpClient client = new DefaultHttpClient();
// params[0]代表连接的url
HttpGet get = new HttpGet(url.getText().toString());
HttpResponse response = client.execute(get);
HttpEntity entity = response.getEntity();
long length = entity.getContentLength();
InputStream is = entity.getContent();
String s = null;
if (is != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[128];
int ch = -1;
int count = 0;
while ((ch = is.read(buf)) != -1) {
baos.write(buf, 0, ch);
count += ch;
}
s = new String(baos.toByteArray());
Log.V(“moandroid sample”,s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}

使用Handler更新界面
       如何将下载的信息显示在界面上了,比如说下载的进度。

Android SDK平台只允许在主线程中调用相关View的方法来更新界面如果返回结果在新线程中获得,那么必须借助Handler来更新界面。为此,在界面Activity中创建一个Handler对象,并在handleMessage()更新UI
//Task在另外的线程执行,不能直接在Task中更新UI,因此创建了Handler

private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
String m = (String) msg.obj;
message.setText(m);
}
};

//只需要将上面的
Log.V(“moandroid sample”,s);
//替换为:
s = new String(baos.toByteArray());
Message mg = Message.obtain();
mg.obj = s;
handler.sendMessage(mg);

AsyncTask

看上去修改后的connect()方法已经可用了,但是这种匿名程的方式是存在缺陷的:不要阻塞UI线程 +  确保只在UI线程中访问Android UI工具包
主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

其他线程中是不能直接访问主UI线程成员

android提供了几种在其他线程中访问UI线程的方法:

  • Activity.runOnUiThread( Runnable ) 
  • View.post( Runnable ) 
  • View.postDelayed( Runnable, long ) 
  • Hanlder 

这些类或方法同样会使你的代码很复杂很难理解。然而当你需要实现一些很复杂的操作并需要频繁地更新UI时这会变得更糟糕

为了解决这一问题,Android在1.5版本引入了AsyncTask: 它使创建需要与用户界面交互的长时间运行的任务变得更简单。相对来说AsyncTask更轻量级一些,适用于简单的异步处理不需要借助线程和Handler即可实现

AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程中执行,这就有效地避免了使用Handler带来的麻烦。阅读AsyncTask的源码可知,AsyncTask是使用java.util.concurrent 框架来管理线程以及任务的执行的,concurrent框架是一个非常成熟,高效的框架,经过了严格的测试。这说明AsyncTask的设计很好的解决了匿名线程存在的问题

AsyncTask是抽象类

5.png

子类必须实现抽象方法doInBackground(Params… p) ,在此方法中实现任务的执行工作,比如连接网络获取数据等。

通常还应该实现onPostExecute(Result r)方法,因为应用程序关心的结果在此方法中返回。需要注意的是AsyncTask一定要在主线程中创建实例

asyncTask的执行分为四个步骤,每一步都对应一个回调方法,需要注意的是这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。在任务的执行过程中,这些方法被自动调用:

1) 子类化AsyncTask
2) 实现AsyncTask中定义的下面一个或几个方法
onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
doInBackground(Params…), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
onProgressUpdate(Progress…),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
1) Task的实例必须在UI thread中创建
2) execute方法必须在UI thread中调用
3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法
4) 该task只能被执行一次,否则多次调用时将会出现异常
doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。

结论:执行顺序 onPreExecute–>doInBackground–>onPostExecute

AsyncTask<Integer, Void, List<ContactInfo>>

第一个是指 doInBackground中接收的参数的类型
第二个是指onProgressUpdate中接收的参数的类型
第三个是每日doInBackground返回值的类型以及onPostExecute接收的参数的类型

AsyncTask实例个数超过5个,那么假如前5个都运行很长时间的话,那么第6个只能等待机会了。这是AsyncTask的一个限制,而且对于2.3以前的版本无法解决。如果你的应用需要大量的后台线程去执行任务,那么你只能放弃使用AsyncTask,自己创建线程池来管理Thread,或者干脆不用线程池直接使用Thread也无妨。

Android 3.0以后,也即SDK/API 11和以后的版本

Google意识到了AsyncTask的局限性了,从Android 3.0开始对AsyncTask的API做出了一些调整:

  • #execute()提交的任务,按先后顺序每次只运行一个

也就是说它是按提交的次序,每次只启动一个线程执行一个任务,完成之后再执行第二个任务,也就是相当于只有一个后台线程在执行所提交的任务

  • 新增了接口#executeOnExecutor(): 

这个接口允许开发者提供自定义的线程池来运行和调度Thread,如果你想让所有的任务都能并发同时运行,那就创建一个没有限制的线程池(Executors.newCachedThreadPool()),并提供给AsyncTask。这样这个AsyncTask实例就有了自己的线程池而不必使用AsyncTask默认的。

  • 新增了二个预定义的线程池SERIAL_EXECUTORTHREAD_POOL_EXECUTOR :

其实THREAD_POOL_EXECUTOR并不是新增的,之前的就有,只不过之前(Android 2.3)它是AsyncTask私有的,未公开而已。THREAD_POOL_EXECUTOR是一个corePoolSize为5的线程池,也就是说最多只有5个线程同时运行,超过5个的就要等待。所以如果使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)就跟2.3版本的AsyncTask.execute()效果是一样的。

AsyncTask的使用注意事项

根据具体的需求情况而选择合适的工具:

  • 改善你的设计,少用异步处理

线程的开销是非常大的,同时异步处理也容易出错,难调试,难维护,所以改善你的设计,尽可能的少用异步。对于一般性的数据库查询,少量的I/O操作是没有必要启动线程的。

  • 与主线程有交互AsyncTask,否则就用Thread

AsyncTask被设计出来的目的就是为了满足Android的特殊需求:非主线程不能操作(UI)组件,所以AsyncTask扩展Thread增强了与主线程的交互的能力。如果你的应用没有与主线程交互,那么就直接使用Thread就好了。

  • 当有需要大量线程执行任务时,一定要创建线程池

线程的开销是非常大的,特别是创建一个新线程,否则就不必设计线程池之类的工具了。

当需要大量线程执行任务时,一定要创建线程池,无论是使用AsyncTask还是Thread,因为使用AsyncTask 它内部的线程池有数量限制,可能无法满足需求;使用Thread更是要线程池来管理,避免虚拟机创建大量的线程。比如从网络上批量下载图片,你不想一个一个的下,或者5个5个的下载,那么就创建一个CorePoolSize为10或者20的线程池,每次10个或者20个这样的下载,即满足了速度,又不至于耗费无用的性能开销去无限制的创建线程。

  • 对于想要立即开始执行的异步任务,要么直接使用Thread,要么单独创建线程池提供给AsyncTask

默认的AsyncTask不一定会立即执行你的任务,除非你提供给他一个单独的线程池。如果不与主线程交互,直接创建一个Thread就可以了,虽然创建线程开销比较大,但如果这不是批量操作就没有问题。

  • Android的开发没有想像中那样简单,要多花心思和时间在代码上和测试上面,以确信程序是优质的

via

AsyncTask的用法

重写ListView的适配器(BaseAdapter),从网络获取图片,图片缓存的处理,xml的解析。

③xml解析,xml的解析有很多方法,这里采用进行dom方式的xml解析。

/**
* 获取XML DOM元素
* @param XML string
* */
public Document getDomElement(String xml){
Document doc = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {

DocumentBuilder db = dbf.newDocumentBuilder();

InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xml));
doc = db.parse(is);

} catch (ParserConfigurationException e) {
Log.e("Error: ", e.getMessage());
return null;
} catch (SAXException e) {
Log.e("Error: ", e.getMessage());
return null;
} catch (IOException e) {
Log.e("Error: ", e.getMessage());
return null;
}

return doc;
}

读音乐信息是用MediaStore这个大类读取的。正确是MediaStore.Audio.Media.XXXX.但是你要上面效果首先要自定义适配器(Adapter)。在相应的界面定义一个方法,还有就是定义三个变量(id,title,artits)分别代表歌的id,歌的标题,艺术家,先实例化后循环获取它们各自的索引列。然后装载适配器完成。

listview获取图片文字

在线音乐应该就是获取图片。只不过还有下载和在线播放

滑动切换加载网络图片可放大缩小

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

生活在西班牙

自己动手丰衣足食

BlueAsteroid

Just another WordPress.com site

Jing's Blog

Just another WordPress.com site

Start from here......

我的心情魔方

天才遠私廚

希望能做一個分享各種資訊的好地方

语义噪声

西瓜大丸子汤的博客

笑对人生,傲立寰宇

Just another WordPress.com site

Where On Earth Is Waldo?

A Project By Melanie Coles

the Serious Computer Vision Blog

A blog about computer vision and serious stuff

Cauthy's Blog

paper review...

Cornell Computer Vision Seminar Blog

Blog for CS 7670 - Special Topics in Computer Vision

datarazzi

Life through nerd-colored glasses

Luciana Haill

Brainwaves Augmenting Consciousness

槑烎

1,2,∞

Dr Paul Tennent

and the university of nottingham

turn off the lights, please

A bunch of random, thinned and stateless thoughts around the Web

%d bloggers like this: