问题记录(用于自己学习)

  1. binder, handler

    Binder 进程间通信机制,内核层,binder驱动 dev-binder,IBinder继承具有跨进程
    多进程,webview,图库,推送,保活 ,闹钟服务

    多进程的优点:扩大内存,隔离风险
    单个进程分配内存是有限的

    优势:1. 性能==数据需要一次拷贝 2.特点==易用(aidl)3.安全性==为每个APP分配UID
    两个jVM,分为两块内存空间,用户空间,内核空间公共的(0~47)2的48次方

    怎么通信:运用内核空间(连续的),拷贝到内核空间,再拷贝到用户空间==传统的通信
    拷贝到内核空间,放到物理内存(共享接收端)mmap==Binder通信

    用户空间不能直接读写文件, mmap直接映射到内存空间

    binder如何跨进程:
    发送到内核空间,内核空间通过Binder驱动创建物理内存,映射到用户空间的物理映射

    Android如何使用binder:aidl(Android接口定义语言,通过接口语言生成java代码)

    IPC:进程间通信(Linux:管道、信号量、共享内容、socket)
    开启进程:1:定义process属性(:remote==>私有进程,没有:代表全局进程)
    2:native去fork一个新的进程

    aidl

    Messenger 底层由aidl实现

    contentProvider

    bindler通讯数据需要一次拷贝,socket进程通信需要两次拷贝,共享内存无需拷贝用户空间和内核空间(逻辑内存)映射到同一块物理内存。socket效率低,并且两个应用通信安全性无法保证,应用端通过虚假的pid传输数据安全性无法保证
    共享内存直接操纵内存,数据需要同步机制,不好维护,控制复杂
    Bindler 为每个APP分配UID 同时支持实名(系统服务)和匿名(自己创建的服务 ->IBinder对象)


    handler 线程间的通信机制

     Handler:负责发送消息及处理消息
     Looper:复制不断的从消息队列中取出消息,并且给发送本条消息的Handler
     MessageQueue:负责存储消息
     Message:消息本身,负责携带数据
    
     我们只能通过Looper.prepare()方法去初始化一个Looper
     Looper.prepare(boolean)方法的逻辑是一个线程中只能有一个Looper对象,
     否则在第二次尝试初始化Looper的时候,就会抛出异常。
     以线程为单位存储Looper的主要逻辑是通过ThreadLocal实现的
     私有的构造方法,禁止外界任意new出一个Looper
    

``
四大组件的底层通信机制(binder机制)

binderService 通信
AMS通过回调ServiceConnection接口的连接状态,把远端service的binder代理对象传递过来,
ServiceManger也是binder对象 AMS管理四大组件 跨了6次进程

intent不能传递大数据 (Activity,Service通过Binder处理,有限制的)
native方法限制
1M-8k

