华为畅享9s 手机启动图变形问题调查结论与过程

关键字

  • layer-list
  • 启动图
  • 华为荣耀
  • windowBackground
  • 变黑

问题描述

在部分华为手机上启动,启动图会变成如下模样

c4198cabae55da33a73fed3b9dbd2a4a
而我们正常的启动图是这个模样
Screenshot_20200405-154051

有意思的地方在于,这个包对应的提交,我们同时打了一个包名不一样的包,用于mtl测试,但是那个包完全没问题。

继续阅读“华为畅享9s 手机启动图变形问题调查结论与过程”

记录一个LifeCycle 多线程使用导致的崩溃

关键字

  • lifecycle
  • 多线程
  • java.lang.IllegalArgumentException
  • bug
  • android
  • androidx

问题描述

在调用 getLifecycle().addObserver() 的时候报出这样的错误

java.lang.IllegalArgumentException  
at androidx.lifecycle.LifecycleRegistry.upEvent(SourceFile:279)  
at androidx.lifecycle.LifecycleRegistry.forwardPass(SourceFile:293)  
at androidx.lifecycle.LifecycleRegistry.sync(SourceFile:333)  
at androidx.lifecycle.LifecycleRegistry.addObserver(SourceFile:189)  

继续阅读“记录一个LifeCycle 多线程使用导致的崩溃”

记一次 Debug android 编译慢的过程

公司因为扩展业务,新建了一个 android 项目,但是这个安卓项目明明比我们的主项目代码量少那么多,但是编译时间却比我们主项目时间长非常多。让我每次编译的时候都思考人生。

项目基本情况

我们主项目是一个 80M 大小的App,dex 包都有 30M,更改一行代码的时间实际上只需要 30s 不到的时间,但是新项目 apk 大小都没有10M编译时间却要 一分多钟,有时候还需要 3分钟

调试方法

gradle 4.3 版本之后,gradle 提供了一个新的方法去扫描编译过程,就是
buildScan 功能,简单一点讲就是这个东西会把task运行过程很多东西记录下来,图形化展示这些数据,让你更好的发现问题所在。

继续阅读“记一次 Debug android 编译慢的过程”

Bitmap.Config 一些理解

Bitmap Config 解析

Bitmap.Config
ALPHA_8 每个像素存储为单透明(alpha)通道。其实就是保存透明度而已
ARGB_4444 已经废弃的格式,推荐使用ARGB_8888
ARGB_8888 每个像素存储在4个Byte上,其实就是ARGB分别占用8bit的意思
HARDWARE 特殊配置,当位图只存储在图形内存中。
RGBA_F16 每个像素存储在8个Byte上,这个我不太看得懂,就是RGBA 格式保存,每一个占用16bit,F并不知道是什么意思
RGBA_F16 每个像素存储在8个Byte上,这个我不太看得懂,就是RGBA 格式保存,每一个占用16bit,F并不知道是什么意思
RGB_565 每个像素存储在2个Byte上,只有RGB通道被编码:红色以5位精度(32个可能值)存储,绿色以6位精度存储(64个可能值),蓝色存储5位精确。

位图占用内存计算

例:
选择的是ARGB_8888 分辨率为 100*100 的位图。
占用的内存应该是

100 * 100 * 4 = 40000 Byte = 39kB

总结

只要记住符号上表示的是bit就很容易计算Bitmap在内存中占用的大小了

RecyclerView 添加Header和fooder

谷歌发布了一个RecyclerView 来代替ListView,然而RecyclerView 并没有ListView header 和 footer.为什么会没有呢,谷歌坑我们??显然不是。
其实ListView 也是“没有”这个功能,我们看看ListView中setAdapter 的代码片段

public void setAdapter(ListAdapter adapter) {
        ....
        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }
        ...
}


我们可以看setAdapter 对是否有headerView 进行了判断,如果有headviewer的话就会对adapter 进行二次封装,所以这就是为什么我说ListView 其实是没有header和footer,这也是为什么设置了adapter之后添加header会出错的问题了,因为adapter是不一样的
既然知道了ListView 是怎么构造头尾部的,我们也可以用同样的办法给Recyclerview 构造一个
因为我使用footer 的原因是要构造一个更多加载,所以我将已构造一个更多加载为示例

