25 Android文件下载

25.1 Android文件单线程下载

25.1.1 J2SE文件单线程下载

1.J2SE从网络获取图片

3)源码再现

package com.sharpandroid.junitTest;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.net.HttpURLConnection;

import java.net.URL;

import org.junit.Test;

public class InternetTest {

@Test

public void getImage() throws Exception {

// 首先我们要得到请求的路径, 路径为我们想要得到的资源

String urlpath = "http://i1.itc.cn/20100326/3b8_921a1797_

8fb1_4551_b4a3_dad3746f53fb_0.jpg";

URL url = new URL(urlpath); // 建立URL类对象,抛异常

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

//得到urlConnection对象

conn.setRequestMethod("GET"); // 声明请求方式

conn.setConnectTimeout(6 * 1000); // 设置链接超时

if(conn.getResponseCode() == 200) {

InputStream inputStream = conn.getInputStream();

// 得到服务端传回来数据,相对客户为输入流

byte[] data = readInstream(inputStream); // 得到数据

File file = new File("sohu.jpg"); // 创建保存文件

FileOutputStream outputStream = new FileOutputStream(file);

// 创建一个文件的输出流

outputStream.write(data);

// 把所得的二进制数据全部写入到我们建好的文件中

outputStream.close(); // 关闭输出流

}

}

2.J2SE从网络获取文本

1)源码回现

在InternetTest类中添加getHtml()方法,具体代码为:

@Test

public void getHtml() throws Exception {

// 首先我们要得到请求的路径 ,路径为我们想要得到的资源

String urlpath = "http://www.sohu.com";

URL url = new URL(urlpath); // 建立URL类对象,抛异常

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

// 得到urlConnection对象

conn.setRequestMethod("GET"); // 声明请求方式

conn.setConnectTimeout(6 * 1000); // 设置链接超时

if (conn.getResponseCode() == 200) {

InputStream inputStream = conn.getInputStream();

//得到输入流

byte[] data = readInstream(inputStream); //得到数据

System.out.println(new String(data)); //将字节转换为字符串打印

在控制台上

}

}

3.J2SE从网络获取可执行文件

从网络上获取可执行文件和获取图片很相似,在InternetTest类中添加getFile()方法,具体代码如下:

@Test

public void getFile() throws Exception {

// 首先我们要得到请求的路径 ,路径为我们想要得到的资源

String urlpath = "http://ftpcnc-js.pconline.com.cn/pub/download/

201003/Fetion_3.6.1900.exe";

URL url = new URL(urlpath); // 建立URL类对象,抛异常

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

// 得到urlConnection对象

conn.setRequestMethod("GET"); // 声明请求方式

conn.setConnectTimeout(6 * 1000); // 设置链接超时

if (conn.getResponseCode() == 200) {

InputStream inputStream = conn.getInputStream();

// 得到输入流

byte[] data = readInstream(inputStream); // 得到数据

File file = new File("feixin.exe"); // 创建保存文件

FileOutputStream outputStream = new FileOutputStream(file);

// 创建一个文件的输出流

outputStream.write(data);

// 把我们所得的二进制数据全部写入到我们建好的文件中

outputStream.close(); // 关闭输出流

}

}

25.1.2 Android客户端单线程下载

1.Android客户端从网络获取文本

2)源码再现

第一步:编写res下values中的string.xml文件。

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">Hello World, DataActivity!</string>

<string name="app_name">从internet中获取数据</string>

<string name="path">图片路径</string>

<string name="button">获取图片</string>

</resources>

第二步:编写res下Layout中的main.xml文件。

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/path"

/>

<EditText

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text=" http://i1.itc.cn/20100326/3b8_921a1797_8fb1_4551_

b4a3_dad3746f53fb_0.jpg "

android:id="@+id/path"

></EditText>

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/button"

android:id="@+id/button"

></Button>

<ImageView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:id="@+id/ImageView"

></ImageView>

</LinearLayout>

第三步:编写核心代码DataActivity.java。

package com.sharpandroid.activity;

import com.sharpandroid.net.utils.NetTool;

import android.app.Activity;

import android.content.Intent;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import
android.widget.ImageView;

import android.widget.Toast;

public class DataActivity extends Activity {

private static final String TAG ="DataActivity";

private EditText imageEditText;

private ImageView imageView;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

imageEditText = (EditText) this.findViewById(R.id.path);

//根据id得到EditText

imageView = (ImageView) this.findViewById(R.id.ImageView);

//根据 id得到imageView

Button button = (Button) this.findViewById(R.id.button);

//根据id得到button

button.setOnClickListener(new View.OnClickListener() {

//给button添加一个单击事件

@Override

public void onClick(View v) {

String path = imageEditText.getText().toString();//从imageEditText中取出

路径

try {

byte [] data =NetTool.getImage(path); //得到图片

Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);

imageView.setImageBitmap(bm);

} catch (Exception e) {

Log.i(TAG, e.getMessage());//有异常打印在控制台上

Toast.makeText(DataActivity.this, "获取图片失败", 1).show();

}

}

});

}

}

新建工具类NetTool.java。

package com.sharpandroid.net.utils;

import java.io.ByteArrayOutputStream;

import java.io.InputStream;

import java.net.HttpURLConnection;

import java.net.URL;

public class NetTool {

public static byte[] getImage(String path) throws Exception {

// 首先我们要得到请求的路径 ,路径为我们想要得到的资源

URL url = new URL(path); // 建立URL类对象,抛

异常

HttpURLConnection conn = (HttpURLConnection) url.openConnection();// 得到

urlConnection对象

conn.setRequestMethod("GET"); // 声明请求方式

conn.setConnectTimeout(6 * 1000); // 设置链接超时

if (conn.getResponseCode() == 200) {

InputStream inputStream = conn.getInputStream(); // 得到输入流

return readInstream(inputStream); // 返回得到的数组

}

return null;//

}

// 读取流文件的内容

public static byte[] readInstream(InputStream inputStream) throws

Exception {

ByteArrayOutputStream byteArrayOutputStream =

new ByteArrayOutputStream(); // 创建ByteArrayOutputStream类

byte[] buffer = new byte[1024]; // 声明缓冲区

int length = -1;// 定义读取默认的长度

while ((length = inputStream.read(buffer)) != -1) {

byteArrayOutputStream.write(buffer, 0, length);// 把缓存区中输

出到内存中

}

byteArrayOutputStream.close(); // 关闭输出流

inputStream.close();// 关闭输入流

return byteArrayOutputStream.toByteArray();// 返回这个输出流的字节数组

}

}

2.Android客户端从网络获取文本

1)实现流程

第一步,新添加两个布局文件:gettext.xml和text.xml。

gettext.xml中代码:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:orientation="vertical">

<EditText android:id="@+id/text"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="http://www.sohu.com"

></EditText>

<Button android:id="@+id/textbutton"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/textbutton"

></Button>

</LinearLayout>

text.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>

<ScrollView

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width=fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical">

<TextView android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:id="@+id/sohu"/>

</ScrollView>

在main.xml文件中添加一个Button控件:

<Button android:id="@+id/totextbutton"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/toTextButton"

></Button>

第二步:

(1)在NetTool.java类中添加方法getHtml()。

public static String getHtml(String path,String encoding) throws

Exception {

URL url = new URL(path); // 建立URL类对象,抛出异常

HttpURLConnection conn = (HttpURLConnection)

url.openConnection(); // 得到urlConnection对象

conn.setRequestMethod("GET"); // 声明请求方式

conn.setConnectTimeout(6 * 1000); // 设置链接超时

if (conn.getResponseCode() == 200) {

InputStream inputStream = conn.getInputStream();

// 得到输入流

byte[] data = readInstream(inputStream); // 得到数据

return new String(data,encoding);

//将字节转换为字符串打印在控制台上

}

return null;

}

(2)在DataActivity.java中添加totextbutton的单击事件。

Button totextbutton = (Button) this.findViewById(R.id.totextbutton);

totextbutton.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Intent intent = new Intent(DataActivity.this,

SohuActivity.class);

startActivity(intent);

}

});

(3)在com.sharpandroid.activity中,新建一个继承了android.app.Activity的SohuActivity. java类。

package com.sharpandroid.activity;

import com.sharpandroid.net.utils.NetTool;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

import android.widget.Toast;

public class SohuActivity extends Activity {

private static final String TAG ="GetTextActivity";

private EditText editText;

private Button textButton;

private TextView textView;

@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

setContentView(R.layout.gettext);

editText = (EditText) this.findViewById(R.id.text);

textButton = (Button) this.findViewById(R.id.textbutton);

textButton.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

String path = editText.getText().toString();

try {

String html = NetTool.getHtml(path,"GBK");

setContentView(R.layout.text);

textView = (TextView)SohuActivity.this.findViewById

(R.id.sohu);

textView.setText(html);

Toast.makeText(SohuActivity.this, "获取文本成功", Toast.

LENGTH_LONG).show();

} catch (Exception e) {

Toast.makeText(SohuActivity.this, "获取文本失败", Toast.

LENGTH_LONG).show();

e.printStackTrace();

}

}

});

}

}

25.2 Android文件多线程下载

25.2.1 J2SE文件多线程下载

源码再现:

package junit.test;

import java.io.InputStream;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import org.junit.Test;

public class DownLoader {

@Test

public void download() throws Throwable{

String filename ="qqGame.exe";

String path="http://dl_dir.qq.com/minigamefile/QQGame2010Beta1

Patch1_setup_web.EXE";

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.openConnection();

conn.setConnectTimeout(5*1000);

conn.setRequestMethod("GET");

int filelength = conn.getContentLength(); //获取下载文件的长度

System.out.println(filelength);

RandomAccessFile file = new RandomAccessFile(filename, "rw");

file.setLength(filelength); //设置本地文件的长度

file.close();

conn.disconnect();

int threadsize = 3; //线程数

int threadlength=filelength % 3==0? filelength/3 : filelength/3+1;

//每条线程下载的长度

for(int i=0; i<threadsize ; i++){

int startposition = i * threadlength;

//计算每条线程应该从文件的什么位置开始下载

RandomAccessFile threadfile = new RandomAccessFile(filename,"

rw");

threadfile.seek(startposition);//从文件的什么位置开始写入数据

//启动3条线程分别从startposition指定的位置下载文件

new DownLoadThread(i, path, startposition, threadfile, threadl

ength).start();

}

int quit = System.in.read();

while('q' != quit){

Thread.sleep(2*1000);

}

}

private class DownLoadThread extends Thread{

private int threadid;

private int startposition;

private RandomAccessFile threadfile;

private int threadlength;

private String path;

public DownLoadThread(int threadid, String path, int startposition,

RandomAccessFile threadfile, int threadlength) {

this.threadid = threadid;

this.startposition = startposition;

this.threadfile = threadfile;

this.threadlength = threadlength;

this.path = path;

}

@Override

public void run() {

try {

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.openCon

nection();

conn.setConnectTimeout(5*1000);

conn.setRequestMethod("GET");

conn.setRequestProperty("Range", "bytes="+startposi

tion+ "-");

//指定从文件的什么位置开始下载

InputStream inStream = conn.getInputStream();

byte[] buffer = new byte[1024];

int len = -1;

int length = 0;

while(length<threadlength && (len = inStream.read

(buffer))!=-1){

threadfile.write(buffer, 0, len);

length += len; //累计下载的长度

}

threadfile.close();

inStream.close();

System.out.println("线程"+ (threadid+1)+ "已经下载完

成");

} catch (Exception e) {

System.out.println("线程"+ (threadid+1)+ "下载出错:"+ e);

}

}

}

}

25.2.2 Android客户端断点、多线程下载

1.准备阶段

String.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">Hello World, DownLoaderActivity!</string>

<string name="app_name">多线程下载器</string>

<string name="path">下载路径</string>

<string name="button">下载</string>

<string name="success">下载成功</string>

<string name="fail">下载失败</string>

<string name="sdcarderror">SDCard不存在或者写保护</string>

</resources>

Main.xml中的代码:

<?xml version="1.0" encoding="utf-8" ?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/

android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<TextView android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/path" />

<EditText android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="http://

bj.itxiazai.com/soft/media/music/ITxiazai_ttpsetup_552.exe"android:id=

"@+id/path" />

<Button android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/button"

android:id="@+id/button" />

<ProgressBar android:layout_width="fill_parent"

android:layout_height="18dip"

style="?android:attr/progressBarStyleHorizontal"

android:id="@+id/progressBar" />

<TextView android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center"

android:id="@+id/result" />

</LinearLayout>

2.数据库阶段

1)完成activity中的代码,给button添加单击事件

首先我们把我们页面上需要的控件都找出来。

public class DownLoaderActivity extends Activity {

private EditText pathText;

private TextView resultView;

private ProgressBar progressBar;

private Button button;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

resultView = (TextView) this.findViewById(R.id.result);

//文件的进度的信息

button = (Button) this.findViewById(R.id.button);

//得到下载按钮

progressBar = (ProgressBar)this.findViewById(R.id.downloadbar);

//得到下载的进度条

pathText = (EditText) this.findViewById(R.id.path);

//得到下载路径

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

}

});

}

}