MessageQueue不能随便new出来,MessageQuede的构造方法是default的

  1. 设计模式(很重要)

    单一职责原则

    对于一个类而言,应该仅有一个引起它变化的原因。通俗地理解是不要在Activity中写Bean文件、网络请求处理和Adapter等。

    开放封闭原则

    类、模块、函数等应该是可以拓展,但是不可以修改。在开发中,需求是变化的,如果有新的需求,我们就要重新把类改一遍显然是很爆炸的,所有进来通过扩展的方式实现

    1. 单例模式(双重检查)

       private A instance;
       private A(){}
       public static A getInstance(){
           if(instance == null){
               synchronized(A.class){
                   if(instance ==null){
                    instance = new A();
                   }
               }
           }
           return instance;
       }
      
    2. 工厂模式 可以用在页面缓存通过map来缓存对象
      如果系统存在大量的相似对象或者缓存池的场景可以使用

    3. 建造者模式 Build模式

    4. 原型模式 (Intent原型模式的使用者 implements Cloneable)

    5. 策略模式 比如代码有很多if…else或者case,会变得比较臃肿,维护成本也比较高,违背开放封闭原则,通过策略模式就可以简化

    6. 观察者模式 定义对象间一对多的依赖关系,每当一个对象改变状态时,则所有依赖于它的对象都会得到通知并且被自动更新。常见运用在发布-订阅事件总线

  2. 数据结构(尤其是HashMap)

    • 基于Map接口实现,元素以键值对存储,并且允许null

    • 基本属性

        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //默认初始化大小 16 
        static final float DEFAULT_LOAD_FACTOR = 0.75f; //负载因子0.75
        static final Entry[] EMPTY_TABLE = {}; //初始化的默认数组
        transient int size; //HashMap中元素的数量
        int threshold;  //判断是否需要调整HashMap的容量  
      
    1. 所有集合都实现Iterable迭代器接口,可以访问结合所有内容

    2. Collection继承Iterable接口,并定义一系列方法

    3. 初始容量与扩容

       HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。
       
       HashMap扩容时是当前容量翻倍即:capacity*2,Hashtable扩容时是容量翻倍+1即:capacity*2+1。
      
    4. 两者计算hash的方法不同

       **Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模**
      
       int hash = key.hashCode();
       int index = (hash & 0x7FFFFFFF) % tab.length;
      
       **HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取摸。**
       
       int hash = hash(key.hashCode());
       int i = indexFor(hash, table.length);
        
       static int hash(int h) {
               // This function ensures that hashCodes that differ only by
               // constant multiples at each bit position have a bounded
               // number of collisions (approximately 8 at default load factor).
               h ^= (h >>> 20) ^ (h >>> 12);
               return h ^ (h >>> 7) ^ (h >>> 4);
           }
        
        static int indexFor(int h, int length) {
               return h & (length-1);
      

    “ << " : 左移运算符,num << n, 相当于 num 乘以2的 n 次方
    " >> " : 右移运算符,num >> n, 相当于 num 除以2的 n 次方

    put:1. 获取key.hashCode()计算hash值
    2. 通过indexFor()计算它应该存储在哈希数组的哪个小标链表里面

hashMap 1.8(transient关键字表示该属性不能被序列化

当某个桶节点数量大于8时,会转换为红黑树。
当某个桶节点数量小于6时,会转换为链表,前提是它当前是红黑树结构。
当整个hashMap中元素数量大于64时,也会进行转为红黑树结构。

  1. 启动模式,service启动方式几种

    四种启动模式

    • standard(同一栈内,每次都创建)
    • singleTop(同一栈内,栈顶复用)
    • singleTask(同一栈内,栈内复用)
    • singleInstance(不同栈内,全局唯一)

    service启动方式

    • startService 启动服务

      onCreate()

      onStartCommand()

      onBind()

      onDestory()
      - bindService 启动服务

    AIDL

    1. 创建AIDL接口文件

    2. 新建一个service。在service里面创建一个内部类,继承你刚才创建的AIDL的名称里的Stub类,并实现接口方法,在onBind返回内部类的实例

    3. 客户端绑定服务

         private ServiceConnection mServiceConnection = new ServiceConnection() {
               @Override
               public void onServiceDisconnected(ComponentName arg0) {
                   Log.e("123", "onServiceDisconnected:" + arg0.getPackageName());
               }
               @Override
               public void onServiceConnected(ComponentName name, IBinder binder) {
                   Log.e("123", "onServiceConnected:" + name.getPackageName());
                   // 获取远程Service的onBinder方法返回的对象代理
                   service = PayAidlInterface.Stub.asInterface(binder);
               }
           };
      
       //使用意图对象绑定开启服务
       Intent intent = new Intent();
       //在5.0及以上版本必须要加上这个
       intent.setPackage("com.txy.umpay.aidl");
       intent.setAction("com.txy.umpay.aidl.MAIDLService");//这个是上面service的action
       bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
      
  2. 设计模式,你在哪些优秀三方库中见到使用了

    okhttp 运用设计模式

     1. 构造者模式;
    
     2.工厂模式;
     
     3.观察者模式;
     
     4.单例模式;
     
     5.策略模式;
     
     6.责任链模式;
     
     7.享元模式
    

    Glide

     1. 构造者模式;
    

    Retrofit

     1. 建造者模式
     2. 工厂模式
     3. 策略模式
     4. 外观模式
     
         当我们访问的子系统拥有复杂额结构,内部调用繁杂,初接触者根本无从下手时,
         不凡由资深者为这个子系统设计一个外观类来供访问者使用,统一访问路径(集
         中到外观类中),将繁杂的调用结合起来形成一个总调用写到外观类中
         ,之后访问者不用再繁杂的方法中寻找需要的方法进行调用
         ,直接在外观类中找对应的方法进行调用即可。
     5. 代理模式
     6. 装饰模式 (动态给一个对象添加一些额外的职责。就增加功能来说,装饰模式生成子类更加灵活。)
     7. 适配器模式
     
         类适配器与对象适配器的使用场景一致,仅仅是实现手段稍有区别,二者主要用于如下场景:
    
       (1)想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。
     
       (2)我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。
     
       以上两个场景其实就是从两个角度来描述一类问题,那就是要访问的方法不在合适的接口里,一个从接口出发(被访问),一个从访问出发(主动访问)。
     
         接口适配器使用场景:
     
       (1)想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。
    

    RxJava

     1. 观察者模式
     2. 装饰者模式
    
  3. 安卓适配 (AndroidQ)

    android Q (存储权限、定位权限、从后台启动Activity、设备标识符、无线扫描权限)
    1. 用户隐私权限变更
    - 为每个应用提供一个“隔离存储沙盒”("/sdcard"),不需要任何权限

             #比如要存储一张图片,则应放在
             Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    

    以下将按访问的目标文件的地址介绍如何适配。

     访问自己文件:Q中用更精细的媒体特定权限替换并取消了 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE权限,并且无需特定权限,应用即可访问自己沙盒中的文件。
     
     访问系统媒体文件:Q中引入了一个新定义媒体文件的共享集合,如果要访问沙盒外的媒体共享文件,比如照片,音乐,视频等,需要申请新的媒体权限:READ_MEDIA_IMAGES,READ_MEDIA_VIDEO,READ_MEDIA_AUDIO,申请方法同原来的存储权限。
     
     访问系统下载文件:对于系统下载文件夹的访问,暂时没做限制,但是,要访问其中其他应用的文件,必须允许用户使用系统的文件选择器应用来选择文件。
     
     访问其他应用沙盒文件:如果你的应用需要使用其他应用在沙盒内创建的文件,请点击使用其他应用的文件,本文不做介绍。
     所以请判断当应用运行在Q平台上时,取消对READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE两个权限的申请。并替换为新的媒体特定权限。
     当满足以下每个条件时,将开启兼容模式,即不开启Q设备中的存储权限改动:
     
     应用targetSDK<=P。
     应用安装在从 Android P 升级到 Android Q 的设备上。
     但是当应用重新安装(更新)时,不会重新开启兼容模式,存储权限改动将生效。
     所以按官方文档所说,无论targetSDK是否为Q,必须对应用进行存储权限改动的适配。
    
    1. 用户的定位权限的变更
     为了让用户更好地控制应用对位置信息的访问权限,Android Q 引入了新的位置权限 ACCESS_BACKGROUND_LOCATION。
    
    1. 设备唯一标识符的变更

      从 Android Q 开始,应用必须具有 READ_PRIVILEGED_PHONE_STATE 签名权限才能访问设备的不可重置标识符(包含 IMEI 和序列号)

性能优化

  • 代码框架结构层面(数据结构,循环,网络加载,View)

SparseArray(二分查找算法,key只能是int)SparseArray使用的内存远远小于HashMap

嵌套循环应该遵循“外小内大”的原则,用迭代器进行for循环,异常捕获

  • 用户体验(内存,apk体积,网络)
  • 机型适配(今日头条)

修改APP的DisplayMetrics

  • 代码质量调优工具

线性数据结构:

数组 物理内存是连续的,长度是确定的

HandlerThreed

    1.  HandlerThread handlerThread = new HandlerThread("downloadImage");
     //必须先开启线程
    2.  handlerThread.start();
    3.  Handler handler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

Handler

应用启动入口,ActivityThread通过Looper.prepareMainLooper创建Looper,同时构造函数中创建MessageQueue,及拿到当前线程
Handler双参构造函数里通过Looper.myLooper得到Looper对象
mLooper.mQueue得到消息队列
Handler.sendMessage()把消息放到消息队列
ActivityThread通过Looper.loop去取消息并分发消息
休眠:pollOnce Looper中的管道上的读端上的睡眠等待。
需要唤醒:
 if (needWake) {
            nativeWake(mPtr);
        }
epoll来完成Looper的休眠与唤醒的

RxJava(基于事件流的链式调用实现异步操作的库)
简单使用:
1:创建被观察者Observable Observable.creat(new ObservableOnSubscribe(){});ObservableOnSubscribe是一个接口,实际是是创建ObservableCreat{}能够执行subscribe方法的类,在subscribe(ObservableEmitter)中定义需发送的事件,通过发射器ObservableEmitter.onNext();发送事件

2:Observable.subscribe(observer)(把Observable与Observer关联起来)->[ObservableCreate]subscribeActual(observer),创建Observer定义响应事件的行为

Retrfit(RESTful(网络应用程序的设计风格)的HTTP网络请求框架的封装)

1.创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
                             .baseUrl("http://  fanyi.youdao.com/")
                             .addConverterFactory   (GsonConverterFactory.create())
//创建一个含有Gson对象实例的GsonConverterFactory并放入到数据转换器工厂converterFactories里
                             .build();
public Retrofit build() {
 <--  配置网络请求执行器(callFactory)-->
  okhttp3.Call.Factory callFactory = this.callFactory;
  // 如果没指定,则默认使用okhttp
  // 所以Retrofit默认使用okhttp进行网络请求
  if (callFactory == null) {
    callFactory = new OkHttpClient();
  }
 <--  配置回调方法执行器(callbackExecutor)-->
  Executor callbackExecutor = this.callbackExecutor;
  // 如果没指定,则默认使用Platform检测环境时的默认callbackExecutor
  // 即Android默认的callbackExecutor
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
  }
 <--  配置网络请求适配器工厂(CallAdapterFactory)-->
  List adapterFactories = new ArrayList<>(this.adapterFactories);
  // 向该集合中添加了步骤2中创建的CallAdapter.Factory请求适配器(添加在集合器末尾)
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// 请求适配器工厂集合存储顺序:自定义1适配器工厂、自定义2适配器工厂...默认适配器工厂(ExecutorCallAdapterFactory)
 <--  配置数据转换器工厂:converterFactory -->
  // 在步骤2中已经添加了内置的数据转换器BuiltInConverters()(添加到集合器的首位)
  // 在步骤4中又插入了一个Gson的转换器 - GsonConverterFactory(添加到集合器的首二位)
  List converterFactories = new ArrayList<>(this.converterFactories);
  // 数据转换器工厂集合存储的是:默认数据转换器工厂( BuiltInConverters)、自定义1数据转换器工厂(GsonConverterFactory)、自定义2数据转换器工厂....

// 注:
//1. 获取合适的网络请求适配器和数据转换器都是从adapterFactories和converterFactories集合的首位-末位开始遍历
// 因此集合中的工厂位置越靠前就拥有越高的使用权限

  // 最终返回一个Retrofit的对象,并传入上述已经配置好的成员变量
  return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
      callbackExecutor, validateEagerly);
}
通过建造者模式创建Retrofit实例,并配置好所需的成员变量
2.创建 网络请求接口实例 并 配置网络请求参数
retrofir.creat(API.class)
// 创建了网络请求接口的动态代理对象,即通过动态代理创建网络请求接口的实例 (并最终返回)ServiceMethod 
// 该动态代理是为了拿到网络请求接口实例上所有注解
3.发送网络请求(封装了 数据转换、线程切换的操作)
搭配Rxjava
        observable.subscribeOn(Schedulers.newThread()) // 1. 指定被观察者 生产事件的线程
              .observeOn(AndroidSchedulers.mainThread())  // 2. 指定观察者 接收 & 响应事件的线程
              .subscribe(observer); // 3. 最后再通过订阅(subscribe)连接观察者和被观察
