Android 开发中遇到的 bug(6)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/willway_wang/article/details/89601197

前言

记录开发中遇到的 bug,不再让自己重复地被同样的 bug 折磨。

正文

1. Android 9.0上通过 Intent 卸载应用无反应

时间:2019年4月27日14:40:33
问题描述:应用已经 target 28,在9.0手机上测试通过 Intent 卸载应用无反应,代码如下:

Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
intent.setAction("android.intent.action.DELETE");
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse("package:" + mAppInfo.getPackname()));
context.startActivity(intent);

在9.0以下的手机上都是正常的。
解决办法:
在调用这块代码时,查看 log,如下:

2019-04-27 14:30:27.626 17763-17763/? E/PackageInstaller: UninstallerActivity:Uid 10950 does not have android.permission.REQUEST_DELETE_PACKAGES or android.permission.DELETE_PACKAGES

可以看到 PackageInstaller,打出了日志,提示没有 android.permission.REQUEST_DELETE_PACKAGES 或者 android.permission.DELETE_PACKAGES。我们知道,通过隐式 Intent,调用的是系统的卸载页面。
查看一下,9.0的对应源码,这是地址,可以看到如下代码:

   if (getMaxTargetSdkVersionForUid(this, callingUid)
                    >= Build.VERSION_CODES.P && AppGlobals.getPackageManager().checkUidPermission(
                    Manifest.permission.REQUEST_DELETE_PACKAGES, callingUid)
                    != PackageManager.PERMISSION_GRANTED
                    && AppGlobals.getPackageManager().checkUidPermission(
                            Manifest.permission.DELETE_PACKAGES, callingUid)
                            != PackageManager.PERMISSION_GRANTED) {
                Log.e(TAG, "Uid " + callingUid + " does not have "
                        + Manifest.permission.REQUEST_DELETE_PACKAGES + " or "
                       + Manifest.permission.DELETE_PACKAGES);

               setResult(Activity.RESULT_FIRST_USER);
               finish();
               return;
            }

从上面的代码可以看到,确实会检查是否申请了 Manifest.permission.REQUEST_DELETE_PACKAGES 这个权限。
解决办法就是在 Manifest.xml 中添加权限:

<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

2. 使用 Lottie 动画,线上报出 OOM 的问题

时间:2019年4月27日16:36:07
问题描述:之前在项目中使用过 Lottie 动画,但使用的并不多;这次的项目却是大量地使用了 Lottie 动画。本想着可以不用自定义控件了,提高生产力,却产生了一个 Top1 的 bug。
错误日志如下