2)在单击事件的onClick()方法中,下载文件并保存到SD卡中

下面是在这个类中的代码:

package com.sharpandroid.service;

import android.content.Context;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

public class DBOpenHelper extends SQLiteOpenHelper {

private static final String DBNAME = "sharpandroid.db";

private static final int VERSION = 1;

public DBOpenHelper(Context context) {

super(context, DBNAME, null, VERSION);

}

创建表:

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL("CREATE TABLE IF NOT EXISTS filedown (id integer primary

key autoincrement, downpath varchar(100), threadid INTEGER, position

INTEGER)");

}

对表的表本进行更新:

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion,int newVersion){

db.execSQL("DROP TABLE IF EXISTS filedown");

onCreate(db);

}

}

大致:呵呵,还是不错的嘛!下面我来完成在工程中需要的对线程相关的业务操作。

3)对各个线程的下载记录进行操作

FileService.java

import java.util.HashMap;

import java.util.Map;

import android.content.Context;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

/**

* 业务bean

*

*/

public class FileService {

private DBOpenHelper openHelper;

public FileService(Context context) {

openHelper = new DBOpenHelper(context);

}

/**

* 获取线程最后下载位置

* @param path

* @return

*/

public Map<Integer, Integer> getData(String path){

SQLiteDatabase db = openHelper.getReadableDatabase();

Cursor cursor = db.rawQuery("select threadid, position from

filedown where downpath=?", new String[]{path});

Map<Integer, Integer> data = new HashMap<Integer, Integer>();

while(cursor.moveToNext()){

data.put(cursor.getInt(0), cursor.getInt(1));

}

cursor.close();

db.close();

return data;

}

/**

* 保存下载线程初始位置

* @param path

* @param map

*/

public void save(String path, Map<Integer, Integer> map){//int

threadid, int position

SQLiteDatabase db = openHelper.getWritableDatabase();

db.beginTransaction();

try{

for(Map.Entry<Integer, Integer> entry : map.entrySet()){

db.execSQL("insert into filedown(downpath, threadid,

position) values(?,?,?)",

new Object[]{path, entry.getKey(), entry.getValue()});

}

db.setTransactionSuccessful();

}finally{

db.endTransaction();

}

db.close();

}

/**

* 实时更新线程的最后下载位置

* @param path

* @param map

*/

public void update(String path, Map<Integer, Integer> map){

SQLiteDatabase db = openHelper.getWritableDatabase();

db.beginTransaction();

try{

for(Map.Entry<Integer, Integer> entry : map.entrySet()){

db.execSQL("update filedown set position=? where downpath=?

and threadid=?",

new Object[]{entry.getValue(), path, entry.

getKey()});

}

db.setTransactionSuccessful();

}finally{

db.endTransaction();

}

db.close();

}

/**

* 当文件下载完成后,清掉该文件对应的下载记录

* @param path

*/

public void delete(String path){

SQLiteDatabase db = openHelper.getWritableDatabase();

db.execSQL("delete from filedown where downpath=?", new Object

[]{path});

db.close();

}

}

3.实现文件下载阶段

新建com.sharpandroid.net.download包下FileDownloader.java类。

package com.sharpandroid.net.download;

import java.io.File;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.LinkedHashMap;

import java.util.Map;

import java.util.UUID;

import java.util.concurrent.ConcurrentHashMap;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

import com.sharpandroid.service.FileService;

import android.content.Context;

import android.util.Log;

/**

* 文件下载器

*/

public class FileDownloader {

private Context context;

private FileService fileService;

private static final String TAG = "FileDownloader";

/* 已下载文件大小 */

private int downloadSize = 0;

/* 原始文件大小 */

private int fileSize = 0;

/* 线程数 */

private DownloadThread[] threads;

/* 下载路径 */

private URL url;

/* 本地保存文件 */

private File saveFile;

/* 下载记录文件 */

private File logFile;

/* 缓存各线程最后下载的位置*/

private Map<Integer, Integer> data = new ConcurrentHashMap<Integer,

Integer>();

/* 每条线程下载的大小 */

private int block;

private String downloadUrl;//下载路径

/**

* 获取线程数

*/

public int getThreadSize() {

return threads.length;

}

/**

* 获取文件大小

* @return

*/

public int getFileSize() {

return fileSize;

}

/**

* 累计已下载大小

* @param size

*/

protected synchronized void append(int size) {

downloadSize += size;

}

/**

* 更新指定线程最后下载的位置

* @param threadId 线程id

* @param pos 最后下载的位置

*/

protected void update(int threadId, int pos) {

this.data.put(threadId, pos);

}

/**

* 保存记录文件

*/

protected synchronized void saveLogFile() {

this.fileService.update(this.downloadUrl, this.data);

}

第三步,构造下载器。

/**

* 构建文件下载器

* @param downloadUrl 下载路径

* @param fileSaveDir 文件保存目录

* @param threadNum 下载线程数

*/

public FileDownloader(Context context,String downloadUrl,File fileSa

veDir,int threadNum) {

try {

this.context = context;

this.downloadUrl = downloadUrl;

fileService = new FileService(context);

this.url = new URL(downloadUrl);

if(!fileSaveDir.exists()) fileSaveDir.mkdirs();

this.threads = new DownloadThread[threadNum];

HttpURLConnection conn = (HttpURLConnection) url.openConn

ection();

conn.setConnectTimeout(6*1000);

conn.setRequestMethod("GET");

conn.setRequestProperty("Accept", "image/gif, image/jpeg,

image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/

xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap,

application/x-ms-application,application/vnd.ms-excel,application/vnd.m

s-powerpoint,

application/msword, */*");

conn.setRequestProperty("Accept-Language", "zh-CN");

conn.setRequestProperty("Referer", downloadUrl);

conn.setRequestProperty("Charset", "UTF-8");

conn.setRequestProperty("User-Agent","Mozilla/4.0

(compatible;MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET

CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR

3.5.30729)");

conn.setRequestProperty("Connection", "Keep-Alive");

conn.connect();

printResponseHeader(conn); //打印返回服务器的响应数据

if (conn.getResponseCode()==200) {

this.fileSize = conn.getContentLength();

//根据响应获取文件大小

if (this.fileSize <= 0) throw new RuntimeException("无法

获知文件大小");

String filename = getFileName(conn);

this.saveFile = new File(fileSaveDir, filename);

/* 保存文件 */

Map<Integer,Integer> logdata = fileService.getData

(downloadUrl);

if(logdata.size()>0){

data.putAll(logdata);

}

this.block = this.fileSize / this.threads.length + 1;

if(this.data.size()==this.threads.length){

for (int i = 0; i < this.threads.length; i++) {

this.downloadSize += this.data.get(i+1)-(this.

block * i);

}

print("已经下载的长度"+ this.downloadSize);

}

}else{

throw new RuntimeException("服务器响应错误 ");

}

} catch (Exception e) {

print(e.toString());

throw new RuntimeException("连接不到下载路径 ");

}

}

/**

* 获取文件名

*/

private String getFileName(HttpURLConnection conn) {

String filename = this.url.toString().substring(this.url.toString

().lastIndexOf('/') + 1);

if(filename==null || "".equals(filename.trim())){

//如果获取不到文件名称

for (int i = 0;; i++) {

String mine = conn.getHeaderField(i);

if (mine == null) break;

if("content-disposition".equals(conn.getHeaderFieldKey

(i).toLowerCase())){

Matcher m = Pattern.compile(".*filename=(.*)").

matcher(mine.toLowerCase());

if(m.find()) return m.group(1);

}

}

filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名

}

return filename;

}

第四步,实现下载功能,并同时可以得到此时各个线程的下载数量。

/**

* 开始下载文件

* @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为

null

* @return 已下载文件大小

* @throws Exception

*/

public int download(DownloadProgressListener listener) throws

Exception{

try {

if(this.data.size() != this.threads.length){

this.data.clear();

for (int i = 0; i < this.threads.length; i++) {

this.data.put(i+1, this.block * i);

}

}

for (int i = 0; i < this.threads.length; i++) {

int downLength = this.data.get(i+1) - (this.block * i);

if(downLength < this.block && this.data.get(i+1)<this.

fileSize){ //该线程未完成下载时,继续下载

RandomAccessFile randOut = new RandomAccessFile(this.

saveFile, "rw");

if(this.fileSize>0) randOut.setLength(this.fileSize);

randOut.seek(this.data.get(i+1));

this.threads[i] = new DownloadThread(this, this.url,

randOut, this.block, this.data.get(i+1), i+1);

this.threads[i].setPriority(7);

this.threads[i].start();

}else{

this.threads[i] = null;

}

}

this.fileService.save(this.downloadUrl, this.data);

boolean notFinish = true; //下载未完成

while (notFinish) { // 循环判断是否下载完毕

Thread.sleep(900);

notFinish = false; //假定下载完成

for (int i = 0; i < this.threads.length; i++){

if (this.threads[i] != null && !this.threads[i].

isFinish()) {

notFinish = true; //下载没有完成

if(this.threads[i].getDownLength() == -1){

//如果下载失败,再重新下载

RandomAccessFile randOut = new RandomAcc

essFile(this.saveFile, "rw");

randOut.seek(this.data.get(i+1));

this.threads[i] = new DownloadThread(this,

this.url, randOut, this.block, this.data.get(i+1), i+1);

this.threads[i].setPriority(7);

this.threads[i].start();

}

}

}

if(listener!=null) listener.onDownloadSize(this.downloadSize);

}

fileService.delete(this.downloadUrl);

} catch (Exception e) {

print(e.toString());

throw new Exception("下载失败");

}

return this.downloadSize;

}

/**

* 获取HTTP响应头字段

* @param http

* @return

*/

public static Map<String, String> getHttpResponseHeader(HttpURLConne

ction http) {

Map<String, String> header = new LinkedHashMap<String, String>();

for (int i = 0;; i++) {

String mine = http.getHeaderField(i);

//得到响应头的各个属性

if (mine == null) break;

header.put(http.getHeaderFieldKey(i), mine);

//得到各个属性值

}

return header;

}

/**

* 打印HTTP头字段

* @param http

*/

public static void printResponseHeader(HttpURLConnection http){

Map<String, String> header = getHttpResponseHeader(http);

for(Map.Entry<String, String> entry : header.entrySet()){

String key = entry.getKey()!=null ? entry.getKey()+":" : "";

print(key+ entry.getValue());

}

}

private static void print(String msg){

Log.i(TAG, msg);

}

}

