待我年逾花甲,重新回到乡下。
池里养些鱼虾,坡上满是山茶。
不见雾霾黄沙,只有朝阳晚霞。
牛羊伴着鸡鸭,瓜田李下小麻。
孙儿承欢膝下,老伴陪与床榻。
纵是满口假牙,却也笑靥如花。
现在的移动端开发主要流行Hybrid开发方式,市面上也很多框架提供了前端访问手机硬件相关的API,但是总有不能满足的时候,所以,我们就自己做一个框架来封装提供给js的各种接口(框架不是本文重点),那么问题就来了,如何调试呢?前期开发可以通过打桩的方式来模拟,但是要发布上线必须要真枪实弹的调试通过才行。于是就输出了一个调试网页的DEMO(android/ios app),只能是通过手动输入网页地址来查看界面效果和测试接口的正确性,虽然团队没得微信那么强大开发一个桌面调试工具,但这体验和效率也确实太差了。确实不能忍,所以就写了个小玩意儿来解决这个问题。
思路:通过socket 长连接的的方式,在PC控制台输入调试的网页地址,就能立马在手机上看到网页的效果。
看看效果截图:
要实现这个小玩意儿,需要一个简单的后端程序,代码如下:
package com.lixue;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/**
*
* @author lh
* @date 2016/12/12 11:30
*/
public class AppServer {
public static void main(String[] args) {
new ServerThread().start();
}
}
class ServerThread extends Thread {
boolean flag = false;
ServerSocket ss;
public void run() {
try {
ss = new ServerSocket(9999);// 创建服务器,并开放9999端口
System.out.println("Server Listening on 9999...");
flag = true;
} catch (Exception e) {
e.printStackTrace();
}
Socket sc = null;
DataOutputStream outputStream = null;
Scanner scanner = null;
while (flag) {
try {
// 监听服务器端口,一旦有数据发送过来,那么就将数据封装成socket对象
// 如果没有数据发送过来,那么这时处于线程阻塞状态,不会向下继续执行
sc = ss.accept();
System.out.println(sc.getInetAddress() + " connect...");
outputStream = new DataOutputStream(sc.getOutputStream());
outputStream.writeUTF("connect successful");
scanner = new Scanner(System.in);
while (true) {
String line = scanner.nextLine();
System.out.println("你输入的是:" + line);
outputStream.writeUTF(line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
if (sc != null) {
sc.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.lixue.debugwebapp;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.view.KeyEvent;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
* @author lh
* @version 1.0.0
* @filename MainActivity
* @description --------------------------------------------------------
* @date 2016/12/12 12:00
*/
public class MainActivity extends AppCompatActivity {
public static final int CONNECT = 99;
public static final int SUCCESS = 100;
public static final int ERROR = 101;
public static final String KEY_CONNECT = "connect";
public static final String KEY_SUCCESS = "success";
public static final String KEY_ERROR = "error";
public static final String ADDRESS = "192.168.191.1";
public static final int PORT = 9999;
private TextView tv;
private WebView webView;
private StringBuilder sb = new StringBuilder();
private SocketThread socketThread = null;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
String str = null;
switch (msg.what) {
case CONNECT:
str = msg.getData().getString(KEY_CONNECT);
tv.append("\n" + str);
scrollToBottom();
break;
case SUCCESS:
str = msg.getData().getString(KEY_SUCCESS);
tv.append("\n" + str);
scrollToBottom();
if(isUrl(str)) {
webView.loadUrl(str);
}else{
tv.append("\t\t\t不合法的url");
}
break;
case ERROR:
str = msg.getData().getString(KEY_ERROR);
tv.append("\n" + str);
scrollToBottom();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
connect();
}
@Override
protected void onDestroy() {
close();
super.onDestroy();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(webView.canGoBack()){
webView.goBack();
}else{
finish();
}
return true;
}
private void init() {
tv = (TextView) this.findViewById(R.id.tv_url);
tv.setMovementMethod(ScrollingMovementMethod.getInstance());
webView = (WebView) this.findViewById(R.id.wv);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
webView.loadUrl(url);
return true;
}
});
}
private void connect() {
if (socketThread == null) {
socketThread = new SocketThread(ADDRESS, PORT, handler);
socketThread.start();
tv.append("发起建立连接请求");
tv.append("\n" + ADDRESS + ":" + PORT);
scrollToBottom();
}
}
private void close() {
if (socketThread != null) {
socketThread.closeThread();
socketThread = null;
}
}
/**
* TV滚动到底部
*/
private void scrollToBottom() {
int offset = tv.getLineCount() * tv.getLineHeight();
if (offset > tv.getHeight()) {
tv.scrollTo(0, offset - tv.getHeight());
}
}
private class SocketThread extends Thread {
private String address;//ip
private int port;//port >1023
private Handler handler;
private DataInputStream in;
private Socket socket;
private boolean flag = false;
public SocketThread(String address, int port, Handler handler) {
this.address = address;
this.port = port;
this.handler = handler;
}
@Override
public void run() {
try {
socket = new Socket(address, port);
in = new DataInputStream(socket.getInputStream());
String content = in.readUTF();
//连接成功发送消息
sendMessage(KEY_CONNECT, content, CONNECT);
while (!flag) {
content = in.readUTF();
//数据传输过程发送消息
sendMessage(KEY_SUCCESS, content, SUCCESS);
}
} catch (IOException e) {
e.printStackTrace();
//发送异常消息
sendMessage(KEY_ERROR, e.getMessage(), ERROR);
} finally {
try {
if (in != null) {
in.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void sendMessage(String key, String value, int code) {
Bundle bundle = new Bundle();
bundle.putString(key, value);
Message message = handler.obtainMessage();
message.what = code;
message.setData(bundle);
handler.sendMessage(message);
}
public void closeThread() {
flag = true;
}
}
/**
* 简单的判断一下是否是网址,严谨性可以自行完善
* @param str
* @return
*/
public static boolean isUrl(String str) {
boolean isUrl = false;
if (str != null) {
if (str.startsWith("http://") || str.startsWith("https://") || str.startsWith("ftp://")) {
isUrl = true;
}
}
return isUrl;
}
}
好了,整体比较简单,这只是一个简单的DEMO,但是也应该可以满足网页真机调试的需求了,另外Android的界面相关优化如IP/Port等配置可以自行实现,DEMO打印了相关地址跳转、以及连接错误和异常等信息,如果需要打印输出更多的功能信息,大家可以自行优化。
最后怕很多小白不知道怎么用,总结下使用方式:
第一步:把服务端程序AppServer跑起来;
第二步:把Android代码中的Socket地址改成你自己电脑的地址;要是觉得改来改去不方便,可以自己做一个Android端的IP/Port配置界面;
第三步:连接成功后,在后端控制台输入要调试的URL地址敲上回车就可以了。服务端我用的是Eclipse,你可以用任意的IDE或者命令行只要你喜欢!!!
第四步:经过上述操作,在手机端就能实时显示后端输入的网页了。
最后,再次说明这只是一个DEMO,纯属笔者无聊花了一小时写的,要是用着不安逸可以自己修改完善,比如Socket重连啊这些。如果要求很高可以用WebSocket,代码会更加简单,重连这些也都有实现。
Android代码地址:https://github.com/allmyson/DebugWebApp
完整附件地址:请点我。