java.lang.OutOfMemoryError
	at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
	at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:683)
	at com.airbnb.lottie.b.b.a(Unknown Source)
	at com.airbnb.lottie.f.b(Unknown Source)
	at com.airbnb.lottie.c.c.c.f(Unknown Source)
	at com.airbnb.lottie.c.c.c.b(Unknown Source)
	at com.airbnb.lottie.c.c.a.a(Unknown Source)
	at com.airbnb.lottie.c.c.b.b(Unknown Source)
	at com.airbnb.lottie.c.c.a.a(Unknown Source)
	at com.airbnb.lottie.c.c.b.b(Unknown Source)
	at com.airbnb.lottie.c.c.a.a(Unknown Source)
	at com.airbnb.lottie.f.draw(Unknown Source)
	at android.widget.ImageView.onDraw(ImageView.java:1058)
	at android.view.View.draw(View.java:15410)
	at android.view.View.buildDrawingCache(View.java:14617)
	at android.view.View.getDisplayList(View.java:14277)
	at android.view.View.getDisplayList(View.java:14343)
	at android.view.View.draw(View.java:15124)
	at android.view.ViewGroup.drawChild(ViewGroup.java:3309)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3146)
	at android.support.constraint.ConstraintLayout.dispatchDraw(Unknown Source)
	at android.view.View.getDisplayList(View.java:14296)
	at android.view.View.getDisplayList(View.java:14343)
	at android.view.View.draw(View.java:15124)
	at android.view.ViewGroup.drawChild(ViewGroup.java:3309)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3146)
	at android.view.View.getDisplayList(View.java:14296)
	at android.view.View.getDisplayList(View.java:14343)
	at android.view.View.draw(View.java:15124)
	at android.view.ViewGroup.drawChild(ViewGroup.java:3309)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3146)
	at android.view.View.draw(View.java:15413)
	at android.view.View.getDisplayList(View.java:14301)
	at android.view.View.getDisplayList(View.java:14343)
	at android.view.View.draw(View.java:15124)
	at android.view.ViewGroup.drawChild(ViewGroup.java:3309)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3146)
	at android.view.View.draw(View.java:15413)
	at android.widget.FrameLayout.draw(FrameLayout.java:472)
	at android.view.View.getDisplayList(View.java:14301)
	at android.view.View.getDisplayList(View.java:14343)
	at android.view.View.draw(View.java:15124)
	at android.view.ViewGroup.drawChild(ViewGroup.java:3309)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3146)
	at android.view.View.getDisplayList(View.java:14296)
	at android.view.View.getDisplayList(View.java:14343)
	at android.view.View.draw(View.java:15124)
	at android.view.ViewGroup.drawChild(ViewGroup.java:3309)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3146)
	at android.view.View.getDisplayList(View.java:14296)
	at android.view.View.getDisplayList(View.java:14343)
	at android.view.View.draw(View.java:15124)
	at android.view.ViewGroup.drawChild(ViewGroup.java:3309)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3146)
	at android.view.View.getDisplayList(View.java:14296)
	at android.view.View.getDisplayList(View.java:14343)
	at android.view.View.draw(View.java:15124)
	at android.view.ViewGroup.drawChild(ViewGroup.java:3309)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3146)
	at android.view.View.draw(View.java:15413)
	at android.widget.FrameLayout.draw(FrameLayout.java:472)
	at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2583)
	at android.view.View.getDisplayList(View.java:14301)
	at android.view.View.getDisplayList(View.java:14343)
	at android.view.HardwareRenderer$GlRenderer.buildDisplayList(HardwareRenderer.java:1570)
	at android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:1449)
	at android.view.ViewRootImpl.draw(ViewRootImpl.java:2777)
	at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2643)
	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2211)
	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)
	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6637)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:814)
	at android.view.Choreographer.doCallbacks(Choreographer.java:614)
	at android.view.Choreographer.doFrame(Choreographer.java:584)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:800)
	at android.os.Handler.handleCallback(Handler.java:733)
	at android.os.Handler.dispatchMessage(Handler.java:95)
	at android.os.Looper.loop(Looper.java:146)
	at android.app.ActivityThread.main(ActivityThread.java:5602)
	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:1283)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
	at dalvik.system.NativeStart.main(Native Method)

这些日志是友盟上统计出来的。但是,这里面却没有指向我自己应用代码调用的地方。可以关注此 issue
解决办法:在网上搜索 lottie issue 里 OutOfMemoryError 的关键词,找到了答案:

重点的解释是:

I think you may misunderstand the point of Lottie. Lottie is made to render After Effects animations that were created as vectors/shapes. You’re much better off with an mp4 or gif if you’re just creating a png sequence. This issue has nothing to do with Lottie and is just because your animation is 100 sequential pngs that you’re loading into memory.

根据这里的表述,我们应用中的 lottie 确实使用了大量的 png,这会是一个风险点。

解决办法:
1,使用 lottie,但减少图片使用;2,使用原生自定义动画。第一点需要跟 UI 沟通一下了。

3. 使用 SPUtils,报出 ClassCastException

时间:2019年5月15日09:54:23
问题描述:
使用的 SPUtils 地址在这里
下面是错误日志:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
        at android.app.SharedPreferencesImpl.getLong(SharedPreferencesImpl.java:279)
        at com.prdsff.veryclean.common.SPUtils.getLong(SPUtils.java:180)
        at com.prdsff.veryclean.fragment.SettingFragment.onClick(SettingFragment.java:165)
        at android.view.View.performClick(View.java:6897)
        at android.view.View$PerformClick.run(View.java:26101)
        at android.os.Handler.handleCallback(Handler.java:789)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6944)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

解决办法:
查看代码发现,有时候会先走,

SPUtils.getInstance().put(PREF_KEY_GPU_CLOCK_MAX, 0);

再走取出:

SPUtils.getInstance().getLong(PREF_KEY_GPU_CLOCK_MAX, -1L);

也就是说,对于 PREF_KEY_GPU_CLOCK_MAX 这个 key,存入的 value 是 0,而取出的时候却使用 getLong 方法。这样会造成类型转换异常。
所以正确的方式是存入时应该是写成:

SPUtils.getInstance().put(PREF_KEY_GPU_CLOCK_MAX, 0L);

4. java.lang.RuntimeException: A TaskDescription’s primary color should be opaque

