Android开发遇到的问题-校园交易平台


本文内容很干,记录下自己实践中遇到的问题与大家分享,目前想到这么多,后续想到了还会更新。

🤖 作者简介: w20,自律才有自由 ⭐⭐⭐

Nginx

1.Nginx端口转发功能:

我们的项目打算采用前后端分离,由于一开始我们组的成员并没有将后台上传至服务器,当初是为了避免端口的不一致,所以我们使用了Nginx。
通过查找资料发现,这涉及到一个跨域拦截的问题。
(1)何为同域?
同域的三个要素:协议:http 域名:localhost 端口:8002地址http://localhost:8002
当两个地址进行比较时,只要三者中有任何一个不同,就算跨域
(2)为何产生跨域?
当项目前后端分离时,前后端若运行在不同的端口上,属于跨域,此时前后端相互请求发送数据时,会因为跨域被浏览器拦截。
(3)如何解决跨域?
方法一:关闭浏览器拦截,开放跨域请求。但这样如果前台中指向后台的地址被恶意设为黑客的服务器,就可能会造成损失。
方法二:将跨域变成同域,用Nginx便可实现。
(4)Nginx端口转发原理:
后台并没有拦截,而是浏览器认为有了跨域请求所以进行了跨域拦截,只需让浏览器认为是在同一个域下的请求,那么浏览器就不会再拦截了。
举例说明:
假设前台使用4200端口,后台使用8080端口,那么,再加入一个8011端口,作为用户访问时连接的端口。在前台的代码中,会有拦截器,如果发现某个请求是指向后台的,就会在Url中加入一个特殊的标识(比如加一个/api/作为前缀)
// header中带有do_not_intercept,且值为true,则不添加url前缀
if ((‘true’ !== req.headers.get(YunzhiInterceptor.DONT_INTERCEPT_HEADER_KEY))
&& !url.startsWith(‘https://’) && !url.startsWith(‘http://’)) {
url = ‘/api/’ + url;
}
现在,无论是指向前台还是后台的请求,都会发送到8011端口,只不过,指向后台的请求会有一个**/api/前缀**。
接下来使用Nginx监听8011端口,当接收到请求时,根据是否有前缀,来判断此请求交给前台或后台处理。
参考网址https://segmentfault.com/a/1190000022415375

2.Nginx的路径问题

当运行APP不成功时,(具体报错忘了),推测Nginx出现了问题,由于对路径格式的不了解,我们查找了大量资料,耗费了大量时间,但怎么也是运行不了。
后来无意间突然发现不是Nginx的路径问题,而是从教学楼到宿舍忘记改局域网的IP了,所以无法运行。当时和我一起捣鼓这个问题的同学也是很无语。大意了…

#Nginx中的部分路径配置:
 location /resources {
       autoindex on;
       #root /;
       **alias D:/project/deal-box/api/resources;**
    }

3.Windows中的Nginx的下载及配置教程
参考网址https://segmentfault.com/a/1190000022415130

ui问题

(1)constraintlayout布局中使用Layout布局,如遇Layout布局爆红,在里面加上tools:ignore="MissingConstraints"即可解决。如下所示:
<LinearLayout
…(省略)
tools:ignore=“MissingConstraints”>
(2)ui界面大小与手机界面大小不匹配的问题,一般来说用wrap_content即可,但当用到Relativelayout布局时,无法使用wrap_content,只能设置具体的dp时,也可以使用ScrollView,设置滑动窗口,通过滑动解决界面不能完整展示的问题。
(3)fragment+Navigation实现底部导航栏,fragment在一个layout中声明后,要在另一个navigationlayout中具体说明有哪几个fragment及其id,即底部导航栏要有几个界面。之后分别定义layout布局文件,根据fragment的id定义界面的布局。

Spring Data中的CrudRepository派生方法

在实现发布记录时,我们从前台将当前用户的id传到了后台,像往常一样,实现了自定义的接口方法,打算使用JPA中的join实现联表查询,我和一起实现的同学(由于返乡后没在一起,开启远程桌面控制一起改代码,这样也是为了避免一个人修改时,改错代码,一个人总是犯糊涂,一个主要负责前端,一个主要负责后端,但前后端我和那位同学也基本都能看懂)多次确认与数据库中的字段无误,但测试运行时后台总是传来无法找到create_user_id,后来我那位同学(hzl)想到了他之前写后端时的经历,用了Spring Data中的CrudRepository中的派生方法来实现。注意的是要先写类型,这时才能有完整的代码提示,后写类型的话可能就提示了。

public interface GoodsRepository extends CrudRepository<Goods, Long>, JpaSpecificationExecutor<Goods> {
//  List<Goods> findAllByOrderByIdDescByName(String name);

  List<Goods> getAllByName(String name);

  **List<Goods> findAllByCreateUserId(Long id);**
}

https://segmentfault.com/a/1190000040821189里面对CrudRepository派生方法有详细的介绍。

自定义接口方法实现

现在回想起来可能比较简单,但在当时没有接触过后端时,感觉很难理解。
这还要从我们的后台结构说起,我们分别定义了entity文件、service文件、controller文件、config文件等等。
(1)config文件里的类主要是实现配置以及添加路径拦截器,路径拦截器里有很多定义的路径,与前台请求方法对应,是为了准确接受前台请求的,还有端口配置、websocket聊天配置等等。
(2)entity文件里的类主要是定义类的成员变量和方法,便于后续实现接口功能。
(3)service文件里主要是定义接口方法,并有对应的类来实现。
(4)controller文件里的类主要是接受前台请求,并通过调用service中的接口方法实现请求,并返回给前台数据。
前台中也与后台类似,要编写请求方法,并于后台的路径拦截器中的路径对应,在具体实现时,经常忘写参数或者路径缺少‘/’等问题,导致请求失败。
(5)在自定义方法实现时,要按照上面一一对照,判断是否有少写的地方,犯错:忘记写路径以及路径没有严格对应一致,参数类型,参数个数不一致等。
例如由于经常要获取当前用户的信息,所以实现一个UserService类中实现一个getCurrentUser()方法获取当前用户信息,并将当前用户信息传给User类中定义的CurrentUser,方便当前用户信息在不同类中的使用。
(6)前台向后台发送请求,将新发布的商品存储在数据库中,在GoodsService类中实现一个add()方法。可根据实际请求,使用get,post,putByForm等。

get与post的对比

get与post都是将数据发送给服务器,与服务器进行通信,但两者有以下区别:
(1)功能不同
get是从服务器上获取数据。
post是向服务器传送数据。
(2)过程不同
get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。
post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。
(3)获取值不同
对于get方式,服务器端用Request.QueryString获取变量的值。
对于post方式,服务器端用Request.Form获取提交的数据。
(4)传送数据量不同
get传送的数据量较小,不能大于2KB。
post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
(5)安全性不同
get安全性非常低。
post安全性较高。

Spring boot中@RequestMapping和@GetMapping @PostMapping的区别

@GetMapping:将HTTP的get请求映射到特定处理程序的方法注解,是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。
@PostMapping:将HTTP的post请求映射到特定处理程序的方法注解,是一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写。

activity与fragment四种跳转

(1)在实现activityA到activityB界面的跳转时,起初用到了Intent(A.this,B.class),再startActivity(intent),此时一切顺利。
(2)但当从fragment中跳转到activity中****时,再用上述方法时却出现了错误。上网所搜后发现原因是fragment并不是一个activity,也不用在AndroidMest.xml中声明,fragment只是activity中的一部分,在跳转时要获得fragment所在的activity,来跳转到新的activity,于是有了Intent(getActivity(),B.class)。
(3)当用到从fragment跳转到同一个activity的fragment时,用上述两种方法又出现了错误,于是经过查找发现了解决方法:
使用getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_not, new HomeFragment(), null).addToBackStack(null).commit();或者定义FragmentManger对象,此时用到getSupportFragmentManager(),报错,原因是又忘记了写getActivity()。
replace(A,B)两个参数,参数A是当前fragment的id,为R.id.fragmentid;参数B是要跳转到或者说将要覆盖的fragment对象,new xxx(),xxx为Fragment的类名称,注意与A不同,不是id。
(4)当用到从fragment跳转到另一个activity的fragment时,用Intent(getActivity(),fragment类名.class)。