编写DownloadThread.java:

package com.sharpandroid.net.download;

import java.io.InputStream;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import android.util.Log;

public class DownloadThread extends Thread {

private static final String TAG = "DownloadThread";

private RandomAccessFile saveFile;

private URL downUrl;

private int block;

/* 下载开始位置 */

private int threadId = -1;

private int startPos;

private int downLength;

private boolean finish = false;

private FileDownloader downloader;

public DownloadThread(FileDownloader downloader, URL downUrl,

RandomAccessFile saveFile, int block, int startPos, int threadId) {

this.downUrl = downUrl;

this.saveFile = saveFile;

this.block = block;

this.startPos = startPos;

this.downloader = downloader;

this.threadId = threadId;

this.downLength = startPos - (block * (threadId - 1));

}

@Override

public void run() {

if(downLength < block){//未下载完成

try {

HttpURLConnection http = (HttpURLConnection) downUrl.

openConnection();

http.setRequestMethod("GET");

http.setRequestProperty("Accept", "image/gif,

image/jpeg,image/pjpeg, image/pjpeg, application/x-shockwave-flash,

application/xaml+xml, application/vnd.ms-xpsdocument, application/

x-ms-xbap,application/x-ms-application,application/vnd.ms-excel,applica

tion/vnd.ms-powerpoint, application/msword, */*");

http.setRequestProperty("Accept-Language", "zh-CN");

http.setRequestProperty("Referer", downUrl.toString());

http.setRequestProperty("Charset", "UTF-8");

http.setRequestProperty("Range", "bytes=" + this.

startPos + "-");

http.setRequestProperty("User-Agent", "Mozilla/4.0

(compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET

CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR

3.5.30729)");

http.setRequestProperty("Connection", "Keep-Alive");

InputStream inStream = http.getInputStream();

int max = block>1024 ? 1024 : (block>10 ? 10 : 1);

byte[] buffer = new byte[max];

int offset = 0;

print("线程 " + this.threadId + "从位置"+ this.startPos+

"开始下载 ");

while(downLength < block && (offset=inStream.read(buffer,0,

max)) != -1) {

saveFile.write(buffer, 0, offset);

downLength += offset;

downloader.update(this.threadId, block * (threadId –

1) + downLength);

downloader.saveLogFile();

downloader.append(offset);

int spare = block-downLength;//求剩下的字节数

if(spare < max) max = (int) spare;

}

saveFile.close();

inStream.close();

print("线程 " + this.threadId + "完成下载 ");

this.finish = true;

this.interrupt();

} catch (Exception e) {

this.downLength = -1;

print("线程"+ this.threadId+ ":"+ e);

}

}

}

private static void print(String msg){

Log.i(TAG, msg);

}

/**

* 下载是否完成

* @return

*/

public boolean isFinish() {

return finish;

}

/**

* 已经下载的内容大小

* @return 如果返回值为-1,代表下载失败

*/

public long getDownLength() {

return downLength;

}

}

编写DownloadProgressListener.java:

package com.sharpandroid.net.download;

public interface DownloadProgressListener {

public void onDownloadSize(int size);

}

完成Activity中的内容:

package com.sharpandroid.download.activity;

import java.io.File;

import com.sharpandroid.net.download.DownloadProgressListener;

import com.sharpandroid.net.download.FileDownloader;

import android.app.Activity;

import android.os.Bundle;

import android.os.Environment;

import android.os.Handler;

import android.os.Message;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.ProgressBar;

import android.widget.TextView;

import android.widget.Toast;

public class DownLoaderActivity extends Activity {

private static final String TAG = "DownLoaderActivity";

private EditText pathText;

private ProgressBar progressBar;

private TextView resultView;

private Handler handler = new Handler(){

@Override

public void handleMessage(Message msg) {

if(!Thread.currentThread().isInterrupted()){

switch (msg.what) {

case 1:

int size = msg.getData().getInt("size");

progressBar.setProgress(size);//设置当前刻度

int result =(int)(((float)progressBar.getProgress()/

(float)progressBar.getMax()) * 100);

resultView.setText(result+ "%");

if(progressBar.getMax()==progressBar.getProgress()){

Toast.makeText(DownLoaderActivity.this, R.

string.success, 1).show();

}

break;

case -1:

String error = msg.getData().getString("error");

Toast.makeText(DownLoaderActivity.this, error, 1).

show();

break;

}

}

super.handleMessage(msg);

}

};//用于往线程绑定的消息队列中发送或者处理消息

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

pathText = (EditText)findViewById(R.id.path);

progressBar = (ProgressBar)findViewById(R.id.progressBar);

resultView = (TextView)findViewById(R.id.result);

Button button = (Button)findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

//下载文件,并保存到SDCard

if(Environment.getExternalStorageState().equals

(Environment.MEDIA_MOUNTED)){

try {

download(pathText.getText().toString(),

Environment.getExternalStorageDirectory());//1

} catch (Exception e) {

Toast.makeText(DownLoaderActivity.this,

R.string.fail, 1).show();

Log.e(TAG, e.toString());

}

}else{

Toast.makeText(DownLoaderActivity.this,

R.string.sdcarderror, 1).show();

}

}

});

}

//运行在主线程(UI线程负责显示控件的显示更新[重绘界面])

private void download(final String path, final File saveDir) throws

Exception{

new Thread(new Runnable() {

@Override

public void run() {

try {

FileDownloader downloader = new FileDownloader

(DownLoaderActivity.this, path, saveDir, 3);

progressBar.setMax(downloader.getFileSize());

//设置进度条的最大刻度

downloader.download(new DownloadProgressListener() {

@Override

public void onDownloadSize(int size) {

//得到文件实时的下载长度

Message msg = new Message();

msg.what = 1;

msg.getData().putInt("size", size);

handler.sendMessage(msg);

}

});

} catch (Exception e) {

Message msg = new Message();

msg.what = -1;

msg.getData().putString("error", "下载失败");

handler.sendMessage(msg);

Log.e(TAG, e.toString());

}

}

}).start();

}

}

在download方法中的代码:

try {loader.download(new DownloadProgressListener() {

@Override

public void onDownloadSize(int size) {//得到当前下载的数量 ...

msg.setData(data);

handler.sendMessage(msg);

}

在Handle的内部类的中的代码:

public void handleMessage(Message msg) {

if(!Thread.currentThread().isInterrupted()){

...

Toast.makeText(DownLoadActivity.this, error, 1).show();

break;

}

}

super.handleMessage(msg);

}

};

最后在AndroidManifest.xml加入相关的权限:

<!--

访问internet权限

-->

<uses-permission android:name="android.permission.INTERNET" />

- <!--

在SDCard中创建与删除文件权限

-->

<uses-permission android:name="android.permission.

MOUNT_UNMOUNT_FILESYSTEMS" />

- <!--

往SDCard写入数据权限

-->

<uses-permission android:name="android.permission.

WRITE_EXTERNAL_STORAGE" />

26 Android文件上传

26.1 Android客户端上传初体验

26.1.1 知识回顾

3.第三步,核心代码实现

新建一个Contact.java类:

package com.sharpandroid.domain;

public class Contact {

private Integer id;

private String name;

private short age;

public Contact(){

}

public Contact(Integer id, String name, short age) {

super();

this.id = id;

this.name = name;

this.age = age;

}

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public short getAge() {

return age;

}

public void setAge(short age) {

this.age = age;

}

}

新建ContactAction类图解。

package com.sharpandroid.action;

import java.util.ArrayList;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;

import org.apache.struts.action.ActionForm;

import org.apache.struts.action.ActionForward;

import org.apache.struts.action.ActionMapping;

import com.sharpandroid.domain.Contact;

public class ContactAction extends Action {

@Override

public ActionForward execute(ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response)

throws Exception {

List<Contact> contacts = new ArrayList<Contact>();

contacts.add(new Contact(01, "laoli", (short) 25));

contacts.add(new Contact(02, "laozhang", (short) 28));

contacts.add(new Contact(03,"laowen",(short) 30));

contacts.add(new Contact(04,"laohou ",(short) 29));

request.setAttribute("contacts", contacts);

return mapping.findForward("contacts");

}

}

26.1.2 Android客户端直通CRM系统

2.第二步,核心代码实现

2)新建一个Contact.java实体类

package com.sharpandroid.domain;

public class Contact {

private Integer id;

private String name;

private short age;

public Contact(){

}

public Contact(Integer id, String name, short age) {

super();

this.id = id;

this.name = name;

this.age = age;

}

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public short getAge() {

return age;

}

public void setAge(short age) {

this.age = age;

}

@Override

public String toString() {

return "Contact [age=" + age + ", id=" + id + ", name=" + name

+ "]";

}

}

代码解析:

分别定义了联系人Contact对象的id、name、age属性,并生成有参数和无参数的构造方法以及相应的set和get方法,为了后面输出的需要,覆盖它的toString方法。

3)建立一个ContactService.java业务bean

package com.sharpandroid.service;

import java.io.InputStream;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.List;

import javax.xml.parsers.SAXParser;

import javax.xml.parsers.SAXParserFactory;

import com.sharpandroid.domain.Contact;

public class ContactService{

/**

* 获取CRM返回的客户数据

* @return

*/

private static InputStream requestContact() throws Exception{

String path = "http://192.168.1.100:8080/CRM/contacts.do";

URL url = new URL(path);

HttpURLConnection conn =(HttpURLConnection)url.openConnection();

conn.setConnectTimeout(5 * 1000);

conn.setRequestMethod("GET");

return conn.getInputStream();

}

}

4)新建一个继承org.xml.sax.helpers.DefaultHandler的ContactDefaultHandler.java类

package com.sharpandroid.service;

import java.util.ArrayList;

import java.util.List;

import org.xml.sax.Attributes;

import org.xml.sax.SAXException;

import org.xml.sax.helpers.DefaultHandler;

import com.sharpandroid.domain.Contact;

public class ContactDefaultHandler extends DefaultHandler {

private List<Contact> contacts;

private Contact contact;//记录当前person

private String preTag;//记录前一个标签的名称

public List<Contact> getContacts() {

return contacts;

}

/**

* 第一个参数为:整个xml字符串

*/

@Override

public void characters(char[] ch, int start, int length) throws

SAXException {

if(preTag!=null){

String data = new String(ch, start, length);

if("name".equals(preTag)){

contact.setName(data);

}else if("age".equals(preTag)){

contact.setAge(new Short(data));

}

}

}

//适合在此方法完成初始化行为

@Override

public void startDocument() throws SAXException {

contacts = new ArrayList<Contact>();

}

@Override

public void startElement(String uri, String localName, String qName,

Attributes attributes) throws SAXException {

if("contact".equals(localName)){

Integer id = new Integer(attributes.getValue(0));

contact = new Contact();

contact.setId(id);

}

preTag = localName;

}

@Override

public void endElement(String uri, String localName, String qName)

throws SAXException {

if("contact".equals(localName)){

contacts.add(contact);

contact = null;

}

preTag = null;

}

}

