因为学院须要提交一个学年论文,正好借此机会开始个人博客生活,我会在随后的日子在里不断更新博客。java
内容摘要:随着移动互联网技术的的发展以及智能手持终端的普及,实现远程数据的通讯成为了智能应用的关键。目前智能终端中大部分采用了Google Android操做系统,所以,我在暑期重点研究了Android远程数据库通讯的实现,并实现了一套可行的实现方案。mysql
Android在数据存储方面提供了四种方式,其中包括轻量级的Sqlite3数据库。对于单机游戏以及简单应用,这几种存储方式已经可以解决。可是,随着移动互联网技术的日臻成熟以及用户对于移动网络数据的巨大需求,单单采用本地存储数据已经很难知足用户的需求。所以,Android链接远程数据库交互数据已经成为了不少应用、手机游戏必不可少的部分。android
Android操做系统在早期已经对Socket、Http网络有了很好的支持,所以,在Android应用中远程通讯是很好实现的。对于访问远程数据库,Google官方SDK文档说是支持的。可是,在实际开发中,若果采用直接访问远程数据库时会出现不少莫名奇妙的问题。对于这个问题而言,个人理解是:Android实质是在Linux上搭建了一个符合Java标准的虚拟机(官方称为Dalvik)。Dalvik是一个针对移动终端权衡性能与功耗优化的Java虚拟机,因为受限于移动终端自身硬件限制,Dalvik必然是作过不少精简的,能够说是“阉割”版的Java虚拟机。所以,Android对于直连远程数据库必然是不可以像PC同样完美支持给中数据库的。sql
既然远程直连数据库从在各类问题,咱们只能经过迂回路线完成远程数据库通讯了。数据库
既然直连远程数据有问题,要解决,必然是在中间加一种中间层。我设计的解决方案是在远程加一个中间层,具体以下:json
在远程数据库端部署一个服务端,服务端做为中间层,一方面经过jdbc链接数据库,另外一方面,服务端经过Socket将查询到的结果反馈给Android终端。如下图说明:数组
基础的解决方案已经有了,咱们要作的就是具体细化方案。服务器
一、Android终端与服务端的通讯及数据网络
Android终端经过Socket与远程服务端进行链接。Android端经过字节流发送SQL查询指令,服务端数据传输采用JSON格式的字节流返回查询结果。socket
二、服务端与远程数据库
服务端和远程数据库部署在同一服务器,所以两者只需经过jdbc链接就能够了。
一、服务端的编写
服务端包括两部分,一部分与数据库链接,另外一部分是远程通讯。
1.1 服务端与数据库链接,能够将其封装成为一个类。以下:
public class ConectDB { public Connection conn; public String strSql; public Statement sqlStmt;//语句对象不带参数 public PreparedStatement preparedStmt;//语句对象带参数 public ResultSet sqlRst; //结果对象集 private static ConectDB mysql; /** * 构造函数 */ private ConectDB() { try { conn = null; sqlStmt = null; sqlRst = null; preparedStmt = null; Connect(); } catch (SQLException ex) { Logger.getLogger(ConectDB.class.getName()).log(Level.SEVERE, null, ex); } catch (ClassNotFoundException ex) { Logger.getLogger(ConectDB.class.getName()).log(Level.SEVERE, null, ex); } catch (InstantiationException ex) { Logger.getLogger(ConectDB.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { Logger.getLogger(ConectDB.class.getName()).log(Level.SEVERE, null, ex); } } public static ConectDB getConetDB()//单例模式 { if(mysql==null) { mysql=new ConectDB(); } return mysql; } Public void Connect() throws SQLException, ClassNotFoundException,InstantiationException, IllegalAccessException{ Stringurl="jdbc:mysql://localhost/projectmanagement?characterEncoding=utf-8"; String user="root"; String pwd="root"; //加载驱动,这一句也可写为:Class.forName("com.mysql.jdbc.Driver"); Class.forName("com.mysql.jdbc.Driver").newInstance(); //创建到MySQL的链接 conn = java.sql.DriverManager.getConnection(url,user,pwd); } }
其中将ConectDB的构造函数设置为私有类型,在得到ConectDB的对象时须要调用ConectDB的静态函数getConetDB()来得到,采用这样的单例模式的好处在于永远保持与数据库的链接保持只有一个,避免多个客户端访问时引发数据库链接饱和,导致数据库崩溃。
1.2 服务端远程通讯
服务端远程通讯采用Socket。思路以下:
首先建立一个ServerSocket的对象mServerSocket,以后执行一个死循环,循环中经过调用mServerSocket.accept()进行阻塞。在有应用请求Socket时,accept的阻塞被解除,开启一个线程来单独处理该应用Socket请求。这时,循环又执行到了accept函数,主线程阻塞。子线程经过Socket的InputStream得到到应用传来的SQL请求,解析后调用ConectDB类中封装好的相关函数向数据库请求执行SQL,SQL被正确执行后会返回ResultSetMetaData类型的结果。为了传输获得结果,须要将ResultSetMetaData类型结果转换为JsonArray类型。最后经过JsonArray.toString().getByte(“UTF-8”)获得字节流,经过OutputStream发送给应用。代码实现以下:
package com.imudges.server; import androidservice.AndroidService; import com.imudges.database.ConectDB; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; import org.json.JSONException; import org.json.JSONObject; /** * * @author CSYYJ */ public class Server extends Thread { private static final int SERVERPORT = 5432; private static List<Socket> mClientList = new ArrayList<Socket>(); private ExecutorService mExecutorService;//线程池 private ServerSocket mServerSocket;//ServerSocket对象 private ConectDB mysql = null; public Server() { try { mServerSocket = new ServerSocket(SERVERPORT); mExecutorService = Executors.newCachedThreadPool();//建立一个线程池 System.out.println("start......"); Socket client = null; while (true) { client = mServerSocket.accept(); System.out.println("接收到请求!"); mClientList.add(client); mExecutorService.execute(new ThreadServer(client)); } } catch (IOException ex) { Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); } } public class ThreadServer implements Runnable { private Socket mSocket; private BufferedReader mBufferedReader; private PrintWriter mPrintWriter; private String mStrMSG; private InputStream in; private OutputStream out; public ThreadServer(Socket socket) throws IOException { this.mSocket = socket; in = socket.getInputStream(); mBufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = mSocket.getOutputStream(); mStrMSG = "user:" + this.mSocket.getInetAddress() + " come total:" + mClientList.size(); } @Override public void run() { while (true) { System.out.println("进入run函数等待数据通讯!"); byte buffer[] = new byte[128]; int len = 0; mStrMSG = ""; try { while ((len = in.read(buffer)) == 128) { mStrMSG += new String(buffer, 0, len); } mStrMSG += new String(buffer, 0, len); } catch (IOException ex) { Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); } mysql = new ConectDB(); mysql.strSql = mStrMSG; try { mysql.preparedStmt = mysql.conn.prepareStatement(mysql.strSql); } catch (SQLException ex) { Logger.getLogger(AndroidService.class.getName()).log(Level.SEVERE, null, ex); } try { mysql.sqlRst = (ResultSet) mysql.preparedStmt.executeQuery(); } catch (SQLException ex) { Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); } try { try { sendMessage(mSocket, mysql.sqlRst); } catch (JSONException ex) { Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); } } catch (IOException ex) { Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); } System.out.println(mStrMSG); } } private void sendMessage(Socket socket, ResultSet rs) throws IOException, JSONException { try { // json数组 MyJsonArray array = new MyJsonArray(); // 获取列数 ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); // 遍历ResultSet中的每条数据 while (rs.next()) { JSONObject jsonObj = new JSONObject(); // 遍历每一列 for (int i = 1; i <= columnCount; i++) { String columnName = metaData.getColumnLabel(i); String value = rs.getString(columnName); jsonObj.put(columnName, value); } array.put(jsonObj); } System.out.println(array.toString()); out.write(array.toString().getBytes("UTF-8")); out.flush(); } catch (SQLException ex) { Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); } } } }
二、Android终端的实现
基于实现以上以后,Android端剩余的任务只剩下了通讯。Android中Socket的实现了和Java标准是相同的。
首先Android链接socket,mSocket=new Socket(SERVERIP,SERVERPOST),而后获取socket的输入输出流,要注意输入输出流须要统一编码方式,我采用的是“UTF-8”。在获取到数据库输入输出流以后,便可经过输出流向远程服务端发送sql指令,发送完指令以后,经过输入流获取到返回的SQL执行结果。固然在接收时也经过JasonArray接受,而后在解析便可。
代码实现以下:
package com.imudges.socket; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.ContentHandler; import java.net.Socket; import java.net.UnknownHostException; import org.json.JSONArray; import org.json.JSONException; import com.imudges.tools.ConectIPs; import com.yangyu.mytitlebar02.R; import android.content.res.Resources; import android.os.Handler; import android.os.Message; import android.util.Log; public class ConnectSQL { public static ConnectSQL connectSQL=null; public static final String SERVERIP =ConectIPs.SERVERIP; private static final int SERVERPOST = ConectIPs.SERVERPOST; private static Thread mThread = null; public static Socket mSocket = null; private static JSONArray array=null; private OutputStream outputStream=null; private static Handler mHandler=null; private InputStream inputStream =null; boolean isReturns=false; private ConnectSQL() throws IOException, Exception { mSocket = new Socket(SERVERIP, SERVERPOST); outputStream=mSocket.getOutputStream(); inputStream = mSocket.getInputStream(); } public static ConnectSQL getConnectSQL() throws IOException, Exception { connectSQL=new ConnectSQL(); return connectSQL; } private static boolean IsConnected () { boolean flag=true; try { mSocket.sendUrgentData(0xFF); System.out.println("socket連接成功"); } catch (Exception e) { flag=false; System.out.println("socket連接斷開"); } return flag; } public JSONArray getJSONArry(String sql) throws IOException, Exception { if (mSocket == null) { connectSQL=new ConnectSQL(); } outputStream.write(sql.getBytes("UTF-8")); outputStream.flush(); byte buffer[] = new byte[1024]; int len = 0; String mStrMSG = ""; try { while ((len = inputStream.read(buffer)) == 1024) { mStrMSG += new String(buffer, 0, len,"UTF-8"); } System.out.println("len:"+len); mStrMSG += new String(buffer, 0, len,"UTF-8"); Log.v("debug", "lebgth:"+mStrMSG.length()); System.out.println("得到到的str:"+mStrMSG); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { array=new JSONArray(mStrMSG); } catch (JSONException e) { e.printStackTrace(); } System.out.println("array:"+array.toString()); return array; } public void close() { try { mSocket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
服务端源码下载地址:http://download.csdn.net/download/u012122069/6297155