数据传输:Intent,Bundle,类中定义public变量

(1)Intent.putExtra()
在界面跳转时,往往也要传递数据,intent.putExtra()就可实现传递数据的功能。参数可以是int,String,Bundle类型等等,后来查找资料发现所有类型数据都是通过Bundle对象容器进行传递的。
Bundle,直译为包,捆的意思。它像一个容器,承载数据。Bundle是一个key-value值,当取数据时,根据key,来确定是哪个包,获得对应的value。
(2)Intent.putExtras(bundle)
虽然putExtra支持的类型不少,但仍不能满足需求,此时我们可以实现接口Serializable,自定义类。

public class Goods_type implements Serializable {
    //创建实例变量
    private String name;
    private Long id;
    public int flag=0;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setid(Long id) {
        this.id = id;
    }
    public Long getid() {
        return id;
    }
    public void setflag(int flag) {
        this.flag =flag;
    }
    public int getflag() {
        return flag;
    }
}

当传送Bundle对象时,putExtra()改为putExtras()。
发送:

                bundle = new Bundle();//实例化一个Bundle
      //把数据放入到 Bundle容器中, goods_type是一个对象,传过去,里面存着当前用户id
                bundle.putSerializable("Goods_type", goods_type);
                Intent intent = new Intent(getActivity(), GoodsListActivity.class);
                intent.putExtras(bundle);
                startActivity(intent);