5)在ContactService类中创建getContacts()方法

public static List<Contact> getContacts() throws Exception{

InputStream inputStream = requestContact();

SAXParserFactory factory = SAXParserFactory.newInstance();

SAXParser saxParser = factory.newSAXParser();

ContactDefaultHandler handler = new ContactDefaultHandler();

saxParser.parse(inputStream, handler);

inputStream.close();

return handler.getContacts();

}

6)在com.sharpandroid.com.activity中建立单元测试类ContactTest.java

public class ContactTest extends AndroidTestCase {

private static final String TAG = "ContactTest";

public void testContacts() throws Throwable{

List<Contact> contacts = ContactService.getContacts();

for(Contact contact : contacts){

Log.i(TAG, contact.toString());

}

}

}

26.2 Android客户端实现请求参数数据上传

26.2.1 知识回顾

2.第二步,核心代码实现

1)新建一个客户信息实体

继承了org.apache.struts.action.ActionForm的ContactForm。

package com.sharpandroid.formbean;

import org.apache.struts.action.ActionForm;

public class ContactForm extends ActionForm {

private String name;

private short age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public short getAge() {

return age;

}

public void setAge(short age) {

this.age = age;

}

}

2)新建一个处理业务操作ContactManageAction.java类

package com.sharpandroid.action;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;

import org.apache.struts.action.ActionForward;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.actions.DispatchAction;

import com.sharpandroid.formbean.ContactForm;

public class ContactManageAction extends DispatchAction {

//保存数据

public ActionForward save(ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response)

throws Exception {

ContactForm formbean = (ContactForm)form;

System.out.println("name="+ formbean.getName());//姓名

System.out.println("age="+ formbean.getAge());//年龄

return mapping.findForward("result");

}

}

26会那么高呢,原来里面有这么多的学问啊!

第一步,业务分析

新建一个单元测试类。

package com.sharpandroid.test;

import java.io.DataOutputStream;

import java.net.HttpURLConnection;

import java.net.URL;

import org.junit.Test;

public class SendDataToCRMTest {

private String name;

private Short age;

@Test

public void testSave() throws Exception{

save(name,age);

}

}

新建testSave()单元测试方法:

private void save(String name, Short age) throws Exception{

String path ="http://192.168.1.100:8080/CRM/contact/

manage.do";

String params ="method=save&name=li&age=36";

byte[] data = params.getBytes();

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.

openConnection();

conn.setDoOutput(true); //允许对外发送请求参数

conn.setUseCaches(false); //不进行缓存

conn.setConnectTimeout(5 * 1000);

conn.setRequestMethod("POST");

//下面设置http请求头

conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg,

image/pjpeg, application/x-shockwave-flash, application/xaml+xml,

application/vnd.ms-xpsdocument, application/x-ms-xbap, application/vnd.

application/x-ms-application, application/vnd.ms-excel, ms-powerpoint,

application/msword, */*");

conn.setRequestProperty("Accept-Language", "zh-CN");

conn.setRequestProperty("User-Agent", "Mozilla/4.0

(compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET

CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR

3.5.30729)");

conn.setRequestProperty("Content-Type", "application/

x-www-form-urlencoded");

conn.setRequestProperty("Content-Length", String.valueOf

(data.length));

conn.setRequestProperty("Connection", "Keep-Alive");

//发送参数

DataOutputStream outStream = new DataOutputStream

(conn.getOutputStream());

outStream.write(data); //把参数发送出去

outStream.flush();

outStream.close();

if(conn.getResponseCode()==200){

byte[] returndata = StreamTool.readInputStream(conn.

getInputStream());

System.out.print(new String(returndata));

}

}

}

编写StreamTool.java:

public class StreamTool {

public static byte[] readInputStream(InputStream inputStream) throws

Exception {

byte[] buffer = new byte[1024];

int len = -1;

ByteArrayOutputStream outSteam = new ByteArrayOutputStream();

while( (len = inputStream.read(buffer)) != -1 ){

outSteam.write(buffer, 0, len);

}

outSteam.close();

inputStream.close();

return outSteam.toByteArray();

}

}

26.2.3 Android客户端之请求参数优化

1.解决Android客户端的中文乱码问题

1)第一种情况,J2EE中表单的post提交上传数据中文乱码问题

用这种方式建立Filter可以自动在web.xml文件中进行配置:

<filter>

<display-name>EncodingFilter</display-name>

<filter-name>EncodingFilter</filter-name>

<filter-class>com.sharpandroid.filter.EncodingFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>EncodingFilter</filter-name>

<url-pattern>*.do</url-pattern>

</filter-mapping>

程序代码:

package com.sharpandroid.filter;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

public class EncodingFilte implements Filter {

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

HttpServletRequest req = (HttpServletRequest) request;

req.setCharacterEncoding("UTF-8");

chain.doFilter(request, response);

}

public void init(FilterConfig fConfig) throws ServletException {

}

public void destroy() {

}

3)第三种,解决客户端应用中get方式发送请求参数中文乱码问题

实现代码如下:

@Test

public void testGetSendData() throws Throwable{

//以get方式发送请求参数

String path = "http://192.168.1.100:8080/CRM/contact/

manage.do?method=save&name="+ URLEncoder.encode("世界","UTF-8") + "

&age=30";

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.

openConnection();

conn.setConnectTimeout(5 * 1000);

conn.setRequestMethod("GET");

if(conn.getResponseCode()==200){

byte[] data = StreamTool.readInputStream(conn.

getInputStream());

System.out.println(new String(data));;

}

}

2.Android客户端post方式发送请求参数优化

post()方法具体代码如下:

public static byte[] post(String path,Map<String, String> params,String

encode) throws Exception{

StringBuilder paramBuilder = new StringBuilder();

if(params!=null && !params.isEmpty()){

for(Map.Entry<String, String> entry : params.entrySet()){

paramBuilder.append(entry.getKey()).append("=").append

(URLEncoder.encode(entry.getValue(), encode))

.append("&");

}

paramBuilder.deleteCharAt(paramBuilder.length()-1);

}

byte[] data = paramBuilder.toString().getBytes();

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.openConnection();

conn.setDoOutput(true);//允许对外发送请求参数

conn.setUseCaches(false);//不进行缓存

conn.setConnectTimeout(5 * 1000);

conn.setRequestMethod("POST");

//下面设置http请求头

conn.setRequestProperty("Accept", "image/gif, image/jpeg,

image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/

xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, appl

ication/x-ms-application, application/vnd.ms-excel, application/vnd.

ms-powerpoint, application/msword, */*");

conn.setRequestProperty("Accept-Language", "zh-CN");

conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible;

MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR

2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR

3.5.30729)");

conn.setRequestProperty("Content-Type", "application/x-www-

form-urlencoded");

conn.setRequestProperty("Content-Length", String.valueOf(data.

length));

conn.setRequestProperty("Connection", "Keep-Alive");

//发送参数

DataOutputStream outStream = new DataOutputStream(conn.

getOutputStream());

outStream.write(data);//把参数发送出去

outStream.flush();

outStream.close();

if(conn.getResponseCode()==200){

return StreamTool.readInputStream(conn.getInputStream());

}

return null;

}

}

26.2.4 Android客户端之发送内容实体

1.自定义HTTP协议发送内容实体

核心代码sendXML方法实现:

/**

* 发送内容

* @param path //请求路径

* @param xml //XML数据

*/

public static byte[] sendXML(String path, String xml) throws Exception{

byte[] data = xml.getBytes(); //要发送的实体数据

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.openConnection();

conn.setDoOutput(true); //允许对外发送请求参数

conn.setUseCaches(false); //不进行缓存

conn.setConnectTimeout(5 * 1000);

conn.setRequestMethod("POST");

//下面设置http请求头

conn.setRequestProperty("Accept", "image/gif, image/jpeg,

image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/

xaml+xml,application/vnd.ms-xpsdocument,application/x-ms-xbap,applicati

on/x-ms-application, application/vnd.ms-excel, application/vnd.ms-

powerpoint, application/msword, */*");

conn.setRequestProperty("Accept-Language", "zh-CN");

conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible;

MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR

2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR

3.5.30729)");

conn.setRequestProperty("Content-Type", "text/xml; charset=

UTF-8");

conn.setRequestProperty("Content-Length", String.valueOf(data.

length));

conn.setRequestProperty("Connection", "Keep-Alive");

//发送参数

DataOutputStream outStream = new DataOutputStream(conn.

getOutputStream());

outStream.write(data); //把XML数据发送出去

outStream.flush();

outStream.close();

if(conn.getResponseCode()==200){

return StreamTool.readInputStream(conn.getInputStream());

}

return null;

}

}

2.写一个单元测试方法准备实体并调用sendXML方法

testsendXML()方法源码:

@Test

public void testsendXML()throws Throwable{

String path = "http://192.168.1.100:8080/CRM/contact/manage.do?

method=getXML";

String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>

<persons><person id=\"23\"><name>李明</name><age>30</age></person>

</persons>";

sendXML(path, xml);

}

3.在CRM客户关系管理系统接受实体

getXML方法源码:

public ActionForward getXML(ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response)

throws Exception {

InputStream inStream = request.getInputStream();

byte[] data = StreamTool.readInputStream(inStream);

String xml = new String(data);

System.out.println(xml);

return mapping.findForward("result");

}

26.2.5 Android客户端发送数据参数到服务器

1.第一种,Android客户端实现post方式发送数据到服务器

1)完成View层

String.xml的代码:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">Hello World, ContactActivity!</string>

<string name="app_name">CRM客户端</string>

<string name="name">姓名</string>

<string name="age">年龄</string>

<string name="button">保存</string>

</resources>

Main.xml的代码:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/name"

/>

<EditText android:id="@+id/name"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

></EditText>

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/age"

/>

<EditText android:id="@+id/age"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

></EditText>

<Button android:id="@+id/button"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/button"

></Button>

</LinearLayout>

2)核心代码实现——Controll层

package com.sharpandroid.crm.activity;

import com.sharpandroid.service.ContactService;

import android.app.Activity;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.Toast;

public class ContactActivity extends Activity {

private static final String TAG = "ContactActivity";

private EditText nameText;

private EditText ageText;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

nameText = (EditText) this.findViewById(R.id.name);

ageText = (EditText) this.findViewById(R.id.age);

Button button = (Button) this.findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

String name = nameText.getText().toString();

String age = ageText.getText().toString();

try {

ContactService.save(name, new Short(age));

Toast.makeText(ContactActivity.this, R.string.

success, 1).show();

} catch (Exception e) {

Toast.makeText(ContactActivity.this, R.string.fail,

1).show();

Log.e(TAG, e.toString());

}

}

});

}

}

3)业务模型实现——Model层

在ContactService业务bean中实现数据上传的功能,具体代码如下:

public static void save(String name,Short age) throws Exception{

String path = "http://192.168.1.100:8080/CRM/contact/

manage.do";

Map<String, String> params = new HashMap<String, String>();

params.put("method", "save");

params.put("name", name);

params.put("age", String.valueOf(age));

post(path, params, "UTF-8");

}

(2)params.put("age", String.valueOf(age))

要将age进行类型转换成需要的String类型。

public static byte[] post(String path,Map<String, String>

params,String encode) throws Exception{

StringBuilder paramBuilder = new StringBuilder();

if(params!=null && !params.isEmpty()){

for(Map.Entry<String, String> entry : params.entrySet()){

paramBuilder.append(entry.getKey()).append("=").append

(URLEncoder.encode(entry.getValue(), encode))

.append("&");

}

paramBuilder.deleteCharAt(paramBuilder.length()-1);

}

byte[] data = paramBuilder.toString().getBytes();

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.

openConnection();

conn.setDoOutput(true);//允许对外发送请求参数

conn.setUseCaches(false);//不进行缓存

conn.setConnectTimeout(5 * 1000);

conn.setRequestMethod("POST");

//下面设置http请求头

conn.setRequestProperty("Accept", "image/gif, image/jpeg,

image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/

xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap,

application/x-ms-application, application/vnd.ms-excel, application/

vnd.ms-powerpoint, application/msword, */*");

conn.setRequestProperty("Accept-Language", "zh-CN");

conn.setRequestProperty("User-Agent", "Mozilla/4.0

(compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET

CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR

3.5.30729)");

conn.setRequestProperty("Content-Type", "application/x-

www-form-urlencoded");

conn.setRequestProperty("Content-Length", String.valueOf

(data.length));

conn.setRequestProperty("Connection", "Keep-Alive");

//发送参数

DataOutputStream outStream = new DataOutputStream(conn.

getOutputStream());

outStream.write(data); //把参数发送出去

outStream.flush();

outStream.close();

if(conn.getResponseCode()==200){

return StreamTool.readInputStream(conn.getInputStream());

}

return null;

}

2.第二种,Android客户端实现get方式发送数据到服务器

在ContactService业务bean中添加GetSendData()方法:

public static void GetSendData(String name,Short age) throws Exception{

//以get方式发送请求参数

String path = "http://192.168.1.100:8080/CRM/contact/manage.

do?method=save&name="+

URLEncoder.encode(name,"UTF-8") + "&age="+age;

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection)url.

openConnection();

conn.setConnectTimeout(5 * 1000);

conn.setRequestMethod("GET");

if(conn.getResponseCode()==200){

byte[] data = StreamTool.readInputStream(conn.

getInputStream());

System.out.println(new String(data));;

}

}

3.用开源框架实现Android客户端发送实体

在ContactService业务bean添加postFromHttpClient()方法:

public static byte[] postFromHttpClient(String path, Map<String, String>

params, String encode) throws Exception{

List<NameValuePair> formparams = new ArrayList<NameValuePair>(); for(Map.Entry<String, String> entry : params.entrySet()){

formparams.add(new BasicNameValuePair(entry.getKey(), entry.

getValue()));

}

UrlEncodedFormEntity entity = new UrlEncodedFormEntity

(formparams, "UTF-8");

HttpPost httppost = new HttpPost(path);

httppost.setEntity(entity);

HttpClient httpclient = new DefaultHttpClient();

//看做是浏览器

HttpResponse response = httpclient.execute(httppost);

//发送post请求

return StreamTool.readInputStream(response.getEntity().

getContent());

}

26.3 Android客户端实现文件上传

26.3.1 知识回顾

1)业务模型实现——Model层

在ContactForm中定义上传的参数类型:

Com.sharpandroid.formbean.ContactForm.java

public class ContactForm extends ActionForm {

private String name;

private Short age;

private FormFile image;

public FormFile getImage() {

return image;

}

public void setImage(FormFile image) {

this.image = image;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Short getAge() {

return age;

}

public void setAge(Short age) {

this.age = age;

}

}

2)核心代码实现——Controll层

save()方法源码:

public ActionForward save(ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response)

throws Exception {

ContactForm formbean = (ContactForm)form;

//System.out.println("get : name="+ new String(request.

getParameter("name").getBytes("ISO-8859-1"),"UTF-8"));

