45

安卓开发中遇到的奇奇怪怪的问题

 5 years ago
source link: http://www.10tiao.com/html/272/201807/2666454291/2.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1.App的首次安装。

问题描述:在我们安装完成一个app时,在安装界面直接点击打开。我们进入了app的首页,这时我们按home键返回桌面,再点击应用图标,会发现没有直接进入首页,而是先进入了app的闪屏页,在进入首页。重复这一步一直如此。这时我们按back键返回,发现没有直接退回桌面,而是返回到之前打开的多个首页。但是如果一开始安装完我们不是直接打开,而是在桌面点击应用进入就不会这样了。

奇奇怪怪~~

记得当时我在应用市场下载了部分应用,也有一些有同样的问题。但是有些虽然重复打开,但双击退出程序将整个重复打开的关闭了。这确实也是一种方法,但是觉得不是最合理。

我贴一下代码

if (!isTaskRoot()) {
            Intent intent = getIntent();            String action = intent.getAction();            if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
                finish();                return;
            }
        }
 
           

2.Android自定义CheckBox

问题描述:曾经写过一个自定义CheckBox,结果运行在4.1的手机上消失不见了。。。你说郁闷不。写法如下:

<CheckBox      android:id="@+id/check_box"      style="@style/MyCheckBox"      android:layout_width="wrap_content"      android:layout_height="match_parent"/>
<style name="MyCheckBox" parent="Widget.AppCompat.CompoundButton.CheckBox">        <item name="android:button">@drawable/checkbox_selector</item></style>
 
           

<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/icon_on" android:state_checked="true"/>
 <!-- 设置选中图片 -->
    <item android:drawable="@drawable/icon_off" android:state_checked="false"/>
 <!-- 设置未选中图片 --></selector>
 
           

写法没有什么问题吧,但是就是不显示,凭空消失。当然如果我加上文字,就出来了,但是文字图片重叠。。。

最后谷歌出了原因:大致是说4.1.2版本中CompoundButton没有getCompoundPaddingXX。两个版本的计算方式不一样,4.1.2以上版本绘制文字时,会把图片的宽度和paddingXX的宽度加上,而4.1.2版本只计算设置的paddingLeft。

知道了原因,解决方法:

<style name="MyCheckBox" parent="Widget.AppCompat.CompoundButton.CheckBox">
        <item name="android:button">@null</item>
        <item name="android:paddingLeft">0dp</item>
        <item name="android:drawableLeft">@drawable/checkbox_selector</item>
</style>
 
           

3. RecyclerView Bug

问题描述:这个是在友盟的错误分析中报的,错误信息如下:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{42fb7f40 position=11 id=-1, oldPos=-1, pLpos:-1 no parent}
    at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4801)
    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4932)
    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4913)
    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2029)
    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1414)
    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1377)
    at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1193)
    at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1043)
    at android.support.v7.widget.RecyclerView.scrollByInternal(RecyclerView.java:1552)
    at android.support.v7.widget.RecyclerView.onTouchEvent(RecyclerView.java:2649)
    at android.view.View.dispatchTouchEvent(View.java:7706)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2224)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1954)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2230)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1968)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2230)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1968)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2230)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1968)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2230)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1968)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2230)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1968)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2230)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1968)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2074)
    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1521)
    at android.app.Activity.dispatchTouchEvent(Activity.java:2569)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2022)
    at android.view.View.dispatchPointerEvent(View.java:7886)
    at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:3967)
    at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3846)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3412)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3462)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3431)
    at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3538)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3439)
    at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3595)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3412)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3462)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3431)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3439)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3412)
    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5552)
    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5532)
    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5503)
    at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5632)
    at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
    at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
    at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176)
    at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:5605)
    at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:5651)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
    at android.view.Choreographer.doCallbacks(Choreographer.java:574)
    at android.view.Choreographer.doFrame(Choreographer.java:542)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5162)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
 
           

