全栈开发中碰到的一些问题及解决方法

        开发至简网格的过程中,既要做服务侧开发,也要多端开发,服务侧分为JAVA服务侧、Android服务侧,端侧分为安卓、Windows,技术繁杂,碰到不少基本问题,全部记录在这里,便于以后查找。

        顺便做个小广告,至简网格是一套端&云结合的开发框架,极大简化了服务侧与端侧开发,服务侧使用简单的json配置与sql、js脚本,就可以搞定95%以上的业务场景;端侧支持Android与Windows,本质是一个轻应用开发框架,用vue+quasar实现UI,使用极其简单。实现了几个业务代码,比如CRM、会员等,已在码云、CSDN开源。项目还在持续完善中,欢迎使用。

目录

1.     环境问题

1.1. AndroidStudio

1.1.1.        加入jar、aar的方法:

1.1.2.      模拟器路径权限

1.1.3.      gradle安装

1.1.4.      gradle问题

1.1.5.      kotlin、gradle插件被禁用

1.1.6.      Kotlin-android not found错误

1.1.7.       修改gradle配置

1.1.8.      打开logcat查看日志

1.1.9.      删除多余import

1.1.10.  修改checkstyle规则

1.1.11.    修改工程名称

1.1.12.  adb覆盖安装

1.1.13.  导入样例工程时需要修改的地方

1.1.14 修改项目applicationId

1.2.             模拟器

1.2.1.      模拟器IP及外部访问

1.2.2.    进入模拟器命令行

1.3.             网络

1.3.1.      手机与PC之间网络不通

1.3.2.    PC不能ping手机

1.3.3.    同局域网下手机访问PC

1.4.             小米手机

1.4.1.      小米手机,无法打开usb安装

1.4.2.    真机进入开发者模式

1.5.             华为或荣耀手机

1.5.1.      打开debug级别日志

1.6. iOS

1.6.1.      IPhone webserver备忘

1.7. Eclipse

1.7.1. 更换包名

1.8. Gradle

1.8.1. asset下以下划线“_”开头的目录被忽略

1.9.          Java

1.9.1.      Windows安装GraalVm

1.9.2 Linux下安装GraalVm

1.10.Linux环境

1.10.1.创建用户

1.10.2.端口转发

1.10.3.图片验证码需要安装字体

2.  安卓开发问题

2.1.             权限

2.1.1.      应用权限设置

2.2.  底层

2.2.1.    动态加载dex插件

2.2.2.   实现禁止手动删除数据

2.2.3.   定义安全策略

2.2.4.  依赖了kotlin编写的库

2.3. 安全

2.3.1.    可信根加解密

2.3.2.   添加自签名根证书

2.4.  JUnit测试

2.4.1.    Android-Unit中无法写文件

2.4.2.  测试准备与清理

2.5.  Logback日志

2.5.1.    配置中的属性

2.5.2.   DATA_DIR等内置属性未定义

2.6. 版本发布

2.6.1.  应用签名

2.6.2.   图标不更新

2.7 进程间共享SharedPreferences

3.   C#开发问题

3.1. 目录权限

3.2. DEBUG宏

3.3. Log4net输出文件路径

3.4. 嵌入资源文件

3.5. 单元测试

3.6. setup工程配置

3.7.  混淆

4.   Hybrid

4.1.             框架

4.2.           禁止选中文字

4.3.          引用component

4.4.           生成二维码

5.  常识

5.1.          统一信用码编码规则及校验

5.2.          行政区划编号

5.3.          Git命令集


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安装

  1.       解压到指定路径;
  2.      配置GRADLE_HOME指向该路径;
  3.       在路径下创建user目录,配置GRADLE_USER_HOME为%GRADLE_HOME%\user,用于存放临时文件;
  4.      将%GRADLE_HOME%\bin加入PATH变量
  5.       AndroidStudio的File->Settings中搜索Gradle,设置Gradle路径及GradleUser路径;
  6.        如果升级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.  导入样例工程时需要修改的地方

  1. 修改项目目录下gradle/wrapper/gradle-wrapper.properties中的distributionUrl,将版本改成graddle中已有的版本,不然又要下载个老的;
  2. 修改项目目录下build.gradle,将ext.kotlin_version改为当前已有的版本,kotlin版本可以在file-settings-plugins中查看;
  3. 修改项目目录下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 或者gradle nativeCompile

Js插件下载:

https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-22.3.2

Gradle配置见以下连接:

https://graalvm.github.io/native-build-tools/latest/gradle-plugin-quickstart.html

1.9.2 Linux下安装GraalVm

  1. 下载以下两个文件,上传到linux服务上;

graalvm-ce-java11-linux-amd64-22.3.2.tar.gz

如果不用内嵌的js支持,可以不用安装js

js-installable-svm-java11-linux-amd64-22.3.2.jar

如果不作原生编译,可以不安装native-image