System.out.println("name="+ formbean.getName());

System.out.println("age="+ formbean.getAge());

if(formbean.getImage()!=null && formbean.getImage().

getFileSize()>0){

String dirRealPath = request.getSession().getServletContext().

getRealPath("/images");

System.out.println(dirRealPath);

File dir = new File(dirRealPath);

if(!dir.exists()) dir.mkdirs();

File saveFile = new File(dir, formbean.getImage().

getFileName());

FileOutputStream outStream = new FileOutputStream(saveFile);

outStream.write(formbean.getImage().getFileData());

outStream.close();

}

return mapping.findForward("result");

}

3)页面美化——View层

index.jsp页面中的代码:

<body>

<form action="${pageContext.request.contextPath}/contact/manage.

do" method="post" enctype="multipart/form-data">

<input type="hidden" name="method" value="save"/>

姓名:<input type="text" name="name"/><br/>

年龄:<input type="text" name="age"/><br/>

图片:<input type="file" name="image"/><br/>

<input type="submit" value="保存"/>

</form>

</body>

26.3.2 Android客户端之文件上传

1)模型实现——Model层

FormFile.java 源代码:

import java.io.InputStream;

/**

* 上传文件

*/

public class FormFile {

private byte[] data;

private InputStream inStream;

private String filname;

private String formname;

private String contentType = "application/octet-stream";

public FormFile(String filname, byte[] data, String formname, String

contentType) {

this.data = data;

this.filname = filname;

this.formname = formname;

if(contentType!=null) this.contentType = contentType;

}

public FormFile(String filname, InputStream inStream, String formname,

String contentType) {

this.filname = filname;

this.formname = formname;

this.inStream = inStream;

if(contentType!=null) this.contentType = contentType;

}

public InputStream getInStream() {

return inStream;

}

public byte[] getData() {

return data;

}

public String getFilname() {

return filname;

}

public void setFilname(String filname) {

this.filname = filname;

}

public String getFormname() {

return formname;

}

public void setFormname(String formname) {

this.formname = formname;

}

public String getContentType() {

return contentType;

}

public void setContentType(String contentType) {

this.contentType = contentType;

}

}

通过面向HTTP协议,实现如下面表单提交功能,具体的代码如下:

package com.sharpandroid.utils;

import java.io.DataOutputStream;

import java.io.InputStream;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.Map;

public class HttpRequester {

public static String post(String actionUrl, Map<String, String> params,

FormFile[] files) {

try {

String BOUNDARY = "---------7d4a6d158c9"; //数据分隔线

String MULTIPART_FORM_DATA = "multipart/form-data";

URL url = new URL(actionUrl);

HttpURLConnection conn = (HttpURLConnection)

url.openConnection();

conn.setConnectTimeout(5* 1000);

conn.setDoInput(true); //允许输入

conn.setDoOutput(true); //允许输出

conn.setUseCaches(false); //不使用Cache

conn.setRequestMethod("POST");

conn.setRequestProperty("Connection", "Keep-Alive");

conn.setRequestProperty("Charset", "UTF-8");

conn.setRequestProperty("Content-Type", MULTIPART_FORM_DATA

+ "; boundary=" + BOUNDARY);

StringBuilder sb = new StringBuilder();

for (Map.Entry<String, String> entry : params.entrySet()) {

//构建表单字段内容

sb.append("--");

sb.append(BOUNDARY);

sb.append("\r\n");

sb.append("Content-Disposition: form-data; name=\""+

entry.getKey() + "\"\r\n\r\n");

sb.append(entry.getValue());

sb.append("\r\n");

}

DataOutputStream outStream = new DataOutputStream

(conn.getOutputStream());

outStream.write(sb.toString().getBytes()); //发送表单字段数据

for(FormFile file : files){ //发送文件数据

StringBuilder split = new StringBuilder();

split.append("--");

split.append(BOUNDARY);

split.append("\r\n");

split.append("Content-Disposition: form-data;name=\""+

file.getFormname()+"\";filename=\""+ file.getFilname() + "\"\r\n");

split.append("Content-Type: "+ file.getContentType()+"

\r\n\r\n");

outStream.write(split.toString().getBytes());

if(file.getInStream()!=null){

byte[] buffer = new byte[1024];

int len = 0;

while((len = file.getInStream().read(buffer))!=-1){

outStream.write(buffer, 0, len);

}

file.getInStream().close();

}else{

outStream.write(file.getData(), 0, file.getData().length);

}

outStream.write("\r\n".getBytes());

}

byte[] end_data = ("--" + BOUNDARY + "--\r\n").getBytes();

//数据结束标志

outStream.write(end_data);

outStream.flush();

int cah = conn.getResponseCode();

if (cah != 200) throw new RuntimeException("请求url失败");

InputStream is = conn.getInputStream();

int ch;

StringBuilder b = new StringBuilder();

while( (ch = is.read()) != -1 ){

b.append((char)ch);

}

outStream.close();

conn.disconnect();

return b.toString();

} catch (Exception e) {

throw new RuntimeException(e);

}

}

public static String post(String actionUrl, Map<String, String> params,

FormFile file) {

return post(actionUrl, params, new FormFile[]{file});

}

}

HttpRequester.java上传数据模型如下:

* 直接通过HTTP协议提交数据到服务器,实现如下面表单提交功能

* <FORM METHOD=POST ACTION="http://192.168.0.200:8080/ssi/

fileload/test.do" enctype="multipart/form-data">

<INPUT TYPE="text" NAME="name">

<INPUT TYPE="text" NAME="id">

<input type="file" name="imagefile"/>

<input type="file" name="zip"/>

</FORM>

* @param actionUrl 上传路径(注:避免使用localhost或127.0.0.1这样的路径

测试,因为它会指向手机模拟器,你可以使用像http://192.168.1.10:8080这样的路径测试)

* @param params 请求参数 key为参数名,value为参数值

* @param file 上传文件

构造一个数据的内容:

{

sb.append("--");

sb.append(BOUNDARY);

sb.append("\r\n");

sb.append("Content-Disposition: form-data; name=\""+ entry.getKey()

+ "\"\r\n\r\n");

sb.append(entry.getValue());

sb.append("\r\n");

}

sb.append("--");

sb.append(BOUNDARY);

文件可能有很多,我们构造一个,然后去迭代。

split.append("--");

split.append(BOUNDARY);

split.append("\r\n");

split.append("Content-Disposition: form-data;name=\""+ file.

getFormname()+"\";filename=\""+ file.getFilname() + "\"\r\n");

split.append("Content-Type: "+ file.getContentType()+

"\r\n\r\n");

outStream.write(split.toString().getBytes());

if(file.getInStream()!=null){

byte[] buffer = new byte[1024];

int len = 0;

while((len = file.getInStream().read(buffer))!=-1){

outStream.write(buffer, 0, len);

}

file.getInStream().close();

}else{

outStream.write(file.getData(), 0, file.getData().length);

}

outStream.write("\r\n".getBytes());

}

byte[] end_data = ("--" + BOUNDARY + "--\r\n").getBytes();//

数据结束标志

sb.append("--");

sb.append(BOUNDARY);

2)核心代码实现——Controll层

编写ContactActivity.java:

package com.sharpandroid.crm.activity;

import java.io.File;

import java.io.FileInputStream;

import com.sharpandroid.service.ContactService;

import com.sharpandroid.utils.FormFile;

import android.app.Activity;

import android.os.Bundle;

import android.os.Environment;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.Toast;