时间:2019年5月15日10:15:31
问题描述:
下面是错误日志:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.prdsff.veryclean/com.prdsff.veryclean.activity.CpuHeatActivity}: java.lang.RuntimeException: A TaskDescription's primary color should be opaque
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2451)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2511)
        at android.app.ActivityThread.access$900(ActivityThread.java:165)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1375)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:150)
        at android.app.ActivityThread.main(ActivityThread.java:5621)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)
     Caused by: java.lang.RuntimeException: A TaskDescription's primary color should be opaque
        at android.app.ActivityManager$TaskDescription.<init>(ActivityManager.java:586)
        at android.app.Activity.onApplyThemeResource(Activity.java:3831)
        at android.view.ContextThemeWrapper.initializeTheme(ContextThemeWrapper.java:150)
        at android.view.ContextThemeWrapper.setTheme(ContextThemeWrapper.java:94)
        at android.support.v7.app.AppCompatActivity.setTheme(AppCompatActivity.java:90)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2394)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2511) 
        at android.app.ActivityThread.access$900(ActivityThread.java:165) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1375) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:150) 
        at android.app.ActivityThread.main(ActivityThread.java:5621) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684) 

解决办法:
从日志里可以看出,

A TaskDescription's primary color should be opaque

一个 TaskDescription 对象的 primary color 应该是完全不透明的。那么什么是 opaque(完全不透明的)呢?就是说一个颜色的色值的 alpha 位应该是 ff。
从上面的日志还可以看出 TaskDescriptionActivityManager 的一个内部类,查看一下文档:

知道这个类是在 API 21 添加的。
接着去查看一下对应的源码:

527        /**
528         * Creates the TaskDescription to the specified values.
529         *
530         * @param label A label and description of the current state of this task.
531         * @param icon An icon that represents the current state of this task.
532         * @param colorPrimary A color to override the theme's primary color.  This color must be opaque.
533         */
534        public TaskDescription(String label, Bitmap icon, int colorPrimary) {
535            if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
536                throw new RuntimeException("A TaskDescription's primary color should be opaque");
537            }
538
539            mLabel = label;
540            mIcon = icon;
541            mColorPrimary = colorPrimary;
542        }

从上面的源码可以看到,如果 colorPrimary 的 alpha 位不是 255,就会抛出一个运行时异常。注意这里指向是很明确的,就是说 colorPrimary 的 alpha 位只能是 255。
看一下自己的代码:

    <style name="MdAppTheme.DarkTranslucent">
        <item name="colorPrimary">@color/half_transparent</item>
        <item name="colorPrimaryDark">@color/half_transparent</item>
        <item name="android:windowTranslucentNavigation">true</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>
    
	<color name="half_transparent">#cc000000</color>

确实使用了 alpha 位不为 255 的色值,作为 colorPrimary。修改后,解决了这个异常。

5. Toolbar 返回箭头颜色不正确

时间:2019年5月15日15:08:22
问题描述:

问题就是返回箭头的颜色应该是白色的,却在 M4TEL M4_B3 这个手机上是这个紫色的。在荣耀6、三星、HTC、小米等几个手机上都是正常的。不过,我还是得把这个问题给修复了。
我的页面 xml 是下面这样的:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appbarlayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay"
            android:background="@drawable/common_gradient_bg"
            app:elevation="0dp">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                app:contentInsetLeft="0dp"
                app:contentInsetStart="0dp"
                app:contentInsetStartWithNavigation="0dp"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/AppTheme.PopupOverlay" />
        </com.google.android.material.appbar.AppBarLayout>
    </LinearLayout>

设置 Toolbar 的代码是:

private void setupToolBar() {
    mToolbar.setTitle(R.string.mobile_info_title);
    setSupportActionBar(mToolbar);
    if (getSupportActionBar() != null) {
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
}

解决办法:
自己设置一个图片作为 NavigationIcon,如下:

    private void setupToolBar() {
        mToolbar.setTitle(R.string.mobile_info_title);
        setSupportActionBar(mToolbar);
        if (getSupportActionBar() != null) {
            mToolbar.setNavigationIcon(R.drawable.toolbar_back_arrow);
//            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//            getSupportActionBar().setDisplayShowHomeEnabled(true);
        }
    }

需要注意的是,必须把 getSupportActionBar().setDisplayHomeAsUpEnabled(true);代码注释掉,因为这句代码打开的意思是说 在ActionBar图标(如果没有设置图标icon,文字标题也可以代替)的左侧添加了一个向左的箭头。这样的话,仍会添加不正常颜色的返回箭头。
getSupportActionBar().setDisplayShowHomeEnabled(true); 是说使左上角图标是否显示,如果设成false,则没有程序图标,仅仅就个标题,否则,显示应用程序图标。这个其实没有影响。

6. kotlin.UninitializedPropertyAccessException: lateinit property diceImage has not been initialized

时间:2019年5月17日13:33:32
问题描述:
先看错误日志,如下:

kotlin.UninitializedPropertyAccessException: lateinit property diceImage has not been initialized
        at com.example.android.diceroller.MainActivity.rollDice(MainActivity.kt:37)
        at com.example.android.diceroller.MainActivity.access$rollDice(MainActivity.kt:9)
        at com.example.android.diceroller.MainActivity$onCreate$1.onClick(MainActivity.kt:17)
        at android.view.View.performClick(View.java:4463)
        at android.view.View$PerformClick.run(View.java:18789)
        at android.os.Handler.handleCallback(Handler.java:808)
        at android.os.Handler.dispatchMessage(Handler.java:103)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:5299)
        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:825)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)
        at dalvik.system.NativeStart.main(Native Method)

