Android开发常见问题备忘

记录一些开发过程中遇到的bug,持续维护中:

    1. map嵌套,在api19以下系统上面,map嵌套然后转JSONObject,子map会被转为字符串
    1. 代码动态给ViewGroup(LinearLayout)设置background,会导致它原来设置的padding无效,必须在代码设置过background后,再用代码重新设置padding。所以这种场景,尽量用margin吧。目前测试api19以下有此问题,api22是好的,21、22没有测试。
    1. 显示一张宽度充满屏幕,高度根据设计稿的横宽比不变形。以前只能通过代码实现,从API18开始可以通过下面方式实现

这里android:adjustViewBounds可以保证它的横宽比。
    1. WebView 加载网页时,如果加载https的网页,中间的资源地址是http的,在android5.0以上默认情况下这些资源无法加载成功的。可以使用如下配置:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); }

http://blog.csdn.net/luofen521/article/details/51783914
    1. 华为手机上面,Log.v()/Log.d()等方法,系统Logcat中看不到输出日志。
      这是因为手机默认吧日志输出关掉了,需要主动打开。通过进入拨号界面输入:##2846579## ,在菜单中找到日志相关的设置(不同机型菜单有些不同)打开,然后重启手机即可
    1. 选取联系人 同时通过跳转系统通讯录选择和通过代码获取。发现有时手机可以获取到系统通讯录选择的联系人,但是通过ContentResolver获取的联系人却为空。根据经验肯定是权限问题导致的,但是如果没有权限,为什么系统通讯录选择的联系人能获取呢?后面通过研究发现,部分厂商修改了权限相关逻辑,调用前申请权限,直接返回已授权。真正访问通讯录时才跳出厂商的对话框,如果用户选择同意就能返回Cursor然后正常访问通讯录,否则返回空访问通讯录失败。此时两种访问通讯录的方法表现是一致的,上述问题出现的原因是,厂商权限对话框弹出时有一个倒计时,倒计时结束时第二种方法获取Cursor为空,但是当前还在系统通讯录页面,此时选择一个联系人,会再次触发权限弹窗,这时点同意,就能获取联系人。所以此时会造成两种方法结果不一致。
    1. Referer引起的网页打不开。APP内一个h5页面,跳转baidu信用卡页面报错。但是复制下来地址直接打开是可以的。后面抓包定位,是由于跳转打开时header中Referer带了当前h5地址,百度行用卡对跳转地址做了限制,不允许非白名单地址跳转。
    1. EditText clearFocus()无效。clearFocus首先循环调用到DecorView,将mFocus置为null。再在DecorView中找到一个能获取焦点的View获取焦点。如果当前页面没有其他view可以获取焦点,焦点会再次被当前EditText获取。
    1. 安装测试包提示[INSTALL_FAILED_TEST_ONLY]。反编译apk发现AndroidManifest.xml中有android:testOnly="true",项目中并没有设置过,最近新升的AS 3.0,个人猜测应该是AS 3.0添加了默认配置,后面在网上看到有人研究得出的结论:通过AS3.0直接运行到手机编译出来的包会强制TEST_ONLY为true,想要修改的话只能通过build apk生成包。不过TEST_ONLY的包也可以通过adb install -t xx.apk安装
    1. RN开发通过 Dimensions.get("window") 获取屏幕尺寸,发现在一些手机里面跳转横屏的页面后,获取到的宽度和高度是反的,目前没有找到原因,通过比较大小判断宽高。
    1. 从JSONObject中获取到字符串,通过TextUtil.isEmpty()判断是否是空值,结果发现会得到"null"字符串的情况导致判断错误。因为JSONObject是无法put null的,使用gson把POLO对象转json字符串时,默认情况下成员变量为null的会忽略掉,所以平时基本碰不到json的value为null的情况。但是js对象value为null时,或者GsonBuilder设置了serializeNulls()后,POLO对象生成的json是包含null的value的,项目中的json是从js传过来的,js业务实现时用户没有填写手机号,所以传过来的json字符phone的值为null,通过JSONObject读取成"null"导致判断出错,可以用object.isNull("key")来判断这种情况。
    1. AsyncStorage存取数据时没有回调,后面看到官方issue里面有人说到跟热加载有关,重新 react-native run-android/run-ios一下就好了,我这边原生项目不是RN生成的,也需要重新运行一下,问题是解决了,原因没有想明白。
    1. 可以设置网络请求不支持代理,这样的话正常的就无法抓包到网络请求:
      OkHttpClient client = new OkHttpClient().newBuilder().proxy(Proxy.NO_PROXY).build();
      HttpURLConnection uc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
      (需要确认,用户使用VPN时是否能正常访问网络)
  • 14)使用vue写h5时,在mounted函数中通过window.location的方式跟原生做交互,在Android webview中,会出现无法回调onPageFinished的情况。测试下来,页面加载耗时时长的情况下(加载一个字体或者url中带/#/),在Android7以上的设备才会重现。目前推测webkit内核做了改动,在页面加载成功前做location重定向,会导致无法调用onPageFinished。目前解决方案是window.location调用加一个timeout,时间可以设很短,主要用来把操作放到线程最后处理
    1. RN开发模式下crash,包如下错误:
11-12 21:50:43.669 25286-25387/? E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher Process: com.jhb.za, PID: 25286 java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at android.util.JsonReader.expect(JsonReader.java:310) at android.util.JsonReader.beginArray(JsonReader.java:277)

【Android开发常见问题备忘】查了下说在开发面板里面去除Use Js Deltas勾选就好了,但是自己默认是没有选的,所以找不到原因,后面跟踪源码发现,调试时从node服务下载代码时有三种类型,分别是BUNDLE("bundle"), DELTA("delta"), MAP("map"); delta模式类似于差量包,在解析下载下来的数据时,有些字段是String类型,代码里面按
Array类型去处理的,所以导致异常。后面删了程序不运行node重新跑,调出Setting,去掉Use Js Deltas就好了
    1. Manifest merger failed with multiple errors, see logs 问题处理
      有的时候引入新的依赖,提示Manifest文件冲突,但是报错信息里看不到冲突的具体内容,可以通过如下方式显示详细日志找出冲突点
      gradlew processDebugManifest -stacktrace gradlew assembleDebug -stacktrace
    1. 通过webview.loadUrl("javascript: " + jsCode) 注入js代码到当前网页时,这里的jsCode如果是多行代码的话,语句结尾要加分号,并且不能包含注释,要不然不能正确执行。
  • 18)使用DialogFragment时,如果使用onCreateDialog()的方式创建弹窗,此时dialogFragment中的contentView是空的,不能使用onCreateDialog()中的布局动态添加子fragment,否则会报错"Fragment does not have a view", 如果想要动态添加子fragment,需要通过继承onCreateView()创建弹窗,并且在onViewCreated()动态添加子view。另外fragment中动态添加子fragment时,要使用this.getChildFragmentManager.beginTransaction().add()的形式,不能使用getFragmentManager(),后者是直接操作父容器(activity)
*19) 使用Fresco库SimpleDraweeView显示gif图,gif处在frgment中,多个fragment切换时,每次显示时gif都会闪一下像是重新加载,看文档是fresco为了防止OOM在gif不显示的时候会释放掉,但是简单翻看了源码并没有找到控制这个的代码。直接使用Glide替换,完美解决
*20)RecycleView中嵌套了一个BannerView(通过ViewPager实现),发现如果BannerView只显示一部分的情况下,切换tab时,会自动滚动RecycleView让BannerView完整显示,后面发现是跟子view焦点有关,直接设置BannerView父容器android:descendantFocusability="blocksDescendants"解决,可以参考:https://blog.51cto.com/7091572/2066688
测试

    推荐阅读