public class ContactActivity extends Activity {

private static final String TAG = "ContactActivity";

private EditText nameText;

private EditText ageText;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

nameText = (EditText) this.findViewById(R.id.name);

ageText = (EditText) this.findViewById(R.id.age);

Button button = (Button) this.findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

String name = nameText.getText().toString();

String age = ageText.getText().toString();

try {

String filename = " android.jpg";

FileInputStream inStream = new FileInputStream(new

File(Environment.getExternalStorageDirectory(), filename));

FormFile file = new FormFile(filename, inStream, "

image", "image/pjpeg");

ContactService.save(name, new Short(age), file);

Toast.makeText(ContactActivity.this, R.string.

success, 1).show();

} catch (Exception e) {

Toast.makeText(ContactActivity.this, R.string.fail,

1).show();

Log.e(TAG, e.toString());

}

}

});

}

}

在ContactActivity.java中构建FormFile ContactActivity.java 关键代码:

String filename = " android.jpg";

FileInputStream inStream = new FileInputStream(new File(Environment.

getExternalStorageDirectory(), filename));

FormFile file = new FormFile(filename, inStream, "image", "image/

pjpeg");

ContactService.save(name, new Short(age), file);

在ContactService.java中完成带文件的save()方法:

public static void save(String name, Short age, FormFile file) throws

Exception{

String path = "http://192.168.1.100:8080/CRM/contact/manage.do";

Map<String, String> params = new HashMap<String, String>();

params.put("method", "save");

params.put("name", name);

params.put("age", String.valueOf(age));

HttpRequester.post(path, params, file);

}

(2)调用HttpRequester中的上传单个文件的post()方法:HttpRequester.post(path, params, file)。

加入访问网络和对SD卡读写的相关的权限:

- <!--

在SD Card中创建与删除文件权限

-->

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_

FILESYSTEMS" />

- <!--

在SD Card中写入数据权限

-->

<uses-permission android:name="android.permission.WRITE_EXTERNAL_

STORAGE" />

27 Socket编程

27.3 Socket编程实战——Android聊天室实例

27.3.1 编写PC端工程代码

2.编写服务器端程序代码

1)编写Server.java

import java.awt.BorderLayout;

import javax.swing.*;

import java.awt.event.*;

public class Sever extends JFrame implements ActionListener

{ //服务器端主程序负责界面,以及服务段主线程ServerThread的启动

//服务端主线程ServerThread又产生BroadCast及ClientThread线程

//建立服务器端主界面中所用到的布局方式

BorderLayout borderLayout1 = new BorderLayout();

BorderLayout borderLayout2 = new BorderLayout();

JPanel jPanel1 = new JPanel(); //创建面板

JPanel jPanel2 = new JPanel();

JButton jButton1 = new JButton(); //创建按钮

JButton jButton2 = new JButton();

JScrollPane jScrollPane1 = new JScrollPane();

//创建服务器端接收信息文本框

static JTextArea jTextArea1 = new JTextArea();

boolean bool = false, start = false;

//声明ServerThread线程类对象

ServerThread serverThread;

Thread thread;

//构造函数,用于初始化

public Sever()

{

super("Server");

//设置内容面板布局方式

getContentPane().setLayout(borderLayout1);

//初始化按钮组件

jButton1.setText("启动服务器");

jButton1.addActionListener(this);

jButton2.setText("关闭服务器");

jButton2.addActionListener(this);

//初始化jPanel1面板对象,并向其中加入组件

this.getContentPane().add(jPanel1, java.awt.BorderLayout.NORTH);

jPanel1.add(jButton1);

jPanel1.add(jButton2);

//初始化jPanel2面板对象,并向其中加入组件

jTextArea1.setText("");

jPanel2.setLayout(borderLayout2);

jPanel2.add(jScrollPane1, java.awt.BorderLayout.CENTER);

jScrollPane1.getViewport().add(jTextArea1);

this.getContentPane().add(jPanel2, java.awt.BorderLayout.CENTER);

this.setSize(400, 400);

this.setVisible(true);

}

public static void main(String[] args)

{

Sever sever = new Sever();

sever.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

//服务器界面中按钮事件处理

public void actionPerformed(ActionEvent e)

{

if(e.getSource() == jButton1)

{

serverThread = new ServerThread();

serverThread.start();

}else if(e.getSource() == jButton2){

bool = false;

start = false;

serverThread.finalize();

this.setVisible(false);

}

}

}

2)编写ServerThread.java

import java.net.*;

import java.util.*;

import java.io.*;

public class ServerThread extends Thread

//服务器监听端口线程

{

//声明ServerSocket类对象

ServerSocket serverSocket;

//指定服务器监听端口常量

public static final int PORT = 8521;

//创建一个Vector对象,用于存储客户端连接的ClientThread对象

//ClientThread类维持服务器与单个客户端的连接线程

//负责接收客户端发来的信息

Vector<ClientThread> clients;

//创建一个Vector对象,用于存储客户端发送过来的信息

Vector<Object> messages;

//声明BroadCast类对象

//BroadCast类负责服务器向客户端广播消息

BroadCast broadcast;

String ip;

InetAddress myIPaddress=null;

public ServerThread()

{

//创建两个Vector数组非常重要

//clients负责存储所有与服务器建立连接的客户端

//messages负责存储服务器接收到的未发送出去的全部客户端的信息

clients=new Vector<ClientThread>();

messages=new Vector<Object>();

try

{

//创建ServerSocket类对象

serverSocket = new ServerSocket(PORT);

}

catch(IOException E){}

//获取本地服务器地址信息

try {

myIPaddress=InetAddress.getLocalHost();

}catch (UnknownHostException e){System.out.println(e.

toString());}

ip = myIPaddress.getHostAddress();

Sever.jTextArea1.append("服务器地址:"+ ip +

"端口号:"+String.valueOf(serverSocket.getLocalPort())+"

\n");

//创建广播信息线程并启动

broadcast = new BroadCast(this);

broadcast.start();

}

//注意:一旦监听到有新的客户端创建即new Socket(ip, PORT)被执行,

//就创建一个ClientThread来维持服务器与这个客户端的连接

public void run()

{

while(true)

{

try

{

//获取客户端连接,并返回一个新的Socket对象

Socket socket = serverSocket.accept();

System.out.println(socket.getInetAddress().getHostAddress());

//创建ClientThread线程并启动

//启动ClientTread之后,可以监听该连接对应的客户端是否发送来消息,

//并获取消息

ClientThread clientThread=new ClientThread(socket,this);

clientThread.start();

if(socket!=null)

{

synchronized (clients)

{

//将客户端连接加入到Vector数组中保存

clients.addElement(clientThread);

}

}

}

catch(IOException E)

{

System.out.println("发生异常:"+E);

System.out.println("建立客户端联机失败!");

System.exit(2);

}

}

}

public void finalize()

{

try

{ //关闭serverSocket方法

serverSocket.close();

}

catch(IOException E){}

serverSocket=null;

}

}

3)编写ClientThread.java

import java.net.*;

import java.io.*;

public class ClientThread extends Thread

{

//维持服务器与单个客户端的连接线程,负责接收客户端发来的信息

//声明一个新的Socket对象,

//用于保存服务器端用accept方法得到的客户端的连接

Socket clientSocket;

//声明服务器端中存储的Socket对象的数据输入/输出流

DataInputStream in = null;

DataOutputStream out = null;

//声明ServerThread对象

ServerThread serverThread;

// String str;

// public static int ConnectNumber=0;

public ClientThread(Socket socket,ServerThread serverThread)

{

clientSocket=socket;

this.serverThread=serverThread;

try

{

//创建服务器端数据输入/输出流

in = new DataInputStream(clientSocket.getInputStream());

out = new DataOutputStream(clientSocket.getOutputStream());

}

catch (IOException e2)

{

System.out.println("发生异常"+e2);

System.out.println("建立I/O通道失败!");

System.exit(3);

}

}

//该方法监听该连接对应得客户端是否有消息发送

public void run()

{

while(true)

{

try

{

//读入客户端发送来的信息

String message=in.readUTF();

synchronized(serverThread.messages)

{

if(message!=null)

{

//将客户端发送来得信息存于serverThread的messages数组中

serverThread.messages.addElement(message);

//在服务器端的文本框中显示新消息

Sever.jTextArea1.append(message+'\n');

}

}

}

catch(IOException E){break;}

}

}

}

4)编写BroadCast.java

import java.io.*;

public class BroadCast extends Thread

{ //服务器向客户端广播线程

//声明ClientThread对象

ClientThread clientThread;

//声明ServerThread对象

ServerThread serverThread;

String str;

public BroadCast(ServerThread serverThread)

{

this.serverThread = serverThread;

}

//该方法的作用是不停地向所有客户端发送新消息

public void run()

{

while(true)

{

try

{

//线程休眠200 ms

Thread.sleep(200);

}

catch(InterruptedException E){}

//同步化serverThread.messages

synchronized(serverThread.messages)

{

//判断是否有未发的消息

if(serverThread.messages.isEmpty()){

continue;

}

//获取服务器端存储的需要发送的第一条数据信息

str = (String)this.serverThread.messages.firstElement();

}

//同步化serverThread.clients

synchronized(serverThread.clients)

{

//利用循环获取服务器中存储的所有建立的与客户端的连接

for(int i=0;i<serverThread.clients.size();i++)

{

clientThread=(ClientThread)serverThread.clients.

elementAt(i);

try

{

//向记录的每一个客户端发送数据信息

clientThread.out.writeUTF(str);

}

catch(IOException E){}

}

//从Vector数组中删除已经发送过的那条数据信息

this.serverThread.messages.removeElement(str);

}

}

}

}

3.编写PC客户端程序代码

PC客户端包括一个类,Client.java。Client.java的代码如下:

import javax.swing.*;

import java.awt.*;

import java.net.*;

import java.sql.Date;

import java.text.SimpleDateFormat;

import java.io.*;

import java.awt.event.*;

public class Client extends JFrame implements Runnable,ActionListener