4.处理服务器返回的数据
通过calllBack拿到数据

OkHttp

1:OkHttpClient client = new OkHttpClient();
通过外观模式、构建者模式创建所需的Builder对象,配置请求中所需参数
2:Request request = new Request.Builder().url("http://www.baidu.com") .build();
使用构建者模式创建一个包含请求参数的Request对象
3:client.newCall(request).execute()/enqueue()

4:RealCall是Call的具体实现类,负责execute分发请求(通过client里的分发器Dispatcher.exected(call))并通过响应拦截器获取Response并返回
5:Dispatcher中有3个队列,1个线程池(同步、异步会放入不同的队列)
exected先加入runningSyncCalls队列
enqueue加入readyAsyncCalls队列
判断正在执行的异步请求数量属否超过64;
判断和当前Call有相同主机的正在执行的AsyncCall的个数是否小于5个;
上述两种情况都符合,将当前AsyncCall直接加入到runningAsyncCalls队列中;并且在线程池中执行,即执行AsyncCall的execute方法;
否则,将AsyncCall加入到readyAsyncCalls队列中,准备执行,后续Dispatcher会进行任务调度。
循环遍历队列并执行Realcall.executeOn()通过执行任务,
executorService.execute(this);
ExecutorService接口 里面创建ThreadPoolExecutor线程池

