ndroid向前端发送数据
一般需要交互的数据无非就是文件和字符串(可以替代很多东西),文件又可以变为字符串流进行传输。本文将使用okhttp包从Android端向后端发送数据和接收返回数据。
注意:个人电脑测试若无https协议的域名或ip地址,请在Android的AndroidManifest.xml文件中声明使用明文传输,即不加密而使用http协议。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:usesCleartextTraffic="true"//此处默认选项为false,不启用明文传输,改为true即可
android:theme="@style/AppTheme">
</application>
数据发送,Android端向客户端发送消息如果为get直接在url中添加参数即可,下面主要介绍post,用字符串作为post参数,文件将经过base64编码为字符串传输,字符串类型数据可采用json传输也可以直接用字符串。
注意:okhttp包需额外下载,包含okhttp和okio两个包且版本存在不适配问题,需要对应,这里采用okhttp3.12, okio1.14。如果使用grade(一般Android都是这个),可直接在build.grade中使用`implementation group: 'com.squareup.okio', name: 'okio', version: '1.14.0'`导入,建议下载对应包。
注意:数据发送操作(向网络申请数据)为耗时操作,不可在主线程中运行,另外不建议直接在oncreate等视图创建方法中调用,可以作为按钮点击事件调用。另外收sdk限制同时会限制jdk版本,这里使用的base64编码是Android自带的,与稍后后端解析时的不同。因为jdk版本原因及编码规范,存在不同格式的base64编码,Androidsdk26以上可采用java自带的base64编码工具。
new Thread()
{
//重写线程run方法
@Override
public void run()
{
//定义okhttp对象
OkHttpClient okHttpClient=new OkHttpClient();
try {
//如果传输文件
FileInputStream inputStream=new FileInputStream(new File(getDataDir() + "/x1.png"));
//文件读取为bytes
byte[] bytes=new byte[inputStream.available()];
inputStream.read(bytes);
//将bytes使用base64编码
String a1=Base64.encodeToString(bytes, Base64.NO_PADDING);
//post的方法体,用来添加数据(key-value方式),但只能添加字符串作为数据
FormBody formBody=new FormBody.Builder().add("w1", a1).add("w2", "wad").build();
//请求创建
Request request=new Request.Builder().url("http://192.168.56.1:8080/titan/login").post(formBody).build();
//执行请求
Call call=okHttpClient.newCall(request);
Response response=call.execute();//获得服务端返回内容
//将返回内容作为字符串输出,也可转换为bytes等
System.out.println(response.body().string());
}
catch (IOException e) {
e.printStackTrace();
}
}
}.start();//线程直接启动
服务端接收与返回数据
项目使用tomcat,利用struts的action请求和返回数据。action可返回不限于json、html网页、流格式的内容。项目大致结构如下。
项目结构图
其中images文件夹作为Android端文件存储库和返回信息库,一般不建议将此类文件放入项目内,一个是通过相对路径不方便调取(存在诸多问题,所以放在外边采用绝对路径访问),还有就是对于tomcat下webapps的内容均可以通过url访问,文件直接就泄露了。index.jsp并无什么用,只是默认主页。
struts.xml配置文件,此文件build完成时需在WEB-INF/classes文件夹下,关于在web.xml里配置struts不再给出.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
//此包用于返回json数据,继承json-default包,也可通过result-types指定,不再介绍
<package name="backJOSN" extends="json-default">
//活动配置,负责返回json数据,可以返回json文件或者自定义json串,这里是有action内部变量组成的json串
<action name="login" class="com.example.titanback.LoginAction">
<result type="json">
</result>
</action>
</package>
//继承默认包,可返回文件流或者html文件文本
<package name="backHTML" extends="struts-default">
//返回图片的活动
<action name="imgBack" class="com.example.titanback.ImgBackAction">
<result type="stream">//返回流
//配置返回流,返回png格式图片,并配置对应变量
<param name="contentType">image/png</param>
<param name="inputStream">inputStream</param>
</result>
</action>
</package>
</struts>
代码,此代码将返回json字符串:
//可继承Action接口或继承ActionSupport类
public class LoginAction extends ActionSupport
{
//变量和setter、getter方法
private String username;
private String password;
public void setUsername(String username) { this.username=username; }
public String getPassword() { return password; }
public String getUsername() { return username; }
public void setPassword(String password) { this.password=password; }
@Override
public String execute() throws Exception
{
//获取上下文,getResponse可获取response对象
HttpServletRequest actionContext=ServletActionContext.getRequest();
//输出获取参数
System.out.println(actionContext.getParameter("w1"));
String a1=actionContext.getParameter("w1");
//文件写入流,采用绝对路径
FileOutputStream outputStream=new FileOutputStream("C:\\Users\\1\\Desktop\\Titan\\titanback\\images\\x1.png");
//将base64解码,注意以为jdk原因,需要去掉换行符,最好采用utf-8编码
ByteArrayInputStream inputStream=new ByteArrayInputStream(Base64.getMimeDecoder().decode(a1.replace("\r\n", "").getBytes(StandardCharsets.UTF_8)));
//输出流直接转换
inputStream.transferTo(outputStream);
outputStream.close();//关闭
setUsername("w112");//设置参数
setPassword("wdaw");
return SUCCESS;
}
}
注意:关于base64编码问题,请看:(https://blog.csdn.net/kevin_mails/article/details/87878601)
此代码将返回png格式图片流:
解释一个名词吧 Hybrid, 相信能看到这篇文章的同学对这个词都不会感到陌生, 可能爱恨交叉的感觉会更强烈一些...
回到正题, Hybrid翻译过来叫混合,混合物,在前端世界里有一个词语叫混合开发便是它,大白话点就是将网页内嵌在原生app中,然后产生一系列的交互
常用的交互方式
这里以android 为例,android 中可以通过WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url, 然后解析该 url 的协议, 如果检测到是预先约定好的协议,就调用相应方法
协议式的通信适用于单向交互, 客户端想要回传给我们参数比较复杂
代码理解
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button type="button" id="button1" onclick="callAndroid()">点击调用Android代码</button>
<script>
function callAndroid() {
/*约定的url协议为:js://webview?arg1=111&arg2=222*/
location.href="js://webview?arg1=111&arg2=222";
}
</script>
</body>
</html>
简单来说,就是客户端为我们做了一层关系映射, 也可以理解原生app端会向webview暴露一个顶层对象,就像js中的window,这个对象包含web需要但不具备因此由原生实现的一些方法
代码理解
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- 点击按钮则调用jsbridge函数 -->
<button type="button" id="button1" onclick="jsbridge()">Hello</button>
<script>
function jsbridge() {
// 由于对象映射,所以调用test对象等于调用Android映射的对象
test.hello("js调用了原生app暴漏出来jsbridge中的hello方法");
}
</script>
</body>
</html>
lt;!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#href-box{
text-align: center;
font-size: 40px;
margin:0 atuo;
background: bisque;
}
#btn-box {
display: flex;
border-radius: 99%;
background: burlywood;
}
.menu h1 {
text-align: center;
background: greenyellow;
}
.menu div {
font-size: 24px;
text-align: center;
background: blueviolet;
}
#btn {
width: 100px;
height: 100px;
font-size: 24px;
background: yellowgreen;
display: block;
margin: 0 auto;
background: blue;
border-radius: 50%;
}
#showtext {
display: block;
margin: 0 auto;
font-size: 15px;
background: pink;
border: 1px solid
}
#showarray {
width: 100px;
height: 100px;
font-size: 24px;
background: yellowgreen;
display: block;
margin: 0 auto;
background: red;
border-radius: 50%;
}
#shuaxin{
width: 1500px;
height: 100px;
font-size: 24px;
background: yellowgreen;
display: block;
margin: 0 auto;
}
#showbackmap {
width: 100px;
height: 100px;
font-size: 24px;
background: yellowgreen;
display: block;
margin: 0 auto;
background: gray;
border-radius: 50%;
}
#text {
display: block;
margin: 0 auto;
font-size: 30px;
background: pink;
border: 1px solid
}
#fattext {
display: block;
margin: 0 auto;
font-size: 30px;
background: gray;
}
#showstruct {
width: 100px;
height: 100px;
font-size: 24px;
background: yellowgreen;
display: block;
margin: 0 auto;
background: red;
border-radius: 50%;
}
</style>
</head>
<body>
<div id="href-box">
<a href="https://blog.csdn.net/wangzhen12138/article/details/118068686?spm=1001.2014.3001.5502">CSDN学代码的小臻</a>
</div>
<button id="shuaxin">刷新</button>
<div class="menu">
<h1>基本的操作命令</h1>
<div>1. dir显示目录</div>
<div>2. md创建文件夹</div>
<div>3. mk创建文件</div>
<div>4. del删除文件</div>
<div>5. rd 删除文件夹</div>
<div>6. cd 文件名</div>
<!--向下找-->
<div>7. cd.. 文件名</div>
<div>8. show 输出当前节点</div>
<div>9.showdir</div>
<!--改变当前位置为父级继续找-->
</div>
<div id="btn-box">
<button id="btn">kick me</button>
<button id="showarray">show array</button>
<button id="showbackmap">show backmap</button>
<button id="showstruct">show struct</button>
</div>
<div>
<textarea name="" id="showtext" cols="30" rows="20"></textarea>
<textarea name="" id="text" cols="15" rows="8"></textarea>
</div>
<textarea name="" id="fattext" cols="30" rows="50"></textarea>
<script>
window.onload=function () {
//位示图定义
/*var tArray=new Array(); / / 先声明一维
for (var k=0; k < i; k++) { //一维长度为i,i为变量,可以根据实际情况改变
tArray[k]=new Array(); //声明二维,每一个一维数组里面的一个元素都是一个数组;
for (var j=0; j < p; j++) { //一维数组里面每个元素数组可以包含的数量p,p也是一个变量;
tArray[k][j]=""; //这里将变量初始化,我这边统一初始化为空,后面在用所需的值覆盖里面的值
}
}*/
var shuaxin=document.getElementById("shuaxin");
shuaxin.οnclick=function(){
location.reload();
}
var creatfile=0;
var backmap=new Array();
var bitmap1=new Array();
for (var i=0; i < 64; i++) {
backmap[i]=" ";
}
for (var i=0; i < 64; i++) {
bitmap1[i]=" ";
}
var useblock=new Array();
for (i=0; i < 64; i++) {
useblock[i]=" ";
}
var arr=new Array();
var wi=8, p=8;
for (var k=0; k < wi; k++) {
arr[k]=new Array();
for (var j=0; j < p; j++) {
arr[k][j]="";
}
}
function getrandom() {
for (i=0; i < 8; i++) {
for (j=0; j < 8; j++) {
var num=Math.random();
if (num > 0.5) {
arr[i][j]=1;
}
else {
arr[i][j]=0;
}
}
}
console.log(arr);
}
var showstruct=document.getElementById("showstruct");
showstruct.onclick=function () {
var text=document.getElementById("showtext");
var kin=0;
for (var i=0; i < 10; i++) {
if (docmessage.doc_name[i] !=="") {
kin=kin + 1;
}
console.log(docmessage.doc_name[i]);
}
console.log(kin);
if (kin===0) {
alert("当前的文件目录为空");
} else {
text.value="1.文件名2.文件层级3.文件大小4.文件父类5.文件创建顺序6.文件占据块数量"+"\n";
for (i=0; i < 10; i++) {
text.value=text.value + " " + docmessage.doc_name[i] + " " + docmessage.doc_state[i] + " " + docmessage.doc_memory[i] + " " + docmessage.doc_parents[i] + " " + docmessage.doc_creatfile[i] + " " + docmessage.doc_creatblock[i]+"\n"
}
}
}
var showarray=document.getElementById("showarray");
showarray.onclick=function () {
var text=document.getElementById("showtext");
for (i=0; i < 8; i++) {
for (j=0; j < 8; j++) {
text.value=text.value + " " + arr[i][j]
}
text.value=text.value + "\n";
}
console.log(arr);
}
var showbackmap=document.getElementById("showbackmap");
showbackmap.onclick=function () {//
var text=document.getElementById("showtext");
for (var i=0; i < 64; i++) {
text.value=text.value + " " + backmap[i] + "";
}
var kin=0;
for (var i=0; i < 64; i++) {
if (backmap[i] !=" ") {
kin=kin + 1;
}
}
if (kin===0) {
alert("当前的backmap还未生成");
}
console.log(backmap);
}
getrandom();
function setarr() {
var text=document.getElementById("text");
text.value=arr[0] + "\n" + arr[1] + "\n" + arr[2] + "\n" + arr[3] + "\n" + arr[4] + "\n" + arr[5] + "\n" + arr[6] + "\n" + arr[7];
}
setarr();
function getbitmap() {
var wnum=0
for (var i=0; i < 8; i++) {
for (var j=0; j < 8; j++) {
wnum=wnum + 1;
if (arr[i][j]==0) {
bitmap1[i * 8 + j]=0;
}
else {
bitmap1[i * 8 + j]=1;
}
}
}
}
getbitmap();
function setfat() {
var fat=document.getElementById("fattext");
for (i=0; i < 64; i++) {
fat.value=fat.value + "第" + i + "个位置的内存占用情况为" + bitmap1[i] + "\n";
//console.log(bitmap1[i]);
}
// console.log(fat.value);
}
var fat=document.getElementById("fattext");
//fat.value="第"+i+"个位置的内存占用情况为+"+bitmap1[i]+"\n";
setfat();
console.log(bitmap1);
//自定义构造函数
function nowdocmessage(state, parents, number, name) {
this.name=name;
this.state=state;
this.parents=parents;
this.number=number;
this.memory="";
this.creatfile=0;
}
var docmessage={
doc_name: Array(),
doc_state: Array(),
doc_parents: Array(),
doc_number: Array(),
doc_memory: Array(),
doc_creatfile: Array(),
doc_creatblock: Array(),
}
for (i=0; i < 10; i++) {
docmessage.doc_name[i]="";
docmessage.doc_memory[i]="";
docmessage.doc_number[i]="";
docmessage.doc_parents[i]="";
docmessage.doc_state[i]="";
docmessage.doc_creatfile[i]="";
docmessage.doc_creatblock[i]="";
}
var nowdoc=new nowdocmessage(0, -1, 0, "outside");
// nowdoc.memory=prompt("请输入您想要的文件大小");
//console.log("创建的文件大小为" + nowdoc.memory);
function isexist(state, name) {
// console.log(state,name);
for (i=0; i < 100; i++) {
//console.log(docmessage.doc_state[i],docmessage.doc_name[i]);
if (docmessage.doc_state[i]==state && docmessage.doc_name[i]==name) {
return false;
}
}
}
console.log("nowdoc.state" + nowdoc.state);
var btn=document.getElementById("btn");
// for (p=0; p <=2; p++) {
//btn.onclick=fun(nowdoc);
// }
k=0;
console.log(nowdoc);
btn.onclick=function fun(events) {
console.log("传进来的参数为" + nowdoc);
var keyword=prompt("请输入您想要进行的操作");
console.log(keyword);
if (keyword=="md") {//创建文件夹
var docname=prompt("请输入您想要的文件夹");
console.log("当前的state为" + nowdoc.state);
console.log("当前的number为" + nowdoc.number);
if (isexist(nowdoc.state, docname)==false) {
alert("this document is exist");
}
else {
alert("this document is not exist");
console.log("当前的state" + nowdoc.state);
console.log("当前的parents " + nowdoc.parents);
docmessage.doc_parents[k]=nowdoc.parents;
docmessage.doc_name[k]=docname;
docmessage.doc_memory[k]="";
docmessage.doc_state[k]=nowdoc.state;
docmessage.doc_number[k]=nowdoc.number;
console.log(docmessage);
k=k + 1;
alert("该文件夹创建成功");
nowdoc.number++;
}
}
if (keyword=="rd") {//删除文件夹
var rdname=prompt("请输入当前目录下您想要删除的文件夹名字");
for (var i=0; i < 10; i++) {
console.log("想要删除文件夹名字" + rdname);
console.log("doc父亲名字" + docmessage.doc_name[docmessage.doc_parents[i]]);
if (rdname=docmessage.doc_name[i]) {
console.log(docmessage.doc_parents[i]);
if (docmessage.doc_name[docmessage.doc_parents[i]]==rdname && docmessage.doc_memory !=" ") {
alert("该文件夹下有内容不能删除");
}
else {
docmessage.doc_name=" ";
docmessage.doc_memory=" ";
docmessage.doc_number=" ";
docmessage.doc_parents=" ";
docmessage.doc_state=" ";
}
}
}
}
if (keyword=="del") {
console.log("del");
var delname=prompt("请输入您想要删除的文件");
console.log("delname" + delname);
console.log(docmessage);
for (var i=0; i < 10; i++) {
console.log("docmessage.doc.name memory" + docmessage.doc_name[i], +docmessage.doc_memory[i]);
if (delname==docmessage.doc_name[i] && docmessage.doc_memory[i]==0) {
alert("该目录是文件夹");
}
if (delname==docmessage.doc_name[i] && docmessage.doc_memory[i] !=0) {
alert("进行删除操作");
var nodenumber=i;
var returnblock=docmessage.doc_creatblock[i] - 1;
var returnnumber=docmessage.doc_creatfile[i];
console.log(returnblock, returnnumber);
var sum=0;
for (var i=0; i < 10; i++) {
if (docmessage.doc_creatfile[i] < returnnumber) {
console.log(docmessage.doc_creatfile[i]);
sum=sum + docmessage.doc_creatfile[i] * docmessage.doc_creatblock[i];
}
}
console.log("sum" + sum);
for (var i=sum; i < sum + returnblock + 1; i++) {
var end=sum + returnblock;
console.log(backmap[i]);
console.log("返回数组的值为" + backmap);
var hang=parseInt(backmap[i] / 8);
var lie=backmap[i] - 8 * hang;
console.log(hang);
arr[hang][lie]=0;
bitmap1[backmap[i]]=0;
backmap[i]=" ";
setfat();
}
console.log(arr);
setarr();
console.log("nodenumber" + nodenumber);
docmessage.doc_creatblock[nodenumber]=" ";
docmessage.doc_creatfile[nodenumber]=" ";
docmessage.doc_memory[nodenumber]=" ";
docmessage.doc_name[nodenumber]=" ";
docmessage.doc_number[nodenumber]=" ";
docmessage.doc_parents[nodenumber]=" ";
docmessage.doc_state[nodenumber]=" ";
console.log("i的值为" + i);
console.log(docmessage);
}
}
alert("该文件删除完毕");
console.log("返回数组的值为" + backmap);
}
if (keyword=="mk") {//创建文件
creatfile=creatfile + 1;
var filename=prompt("请输入您想要创建的文件的名字");
var filememory=prompt("请输入您想要创建文件的大小");
docmessage.doc_parents[k]=nowdoc.state;
docmessage.doc_name[k]=filename;
docmessage.doc_memory[k]=filememory;
docmessage.doc_state[k]=nowdoc.state;
docmessage.doc_number[k]=nowdoc.number;//有两行赋值在下面
docmessage.doc_creatfile[k]=creatfile;
var n=filememory / 512;
var a=Math.round(n);
docmessage.doc_creatblock[k]=a;
console.log("需要的块数" + a);
for (var i=0; i < 8; i++) {
for (var j=0; j < 8; j++) {
if (arr[i][j]==0 && a !=0) {
arr[i][j]="1";
a=a - 1;
useblock[i * 8 + j]="1";
}
}
}
console.log(useblock);
console.log(arr);
setarr();
var fattext=document.getElementById("fattext");
/* for (var i=0; i < 8; i++) {
for (var j=0; j < 8; j++) {
if (useblock[i][j]==1) {
console.log(i,j);
var number=i * 8 + j;
c
}
}
}*/
for (var i=0; i < 64; i++) {
if (useblock[i]==1) {
console.log("useblock0" + useblock[0]);
console.log("i的值" + i);
fattext.value=fattext.value + "\n" + i;
for (var j=0; j < 64; j++) {
if (backmap[j]===" ") {
console.log(backmap[0]);
backmap[j]=i;///空默认等于零
break;
}
}
}
}
console.log(backmap)
fattext.value=fattext.value + "\n" + "该文件内存占用完毕" + "\n";
for (var i=0; i < 64; i++) {
useblock[i]=" ";
}
console.log("creatfile" + docmessage.doc_creatfile[k]);
console.log("creatblock" + docmessage.doc_creatblock[k]);
console.log(docmessage);
console.log(backmap);
k=k + 1;
}
if (keyword=="cd") //切换
{
var choosename=prompt("请输入您想要选择的文件夹");
console.log("测试数据");
for (var i=0; i < 10; i++) {
console.log(choosename + " " + docmessage.doc_name[i]);
console.log(docmessage.doc_state[i] + " " + nowdoc.state);
if (docmessage.doc_name[i]==choosename && docmessage.doc_state[i]==nowdoc.state) {
nowdoc.state=docmessage.doc_state[i] + 1;
nowdoc.parents=i;
nowdoc.name=docmessage.doc_name[i];
nowdoc.number=docmessage.doc_number[i]
}
}
}
if (keyword=="cd..") {
alert("已返回上一级文件夹 ");
for (var i=0; i < 10; i++) {
if (nowdoc.parents==i) {
nowdoc.parents=docmessage.doc_parents[i];
nowdoc.name=docmessage.doc_name[i];
nowdoc.number=docmessage.doc_number[i];
nowdoc.state=docmessage.doc_state[i];
}
}
console.log(docmessage);
}
if (keyword=="showdir") {//显示全部文件夹
console.log("文件名 文件阶段 文件大小")
console.log(docmessage.doc_name);
var text=document.getElementById("showtext");
text.value="文件名 文件阶段 文件大小\n";
for (var i=0; i < 10; i++) {
if (docmessage.doc_name[i] !=" ") {
console.log(docmessage.doc_name[i] + " " + docmessage.doc_state[i] + " " + docmessage.doc_memory[i]);
text.value=text.value+" "+docmessage.doc_name[i] + " " + docmessage.doc_state[i] + " " + docmessage.doc_memory[i]+"\n";
}
}
}
if (keyword=="dir") {//显示当前路径的文件
console.log("文件名 文件阶段 文件大小")
var text=document.getElementById("showtext");
text.value="文件名 文件阶段 文件大小\n";
for (var i=0; i < 10; i++) {
if (nowdoc.state==docmessage.doc_state[i]) {
console.log(docmessage.doc_name[i] + " " + docmessage.doc_state[i] + " " + docmessage.doc_memory[i]);
text.value=text.value+" "+docmessage.doc_name[i] + " " + docmessage.doc_state[i] + " " + docmessage.doc_memory[i]+"\n";
}
}
}
if (keyword="show")//显示当前节点
{
console.log("当前节点名字" + nowdoc.name);
console.log("当前节点阶段" + nowdoc.state);
console.log("当前节点数字" + nowdoc.number);
console.log("当前节点父亲" + nowdoc.parents);
}
console.log("btn按钮执行完毕");
}
}
</script>
</body>
</html>
*请认真填写需求信息,我们会在24小时内与您取得联系。