-
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的
-
设计模式(很重要)
单一职责原则
对于一个类而言,应该仅有一个引起它变化的原因。通俗地理解是不要在Activity中写Bean文件、网络请求处理和Adapter等。
开放封闭原则
类、模块、函数等应该是可以拓展,但是不可以修改。在开发中,需求是变化的,如果有新的需求,我们就要重新把类改一遍显然是很爆炸的,所有进来通过扩展的方式实现
-
单例模式(双重检查)
private A instance; private A(){} public static A getInstance(){ if(instance == null){ synchronized(A.class){ if(instance ==null){ instance = new A(); } } } return instance; }
工厂模式 可以用在页面缓存通过map来缓存对象
如果系统存在大量的相似对象或者缓存池的场景可以使用建造者模式 Build模式
原型模式 (Intent原型模式的使用者 implements Cloneable)
策略模式 比如代码有很多if…else或者case,会变得比较臃肿,维护成本也比较高,违背开放封闭原则,通过策略模式就可以简化
观察者模式 定义对象间一对多的依赖关系,每当一个对象改变状态时,则所有依赖于它的对象都会得到通知并且被自动更新。常见运用在发布-订阅事件总线
-
-
数据结构(尤其是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的容量
所有集合都实现Iterable迭代器接口,可以访问结合所有内容
Collection继承Iterable接口,并定义一系列方法
-
初始容量与扩容
HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。 HashMap扩容时是当前容量翻倍即:capacity*2,Hashtable扩容时是容量翻倍+1即:capacity*2+1。
-
两者计算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时,也会进行转为红黑树结构。
-
启动模式,service启动方式几种
四种启动模式
- standard(同一栈内,每次都创建)
- singleTop(同一栈内,栈顶复用)
- singleTask(同一栈内,栈内复用)
- singleInstance(不同栈内,全局唯一)
service启动方式
- startService 启动服务
onCreate()
onStartCommand()
onBind()
onDestory()
- bindService 启动服务
AIDL
创建AIDL接口文件
新建一个service。在service里面创建一个内部类,继承你刚才创建的AIDL的名称里的Stub类,并实现接口方法,在onBind返回内部类的实例
-
客户端绑定服务
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);
-
设计模式,你在哪些优秀三方库中见到使用了
okhttp 运用设计模式
1. 构造者模式; 2.工厂模式; 3.观察者模式; 4.单例模式; 5.策略模式; 6.责任链模式; 7.享元模式
Glide
1. 构造者模式;
Retrofit
1. 建造者模式 2. 工厂模式 3. 策略模式 4. 外观模式 当我们访问的子系统拥有复杂额结构,内部调用繁杂,初接触者根本无从下手时, 不凡由资深者为这个子系统设计一个外观类来供访问者使用,统一访问路径(集 中到外观类中),将繁杂的调用结合起来形成一个总调用写到外观类中 ,之后访问者不用再繁杂的方法中寻找需要的方法进行调用 ,直接在外观类中找对应的方法进行调用即可。 5. 代理模式 6. 装饰模式 (动态给一个对象添加一些额外的职责。就增加功能来说,装饰模式生成子类更加灵活。) 7. 适配器模式 类适配器与对象适配器的使用场景一致,仅仅是实现手段稍有区别,二者主要用于如下场景: (1)想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。 (2)我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。 以上两个场景其实就是从两个角度来描述一类问题,那就是要访问的方法不在合适的接口里,一个从接口出发(被访问),一个从访问出发(主动访问)。 接口适配器使用场景: (1)想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。
RxJava
1. 观察者模式 2. 装饰者模式
-
安卓适配 (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,必须对应用进行存储权限改动的适配。
- 用户的定位权限的变更
为了让用户更好地控制应用对位置信息的访问权限,Android Q 引入了新的位置权限 ACCESS_BACKGROUND_LOCATION。
-
设备唯一标识符的变更
从 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<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
// 向该集合中添加了步骤2中创建的CallAdapter.Factory请求适配器(添加在集合器末尾)
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// 请求适配器工厂集合存储顺序:自定义1适配器工厂、自定义2适配器工厂...默认适配器工厂(ExecutorCallAdapterFactory)
<-- 配置数据转换器工厂:converterFactory -->
// 在步骤2中已经添加了内置的数据转换器BuiltInConverters()(添加到集合器的首位)
// 在步骤4中又插入了一个Gson的转换器 - GsonConverterFactory(添加到集合器的首二位)
List<Converter.Factory> 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<Interceptor> 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中修改具体实现:
- BitmapShader(bitmap渲染器)
- 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交互
通过WebView的loadUrl()
通过WebView的evaluateJavascript()
通过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运行就是了