**
     * Created With Android Studio
     * User 47
     * Date 15/5/12
     * Time 上午10:07
     * 一个Load Adapter
     */
    private class MoreAdapter extends Adapter<ViewHolder> {


        public static final int LAST_ITEM_TYPE = -2333333;
        private  Adapter mAdapter;


        private AdapterDataObserver observer = new AdapterDataObserver() {
            MoreAdapter moreAdapter = MoreAdapter.this;
            @Override
            public void onChanged() {
                notifyDataSetChanged();
            }


            @Override
            public void onItemRangeChanged(int positionStart, int itemCount) {
                moreAdapter.notifyItemRangeChanged(positionStart,itemCount);
            }


            @Override
            public void onItemRangeInserted(int positionStart, int itemCount) {
                moreAdapter.notifyItemRangeInserted(positionStart,itemCount);
            }


            @Override
            public void onItemRangeRemoved(int positionStart, int itemCount) {
                moreAdapter.notifyItemRangeRemoved(positionStart,itemCount);
            }


            @Override
            public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
                moreAdapter.notifyItemMoved(fromPosition,itemCount);
            }
        };




        public void setWrapAdapter(Adapter adapter){
            if(adapter == null) return;
            if(mAdapter != null) mAdapter.unregisterAdapterDataObserver(observer);
            mAdapter = adapter;
            mAdapter.registerAdapterDataObserver(observer);


        }


        public Adapter getWrapAdapter(){
            return mAdapter;
        }




        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if(viewType == LAST_ITEM_TYPE)
                return  mMoreViewHolder;
            return onRealCreateViewHolder(parent, viewType);
        }


        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            if(position == getRealItemCount()) {
                return;
            }
            onRealBindViewHolder(holder, position);
        }






        @Override
        public int getItemViewType(int position) {
            if(position == getRealItemCount()) return LAST_ITEM_TYPE;
            return mAdapter.getItemViewType(position);
        }

其实整个代码很简单,就是自己封装一个Adapter,然后可以把Adapter放到里面去,让它注册自己的数据观察者,以便到时候可以通知刷新

实例化布局注意的事情

很多时候我们会把一个布局实例化之后添加到布局上
例如

View.inflater(context,R.layout.xxx,null);

但是即使是设置了外围布局的LayoutParam ,实例化之后的对象,也是没有LayoutParam的,所以很多时候都是加入布局后,布局给的默认LayoutParam,所以很多时候我们应该在实例化布局后,添加LayoutParam,不然你是不知道加入布局之后会发生什么事情

2016 修正

LayoutInflater.from(context).inflate(R.layout.xx,viewGroup,false);

上面的代码就能把最外层的布局给带入代买里面去了

Android 开发中慎用静态变量

静态变量,在Java中是当类被卸载的时候会被清空,一般情况下被卸载的时候是JVM结束的时候。但是在Android这里,使用的是Dalvik虚拟机,每个应用启动的时候都会实例化一个虚拟机对象,应用结束的时候就会消失,然后静态变量就会被回收了。

常见场景

应用后台的时候,被回收了,当用户恢复应用的时候,静态变量就会被重置为空,就会出现问题.

Git 工作流

场景

项目已经上线,有用户反馈说我们的程序有bug。同时,产品狗扔下了两个发布时间不一样的需求修复,拍照功能和美颜功能,拍照功能要下一版本发布,美颜功能要下下版本发布

问题

如何修复Bug同时,不影响新功能的开发,并且把两次的需求隔离开来.

解决方案

建立完善的工作流

项目初始化的时候我们新建三个分支,分别是release ,develop和master。同时,项目发布的时候我们要建立tag,通过tag我们找回对应版本的代码。

release 分支

保存可以发布的或者已经发布的代码分支

master 分支

保存着开发好测试过的代码,每次develop上开发好的功能都可以合并到master分支

develop 分支

我们的开发分支,开发过程中分享的代码

有这三个分支的前提下我们就可以从容的应对需求了。

修复线上的bug

从v1.0.0(假设出现问题的就是v1.0.0版本) tag 中 checkout出新的分支,hotfix_xxx, 修复好之后直接合并回去发布

开发拍照功能

直接在develop上开发,开发完成后合并到master上,测试过后就可以合并到release上了

开发美颜功能

从master中新建一个分支,叫feature_pretty ,在这个分支上同步开发,功能要上线的时候再合并到release分支上

这样就比较好的方式解决了上面这个问题了

参考