接收:

            Intent intent4 = getActivity().getIntent();
        //从Intent中取出Bundle
            Bundle bundle2 = intent4.getExtras();
           //获取goos_type里的数据数据
       //assert bundle != null;
            System.out.println("qwewqr");
       //根据key值获得对应的id
            goods_type2 = (Goods_type) bundle2.getSerializable("Goods_type");
       //assert goods_type1 != null;
            id = goods_type2.getid();
            flag = goods_type2.getflag();

(3)在A类中定义public变量,通过在其他类中调用A.publicvar,有时也能解决燃眉之急。

ui界面更新,数据刷新问题

由于在代码设计前期,并没有考虑到数据绑定(数据绑定的功能主要是不用自己调用刷新界面,当数据变化后,ui界面会自动更新到最新数据对应的界面,当然还有其他一些功能,没有用到,所以就没有介绍。)
当我们通过多次添加输出,以及日志和调试,确定了前台已经接收到了后台更新来的数据,却不知道该如何将数据传给ui界面,将其更新。经过了大量搜索我带着迷惑,甚至尝试了再写一个新的布局和java类以实现刷新。后经过平平无奇的搜索过后,得到了想要的答案flush()刷新。我在里面惊讶地看到用Intent从自己跳到自己这样的神奇操作,突然感觉到自己的愚昧与无知。之后通过Intent自己跳到自己,并传送新数据过去,并写一个**getIntent()**进行接受(Fragment中用getActivity.getIntent())数据,跳到了自己,并且此时的数据是新数据,就这样实现了ui界面的刷新。

聊天正常到失败,后发现问题恢复聊天

后台websocketconfig对聊天相关信息进行配置,实现接口类完成聊天功能。
但由于前后台分离,代码在组员修改其他功能时修改错误,导致聊天功能失败,后经过多次调试、日志检查均为解决,后来在实现查看发布记录功能时查看了后台端口,偶然发现后台端口与前台websocket端口不一致,端口修改后,聊天功能恢复。这个故事告诉我们端口一致的重要性,聊天端口没有经过Nginx,因为使用Nginx会有延迟,不利于实时聊天,所以设计到对实时要求较高的功能时,我们一定要保持前后台端口的一致。

数据库并发承载能力

(1)Nginx具有负载均衡的功能,通过将信息发送给不同的服务器或指定的服务器,能一定程度上提高数据库的并发承载能力,但本app中并没有多个服务器,所以不涉及到负载均衡。
(2)分库分表,将访问量大的数据库的表分为多个。(还有其他分库分表方法未涉及,未一一列举)
更多方法可参考https://blog.csdn.net/weixin_45101064/article/details/105272747

教训

(1)未采用团队代码协作平台与项目管理平台,例如Git、Zoho Projects等。
(2)代码规范性不足。
(3)团队沟通不及时,代码错误删改代码未备份完整。
(4)前期对代码的设计规划少,导致运行时才出现问题。
未来展望:
1.支付宝沙箱模拟,实现在线支付功能
2.缓存数据库Redits,提高数据库的并发承载能力

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值