Wednesday, February 24, 2021

Segmentation fault

Segmentation fault,又譯為記憶體段錯誤,也稱存取權限衝突(access violation),是一種程式錯誤

它會出現在當程式企圖存取CPU無法定址的記憶體區段時。當錯誤發生時,硬體會通知作業系統產生了記憶體存取權限衝突的狀況。作業系統通常會產生核心轉儲(core dump)以方便程式員進行除錯。通常該錯誤是由於調用一個位址,而該位址為空(NULL)所造成的,例如連結串列中調用一個未分配位址的空鏈錶單元的元素。陣列存取越界也可能產生這個錯誤。

example 1


Linux环境下段错误的产生原因及调试方法小结



響應速度優化

响应速度优化的核心思想是 避免在主线程中做耗时操作 ,常见的就是IO操作以及计算量大的操作等。

Tuesday, February 23, 2021

內存優化

 1. 常见内存泄漏

造成内存泄漏的根本原因

生命周期较短的某个对象被生命周期更长的对象所持有,导致该对象不能及时释放。

1.1 静态变量导致的内存泄漏

因为静态变量生命周期等于应用程序的生命周期,所以静态变量引用的变量不会被回收掉。这里涉及到 GC Roots 的概念。

下面是两种明显的内存泄漏:

======================================================================

public class MyCouponActivity extends BaseActivity  {
    private static Context sContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_coupon);
        sContext = this;
    }
}
// or
public class MyCouponActivity extends BaseActivity  {
    private static View sView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_coupon);
        sView = new View(this);
    }
}

======================================================================

1.2 非静态内部类(匿名类)内存泄露

注意一下静态匿名内部类和非静态匿名内部类的区别

非静态匿名内部类会持有外部class的强引用。

Handler 需要使用 static 修饰,且持有 Activity 时需要持有 WeakReference的缘故


1.2.1 HANDLER 内存泄漏

使用非静态内部类来实现Handler,lint就会给出警告。这涉及到Handler的原理。

如果Handler中有延迟的任务或者是等待执行的任务队列过长,都有可能因为Handler继续执行而导致Activity发生泄漏。

首先,非静态的Handler类会默认持有外部类的引用,包含Activity等

然后,还未处理完的消息(Message)中会持有Handler的引用

还未处理完的消息会处于消息队列中,即消息队列MessageQueue会持有Message的引用

消息队列MessageQueue位于Looper中,Looper的生命周期跟应用一致 

因此,此时的引用关系链是Looper -> MessageQueue -> Message -> Handler -> Activity。所以,这时退出Activity的话,由于存在上述的引用关系,垃圾回收器将无法回收Activity,从而造成内存泄漏。

1.2.2 多线程引起的内存泄露

我们一般使用匿名类等来启动一个线程,如下:

======================================================================

new Thread(new Runnable() {
    @Override
    public void run() {
    }
}).start();

======================================================================

同样,匿名 Thread 类里持有了外部类的引用。当 Activity 退出时,Thread有可能还在后台执行,这时就会发生了内存泄露。

解决方案和上面 Handler 类似:要不就是变成

静态内部类,引用外面资源时使用WeakReference

要不就是在Activity退出时,结束线程。

1.3 其他情况造成的内存泄漏

集合类内存泄露

集合类添加元素后,将会持有元素对象的引用,导致该元素对象不能被垃圾回收,从而发生内存泄漏

 属性动画导致的内存泄漏

属性动画中有一类无限循环的动画,如果Activity中播放此类动画且没有在onDestory方法中去停止动画,那么动画会一直播放下去。我们需要在Activity#onDestory中调用animator.cancel()方法来停止动画。

 网络、文件等流忘记关闭

手动注册广播时,退出时忘记unregisterReceiver()

Service执行完后忘记stopSelf()

EventBus等观察者模式的框架忘记手动解除注册

2 内存管理


Android性能优化

佈局優化

繪制優化

onDraw()

內存優化

響應速度優化

ListView 優化

Bitmap 優化

Thread 優化

Reference:

https://blog.yorek.xyz/android/framework/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/

