目录

  • 前言
  • 正文
    • 1. Error: Static interface methods are only supported starting with Android N (--min-api 24)
    • 2. An enum switch case label must be the unqualified name of an enumeration constant.
    • 3. AndroidStudio3.5 选择了 No Proxy 后,还去走代理的问题
    • 4. AndroidStudio 编译报错:Program type already present:com.xx.xx
    • 5. AndroidStudio 打包如何动态修改 aar 的名称?
    • 6. RecyclerView 在切换网格,列表布局时,ItemDecoration 出现混用
    • 7. java.lang.IllegalStateException: Software rendering doesn't support hardware bitmaps
    • 8. 错误: 不兼容的类型: RequestOptions无法转换为GlideOptions
    • 9. 应用图标不正常, 变为了默认的机器人
    • 10. 使用 ProgressBar 来显示加载进度,但是加载时间太短,ProgressBar 会在屏幕上一闪而过
  • 最后

前言

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

正文

1. Error: Static interface methods are only supported starting with Android N (–min-api 24)

时间:2019年9月16日20:31:41
问题描述:在升级 butterknife 后报出这个错误。
问题分析:查看 https://github.com/JakeWharton/butterknife/blob/master/CHANGELOG.md#version-900-rc2-2018-11-19。
解决办法:

android {  ...  compileOptions {    sourceCompatibility JavaVersion.VERSION_1_8    targetCompatibility JavaVersion.VERSION_1_8  }}

2. An enum switch case label must be the unqualified name of an enumeration constant.

时间:2019年9月22日10:37:32
问题分析:
出错代码如下:

private float mapPx(Coordinate coordinate) {    switch (coordinate) {        case Coordinate.ZERO: // 这里编译报错:An enum switch case label must be the unqualified name of an enumeration constant.            return 0;        default:            return 0;    }}enum Coordinate {    ZERO(0),    HALF_WIDTH(1),    WIDTH(2),    HALF_HEIGHT(3),    HEIGHT(4);    private int coordinate;    Coordinate(int coordinate) {        this.coordinate = coordinate;    }}

解决办法:
去掉 ZERO 前面的 Coordinate,具体原因可以看 https://www.jianshu.com/p/380a503c7d37。

3. AndroidStudio3.5 选择了 No Proxy 后,还去走代理的问题

时间:2019年9月25日19:09:52
问题描述:最近上网比较困难,昨天连了同事的代理,可以上网了。但今天又挂了,好在公司提供了内网专线。但是,使用浏览器都可以访问外网的,Android Studio 却不可以 Sync,已经选择Settings->HttpProxy->NoProxy了。开始以为是网络不好的原因。
问题分析:旁边的同事,发现了虽然选择了 No Proxy,但还是会走昨天设置的代理。真是奇怪!!!最后发现在 C:\Users\Administrator.gradle\gradle.properties文件下,竟然还写着昨天的代理。
Android 开发中遇到的 bug(9)_第1张图片
直接把方框里的内容注释掉。解决了这个问题。

4. AndroidStudio 编译报错:Program type already present:com.xx.xx

时间:2019年10月23日14:42:30
解决办法:查看了自己的依赖,并不是因为重复依赖导致的。clean project 后正常编译。

5. AndroidStudio 打包如何动态修改 aar 的名称?

时间:2019年10月23日14:44:45
解决办法:
在类库工程的 build.gradle 文件中添加:

android {    ...    android.libraryVariants.all { variant ->        if (variant.buildType.name.equals('release')) {            variant.outputs.all {                def time = new Date().format("yyyyMMddHHmmss", TimeZone.getTimeZone("GMT+08"))                outputFileName = "yourlibname_v${defaultConfig.versionName}_${time}.aar"            }        }    }}

6. RecyclerView 在切换网格,列表布局时,ItemDecoration 出现混用

时间:2019年10月23日20:22:49

问题描述
应用增加了切换布局:网格布局和列表布局。对于这两种布局,分别设置有 ItemDecoration:其中网格布局设置的是间距,列表布局设置的是分隔线。代码如下:

    private void setupGridAdapter() {        recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));    recyclerView.removeItemDecoration(listItemDecoration);        recyclerView.addItemDecoration(gridItemDecoration);        recyclerView.setAdapter(adapterList.get(currentAdapterIndex));    }
    private void setupListAdapter() {        clearRecylerViewItemDecorations();        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));recyclerView.removeItemDecoration(gridItemDecoration);        recyclerView.addItemDecoration(listItemDecoration);        recyclerView.setAdapter(adapterList.get(currentAdapterIndex));    }

