开发至简网格的过程中,既要做服务侧开发,也要多端开发,服务侧分为JAVA服务侧、Android服务侧,端侧分为安卓、Windows,技术繁杂,碰到不少基本问题,全部记录在这里,便于以后查找。
顺便做个小广告,至简网格是一套端&云结合的开发框架,极大简化了服务侧与端侧开发,服务侧使用简单的json配置与sql、js脚本,就可以搞定95%以上的业务场景;端侧支持Android与Windows,本质是一个轻应用开发框架,用vue+quasar实现UI,使用极其简单。实现了几个业务代码,比如CRM、会员等,已在码云、CSDN开源。项目还在持续完善中,欢迎使用。
目录
1.1.6. Kotlin-android not found错误
1. 环境问题
1.1. AndroidStudio
1.1.1. 加入jar、aar的方法:
a)在app下创建目录libs
b)在app\build.grale中增加implementation fileTree(dir: 'libs', include : ['*.jar','*.aar'])
c)将jar、aar文件拷贝到下面;
d)如果AndroidStudio不能识别,则点击菜单File->Invalidate caches/Restart,然后等等重启即可;
1.1.2. 模拟器路径权限
如果手动在AndroidStudio的Device File Explore中创建路径、文件,会导致在app中无权限访问,必须在App中自己创建。
1.1.3. gradle安装
- 解压到指定路径;
- 配置GRADLE_HOME指向该路径;
- 在路径下创建user目录,配置GRADLE_USER_HOME为%GRADLE_HOME%\user,用于存放临时文件;
- 将%GRADLE_HOME%\bin加入PATH变量
- AndroidStudio的File->Settings中搜索Gradle,设置Gradle路径及GradleUser路径;
- 如果升级gradle,建议下载后,仍然解压到相同路径,这样所有应用的设置不用变动。
1.1.4. gradle问题
工程目录下build.gradle中指定的是AndroidStudio的gradle的版本,可能是适配器,尽量不要改,或者改成AndroidStudio的版本;
gradle\wrapper\gradle-wrapper.properties指定gradle版本,路径可以写成本地下载的zip文件,比如file\:///本地路径,所以这个目录下gradle的zip文件不可以删除。这样可以避免不同的工程都下载一遍。
1.1.5. kotlin、gradle插件被禁用
这两个插件是不可以禁用的,如果禁用,AndroidStudio启动会异常。
这时可以在disabled_plugins.txt中删除相应记录即可,位置如下:
C:\Users\用户名\AppData\Roaming\Google\AndroidStudio4.1\disabled_plugins.txt
1.1.6. Kotlin-android not found错误
在项目build.gradle中删除导致错误的行,然后在Tools-Kotlin选择运行Config Kotlin in Project即可。
1.1.7. 修改gradle配置
每次修改gradle文件,会导致无法编译运行工程,这时选择File->Sync Project With Gradle Files后即可。
1.1.8. 打开logcat查看日志
菜单View-Tool Windows中,打开logcat查看日志。
还有其他一些功能也在此目录下;
1.1.9. 删除多余import
菜单 Code-Optimize Imports可以自动删除所有多余的import;或者使用ctrl+alt+’o’热键。
1.1.10. 修改checkstyle规则
在Settings-Inspections中搜索提示的关键词,找到规则,然后勾选或勾除
1.1.11. 修改工程名称
比如将样例工程修改成最终的工程名称,按以下步骤即可完成:
1.关闭Android Studio;
2.修改项目文件夹的名字;
3.修改OldProjectName.iml文件(在项目的根目录的.idea目录下)的名称为新项名称,即OldProjectName.iml修改为NewProjectName.iml;
4.修改.idea/workspace.xml中相应的名称;
5.修改app/build.gradle中的applicationId;
6.然后把该文件中的external.linked.project.id的值也设置为新项目的名称,即 external.linked.project.id=”NewProjectName”;
7.再次打开AndroidStudio即可。
1.1.12. adb覆盖安装
adb install xxx.apk 如果已安装了,此时会提示
Failure [INSTALL_FAILED_ALREADY_EXISTS: Attempt to re-install xxx without first uninstalling.]
使用adb install -r xxx.apk,可以覆盖安装它,但是仍然保留前面的数据。
1.1.13. 导入样例工程时需要修改的地方
- 修改项目目录下gradle/wrapper/gradle-wrapper.properties中的distributionUrl,将版本改成graddle中已有的版本,不然又要下载个老的;
- 修改项目目录下build.gradle,将ext.kotlin_version改为当前已有的版本,kotlin版本可以在file-settings-plugins中查看;
- 修改项目目录下app/build.gradle,删除buildSdkVersion,使用当前已有的版本,修改compileSdk、targetSdk为AndroidStudio当前最新版本,修改minSdk为合适的版本,注意这三个配置项的名称在新版本的gradle中,末尾不能带Version;
1.1.14 修改项目applicationId
以下操作是在“Android Studio Flamingo | 2022.2.1 Patch 2”中执行的,其他版本可能不同。
在项目根目录的build.gradle中修改applicationId,如果需要generated的包名也跟着改变,还需要修改build.gradle中的namespace。然后在build菜单中选择“Clean project”,然后在File菜单中选择“Sync Project With Gradle files”,一次不行就执行几次。如果还不行就在File菜单中选择“Invalidate Caches”,重启后再同步几次,直到出现generated目录为止。
1.2. 模拟器
1.2.1. 模拟器IP及外部访问
在模拟器内部,宿主机器IP为10.0.0.2,模拟器自身IP为10.0.2.15/127.0.0.1/localhost
如果需要在宿主机中直接访问模拟器内部的TCP端口,需要先做映射。
adb forward tcp:8081 tcp:8080
这样就可以访问 http://localhost:8081/xxxxx,请求会被转到虚拟机的8080端口
1.2.2. 进入模拟器命令行
adb -s emulator-5554 shell
1.3. 网络
1.3.1. 手机与PC之间网络不通
一般是路由器设置有问题,可能在路由器无线设置中开启了AP隔离,使得同一路由器下各个节点之间不可互通。
1.3.2. PC不能ping手机
网络防火墙默认是不会禁用出站请求的,但是如果安装了360,在360的安全防护中心->入口防护体系中,如果选择了局域网防护,则PC无法联通手机。
1.3.3. 同局域网下手机访问PC
首先,PC上需启动web服务;
其次,要在系统防火墙高级设置中,添加入站规则开放相应的端口,比如TCP的8080端口;
最后,如果安装了360,需要在安全防护中心->系统防护体系中,关闭网络安全防火。
1.4. 小米手机
1.4.1. 小米手机,无法打开usb安装
插入一张Sim卡,没用的Sim卡也可以
1.4.2. 真机进入开发者模式
不同型号得手机,包括华为、小米等,都是在设置的安卓版本上多次点击,即可进入开发者模式。进入开发者模式后,才可以打开USB调试。
1.5. 华为或荣耀手机
1.5.1. 打开debug级别日志
华为手机默认日志级别是info,无论AndroidStudio中设置的是什么,如果要打开debug级别,按以下步骤设置。
1.拨号界面拨号*#*#2846579#*#*可以看到工程菜单;
2.选择后台设置进入;
3.打开 LOG设置,选择 AP日志;
4.回到AndroidStudio中,改变一下日志级别,就可以看到debug了;
5.如果还是无法显示,但是adb logcat -d可以查看,则重启以下AndroidStudio即可。
1.6. iOS
1.6.1. IPhone webserver备忘
基于SwiftNio开发webserver,SwiftNio是iOS中的netty。
https://www.5axxw.com/wiki/content/zdz096https://www.5axxw.com/wiki/content/zdz096
其他的如GCDWebServer、CocoaHttpServer都已长期无更新
1.7. Eclipse
1.7.1. 更换包名
在包上点右键,选择Refactor,出现更名窗口,输入新的名称,一定要选择Rename subpackages,否则只会新建一个空的包
1.8. Gradle
1.8.1. asset下以下划线“_”开头的目录被忽略
在项目的gradle文件的android下添加以下配置,将此功能关闭掉
aaptOptions{
ignoreAssetsPattern '!._'
}
1.9. Java
1.9.1. Windows安装GraalVm
下载安装:与java配置完全相同,解压后,配置JAVA_HOME以及将bin设置到系统变量path中
安装native-image:gu install native-image
安装llvm:gu install llvm-toolchain
安装js引擎:gu install js
原生编译命令:native-image
1.9.2 Linux下安装GraalVm
- 下载以下两个文件,上传到linux服务上;
graalvm-ce-java11-linux-amd64-22.3.1.tar.gz
js-installable-svm-java11-linux-amd64-22.3.1.jar
- 解压graalvm;
tar xfz graalvm-ce-java11-linux-amd64-22.3.1.tar.gz
【注意】不要解压在/root下,因为这个目录是root用户的根目录,其他用户无法访问
- 并在/etc/profile中增加以下配置
export JAVA_HOME=解压路径
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
- 安装js支持,通过-L选项选择本地安装。
gu install -L js-installable-svm-java11-linux-amd64-22.3.1.jar
不要使用gu install js,因为国内访问github不顺畅,安装极难成功,所以用下载工具(比如迅雷)下载后再在本地安装。
1.10.Linux环境
1.10.1.创建用户
用root用户安装服务是个坏习惯,特别是对外保留接口的服务,一旦有漏洞,黑客获取的就是root权限,所以另建用户安装服务。
useradd -m mesh
-m参数要求系统在/home下自动创建用户目录,mesh为用户名称
passwd mesh
为用户设置一个密码
1.10.2.端口转发
服务程序运行在8523端口,需要将80与443都转发到这个端口,用iptables添加转发规则就可以实现。
1.首先安装iptables,如果已安装,开启它就可以了;
//systemctl stop firewalld # 关闭防火墙
yum -y install iptables-services # 安装 iptables 服务
systemctl enable iptables # 设置 iptables 服务开机启动
systemctl start iptables # 启动 iptables 服务
service iptables save # 保存 iptables 配置
service iptables restart # 重启 iptables 服务
2.开放端口;
iptables服务启动后,默认禁止了1024以上的端口,所以必须打开
iptables -I INPUT -p tcp --dport 8523 -j ACCEPT
3.然后添加端口转发规则;
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8523
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8523
4.查看某个端口的转发规则
iptables -t nat -L -n | grep 80
5.删除端口转发规则
iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8523
6.最后,保存规则。
service iptables save
这样在80与443端口都可以访问。
1.10.3.图片验证码需要安装字体
Linux下生成图片验证码,在FontManagerFactory出现中异常,是因为没有安装字体。使用以下命令安装:
yum -y install fontconfig
fc-list查看已安装的字体
2. 安卓开发问题
2.1. 权限
2.1.1. 应用权限设置
在AndroidMenifest.xml中设置,与application同一级别
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
同时,在application中设置android:requestLegacyExternalStorage="true"
2.2. 底层
2.2.1. 动态加载dex插件
- 使用PathClassLoader类加载器实现动态加载dex插件;
- 加载前需要调用dex命令将jar文件转为dex文件,dex文件可以从外部下载获得;
- 因为ClassLoader加载的规则,不同插件不能互访,但是,插件可以访问apk中的类;
2.2.2. 实现禁止手动删除数据
实现一个删除数据的Activity,并在AndroidMenifest.xml-application-android:manageSpaceActivity引用此Acitivity,实现自定义的删除数据管理界面,在此只删除可以删除的,或者全部不删除,比如禁止删除sqlite数据库等。
此Activity的定义与普通Activity毫无差异。
2.2.3. 定义安全策略
在AndroidMenifest.xml-application-android:networkSecurityConfig中可以自定义安全策略,比如预置自签名的根证书等。
2.2.4. 依赖了kotlin编写的库
比如okhttp4.x,提示Failed resolution of: Lkotlin/jvm/internal/Intrinsics,
Kotlin并无特别的优点,建议别用了。限制OkHttp4依赖Kotlin,也用不成了。
2.3. 安全
2.3.1. 可信根加解密
使用KeyStore进行加解密,KeyStore的底层用的是Tee。
它的问题是,在黑屏情况下,无法使用。
EncryptedSharedPreferences 使用的也是KeyStore。
https://source.android.google.cn/security/keystore?hl=zh-cn
2.3.2. 添加自签名根证书
使用CA机构签发证书,通常成本较高,对于一个测试应用,没必要。所以自己产生一个自签名的根证书;然后用根证书产生二级证书;最后用二级证书生成自己的用户证书。这样就形成了一个证书链。在程序中预置根证书,并信任自己的根证书即可。
自签名证书链可以参照以下连接:
KeyTool生成证书链及使用_flyinmind的博客-CSDN博客
以上连接介绍了使用keytool生成根证书、二级证书、三级证书的全部过程。
2.4. JUnit测试
2.4.1. Android-Unit中无法写文件
使用ApplicationProvider.getApplicationContext获得Context,在这个Context中取得的路径是可以读写,写入的内容会存在正式的应用中,而不是在测试的应用中。
2.4.2. 测试准备与清理
在测试函数前加@Before与@After注解,可以控制放在最前面与最后面执行,利用它们做准备与清理工作。
2.5. Logback日志
2.5.1. 配置中的属性
logback读配置文件时,其中用到的属性,用${propertyName}引用。属性需要在初始化的Context中设置,比如指定根路径。此Context不能reset,否则property会丢失。
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
lc.putProperty("loggerHome", outputDir);
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(lc);
//lc.reset(); //reset会清除property
configurator.doConfigure(cfgFile);
StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
2.5.2. DATA_DIR等内置属性未定义
在一些例子中,出现诸如DATA_DIR、PACKAGE_NAME等属性,其实它们不能用,通过看代码,猜测可能是因为logback获取应用Context的方法有误。所以需要在程序里加载配置前,设置自定义属性,然后在logback.xml中引用。
2.6. 版本发布
2.6.1. 应用签名
版本发布需证书进行签名,这个证书可以使用EC也可以使用RSA,可以用证书链进行签名。Debug情况下,生成了默认的证书,但是发布时不要使用。
Release时,选择菜单Build->Generate Signed Bundle/APK,选择已有的证书或新建一个证书,此证书要伴随应用终身,所以必须保存好,并且记住key密码及store密码。
也可以使用自己签名的证书,生成方法请参考以下连接:
KeyTool生成证书链及使用_flyinmind的博客-CSDN博客
2.6.2. 图标不更新
在“new->image asset”中创建的图标可以保证在不同分辨率下,提供不同的图标,保障合适的清晰度。但是image asset创建的图标与app的工程是独立的,需要将它们拷贝到main的res目录下,并且,不能忘记拷贝mipmap-anydpi-v26或者mipmap-anydpi-v24与values目录,这两个目录不是打酱油的,如果不拷贝它,图标就不会更新,因为安卓里面使用的是mipmap-anydpi-vxx.xml,由它区分不同的分辨率,选择不同的图标。
3. C#开发问题
3.1. 目录权限
应用安装在programs目录下时,程序是无权限写当前路径的,可以通过
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
获得应用可以写入的路径,比如C:\Users\帐号\AppData\Roaming\应用名称。在此路径下可以写入日志、运行时文件等。
3.2. Log4net输出文件路径
与目录权限有关,需要根据运行时情况设置日志输出的根路径,可以设置 GlobalContext.Properties["loggerHome"] = outputDir;
然后在log4net.xml的appender.file中引用loggerHome,形式如下:
<file type="log4net.Util.PatternString" value="%property{loggerHome}\\logs\\mesh\\run.log" />
注意,一定要设type为log4net.Util.PatternString,否则%property{loggerHome}被当成普通字符串解析
3.3. 嵌入资源文件
推荐以文件形式嵌入资源,这样便于直接在文件夹中修改文件,而不必每次修改文件后重新刷新到Resources.resx中
在工程上右键,选择添加->新建文件夹,建立Resources目录,然后在里面添加各种文件,注意,资源的生成操作一定要选择“嵌入的资源”。
然后在程序中,使用如下方式打开资源文件流:
Assembly assm = Assembly.GetExecutingAssembly();
Stream s = assm.GetManifestResourceStream("工程名.Resources." + fileName);
此处的fileName是包括扩展名的。
3.4. 单元测试
首先写单元测试函数,在class上面写 [TestClass],测试函数上写[TestMethod],通过Assert.xxx断言。
然后在视图菜单中打开“测试资源管理器”,一定要选中那个烧瓶形状的图标,然后运行所有测试。
3.5. setup工程配置
.net自带的打包工具很别扭,开发了这么多年也没有提升。所以,项目使用inno setup制作安装文件,其中要包括release目录下的dll。在使用webview2的情况下,需要包括runtimes\win-x64\native\WebView2Loader.dll。
如果用打包成中文,在添加ChineseSimplified.isl时,需要转为utf8-with-BOM格式(可以用notepad++修改),否则界面会显示乱码;如果还需要指定license等文件,也同样要改成utf8-with-BOM格式。
3.6. 混淆
使用.Net reactor,选中release下的主程序exe,然后选中obfuscation,对程序进行混淆。混淆之后再用inno settup生成安装包。
4. Hybrid
4.1. 框架
使用vue+vue-router+quasar开发,在浏览器中输出界面,调用底层的接口。
注意:vue要使用vue.global.prod.js版本,不能使用vue.runtime.global.prod.js。
可以从https://cdn.jsdelivr.net/npm/vue@next/dist/下载。
quasar从https://quasar.dev/start/umd下载,包括quasar.umd.prod.js与quasar.prod.css,这个连接中css可以与quasar.prod.css合并,其中用到的字体也需要逐个下载,放到本地,链接为:
https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons
4.2. 禁止选中文字
在游戏的场景中,经常要用到拖动,容易造成文字被选中,这时可以通过在css中增加user-select: none; 来禁止选中文字
4.3. 引用component
1) 引入component文件
import AlertDialog from "/assets/v3/components/alert_dialog.js"
2) 注册component
app.component('component-alert-dialog', AlertDialog);
注册要放在app.mount之前,否则调用component中方法会提示xxx is not a function
3) 在template中引入component
<component-alert-dialog :title="tags.failToCall" :close="tags.close" ref="errDlg"></component-alert-dialog>
4) 在js中调用component
this.$refs.errDlg.show(“xxxx”);
4.4. 生成二维码
因为不是在nodejs中开发,不能用import方式引入qrcodejs2,所以只能在index.html直接包含它:
<script src="/js路径/qrcode.js"></script>
然后,在template中增加一个div,用以容纳二维码,这里用的是相对宽度vw,所以在生成时要计算一下。
<div ref="qrCodeUrl" style="width:60vw; height:60vw;"></div>
最后,在需要显示时调用:
new QRCode(this.$refs.qrCodeUrl, {
text: 'https://www.baidu.com',
width: document.documentElement.clientWidth * 0.6,
height: document.documentElement.clientWidth * 0.6,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
});
如果用在dialog中,必须在dialog的@show中调用显示二维码,如果太早了,dialog的元素还没有创建,此时显示就会失败
5. 常识
5.1. 统一信用码编码规则及校验
Java实现:校验社会统一信用代码JAVA_王魂凤气的博客-CSDN博客_校验统一社会信用代码
5.2. 行政区划编号
https://www.mca.gov.cn/article/sj/xzqh/1980/202105/20210500033655.shtml
5.3. Git命令集
请参照这篇文章Git操作备忘_flyinmind的博客-CSDN博客