getResponseWithInterceptorChain()责任链模式

private Response getResponseWithInterceptorChain() throws IOException {
    //创建一个拦截器列表
    List interceptors = new ArrayList<>();
    //优先处理自定义拦截器
    interceptors.addAll(client.interceptors());
    //失败重连拦截器
    interceptors.add(retryAndFollowUpInterceptor);
    //接口桥接拦截器(同时处理cookie逻辑)
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //缓存拦截器
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //分配连接拦截器
    interceptors.add(new ConnectInterceptor(client));
    //web的socket连接的网络配置拦截器
    if (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    //最后是连接服务器发起真正的网络请求的拦截器
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket())); 
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    //流式执行并返回response
    return chain.proceed(originalRequest);
  }

自定义拦截器

public class LoggingInterceptor implements Interceptor{
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        HttpUrl url=original.url().newBuilder()
                .addQueryParameter("source","android")
                .build();
        //添加请求头
        Request request = original.newBuilder()
                .url(url)
                .build();
        return chain.proceed(request);
    }

}

Glide

Glide.with(context).load(url).into(imageview)
1:Glide.with(context)重载多个方法
    1>:getRetriever(context)中初始化GlideBuilder
    2>:getRetriever(context).get(context)加入一个隐藏的fragment
目的:加入一个隐藏的fragment,为了同步生命周期
registerActivityLifecycleCallbacks存在资源浪费的现象
返回RequestManager有同步生命周期的Glide实例