从上面可以看到并没有报到我们自己的代码里面来,这就很尴尬了。老办法谷歌,找到了Drakeet大神的一篇博客,说到了我的心坎里。

重现的方法是:使用 RecyclerView 加官方下拉刷新的时候,如果绑定的 List 对象在更新数据之前进行了 clear,而这时用户紧接着迅速上滑 RV,就会造成崩溃,而且异常不会报到你的代码上,属于RV内部错误。初次猜测是,当你 clear 了 list 之后,这时迅速上滑,而新数据还没到来,导致 RV 要更新加载下面的 Item 时候,找不到数据源了,造成 crash.

真是一样一样的,那么解决方法大神也提供了,就是在刷新,也就是 clear 的同时,让 RecyclerView 暂时不能够滑动,之后再允许滑动即可。代码就是在 RecyclerView 初始化的时候加上是否在刷新进而拦截手势:

mRecyclerView.setOnTouchListener(        new View.OnTouchListener() {            @Override
            public boolean onTouch(View v, MotionEvent event) {                if (mIsRefreshing) {                    return true;
                } else {                    return false;
                }
            }
        }
);
 
           
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

当然如果觉得刷新时不能滑动可以用这种方案:

public class WrapContentLinearLayoutManager extends LinearLayoutManager {
    //... constructor
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {        try {            super.onLayoutChildren(recycler, state);
        } catch (IndexOutOfBoundsException e) {
            Log.e("probe", "meet a IOOBE in RecyclerView");
        }
    }
}


使用:

RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);recyclerView.setLayoutManager(new WrapContentLinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false));
 
           

补充:但是这里要注意的是,禁止 RecycleView “下拉刷新” 和 “加载更多” 同时执行 。否则会报:

java.lang.IllegalStateException: Added View has RecyclerView as parent but view is not a real child. Unfiltered index:0
    at android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:7048)
    at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:7012)
    at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:7000)
    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1428)
    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1377)
    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:578)
    at com.jingzhao.shopping.customview.WrapContentLinearLayoutManager.onLayoutChildren(WrapContentLinearLayoutManager.java:27)<---这里
    at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3260)
    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3069)
    at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3518)
    at android.view.View.layout(View.java:15697)
    at android.view.ViewGroup.layout(ViewGroup.java:5050)
    at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1077)
    at android.view.View.layout(View.java:15697)
    at android.view.ViewGroup.layout(ViewGroup.java:5050)
    at android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:598)
    at android.view.View.layout(View.java:15697)
    at android.view.ViewGroup.layout(ViewGroup.java:5050)
    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
    at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1692)
    at android.widget.LinearLayout.onLayout(LinearLayout.java:1468)
    at android.view.View.layout(View.java:15697)
    at android.view.ViewGroup.layout(ViewGroup.java:5050)
    at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1077)
    at android.view.View.layout(View.java:15697)
    at android.view.ViewGroup.layout(ViewGroup.java:5050)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
    at android.view.View.layout(View.java:15697)
    at android.view.ViewGroup.layout(ViewGroup.java:5050)
    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
    at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
    at android.view.View.layout(View.java:15697)
    at android.view.ViewGroup.layout(ViewGroup.java:5050)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
    at android.view.View.layout(View.java:15697)
    at android.view.ViewGroup.layout(ViewGroup.java:5050)
    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2232)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1984)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1163)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6190)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:773)
    at android.view.Choreographer.doCallbacks(Choreographer.java:586)
    at android.view.Choreographer.doFrame(Choreographer.java:556)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:759)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:159)
    at android.app.ActivityThread.main(ActivityThread.java:5540)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)
 
           

可以做类似的处理:

if(swipeRefreshLayout.isRefreshing()){ 
   return;
}
 
           

4.华为部分设备不打印Log

部分的华为设备工程模式下log是关闭的