实际上,在添加网格布局的 ItemDecoration 前,会移除列表布局的 ItemDecoration;同样地,在添加列表布局的 ItemDecoration 前,也会移除网格布局的 ItemDecoration。但是,实际测试发现,还是会出现分割线出现在网格布局里,列表布局的间距也出现了增大。这样的话,是非常影响 UI 效果的。

解决办法
查看了 RecyclerView 的代码:

final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();    public void removeItemDecoration(@NonNull ItemDecoration decor) {        if (mLayout != null) {            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"                    + " layout");        }        mItemDecorations.remove(decor);        if (mItemDecorations.isEmpty()) {            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);        }        markItemDecorInsetsDirty();        requestLayout();    }

注意到,ItemDecoration 对象都是保存在 mItemDecorations 这个 List 里面。而每次调用 addItemDecoration 都是添加,而不是设置:

    public void addItemDecoration(@NonNull ItemDecoration decor, int index) {        if (mLayout != null) {            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"                    + " layout");        }        if (mItemDecorations.isEmpty()) {            setWillNotDraw(false);        }        if (index < 0) {            mItemDecorations.add(decor);        } else {            mItemDecorations.add(index, decor);        }        markItemDecorInsetsDirty();        requestLayout();    }

这样就可能导致多次添加,其实打印 getItemDecorationCount() 的值也可以发现它的值会出现大于 1 的情况。
要是有一个 setItemDecoration() 方法该多好啊。这边就添加了一个这样的方法:

    public void setItemDecoration(@NonNull RecyclerView.ItemDecoration decor) {    // 先取出所有的 ItemDecoration        List<RecyclerView.ItemDecoration> list = new ArrayList<>();        for (int i = 0; i < recyclerView.getItemDecorationCount(); i++) {            RecyclerView.ItemDecoration itemDecoration = recyclerView.getItemDecorationAt(i);            list.add(itemDecoration);        }        // 再移除所有的 ItemDecoration        for (RecyclerView.ItemDecoration itemDecoration : list) {            recyclerView.removeItemDecoration(itemDecoration);        }        // 最后添加新的 ItemDecoration        recyclerView.addItemDecoration(decor);    }

7. java.lang.IllegalStateException: Software rendering doesn’t support hardware bitmaps

时间:2019年10月23日20:54:53
问题描述:
在友盟上捕获到这个错误,都是在 android O 以后出现的。
完整日志如下:

java.lang.IllegalStateException: Software rendering doesn't support hardware bitmaps    at android.graphics.BaseCanvas.throwIfHwBitmapInSwMode(BaseCanvas.java:532)    at android.graphics.BaseCanvas.throwIfCannotDraw(BaseCanvas.java:62)    at android.graphics.BaseCanvas.drawBitmap(BaseCanvas.java:120)    at android.graphics.Canvas.drawBitmap(Canvas.java:1434)    at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:529)    at android.widget.ImageView.onDraw(ImageView.java:1349)    at android.view.View.draw(View.java:19196)    at android.view.View.draw(View.java:19066)    at android.view.ViewGroup.drawChild(ViewGroup.java:4236)    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4022)    at android.view.ViewOverlay$OverlayViewGroup.dispatchDraw(ViewOverlay.java:251)    at android.view.View.draw(View.java:19199)    at android.view.View.buildDrawingCacheImpl(View.java:18441)    at android.view.View.buildDrawingCache(View.java:18304)    at android.view.View.getDrawingCache(View.java:18210)    at android.view.View.getDrawingCache(View.java:18175)    at com.omnipotent.free.videodownloader.pro.utils.ViewUtils.captureView(ViewUtils.java:70)    at com.omnipotent.free.videodownloader.pro.ui.main.MainActivity.getCurrentTabsData(MainActivity.java:325)    at com.omnipotent.free.videodownloader.pro.ui.main.MainActivity.access$getCurrentTabsData(MainActivity.java:84)    at com.omnipotent.free.videodownloader.pro.ui.main.MainActivity$initView$5.onClick(MainActivity.java:252)    at android.view.View.performClick(View.java:6294)    at android.view.View$PerformClick.run(View.java:24774)    at android.os.Handler.handleCallback(Handler.java:790)    at android.os.Handler.dispatchMessage(Handler.java:99)    at android.os.Looper.loop(Looper.java:164)    at android.app.ActivityThread.main(ActivityThread.java:6518)    at java.lang.reflect.Method.invoke(Method.java)    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

定位到一个把 view 转成 Bitmap 的方法:

fun captureView(view: View): Bitmap {        val tBitmap = Bitmap.createBitmap(            view.width, view.height, Bitmap.Config.RGB_565        )        val canvas = Canvas(tBitmap)        view.draw(canvas)        canvas.setBitmap(null)        return tBitmap}

问题分析:
查询网上资料,Glide 文档上的硬件位图 讲的非常详细。其中提到了哪些情况不能使用硬件位图? 有一个情况是:

在代码中触发截屏操作,它会尝试使用 Canvas 来绘制视图层级。
作为一个替代方案,在 Android O 以上版本你可以使用 PixelCopy.

很明显,Glide 文档建议使用 PixelCopy 这个类,来解决在代码中触发截屏操作导致的异常。
解决办法:
所以这边采用的方案是在 android O 以后使用 PixelCopy 来获取 Bitmap,在 android O 以下还是使用原方案。代码如下:

fun captureView(view: View, window: Window, bitmapCallback: (Bitmap)->Unit) {    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {        LogUtils.dTag(TAG, "captureView version O 以上")        // 高于 O 的,使用 PixelCopy        val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)        val location = IntArray(2)        view.getLocationInWindow(location)        PixelCopy.request(window,            Rect(location[0], location[1], location[0] + view.width, location[1] + view.height),            bitmap,            {                if (it == PixelCopy.SUCCESS) {                    LogUtils.dTag(TAG, "captureView 获取到 bitmap")                    bitmapCallback.invoke(bitmap)                } else {                    LogUtils.dTag(TAG, "captureView 未获取到 bitmap, copyResult = $it")                }            },            Handler(Looper.getMainLooper()) )    } else {        LogUtils.dTag(TAG, "captureView version O 以下")        val tBitmap = Bitmap.createBitmap(            view.width, view.height, Bitmap.Config.RGB_565        )        val canvas = Canvas(tBitmap)        view.draw(canvas)        canvas.setBitmap(null)        bitmapCallback.invoke(tBitmap)    }}

需要特别说明的是,这里通过回调获取 Bitmap,原因是 PixelCopy 需要通过回调返回 Bitmap。从目前的友盟错误上已经看不到这个异常了,说明改动是有效的。
同时可以关注一下,我在 Stack Overflow 上提的这个问题:java.lang.IllegalStateException: Software rendering doesn’t support hardware bitmaps 。

8. 错误: 不兼容的类型: RequestOptions无法转换为GlideOptions

时间:2019年10月31日16:18:39
问题描述:在编译项目时报出这个异常。
解决办法:发现自己的依赖版本不一致:

implementation 'com.github.bumptech.glide:glide:4.9.0'annotationProcessor "com.github.bumptech.glide:compiler:4.8.0"

把 4.8.0 改成 4.9.0 后运行正常。

9. 应用图标不正常, 变为了默认的机器人

时间:2019年11月30日13:44:21
问题描述:在集成了一个功能模块的代码后,发现在 Honor Play 手机上运行后,图标变为了默认的机器人。
解决办法:因为在没有集成这个功能模块之前,桌面的图标是正常的,所以就去查看一下这个功能模块。查看后,发现在这个功能模块里存在默认的机器人图标,名字是 ic_launcher。而这个模块并不需要 ic_launcer 的图片资源。果断删除它们,重新运行后桌面图标显示正常。

10. 使用 ProgressBar 来显示加载进度,但是加载时间太短,ProgressBar 会在屏幕上一闪而过

时间:2019年11月30日13:50:48
问题描述:
我们会遇到这样的情况,开启异步线程加载数据,同时显示 ProgressBar;在获取到数据后,就把之前显示的 ProgressBar 隐藏掉。但问题是,如果间隔时间较短,就会看到 ProgressBar 在屏幕上一闪而过。这样的用户体验是较差的。
解决办法:
之前自己的解决办法是在异步线程里故意去增加一点延时,比如 300 ms。但是这多么暴力啊,有略显无脑。那么有没有更好的办法呢?有的有的。那就是 ContentLoadingProgressBar 类,是官方库里面的。这个类就是为了解决这样的问题而生的。这里把源码放出来,方便及时查看:

package androidx.core.widget;import android.content.Context;import android.util.AttributeSet;import android.view.View;import android.widget.ProgressBar;import androidx.annotation.NonNull;import androidx.annotation.Nullable;/** * ContentLoadingProgressBar implements a ProgressBar that waits a minimum time to be * dismissed before showing. Once visible, the progress bar will be visible for * a minimum amount of time to avoid "flashes" in the UI when an event could take * a largely variable time to complete (from none, to a user perceivable amount) */public class ContentLoadingProgressBar extends ProgressBar {    private static final int MIN_SHOW_TIME = 500; // ms    private static final int MIN_DELAY = 500; // ms    long mStartTime = -1;// 发送隐藏任务的标记    boolean mPostedHide = false;// 发送显示任务的标记    boolean mPostedShow = false;// 被清除的标记    boolean mDismissed = false;    private final Runnable mDelayedHide = new Runnable() {        @Override        public void run() {            mPostedHide = false;            mStartTime = -1;            setVisibility(View.GONE);        }    };    private final Runnable mDelayedShow = new Runnable() {        @Override        public void run() {            mPostedShow = false;            if (!mDismissed) {                mStartTime = System.currentTimeMillis();                setVisibility(View.VISIBLE);            }        }    };    public ContentLoadingProgressBar(@NonNull Context context) {        this(context, null);    }    public ContentLoadingProgressBar(@NonNull Context context, @Nullable AttributeSet attrs) {        super(context, attrs, 0);    }    @Override    public void onAttachedToWindow() {        super.onAttachedToWindow();        removeCallbacks();    }    @Override    public void onDetachedFromWindow() {        super.onDetachedFromWindow();        removeCallbacks();    }    private void removeCallbacks() {        removeCallbacks(mDelayedHide);        removeCallbacks(mDelayedShow);    }    /**     * Hide the progress view if it is visible. The progress view will not be     * hidden until it has been shown for at least a minimum show time. If the     * progress view was not yet visible, cancels showing the progress view.     * 如果进度控件是可见的,就隐藏它。进度控件不会被隐藏知道它已经至少显示了一段最小     * 的显示时间。如果进度控件不可见,就删除显示进度控件。     */    public synchronized void hide() {        mDismissed = true;        removeCallbacks(mDelayedShow);        mPostedShow = false;        long diff = System.currentTimeMillis() - mStartTime;        if (diff >= MIN_SHOW_TIME || mStartTime == -1) {            // The progress spinner has been shown long enough            // OR was not shown yet. If it wasn't shown yet,            // it will just never be shown.            setVisibility(View.GONE);        } else {            // The progress spinner is shown, but not long enough,            // so put a delayed message in to hide it when its been            // shown long enough.            if (!mPostedHide) {                postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);                mPostedHide = true;            }        }    }    /**     * Show the progress view after waiting for a minimum delay. If     * during that time, hide() is called, the view is never made visible.     * 在等待一个最小的延时时间后才显示进度控件。如果在那段最小的延时时间内,     * hide() 方法被调用了,那么这个进度控件就不会变为可见了。     */    public synchronized void show() {        // Reset the start time. 重置开始时间        mStartTime = -1;        mDismissed = false;        removeCallbacks(mDelayedHide);        mPostedHide = false;        if (!mPostedShow) {            postDelayed(mDelayedShow, MIN_DELAY);            mPostedShow = true;        }    }}

最后

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

更多相关文章

  1. 《第一行代码--Android》 Git时间
  2. Android中获得当前日期时间
  3. android bitmap图片压缩,打时间水印。
  4. android时间对话框的核心代码
  5. android实现快递跟踪进度条
  6. android关于时间的demo

随机推荐

  1. MySQL 存储过程的优缺点分析
  2. IDEA 链接Mysql数据库并执行查询操作的完
  3. MySQL 覆盖索引的优点
  4. MySQL 视图(View)原理解析
  5. 超详细教你怎么升级Mysql的版本
  6. 详解mysql三值逻辑与NULL
  7. MySQL时间盲注的五种延时方法实现
  8. 分析MySQL抛出异常的几种常见解决方式
  9. 详解MySQL数据库千万级数据查询和存储
  10. 详解MySQL连接挂死的原因