同时GlideBuilder.build()创建BitmapPool、MemoryCache、ArrayPool等
Engine.onResourceReleased()释放资源的时候 cache.put(key,resource)

BitmapPool(Glide中对Bitmap复用进行统一管理的接口)

LruBitmapPool:采用策略模式,它自身不处理具体逻辑,真正的逻辑在LruPoolStrategy中
put:把Bitmap存储到LruPoolStrategy->groupedMap.put(key, bitmap);->entry.add(value);

自定义Transformation继承BitmapTransformation ,在transform中修改具体实现:

  1. BitmapShader(bitmap渲染器)
  1. canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) 画圆角矩形
Bitmap bitmap = TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight);  
拿到剪切过的Bitmap去处理,

LruCache(LruCache算法的算法核心 = LRU 算法 + LinkedHashMap数据结构:即 近期最少使用算法) put() -> trimToSize(maxSize);

LinkedHashMap(当accessOrder为true,优先遍历插入数据,再遍历访问数据):
所以插入先插入头,删除先删除尾

/** 
  * LinkedHashMap 构造函数
  * 参数accessOrder = true时,存储顺序(遍历顺序) = 外部访问顺序;为false时,存储顺序(遍历顺序) = 插入顺序
  **/ 
  public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {

        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;

    }
2:RequestManger.load()重载多个方法
    1>:asDrawable 创建RequestBuilder对象并转为Drawable资源对象
    2>:RequestBuilder.load()传入URL地址及设置标志位(必须先load()传入数据)
3:RequestBuilder.into()
   
   buildRequest()构建请求,并把Request设置给ViewTarget
   SingLeRequest.obtain()
   调用requestManager.track()方法执行请求
   1:把target加入到Set集合中,2:把构建的请求加入Set集合(requests),request.begin()->onSizeReady()里通过engine.load()构建EngineJob(用来开启线程的,为后面的异步加载图片做准备),构建DecodeJob(用来对图片进行解码)
    由DataFetcher的多种实现类来通过不同方式获取数据源,并由DecodeJob去解码
    默认使用HttpURLConnection获取数据源

图片内存占用:
获取图片大小:

  //方法一:通过uri把图片转化为bitmap的方法
    Bitmap bitmap= BitmapFactory.decodeFile(path);
   int height= bitmap.getHeight();
    int width= bitmap.getWidth();
    Log.e("通过bitmap获取到的图片大小","width:"+width+"height"+height);
    //方法二:使用Options类来获取
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;//这个参数设置为true才有效,
    Bitmap bmp = BitmapFactory.decodeFile(path, options);//这里的bitmap是个空
    if(bmp==null){
        Log.e("通过options获取到的bitmap为空","===");
    }
    int outHeight=options.outHeight;
    int outWidth= options.outWidth;