native-image-installable-svm-svmee-java11-windows-amd64-22.3.2.jar

插件安装命令 gu install js 或者native-image是从github下载的,所以很慢,并且经常搞到一半提示下载失败,所以找到github网站直接用迅雷下载到本地后,再用命令行从本地安装。

  1. 解压graalvm;

tar xfz graalvm-ce-java11-linux-amd64-22.3.1.tar.gz

【注意】不要解压在/root下,因为这个目录是root用户的根目录,其他用户无法访问

  1. 并在/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

  1. 安装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同一级别

同时,在application中设置android:requestLegacyExternalStorage="true"

2.2.  底层

2.2.1.    动态加载dex插件

  1. 使用PathClassLoader类加载器实现动态加载dex插件;
  2. 加载前需要调用dex命令将jar文件转为dex文件,dex文件可以从外部下载获得;
  3. 因为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

全栈开发中碰到的一些问题及解决方法_第1张图片

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,由它区分不同的分辨率,选择不同的图标。

2.7 进程间共享SharedPreferences

虽然名称中有shared的,其实进程间共享时有诸多问题,最大的问题是不同步。

SharedPreferences第一次打开时会从配置文件中读取k-v对,存到一个map中,后面的变更使用Editor.apply,Editor.commit写到文件中。如果其他进程也在修改SharedPreferences,当前进程是不能即使感知的,除非重新调用getSharedPreferences:

Context.getSharedPreferences(NAME, Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);

注意要设置MODE_MULTI_PROCESS(此标志位已不建议使用了),此标志位只是告诉getSharedPreferences在获得SharedPreferences对象时,重新读取一次文件,并不会保证多进程之间的同步;如果不设置,则直接使用以前曾经打开过的SharedPreferences对象(SharedPreferences也会缓存)。

3.   C#开发问题

3.1. 目录权限

应用安装在programs目录下时,程序是无权限写当前路径的,可以通过

Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)

获得应用可以写入的路径,比如C:\Users\帐号\AppData\Roaming\应用名称。在此路径下可以写入日志、运行时文件等。

​​​​​​​3.2. DEBUG宏

代码中可以通过判断DEBUG宏是否定义给出不同的实现。

#if DEBUG

public const string API_DOMAIN = "192.168.0.102";

#else

     public const string API_DOMAIN = "api" + CERT_DOMAIN;

#endif

为了使DEBUG生效,还必须右键项目,选择“属性->生成”,在DEBUG配置中,选中“定义DEBUG常数”,否则#if DEBUG判断将失败

3.3. Log4net输出文件路径

与目录权限有关,需要根据运行时情况设置日志输出的根路径,可以设置 GlobalContext.Properties["loggerHome"] = outputDir;

然后在log4net.xml的appender.file中引用loggerHome,形式如下:

注意,一定要设type为log4net.Util.PatternString,否则%property{loggerHome}被当成普通字符串解析

3.4. 嵌入资源文件

推荐以文件形式嵌入资源,这样便于直接在文件夹中修改文件,而不必每次修改文件后重新刷新到Resources.resx中

在工程上右键,选择添加->新建文件夹,建立Resources目录,然后在里面添加各种文件,注意,资源的生成操作一定要选择“嵌入的资源”。

然后在程序中,使用如下方式打开资源文件流:

Assembly assm = Assembly.GetExecutingAssembly();

Stream s = assm.GetManifestResourceStream("工程名.Resources." + fileName);

此处的fileName是包括扩展名的。

3.5. 单元测试

首先写单元测试函数,在class上面写 [TestClass],测试函数上写[TestMethod],通过Assert.xxx断言。

然后在视图菜单中打开“测试资源管理器”,一定要选中那个烧瓶形状的图标,然后运行所有测试。单元测试做完之后,建议关闭“测试资源管理器”,它会占用一部分资源。

全栈开发中碰到的一些问题及解决方法_第2张图片

3.6. setup工程配置

        .net自带的打包工具很别扭,开发了这么多年也没有提升。所以,项目使用inno setup制作安装文件,其中要包括release目录下的dll。在使用webview2的情况下,需要包括runtimes\win-x64\native\WebView2Loader.dll。

        如果用打包成中文,在添加ChineseSimplified.isl时,需要转为utf8-with-BOM格式(可以用notepad++修改),否则界面会显示乱码;如果还需要指定license等文件,也同样要改成utf8-with-BOM格式。

3.7.  混淆

        使用.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

4)       在js中调用component

this.$refs.errDlg.show(“xxxx”);

4.4.           生成二维码

因为不是在nodejs中开发,不能用import方式引入qrcodejs2,所以只能在index.html直接包含它:

然后,在template中增加一个div,用以容纳二维码,这里用的是相对宽度vw,所以在生成时要计算一下。

最后,在需要显示时调用:

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博客

你可能感兴趣的:(java,至简网格,常见问题,android,android,studio,java)