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.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.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.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.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.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);
}
};
}
编写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>
准备一个名为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>
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>