图片高 * 图片宽 * 1个像素占用的byte

ALPHA_8: 每个像素占用1byte内存

ARGB_4444:每个像素占用2byte内存

ARGB_8888:每个像素占用4byte内存

RGB_565: 每个像素占用2byte内存

API获取(第一个效率高,底层调用native方法getAllocationByteCount)

   /**
     * 得到bitmap的大小
     */
    public static int getBitmapSize(Bitmap bitmap) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {    //API 19
            return bitmap.getAllocationByteCount();
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {//API 12
            return bitmap.getByteCount();
        }
        // 在低版本中用一行的字节x高度
        return bitmap.getRowBytes() * bitmap.getHeight();                //earlier version
    }

多线程wait、notify、notifyAll

wait、notify、notifyAll都必须在synchronized中执行

join()作用是让指定的线程先执行完再执行其他线程,而且会阻塞主线程

yield()的作用是指定线程先礼让一下别的线程的先执行

内存泄漏

webView内部的一些线程持有activity对象,导致activity无法释放。继而内存泄漏

Fragment

以前懒加载,Fragment 中处理 setUserVisibleHint + onHiddenChanged 这两个函数,而在 Androidx 模式下,我们可以使用 FragmentTransaction.setMaxLifecycle() 的方式来处理 Fragment 的懒加载
切换fragment 使用add/hide/show 不会走任何的生命周期 无法通过生命周期进行刷新,可以通过onHiddenChanged()

当fragment结合viewpager使用的时候;使用setUserVisibleHint()

androidx下实现懒加载:

将需要显示的 Fragment ,在调用 add 或 show 方法后,setMaxLifecycle(showFragment, Lifecycle.State.RESUMED)
将需要隐藏的 Fragment ,在调用 hide 方法后,setMaxLifecycle(fragment, Lifecycle.State.STARTED)

kotlin 协程(轻量级的线程)

launch/join()函数开启了一个协程 async/await()开启协程返回Deferred

webview

内存泄漏

webView关联activity的时候会自动创建线程,而activity无法确定这个线程的销毁时间,这个线程的生命周期和我们activity生命周期是不一样的,这就导致了 我们activity销毁时,webView还持有我们activity的引用,从而出现内存泄漏问题。

解决:

1、独立进程,简单暴力,不过可能涉及到进程间通信

2、动态添加WebView,对传入WebView中使用的Context使用弱引用,动态添加WebView意思是在布局创建一个ViewGroup用来防止WebView,activity创建时add进来,在Activity停止时remove掉。

js与webview交互

  1. 通过WebView的loadUrl()

  2. 通过WebView的evaluateJavascript()

  3. 通过WebView的addJavascriptInterface()进行对象映射, 定义一个与JS对象映射关系的Android类:方法采用@JavascriptInterface

AOP技术

java动态代理(InvocationHandler 接口是动态代理的核心)

public class JdkProxy implements InvocationHandler {
    private UserDaoJDK userDaoJDK;
    public Object createProxy(UserDaoJDK userDaoJDK) {
        this.userDaoJDK=userDaoJDK;
        ClassLoader classLoader = JdkProxy.class.getClassLoader();
        Class[] interfaces = userDaoJDK.getClass().getInterfaces();
        
        return Proxy.newProxyInstance(classLoader, interfaces, this);
        
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MyAspect myAspect = new MyAspect();
        myAspect.check_Permissions();
        Object object = method.invoke(userDaoJDK, args);
        myAspect.log();
        return object;
    }

}

SDK打包注意事项

  • 打包出来的位置不同

         AS低版本
         jar: /build/intermediates/bundles/debug(release)/classes.jar
         AS高版本
         jar: /build/intermediates/packaged-classes/release/classes.jar
    
         aar: /build/outputs/aar/libraryname.aar
    
  • jar 中只包含了class文件与清单文件,

  • aar中除了包含jar中的class文件还包含工程中使用的所有资源,class及res资源文件全部包含

  • 使用方式不同

  • arr的打包很简单的

我们新建好了library的module
找到IDE右边的gradle,找到本library下build文件夹下的assemble运行就是了

你可能感兴趣的:(问题记录(用于自己学习))