{ //客户端程序

private static final long serialVersionUID = 1L;

//创建Socket通信端口号常量

public static final int PORT = 8521;

Swing代码生成界面省略

//声明套接字对象

Socket socket;

//声明线程对象

Thread thread;

//声明客户器端数据输入/输出流

DataInputStream in;

DataOutputStream out;

//是否登录的标记

boolean flag=false;

//声明字符串,name存储用户名,chat_txt存储发言信息,chat_in存储从服务器接收到

的信息

String name,chat_txt,chat_in;

String ip=null;

//构造方法,生成客户端界面

public Client()

{

Swing代码生成界面省略

}

public static void main(String[] args)

{

Client client = new Client();

client.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

//客户端线程启动后的动作

public void run()

{

//循环执行,作用是一直监听服务器端是否有消息

while(true)

{

try

{

//读取服务器发送来的数据信息

chat_in = in.readUTF();

//将消息并显示在客户端的对话窗口中

jTextArea1.append(chat_in+"\n\n");

}

catch(IOException e){}

}

}

//界面按钮的事件处理机制

public void actionPerformed(ActionEvent e)

{

//"进入聊天室"按钮的处理

if(e.getSource() == loginJButton)

{

//获取用户名

name=jTextField1.getText();

//获取服务器ip

ip=jTextField3.getText();

//判断用户名是否有效及ip是否为空

if(name!="用户名输入"&&ip!=null)

{

try

{

//创建Socket对象

socket = new Socket(ip, PORT);

//创建客户端数据输入/输出流,用于对服务器端发送或接收数据

in = new DataInputStream(socket.getInputStream());

out = new DataOutputStream(socket.getOutputStream());

Date now = new Date(System.currentTimeMillis());

SimpleDateFormat format = new SimpleDateFormat("hh:mm:

ss");

String nowStr = format.format(now);

out.writeUTF("$$"+name +" "+ nowStr + " 上线了!");

} catch (IOException e1) {System.out.println("can not

connect");}

thread = new Thread(this);

//开启线程,监听服务器端是否有消息

thread.start();

//说明已经登录成功

flag = true;

}

}

//"发送"按钮的处理

else if(e.getSource() == sendMessageJButton)

{

//获取客户端输入的发言内容

chat_txt=jTextField2.getText();

if(chat_txt!=null)

{

Date now = new Date(System.currentTimeMillis());

SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");

String nowStr = format.format(now);

//发言,向服务器发送发言的信息

try{out.writeUTF("^_^" +jTextField1.getText()+ " " +

nowStr + " 说\n"+chat_txt);}

catch(IOException e2){}

}

else

{

try{out.writeUTF("请说话");}

catch(IOException e3){}

}

}

//"退出聊天室"按钮事件的处理

else if(e.getSource() == leaveJButton)

{

if(flag==true)

{

try

{

out.writeUTF("##"+ name +"下线了!");

//关闭socket

out.close();

in.close();

socket.close();

} catch (IOException e4) {}

}

flag=false;

this.setVisible(false);

}

}

}

27.3.2 编写手机客户端工程代码

2.编写strings.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">Hello World, ChatClientActivity!</string>

<string name="app_name">聊天室Android客户端</string>

<string name="username">用户名:</string>

<string name="ip">服务器IP:</string>

<string name="login">进入</string>

<string name="leave">退出</string>

<string name="send">发送</string>

</resources>

3.编写main.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/

android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<TextView

android:text="@string/username"

android:layout_width="70px"

android:layout_height="40px"

android:id="@+id/usernameText"/>

<EditText

android:text=" "

android:layout_width="180px"

android:layout_height="40px"

android:maxLength="10"

android:layout_toRightOf="@id/usernameText"

android:id="@+id/username" >

</EditText>

<Button

android:layout_width="wrap_content"

android:layout_height="40px"

android:text="@string/login"

android:layout_toRightOf="@id/username"

android:id="@+id/LoginButton"/>

<TextView

android:text="@string/ip"

android:layout_width="70px"

android:layout_height="40px"

android:layout_below="@id/username"

android:layout_alignParentLeft="true"

android:id="@+id/ipText"/>

<EditText

android:text="192.168.1.188"

android:layout_width="180px"

android:layout_height="40px"

android:layout_toRightOf="@id/ipText"

android:layout_alignTop="@id/ipText"

android:digits=".1234567890"

android:id="@+id/ip" >

</EditText>

<Button

android:layout_width="wrap_content"

android:layout_height="40px"

android:text="@string/leave"

android:layout_toRightOf="@id/ip"

android:layout_alignTop="@id/ip"

android:id="@+id/LeaveButton"/>

<EditText

android:layout_width="fill_parent"

android:layout_height="280px"

android:layout_below="@id/ipText"

android:editable="false"

android:gravity="top"

android:id="@+id/history" >

</EditText>

<EditText

android:layout_width="280px"

android:layout_height="240px"

android:layout_below="@id/history"

android:gravity="top"

android:id="@+id/message" >

</EditText>

<Button

android:layout_width="wrap_content"

android:layout_height="240px"

android:text="@string/send"

android:layout_toRightOf="@id/message"

android:layout_alignTop="@id/message"

android:id="@+id/SendButton"/>

</RelativeLayout>

4.编写功能清单文件,添加网络访问权限

编写AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.sharpandroid.chatclient"

android:versionCode="1"

android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/

app_name">

<activity android:name=".ChatClientActivity"

android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER"

/>

</intent-filter>

</activity>

</application>

<uses-sdk android:minSdkVersion="7" />

<uses-permission android:name="android.permission.INTERNET"></uses-

permission>

</manifest>

5.编写ChatClientActivity.java

package com.sharpandroid.chatclient;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.net.InetAddress;

import java.net.Socket;

import java.sql.Date;

import java.text.SimpleDateFormat;

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.Toast;

public class ChatClientActivity extends Activity implements Runnable{

private EditText usernameEdit;

private EditText ipEdit;

private EditText historyEdit;

private EditText messageEdit;

private Button loginButton;

private Button sendButton;

private Button leaveButton;

//声明字符串,name存储用户名

//chat_txt存储发言信息

//chat_in存储从服务器接收到的信息

private String username,ip,chat_txt,chat_in;

//创建Socket通信端口号常量

public static final int PORT = 8521;

//声明套接字对象

Socket socket;

//声明线程对象

Thread thread;

//声明客户器端数据输入输出流

DataInputStream in;

DataOutputStream out;

//是否登录的标记

boolean flag=false;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

//获取main.xml文件中定义的一系列组件

usernameEdit = (EditText) findViewById(R.id.username);

ipEdit = (EditText) findViewById(R.id.ip);

historyEdit = (EditText) findViewById(R.id.history);

messageEdit = (EditText) findViewById(R.id.message);

loginButton = (Button) findViewById(R.id.LoginButton);

sendButton = (Button) findViewById(R.id.SendButton);

leaveButton = (Button) findViewById(R.id.LeaveButton);

//为三个按钮注册监听器

loginButton.setOnClickListener(listener);

sendButton.setOnClickListener(listener);

leaveButton.setOnClickListener(listener);

}

View.OnClickListener listener = new View.OnClickListener() {

@Override

public void onClick(View v) {

switch (v.getId()) {

//"进入聊天室"按钮的处理

case R.id.LoginButton:

if(flag == true){

Toast.makeText(ChatClientActivity.this,"已经登陆过

了!", Toast.LENGTH_SHORT).show();

return;

}

//获取用户名

username = usernameEdit.getText().toString();

//获取服务器ip

ip = ipEdit.getText().toString();

//判断用户名是否有效及ip是否为空

if(username != "" && username!=null

&& username!="用户名输入" && ip!=null){

try

{

//创建Socket对象

socket = new Socket(ip, PORT);

//创建客户端数据输入/输出流,用于对服务器端发送或接收数据

in = new DataInputStream(socket.getInputStream());

out = new DataOutputStream(socket.getOutputStream());

Date now = new Date(System.currentTimeMillis());

SimpleDateFormat format = new SimpleDateFormat("

hh:mm:ss");

String nowStr = format.format(now);

out.writeUTF("$$" + username +" "+ nowStr + " 上

线了!");

} catch (IOException e1) {System.out.println("can not

connect");}

thread = new Thread(ChatClientActivity.this);

//开启线程,监听服务器段是否有消息

thread.start();

//说明已经登录成功

flag = true;

}

break;

//"发送"按钮的处理

case R.id.SendButton:

if(flag ==false){

Toast.makeText(ChatClientActivity.this, "没有登录,

请登录!",Toast.LENGTH_SHORT).show();

return;

}

//获取客户端输入的发言内容

chat_txt = messageEdit.getText().toString();

if(chat_txt != null)

{

Date now = new Date(System.currentTimeMillis());

SimpleDateFormat format = new SimpleDateFormat("hh:mm:

ss");

String nowStr = format.format(now);

//发言,向服务器发送发言的信息

try{out.writeUTF("^_^" + username+ " " + nowStr + "

说\n" + chat_txt);}

catch(IOException e2){}

}

else

{

try{out.writeUTF("请说话");}

catch(IOException e3){}

}

break;

//"退出聊天室"按钮事件的处理

case R.id.LeaveButton:

if(flag==true)

{

if(flag ==false){

Toast.makeText(ChatClientActivity.this, "没有

登录,请登录!",Toast.LENGTH_SHORT).show();

return;

}

try {

out.writeUTF("=="+username+"下线了!");

//关闭socket

out.close();

in.close();

socket.close();

} catch (IOException e4) {}

}

flag=false;

Toast.makeText(ChatClientActivity.this, "已退出!",

Toast.LENGTH_SHORT).show();

break;

}

}

};

//客户端线程启动后的动作

@Override

public void run() {

//循环执行,作用是一直监听服务器端是否有消息

while(true)

{

try

{

//读取服务器发送来的数据信息

chat_in = in.readUTF();

chat_in = chat_in + "\n";

//发送一个消息,要求刷新界面

mHandler.sendMessage(mHandler.obtainMessage());

}

catch(IOException e){}

}

}

Handler mHandler = new Handler()

{

public void handleMessage(Message msg)

{

//将消息并显示在客户端的对话窗口中

historyEdit.append(chat_in);

// 刷新

super.handleMessage(msg);

}

};

}

28 WebView的介绍及应用

28.2 WebView浏览网页简单示例

编写strings.xml:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">请输入网址:</string>

<string name="app_name">WebView简单示例</string>

<string name="button">LoadUrl</string>

<string name="button1">LoadData</string>

</resources>

编写main.xml文件:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/hello"/>

<EditText

android:id="@+id/path"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="http://www.baidu.com"/>

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/button"

android:id="@+id/button"/>

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/button1"

android:id="@+id/button1"/>

<WebView

android:layout_height="fill_parent"

android:layout_width="fill_parent"

android:id="@+id/webview"/>

</LinearLayout>

编写WebViewActivity1.java:

package com.sharpandroid.webviewdemo1;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.webkit.WebView;

import android.widget.Button;

import android.widget.EditText;

public class WebViewActivity1 extends Activity {

private WebView webView;

private EditText pathEdit;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

webView = (WebView) findViewById(R.id.webview);

Button Button = (Button) findViewById(R.id.button);

//为第一个button添加单击事件

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

//获取文本框里的内容

pathEdit = (EditText) findViewById(R.id.path);

String path = pathEdit.getText().toString();

//在WebView中加载指定的路径,路径以“http://”开头

webView.loadUrl(path);

}

});

Button button1 = (Button) findViewById(R.id.button1);

//为第二个Button添加单击事件

button1.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

pathEdit = (EditText) findViewById(R.id.path);

//定义一个字符串,内容包含一个超链接,指向百度

String data = "<a href='http://www.baidu.com'>baidu</a>" ;

//在WebView中加载该数据,数据类型是“text/html”,编码格式是“utf-8”

webView.loadData(data,"text/html" ,"utf -8");

}

});

}

}

为程序添加网络访问权限。编写功能清单文件,内容如下:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.sharpandroid.webviewdemo1"

android:versionCode="1"

android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/

app_name">

<activity android:name=".WebViewActivity1"

android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER"

/>

</intent-filter>

</activity>

</application>

<uses-sdk android:minSdkVersion="7" />

<uses-permission android:name="android.permission.INTERNET" />

</manifest>

28.3 WebView浏览网页复杂示例

准备一个名为index.html的HTML文件,放置到工程的“/assets”目录下。文件中包含三种不同的弹出对话框,供WebChromeClient测试使用,内容如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "

http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=

gb2312" />

<title>测试JavaScript的三种不同对话框</title>

<script language="JavaScript">

function alertFun()

{

alert("Alert警告对话框!-你好吗?");

}

function confirmFun()

{

if(confirm("访问搜狐?"))

{

location.href="http://www.sohu.com";

}

else

{

alert("取消访问!");

}

}

function promptFun()

{

var word =prompt("Prompt对话框","请输入你最想说的一句话:");

if(word)

{

alert("你说:"+ word)

}else{

alert("呵呵,你沉默了!");

}

}

</script>

</head>

<body>

<p>三种对话框示例</p>

<p>Alert对话框-你好啊!</p>

<p>

<input type="submit" name="Submit" value="按钮1" onclick="

alertFun()" />

</p>

<p>Confirm对话框-是否访问搜狐!</p>

<p>

<input type="submit" name="Submit2" value="按钮2" onclick=

"confirmFun()" />

</p>

<p>Prompt对话框-告诉我你想说的话!</p>

<p>

<input type="submit" name="Submit3" value="按钮3" onclick=

"promptFun()" />

</p>

</body>

</html>

编写strings.xml文件:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="app_name">WebView示例2</string>

<string name="hello">请输入网址:</string>

<string name="button">LoadUrl</string>

</resources>

编写main.xml文件:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/hello"/>

<EditText

android:id="@+id/path"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="file:///android_asset/index.html"/>

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/button"

android:id="@+id/button"/>

<WebView

android:layout_height="fill_parent"

android:layout_width="fill_parent"

android:id="@+id/webview"/>

</LinearLayout>

编写prompt_view.xml文件:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:gravity="center_horizontal"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

>

<TextView

android:id="@+id/text"

android:layout_width="fill_parent"

android:layout_height="wrap_content"/>

<EditText

android:id="@+id/edit"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:selectAllOnFocus="true"

android:scrollHorizontally="true"/>

</LinearLayout>

编写WebViewActivity2.java:

package com.sharpandroid.webviewdemo2;

import android.app.Activity;

import android.app.AlertDialog;

import android.app.AlertDialog.Builder;

import android.content.DialogInterface;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.View.OnClickListener;

import android.webkit.JsPromptResult;

import android.webkit.JsResult;

import android.webkit.WebChromeClient;

import android.webkit.WebSettings;

import android.webkit.WebView;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

public class WebViewActivity2 extends Activity {

private Button mButton;

private EditText mEditText;

private WebView mWebView;

@Override

public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

mButton = (Button) findViewById(R.id.button);

mEditText = (EditText) findViewById(R.id.path);

mWebView = (WebView) findViewById(R.id.webview);

//获取WebSettings对象

WebSettings webSettings = mWebView.getSettings();

//设置支持JavaScript脚本

webSettings.setJavaScriptEnabled(true);

//设置可以访问文件

webSettings.setAllowFileAccess(true);

//设置支持缩放

webSettings.setBuiltInZoomControls(true);

//设置WebChromeClient,对网页中 JavaScript代码的各种事件进行处理

mWebView.setWebChromeClient(new MyWebChromeClient());

//按钮单击事件监听

mButton.setOnClickListener(new OnClickListener()

{

public void onClick(View v)

{

//取得编辑框中我们输入的内容

String url = mEditText.getText().toString();

mWebView.loadUrl(url);

}

});

}

class MyWebChromeClient extends WebChromeClient {

@Override

// 处理javascript中的alert

public boolean onJsAlert(WebView view, String url, String message,

final JsResult result) {

// 构建一个Builder来显示网页中的对话框

new Builder(WebViewActivity2.this).setTitle("Alert对话框")

.setMessage(message)

// android.R.string.ok来自系统自带的文本,内容为“确定”

.setPositiveButton(android.R.string.ok,

new AlertDialog.OnClickListener() {

public void onClick(DialogInterface dialog,int which) {

// 单击确定按钮之后,继续执行网页中的操作

result.confirm();

}

})

.setCancelable(false).show();

return true;

};

@Override

// 处理javascript中的confirm

public boolean onJsConfirm(WebView view, String url, String

message,

final JsResult result) {

new Builder(WebViewActivity2.this)

.setTitle("Confirm对话框")

.setMessage(message)

.setPositiveButton(android.R.string.ok,

new AlertDialog.OnClickListener() {

public void onClick(DialogInterface dialog,int which) {

result.confirm();

}

})

// android.R.string.ok来自系统自带的文本,内容为“取消”

.setNegativeButton(android.R.string.cancel,

new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int

which) {

result.cancel();

}

})

.setCancelable(false)

.show();

return true;

};

@Override

// 处理javascript中的prompt

// message为网页中对话框的提示内容

// defaultValue在没有输入时,默认显示的内容

public boolean onJsPrompt(WebView view, String url, String message,

String defaultValue, final JsPromptResult result) {

//获得一个LayoutInflater对象factory,该对象可以将指定的xml布局文

件生成相应的对象

final LayoutInflater factory = LayoutInflater.from

(WebViewActivity2.this);

// 获取自定义的带输入框的对话框由TextView和EditText构成

final View dialogView = factory.inflate(R.layout.prompt_view,

null);

// 设置TextView对应网页中的提示信息

((TextView) dialogView.findViewById(R.id.text))

.setText(message);

//为dialogView上的edit输入框 设置来自网页的默认文字

((EditText) dialogView.findViewById(R.id.edit))

.setText(defaultValue);

new Builder(WebViewActivity2.this)

.setTitle("Prompt对话框")

//将前面获取的diaoView这个视图文件添加到对话框上面

.setView(dialogView)

.setPositiveButton(android.R.string.ok,

new AlertDialog.OnClickListener() {

public void onClick(DialogInterface dialog,int which){

// 单击确定之后,取得输入的值,传给网页处理

String value = ((EditText) dialogView.findViewById

(R.id.edit)).getText().toString();

result.confirm(value);

}

})

.setNegativeButton(android.R.string.cancel,

new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog,intwhich){

result.cancel();

}

})

.show();

return true;

};

}

}

由于在工程中需要访问Internet,因此,添加访问网络权限,编写功能清单文件:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.sharpandroid.webviewdemo2"

android:versionCode="1"

android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/

app_name">

<activity android:name=".WebViewActivity2"

android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER"

/>

</intent-filter>

</activity>

</application>

<uses-sdk android:minSdkVersion="7" />

<uses-permission android:name="android.permission.INTERNET"></uses-

permission>

</manifest>

28.4 WebView使用HTML替代Layout做界面

2.编写网页文件

在assets目录下新建一个HTML文件phoneui.html,内容如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "

http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Insert title here</title>

<script type="text/javascript">

//该部分用到了JavaScript的一些知识,不熟练的读者可以查阅相关书籍

//或者可由美工做出来之后,进行必要的修改

function show(jsondata){

//将传递过来的json数据转换为对象

var jsonobjs = eval(jsondata);

//获取下面定义的的表格

var table = document.getElementById("personTable");

//遍历上面创建的json对象,将每个对象添加为表格中的一行,其每个属性为一列

for(var i=0; i<jsonobjs.length; i++){

//添加一行

var tr = table.insertRow(table.rows.length);

//添加三个单元格

var td1 = tr.insertCell(0);

var td2 = tr.insertCell(1);

td2.align = "center";

var td3 = tr.insertCell(2);

//设置单元格的内容和属性,

//其中innerHTML为设置或获取位于对象起始和结束标签内的 HTML

//jsonobjs[i]为对象数组中的第i个对象

td1.innerHTML = jsonobjs[i].id;

td2.innerHTML = jsonobjs[i].name

//为显示的内容添加超链接。

//超链接会调用Java代码中的call()方法并且把内容作为参数传递过去

td3.innerHTML = "<a href='javascript:sharp.call(\""+

jsonobjs[i].phone +"\")'>"+jsonobjs[i].phone+ "</a>"; ;

}

}

</script>

</head>

<!--onload指定了当该页面被加载时调用的方法,本例调用了Java代码中的contactlist方

法--><body bgcolor="#000000" text="#FFFFFF" style="margin:0000"

onload="javascript:sharp.contactlist()">

<!-- 定义一个表格,显示数据-->

<table border="0" width="100%" id="personTable" cellspacing="0"

>

<tr>

<td width="15%">编号</td><td align="center">姓名</td><td

width="15%">电话</td>

</tr>

</table>

</body>

</html>

3.编写main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<WebView

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:id="@+id/webView"

/>

</LinearLayout>

4.编写Contact.java

其代码如下:

package com.sharpandroid.domain;

public class Contact {

private Integer id;

private String name;

private String phone;

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getPhone() {

return phone;

}

public void setPhone(String phone) {

this.phone = phone;

}

public Contact(Integer id, String name, String phone) {

this.id = id;

this.name = name;

this.phone = phone;

}

public Contact(){}

}

5.编写HtmlActivity.Java

HtmlActivity.java

package com.sharpandroid.html;

import java.util.ArrayList;

import java.util.List;

import org.json.JSONArray;

import org.json.JSONObject;

import android.app.Activity;

import android.content.Intent;

import android.net.Uri;

import android.os.Bundle;

import android.webkit.WebView;

import com.sharpandroid.domain.Contact;

public class HtmlActivity extends Activity {

private WebView webView;

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

webView = (WebView)this.findViewById(R.id.webView);//浏览器

webView.getSettings().setJavaScriptEnabled(true);//支持js

webView.getSettings().setSaveFormData(false);//不保存表单

webView.getSettings().setSavePassword(false);//不保存密码

webView.getSettings().setSupportZoom(false);//不支持放大功能

webView.addJavascriptInterface(new SharpJavaScript(), "sharp");

//addJavascriptInterface方法中要绑定的Java对象

webView.loadUrl("file:///android_asset/index.html");

}

public class SharpJavaScript{

public void contactlist(){

try {

String json = buildJson(getContacts());

webView.loadUrl("javascript:show('"+ json + "')");//调

用js的show()方法

} catch (Exception e) {

e.printStackTrace();

}

}

public void call(String phone){

Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+

phone));

HtmlActivity.this.startActivity(intent);

}

}