解决办法:
首先看一下代码:

class MainActivity : AppCompatActivity() {
    lateinit var diceImage: ImageView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
//        diceImage = findViewById(R.id.dice_image)
        val rollButton: Button = findViewById(R.id.roll_button)
        rollButton.setOnClickListener { rollDice() }
        val resetButton: Button = findViewById(R.id.reset_button)
        resetButton.setOnClickListener { reset() }
    }

    private fun reset() {
        diceImage.setImageResource(R.drawable.empty_dice)
    }

    private fun rollDice() {
        val ramdomInt = Random().nextInt(6) + 1
        val drawableResource = when (ramdomInt) {
            1 -> R.drawable.dice_1
            2 -> R.drawable.dice_2
            3 -> R.drawable.dice_3
            4 -> R.drawable.dice_4
            5 -> R.drawable.dice_5
            else -> R.drawable.dice_6
        }
        diceImage.setImageResource(drawableResource)
    }
}

使用 lateinit var 来修饰类属性 diceImage,它的作用是让编译期在检查时不要因为属性变量未被初始化而报错。从日志里可以看出,是因为没有初始化 diceImage 造成的。
那么,就对 diceImage 进行初始化操作。

diceImage = findViewById(R.id.dice_image)

运行一下,正常了。
另外,觉得 lateinit 真得不好用,查看一下这篇文章:在 Kotlin 代码中慎用 lateinit 属性

7. 使用字体包,但是字体却无法居中(实际上是偏上了)

时间:2019年5月31日19:17:57
问题描述:
先看一下没有使用字体包的情形,字体确实是居中的:

然后,使用了字体包,字体却是偏上的。我这次使用的是 fontFamily 属性,但使用 typeface 的字体包也会出现这种情况:
代码设置是这样的:

	<style name="keyboardStyle">
		<item name="android:fontFamily">@font/poppins</item>
	</style>

可以看到下图,字体确实是偏上了:

解决办法:
最初的想法是把背景调整一下,使得字体看起来还是居中的。但这种方法未免太不优雅了,即便这个解决了,那其他的呢?又得找 UI 调整图片。何况还会有手机屏幕的适配问题。总之,这样是不可行的。
还有一种解决办法是暂时不用字体包了。前年我就是这么弄的,但这个肯定不是长久之计。
想了想,肯定是有办法的,就去查找资料,查看 TextView 的属性,找到下面的属性:

从字面意思看是是否包含字体的内边距。把这个属性设置为 false,尝试一下效果:

	<style name="keyboardStyle">
		<item name="android:fontFamily">@font/poppins</item>
		<item name="android:includeFontPadding">false</item>
	</style>

确实好了:

8. 关闭一个页面,回到之前的页面,花费的时间明显地长很多

时间:2019年5月31日19:39:46
解决办法:
针对这种体验不好的问题,我在关闭的页面 onPause 方法里,记录一个 start = System.currentTimeMillis();的时间戳,在 onDestroy 方法里记录 costTime = System.currentTimeMillis() - start,打印这个值。
我测试了简单的空页面A,打开空页面B,再关闭空页面B,这个耗时在 510 ms 左右;而我的应用对应的时间却是 1900 ms 左右。都快 4 倍了,肯定是有问题的。
首先,我把关闭页面里的代码全部注掉了,就是相当于是一个空页面了,原因是这个页面里做的事情比较多:有自定义控件,有各种数据的处理,怀疑是这个页面处理数据或者 UI 绘制导致的耗时。实测结果却不是这样的。在注掉了那么多代码后,对应的耗时仍然是不变的。
其次,我就去看前面那个页面的代码,发现它在 onResume 方法里会去做刷新列表的工作。我就暂且把它这行代码放到 onCreate 方法里去执行。实测结果是耗时成了 500 ms 左右,正常了。说明在 onResume 方法刷新列表,确实会导致关闭页面变慢的。
进一步验证:打印在关闭 ActivityB 回到 MainActivity 时的生命周期方法,如下:

05-31 20:42:07.897 541-541 D/ActivityB: onPause: 
05-31 20:42:07.905 541-541 D/MainActivity: onRestart: 
05-31 20:42:07.905 541-541 D/MainActivity: onStart: 
05-31 20:42:07.906 541-541 D/MainActivity: onResume: 
05-31 20:42:08.279 541-541 D/ActivityB: onStop: 
05-31 20:42:08.279 541-541 D/ActivityB: onDestroy: 

从日志里面可以看到,需要 MainActivityonResume 方法走完之后,才会去走 ActivityBonStop 方法。可见在 onResume 方法里做耗时操作是不合适的。

9. Android 9.0获取 wifi ssid 为 unknow ssid 的问题

时间:2019年5月31日20:57:47
问题描述:在之前的博客 Android 开发中遇到的 bug(5) 中解决了 8.0 手机上获取 WiFi ssid 为 unknown ssid 的问题。这次测试又提出了 9.0 出现的这个问题。
解决办法:

public String getSSID_P() {
    String ssid = "";
    WifiManager wifiManager = (WifiManager) Utils.getApp().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
    if (wifiManager != null) {
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        int networkId = wifiInfo.getNetworkId();
        List<WifiConfiguration> configuredNetworks = wifiManager.getConfiguredNetworks();
        for (WifiConfiguration configuredNetwork : configuredNetworks) {
            if (configuredNetwork.networkId == networkId) {
                ssid = configuredNetwork.SSID;
                break;
            }
        }
    }
    return ssid;
}

思路是先获取所有 wifi 配置的集合,再遍历集合找出和当前网络id相等的 ssid 即可。

10. 使用 BadgeView,却发现小红点重合叠加的问题

时间:2019年5月31日21:23:47
问题描述:
使用到了 BadgeView 来显示图片右上角的小红点,却发现小红点会出现重叠:

实际上后面那一部分是广告字样:

正常情况下,应该是 1 出来后,AD 就被替换掉才对。
解决办法:
查看一下代码:

override fun showUpdateHintBadge(remain: Int) {
    val badge:Badge = QBadgeView(this).bindTarget(gameHintImage)
    badge.badgeText = if (remain > 0 ) remain.toString() else  getString(R.string.ad)
}

发现每次都会创建一个新的 Badge 对象,这很可能是不对的。
改变代码如下:

var badge: Badge? = null
override fun showUpdateHintBadge(remain: Int) {
    if (badge == null) {
        badge = QBadgeView(this).bindTarget(gameHintImage)
    }
    badge?.badgeText = if (remain > 0 ) remain.toString() else  getString(R.string.ad)
}

运行后是正常的。

查看一下 BadgeView 的源码后知道:Badge和TargetView绑定是采用替换TargetView的Parent方式实现的。

public Badge bindTarget(final View targetView) {
    if (targetView == null) {
        throw new IllegalStateException("targetView can not be null");
    }
    if (getParent() != null) {
        ((ViewGroup) getParent()).removeView(this);
    }
    ViewParent targetParent = targetView.getParent();
    if (targetParent != null && targetParent instanceof ViewGroup) {
        mTargetView = targetView;
        if (targetParent instanceof BadgeContainer) {
            ((BadgeContainer) targetParent).addView(this);
        } else {
            ViewGroup targetContainer = (ViewGroup) targetParent;
            int index = targetContainer.indexOfChild(targetView);
            ViewGroup.LayoutParams targetParams = targetView.getLayoutParams();
            targetContainer.removeView(targetView);
            final BadgeContainer badgeContainer = new BadgeContainer(getContext());
            if(targetContainer instanceof RelativeLayout){
                badgeContainer.setId(targetView.getId());
            }
            targetContainer.addView(badgeContainer, index, targetParams);
            badgeContainer.addView(targetView);
            badgeContainer.addView(this);
        }
    } else {
        throw new IllegalStateException("targetView must have a parent");
    }
    return this;

如果两次更新时,是两个不同的 QBadgeView 对象,就会被添加两次。

最后

代码出错了,关键是要仔细查看日志。能够仔细地查看日志,就离解决问题很近了。

猜你喜欢

转载自blog.csdn.net/willway_wang/article/details/89601197