Android Proxy Pattern

 

1.定义

为其他对象提供一种代理以控制这个对象的访问。

2.介绍

  • 代理模式属于结构型模式。
  • 代理模式也叫委托模式。
  • 生活中,比如代购、打官司等等,实际上都是一种代理模式。
角色说明:
  • Subject(抽象主题类):接口或者抽象类,声明真实主题与代理的共同接口方法。
  • RealSubject(真实主题类):也叫做被代理类或被委托类,定义了代理所表示的真实对象,负责具体业务逻辑的执行,客户端可以通过代理类间接的调用真实主题类的方法。
  • Proxy(代理类):也叫委托类,持有对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行。
  • Client(客户端类):使用代理模式的地方。

4.实现

以海外代购为例,在国内的人想买国外的东西只能去找国外的人去进行代购。

4.1 创建抽象主题类

人都是有购买这个方法的:

=========================================================

    public interface People {
        void buy();//购买
    }

=========================================================

4.2 创建真实主题类

国内的人想购买某些产品,定义具体的购买过程:

=========================================================

    public class Domestic implements People {
        @Override
        public void buy() {//具体实现
            System.out.println("国内要买一个包");
        }
    }

=========================================================

4.3 创建代理类

海外的代购党需要知道是谁(持有真实主题类的引用)想购买啥产品:

=========================================================

     public class Oversea implements People {
        People mPeople;//持有People类的引用
        public Oversea(People people) {
            mPeople = people;
        }
        @Override
        public void buy() {
            System.out.println("我是海外代购:");
            mPeople.buy();//调用了被代理者的buy()方法,
        }
    }

=========================================================

4.4 客户端测试:
=========================================================
     public void test() {
        People domestic = new Domestic();        //创建国内购买人
        People oversea = new Oversea(domestic);  //创建海外代购类并将domestic作为构造函数传递
        oversea.buy();                           //调用海外代购的buy()
    }
=========================================================

输出结果:
=========================================================
我是海外代购:
国内要买一个包
=========================================================

5 静态代理与动态代理

从代码的角度来分,代理可以分为两种:一种是静态代理,另一种是动态代理。

静态代理就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。上面的例子实现就是静态代理。

动态代理类的源码是在程序运行期间根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。

下面我们实现动态代理,Java提供了动态的代理接口InvocationHandler,实现该接口需要重写invoke()方法:

5.1 创建动态代理类
=========================================================
    public class DynamicProxy implements InvocationHandler {//实现InvocationHandler接口
        private Object obj;//被代理的对象

        public DynamicProxy(Object obj) {
            this.obj = obj;
        }

        //重写invoke()方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("海外动态代理调用方法: "+method.getName());
            Object result = method.invoke(obj, args);//调用被代理的对象的方法
            return result;
        }
    }
=========================================================

5.2 修改客户端的测试方法:
=========================================================
    public void test() {
        People domestic = new Domestic();                                 //创建国内购买人
        DynamicProxy proxy = new DynamicProxy(domestic);                  //创建动态代理
        ClassLoader classLoader = domestic.getClass().getClassLoader();   //获取ClassLoader
        People oversea = (People) Proxy.newProxyInstance(classLoader, new Class[]{People.class}, proxy); //通过 Proxy 创建海外代购实例 ,实际上通过反射来实现的。
        oversea.buy();//调用海外代购的buy()
    }
=========================================================
输出结果:
=========================================================
海外动态代理调用方法: buy
国内要买一个包
=========================================================

5.3 静态代理与动态代理比较

静态代理的缺点:

=========================================================

  • 静态代理如果接口新增一个方法,除了所有实现类(真实主题类)需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
  • 代理对象只服务于一种类型的对象,如果要服务多类型的对象。必须要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

  • =========================================================

    动态代理的优点:

    =========================================================
  • 可以通过一个代理类完成全部的代理功能,接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。当接口方法数量较多时,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
  • 动态代理的应用使我们的类职责更加单一,复用性更强。
  • =========================================================

    动态代理的缺点:

    =========================================================
    1. 不能对类进行代理,只能对接口进行代理,如果我们的类没有实现任何接口,那么就不能使用这种方式进行动态代理(因为$Proxy()这个类集成了Proxy,Java的集成不允许出现多个父类)。
    =========================================================

    Reference:

    https://www.jianshu.com/p/a0e687e0904f

    Monday, February 22, 2021

    Android Facade Pattern

     

    1.定义

    要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易于使用。

    5. 应用场景

    • 为一个复杂的子系统提供一个简单接口,对外隐藏子系统的具体实现、隔离变化。
    • 使用外观模式可以将一个子系统和使用它的客户端以及其它的子系统分离开来,这就提高了子系统的独立性和可移植性。
    • 在构建一个层次化结构的时候,可以使用外观模式定义每一个层次对外交互的接口。这样,层与层之间只需要通过外观进行通信,从而简化层与层之间的依赖关系。

    6. 优点

    • 降低了客户端与子系统类的耦合度,实现了子系统与客户之间的松耦合关系。
    • 外观类对子系统的接口封装,使得系统更易于使用。
    • 提高灵活性,不管子系统如何变化,只要不影响门面对象,就可以自由修改。

    7. 缺点

    • 增加新的子系统可能需要修改外观类的源代码,违背了“开闭原则”。
    • 所有子系统的功能都通过一个接口来提供,这个接口可能会变得很复杂。

    8. Android中的源码分析

    外观模式在Android中应用也非常广泛,比如Context类,里面封装了很多方法,还是以startActivity()方法为例。

    实际上startActivity()是通过ActivityManagerService来实现的,ActivityManagerService我们应该都有耳闻,但是实际开发中一般都用不到,通过封装的方式,Context类隐藏了这些细节,我们只要简单调个方法就可以启动一个新的Activity。
    这就是外观模式在Android应用的例子了。当然这种应用比比皆是,我们平时开发也经常用的到。

    Android Design Pattern Index

    创建型模式

    Singleton Pattern (确保某一个类只有一个实例,并且提供一个全局访问点)

    Builder Pattern (用来创建复杂的复合对象)

    Prototype Pattern (通过复制原型来创建新对象)

    Simple Pattern (确定只有一个工厂类,可以使用简单工厂模式) 

    Factory Pattern (让子类来决定要创建哪个对象)

    Abstract Factory Pattern (创建多个产品族中的产品对象)

    行为型模式

    Strategy Pattern (封装不同的算法,算法之间能互相替换)

    Status Pattern (根据不同的状态做出不同的行为)

    Chain of Responsibility Pattern (将事件沿着链去处理)

    Observer Pattern (状态发生改变时通知观察者,一对多的关系)

    Template Method (定义一套流程模板,根据需要实现模板中的操作)

    Iterator Pattern (提供一种方法顺序访问一个聚合对象中的各个元素)

    Memento Pattern (保存对象的状态,在需要时进行恢复)

    Visitor Pattern (稳定数据结构中,定义新的操作行为)

    Mediator Pattern (将网状结构转变为星型结构,所有行为都通过中介)

    Interpreter Pattern (定义语法,并对其进行解释)

    Command Pattern (将请求封装成命令,并记录下来,能够撤销与重做)

    结构型模式

    Proxy Pattern (控制客户端对对象的访问)

    Composite Pattern (将整体与局部(树形结构)进行递归组合,让客户端能够以一种的方式对其进行处理)

    Adapter Pattern (将原来不兼容的两个类融合在一起)

    Decorator Pattern (为对象添加新功能)

    Flyweight Pattern (使用对象池来减少重复对象的创)

    Facade Pattern (对外提供一个统一的接口用来访问子系统)

    Bridge Pattern (将两个能够独立变化的部分分离开来)

    Reference:

    https://juejin.cn/post/6844903437700710408#heading-0

    https://www.jianshu.com/p/9a480322aee1



    n8n index

     【n8n免費本地端部署】Windows版|程式安裝x指令大補帖  【一鍵安裝 n8n】圖文教學,獲得無限額度自動化工具&限時免費升級企業版功能