一个小项目总结

1,783 阅读7分钟

基础

  • 依赖倒置原则:抽象不依赖于实现、让具体实现依赖于抽象。其实就是基本的编程想法,但是在设计
  • activity的切换,A.pause→B.onCreate→B.onStart→B.onResume→A.onStop
    所以别在pause做耗时操作,会影响下个页面的启动速度。
  • Filter实现数据过滤,在Adapter里面写一个内部类,然后继承接口。这不仅可以实现搜索,在数据较大且很恶心人还要分类的时候特别有用。
  • 双重Viewpager的嵌套、在supportV4包下的viewpager不存在滑动冲突,但是层叠Viewpager的时候,subViewPager的适配器要用getChildFragmentManager(),不然会造成页面滑动缓慢,和fragment丢失的问题。
  • viewpager嵌套swiperefresh嵌套listView里面的N个seekbar,通过监控seekbar的触摸事件,决定viewpager是否分发触摸事件。解决横向滑动冲突。
  • CustomView 在onMeasure之后再获取宽高尺寸API,一般放在onLayout初始化就可以了。
  • 包装Vector颜色
    Drawable drawable = getDrawable; 
    drawable = DrawableCompat.wrap(drawable); 
    DrawableCompat.setTint(drawable, Color.GREEN); 
    DrawableCompat.setTintMode(drawable, PorterDuff.Mode.SRC_IN); 
    // 恢复颜色: DrawableCompat.unwrap(Drawable drawable);
    - 透明度分为256阶(0-255),计算机上用16进制表示为(00-ff)
  • 公司新项目还是多用Fragment,移植,合并模块的快,Activity不方便移动。
  • 项目开始的时候写个Python脚本解决机型尺寸适配问题,毕竟设计师给的都是px。
  • System.exit(0); 真的会把application搞退出来。android.os.Process.killProcess(android.os.Process.myPid());也是可以的
  • 不要把Int型变量直接放在setText();里面,会被当成资源ID去查找,然后极有可能就出现找不到资源的错误。
  • Custom view 在定义属性的时候,注意static的应用,如果是开放设置的属性别设static,不然会联动到所有的控件里面。
  • 开发过程中有时候空指针,数组越界等问题导致了activity销毁重启,然而里面的旧fragment还在,activity重启带来了一个新的fragment,导致新旧fragment就重叠了,怎么避免?在activity里面检查savedInstanceState是否为空,不为空可以直接用FragmentManager把它找出来,避免重复创建。
  • 对于第三方的回调的数据,判空后再使用。
  • 适配器里面有用到context,传递的最好是activity/fragment,为什么呢,因为适配器里面时常更新UI,这么做就是把数据绑定到UI的生命周期上,防止更新页面的时候页面已经销毁
  • 注意检查activity/fragment结束的时候,popupWin/dialog等弹窗是否关闭。
  • 一个比较方便的屏幕截图:
    View viewRoot = getWindow().getDecorView().getRootView(); 
    viewRoot.setDrawingCacheEnabled(true); 
    Bitmap screenShotAsBitmap = Bitmap.createBitmap(viewRoot.getDrawingCache()); 
    viewRoot.setDrawingCacheEnabled(false);
  • API19开始原生支持 PDF文件加载。PdfDocument
  • 事件分发,这是我自己的一点总结,和项目相关,理解方式可能和别人不同:
dispatchTouchEvent onInterceptTouchEvent onTouchEvent
调度 拦截(ViewGroup) 触摸
这玩意儿相当成功的避免了 true 拦截,发给自己的onTouchEvent true 拦截
onInterceptTouchEvent 的多次调用 false 不拦截,发给child的dispatchTouchEvent false 不拦截,发给parent的onTouch
  • 需要协调一连串的动画 用属性动画的playwith after together 联动。
  • 属性动画+事件分发+尺寸+布局 custom viewgroup
  • 唯一约束是一个比较简单直接的控制数据唯一性的方法。
  • 把SQL语句和数据库字段分离,多线程操作数据库的时候容易发生死锁,注意实现线程的同步和互斥。
  • Vysor用来演示不错
  • KMP用来做字符匹配搜索很中肯,特别是在解析长数据的时候。当然不排除其他模型树有更好的解法。

  • Gradle Console里面的构建信息真心可以查出好多问题。

  • adapter必须封装好,不然写死你,
  • listview的addHeadView会导致页面position +1完全没必要搞这玩意儿导致数据紊乱,在布局里面的头部自己加就可以。
  • listview的notifyDataSetChanged()是检查指定的list的堆地址,通过list.add把数据加在堆地址,可以实现数据更新,但是如果通过list = changelist 是改变了 list 的引用地址到changelist的堆地址,notifyDataSetChanged()会去检查原来的list的内存地址,起不到更新数据的效果。可以通过list.clear(); list.addAll(changelist);来添加。
  • <include>标签、如果指定了ID,那么将不能直接通过findviewbyId找到,可以通过
    View layout = getLayoutInflater().inflate(R.layout.head, null); //head.xml
    RelativeLayout index= (RelativeLayout)layout.findViewById(R.id.index); //找到id
    index.xxxxx;//做操作
  • bottomsheet可以用23design里面提供的,没必要自己写。多处应用的话,写个BottomSheetDialogFragment包装起来就可以了。至于那些属性配置透明度、状态栏颜色什么的,全部可以在style里面完成。
  • style="?android:attr/borderlessButtonStyle"可以去除BUTTON的投影效果
  • 有MVP架构的代码,写单元测试那叫一个舒服。(但是MVP用了一年之后的感觉就是不想再用,恶心。没必要。MVVM或许还好点,或者自己定制一种MVC的方式,把controller写好。)
  • 混淆的时候注意第三方框架,自定义控件。注解。
  • 6.0动态权限申请流程

    6.0权限

线程

  • 主线程 = UI线程 = mainthread
  • 通过多个Thead来跑同一个Runnable可以较好的实现资源共享,注意锁住关键对象。
  • handler和线程和thread一一对应:Runnable并没有实现多线程
    主线程里面(也是在activity或者fragment里面) handler.post(new Runnable(){...})其实也是在主线程里面运行的。要实现多线程就new Thread(new Runnable(){...}).start();这也就是为什么我们可以传主线程的handler给adapter或者service或者任何其他文件,然后通过handler来sendmsg来更新UI。
    非主线程里面, handler.post(new Runnable(){...})是在子线程运行的,new Thread(new Runnable(){...}).start()也一样。
  • 线程并发数量控制在2N+1 (N是cpu核数)
  • 多线程访问同一个对象,要保证线程安全,不然容易抛出java.util.ConcurrentModificationException错误,怎么解决,保证线程安全:
    • 给对象加同步锁
    • 使用ConcurrentHashMap替换HashMap,CopyOnWriteArrayList替换ArrayList;
    • 使用Vector替换ArrayList,Vector是线程安全的。Vector的缺点:大量数据操作时,由于线程安全,性能比ArrayList低.(这段话来至Bugly)
  • FutureTask用来预加载,
  • Semaphore 信号量 实现仿PV原语。
  • AsyncTask就是一个封装了比较好的串行线程池(4.0+),在2.0+是并行的,但是现在2.0用的也不多,线程池配置不符合项目要求就自己定义比较灵活。
  • HanlderThread的run()方法是一个无限制的循环,不需要了要quit掉。
  • IntentService里面封装了HanlderThread,并用HanlderThread的Looper定义一个Hanlder来发送进来的intent携带的信息。是一个优先级比较高的service,用到服务的时候可以优先考虑。

第三方库

  • 公司项目慎用Butter Knife,自己玩玩就行。
  • EventBus这库,使用前,先画应用的数据流图!千万千万要画,数据流向不清晰,会导致后面很难维护。
  • EventBus,普通的几次调用还可以,如果一两秒post它十几二十次,绝壁挂掉。
  • 好吧,今天2016/11/8。我把EventBus的源码给过了一遍。为什么他会挂掉,是因为用了太多ThreadMode.Main,handler返回处理的太慢了?锁住了main thread。嗯,是这样的。

内存优化

  • 关于内存抖动:发生在一个颜色采集器。
    onDraw()、onLayout()是会频繁被调用的,应该避免对象分配的暴涨,和复杂的操作,诸如new Paint()等操作;有部分解决不了,用对象池解决。
  • 关于内存飙升:不断地滑动viewpager。
  • 使用懒加载,优化首次进入页面的速度和滑动的流畅度(UserVisibleHint=true时再加载网络数据和数据库数据)。然而我觉得如果不是很多实在没必要。
  • 覆盖finalize方法,能比较简单查出内存泄漏的地方
    @Override 
    protected void finalize() throws Throwable { 
         super.finalize();
         Log.d("TGA", "===="+this.getClass().getSimpleName());
    }

UDP

  • 善用 socket重用。
  • RTP,多用于流媒体传输、尽量保证大文件UDP传输的稳定性。
  • 每次只能传64K,传文件实在不靠谱
  • 255.255.255.255广播
  • 短时间内收发包数量过多,容易丢包的问题
    • 调试设置符合实际情况的时间间隔。
    • 设置符合实际情况的缓冲区大小。
    • 如果是包太大就分包发。

C++

  • string→char*
    //const char*
    std::str = "oaosj";
    const char*  c = str.c_str();
    //char*
    std::str2 = "oaosj2";
    int len = str2.lenght();
    char* c2;
    c2= (char *)malloc((len+1)*sizeof(char));
    str2.copy(c2,len,0);