//模拟从数据库或者从网络获取数据

public List<Contact> getContacts(){

List<Contact> contacts = new ArrayList<Contact>();

contacts.add(new Contact(1, "张三", "5554"));

contacts.add(new Contact(2, "李四", "5556"));

contacts.add(new Contact(3, "王五", "5557"));

return contacts;

}

public String buildJson(List<Contact> contacts) throws Exception{

JSONArray array = new JSONArray();

for(Contact contact : contacts){

JSONObject jsonObject = new JSONObject();

jsonObject.put("id", contact.getId());

jsonObject.put("name", contact.getName());

jsonObject.put("phone", contact.getPhone());

array.put(jsonObject);

}

return array.toString();

}

}

6.添加拨号权限

功能清单文件如下:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.sharpandroid.phoneuibyhtml"

android:versionCode="1"

android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/

app_name">

<activity android:name=".PhoneActivity"

android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

<uses-sdk android:minSdkVersion="7" />

<uses-permission android:name="android.permission.CALL_PHONE"/>

</manifest>

更多相关文章

  1. Android如何更新app的版本(中级)
  2. 关于android UDP 客户端与delphi UDP服务端通讯中文乱码问题
  3. android下载的进度条
  4. android 简单例子
  5. Android(安卓)Studio更新升级方法
  6. android 开发书籍打包下载(包含十几本Android开发书籍)
  7. Android使用HttpURLConnection访问网络
  8. Ubuntu下建立Android开发环境
  9. Android(安卓)开发环境 adt-bundle android-studio sdk adt 下载

随机推荐

  1. 设置可见和隐藏
  2. android中GPS信息的获取
  3. TTS在Android中的简单使用
  4. Android中获取时间
  5. android view的触摸事件坐标
  6. Android WIFI热点默认安全性的修改方法
  7. android获取电话号码实例
  8. Android 以流的方式读取服务器图片文件
  9. Android之自定义Adapter的ListView
  10. Android framwork 锁屏界面开发 笔记