1.如果是华为手机,进入拨号界面输入:*#*#2846579#*#*进入页面设置。

2.如果是华为pad,进入计算器输入:()()2846579()()= 进入页面设置。

5.一些注意点

(1)平时为了给apk瘦身,我们会对图片进行有损或无损的压缩。那我平时有用到的一个有损压缩网站tinypng,但是切记我们的9图不要压缩,不然会有问题。(这里顺便分享一个做9图的在线工具http://inloop.github.io/shadow4android/)

(2)Android的透明主题需谨慎使用。

(3)for循环不要把获得数量的代码写在循环当中:for(int i = 0; i <= list.size(); i ++)

(4)ListView的如果其宽度或高度被设置为wrap_content,会有性能等问题。参见:http://stackoverflow.com/questions/4270278/layout-width-of-a-listview

1.RecyclerView自动滚动

之前碰到的一个奇怪问题:RecyclerView 嵌套 RecyclerView,结果内部的RecyclerView会自动的滑动至顶部。最终找到了解决方法:RecyclerView常见问题解决方案

两种解决办法:

一. 内部Recyclerview去除焦点,父布局里获得焦点。

recyclerview.setFocusableInTouchMode(false);recyclerview.requestFocus();
 
           

比如父布局的一个textview。

textview.setFocusableInTouchMode(true);  textview.requestFocus(); 
 
           

二. 内部的Recyclerview加

android:overScrollMode="never"
 
           

父布局覆盖子VIew获取焦点:

android:descendantFocusability="blocksDescendants"
 
           
  • 1

当然不推荐使用RecyclerView 嵌套 RecyclerView这种写法了。。。

2.NumberFormatException

起因是Bugly上报了一个错误:

说是我格式化“0,00”这样的一个字符串。代码大致如下:

String.format("%.2f", number);
 
           
  • 1

排除了格式字符串的问题后我就纳闷了。中间经过了大量的搜索。。。,发现是格式化的问题。也就是在不指定Locale时,是跟随系统语言。在法语、德语、意大利语的语言中,格式化小数是逗号的。所以解决方法:

String.format(Locale.CHINA, "%.2f", number);
 
           
  • 1

3.SecurityException

在之前项目中做了6.0的动态权限后,Bugly报错如下:

更奇怪的是报错的全部都是6.0的手机。查了一下,找到了问题。发现是6.0的一个bug,在部分6.0上CHANGE_NETWORK_STATE权限获取不到,那么只能去获取WRITE_SETTINGS这个权限了。这个问题已经在6.0.1修复了。附上链接地址,

既然是6.0的问题,我们可以对6.0进行单独处理。抛出异常处或者在使用CHANGE_NETWORK_STATE权限前跳转到系统设置页去设置。

if(Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
     if (!Settings.System.canWrite(context)) {
         Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
         intent.setData(Uri.parse("package:" + context.getPackageName()));
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         context.startActivity(intent);
      } 
}             
 
           

当然也可以参考这位兄弟的方法:Android 6 完美解决 WRITE_SETTINGS 权限设置问题

4.WebView中Http和Https加载问题

比如https页面加载http图片或者http页面加载https图片时图片显示不出来,原因是因为在Android 5.0开始WebView默认不允许加载http与https混合页面,

解决办法:

//https与http混合资源处理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);}
 
           

当然最好还是不要混合,保持统一。

5.一些注意点

  • 使用Glide时,注意对传入的ActicityFragment进行判断,避免传入已经销毁Acticity,造成IllegalArgumentException异常。可以参考这篇Glide类似You cannot start a load for a destroyed activity异常简单分析

  • 不要在Android的Application对象中缓存数据!

  • 在做6.0动态权限时,如果有申请相机权限时,应该保证Manifest.permission.CAMERAManifest.permission.WRITE_EXTERNAL_STORAGE权限同时申请,不要漏掉后者。

                        喜欢 就关注吧,欢迎投稿!




About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK