Handler的任务取消
在线程间交互时,取消的任务应该是消息的方式发给该Handler来取消,否则会出现消息取消失败的情况,因为当前正在实行的任务会执行完才能成功取消。
架构思考
优秀架构师必须掌握的架构思维 http://www.uml.org.cn/zjjs/201807034.asp
抽象
分层
分治
面向对象,谁的事谁管理
演化
系统框架演化,从小到大,从简单到复杂
Android组件化
主工程多组件开发 主工程多子工程开发 https://blog.csdn.net/qq_31391977/article/details/83586863 https://www.jianshu.com/p/06931c9b78dc
Android系统框架演化-中间件
框架
CleanArt+AAC+Dagger2+Rxjava2.x+Retrofit
CleanArt
Dagger2
Dagger2 最清晰的使用教程 https://www.jianshu.com/p/24af4c102f62 Dagger 2 完全解析(一),Dagger 2 的基本使用与原理 https://www.jianshu.com/p/26d9f99ea3bb
依赖的生命周期: @Singleton是@Scope的默认实现 在@Provide和@Component中同时 使用才起作用
进阶
Java clone
浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。
举例来说更加清楚:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。2深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。举例来说更加清楚:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。
若不对clone()方法进行改写,则调用此方法得到的对象即为浅拷贝。
https://www.cnblogs.com/xuanxufeng/p/6558330.html
(10k)性能比较:
//dozer:721
//commons-beanutils:229
//cglib:133
//serializable:687
//orika:83
//clone:8
https://blog.csdn.net/u012611620/article/details/81698596
单例
https://www.cnblogs.com/ngy0217/p/9006716.html
饿汉式(线程安全,调用效率高,但是不能延时加载)
懒汉式(线程安全,调用效率不高,但是能延时加载)
Double CheckLock实现单例:DCL也就是双重锁判断机制(由于JVM底层模型原因,偶尔会出问题,不建议使用)
静态内部类实现模式(线程安全,调用效率高,可以延时加载)
枚举类(线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用) 总结:
单例对象 占用资源少,不需要延时加载,枚举 好于 饿汉
单例对象 占用资源多,需要延时加载,静态内部类 好于 懒汉式
Thread
Java 线程状态转换图 https://www.cnblogs.com/huangzejun/p/7908898.html
join()方法 在 Parent 调用 child.join() 后,child 子线程正常运行,Parent 父线程会等待 child 子线程结束后再继续运行。
yield()方法 https://www.cnblogs.com/huzi007/p/7085866.html 使当前线程从执行状态(运行状态)变为可执行态(就绪状态),可能再次被cpu选中执行
Executors 返回的线程池对象的弊端如下:
FixedThreadPool 和 SingleThreadPool : 允 许 的 请 求 队 列 长 度 为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM;
CachedThreadPool 和 ScheduledThreadPool : 允 许 的 创 建 线 程 数 量 为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
https
https://blog.csdn.net/duanbokan/article/details/50847612
单向认证
双向认证
Java引用
引用类型
GC时JVM内存充足
GC时JVM内存不足
强引用 new Object()
不被回收
不被回收
软引用 SoftReference
不被回收
被回收
弱引用 WeakReference
被回收
被回收
虚引用 PhantomReference
任何时候都会被回收
任何时候都会被回收
虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有 引用与之关联一样,在任何时候 都可能被垃圾回收器回收。
Android隐式跳转
Activity 间通过隐式 Intent 的跳转,在发出 Intent 之前必须通过 resolveActivity检查,避免找不到合适的调用组件,造成 ActivityNotFoundException 的异常。
Android 基础组件如果使用隐式调用,应在 AndroidManifest.xml 中使用 或在代码中使用 IntentFilter 增加过滤。 说明: 如果浏览器支持 Intent Scheme Uri 语法,如果过滤不当,那么恶意用户可能通过浏览器 js 代码进行一些恶意行为,比如盗取 cookie 等。如果使用了 Intent.parseUri函数,获取的 intent 必须严格过滤。 正例:
// 将 intent scheme URL 转换为 intent 对象 Intent intent = Intent.parseUri(uri); // 禁止没有 BROWSABLE category 的情况下启动 activity intent.addCategory(“android.intent.category.BROWSABLE”); intent.setComponent(null); intent.setSelector(null); // 使用 intent 启动 activity context.startActivityIfNeeded(intent, -1)
切换Activity生命周期回调
Activity切换时生命周期调用的顺序(1->2)
Activity切换:
onCreate 1 onStart 1 onResume 1 onPause 1
onCreate 2 onStart 2 onResume 2
onStop 1 onDestroy 1
----------back onPause 2 onStop 2 onDestroy 2
点击Back键返回:
onCreate 1 onStart 1 onResume 1 onPause 1
onCreate 2 onStart 2 onResume 2
onStop 1
---------back onPause 2
onRestart 1 onStart 1 onResume 1
onStop 2 onDestroy 2
屏幕适配
RL vs LL
dp https://blog.csdn.net/wangwangli6/article/details/63258270/
密度类型
代表的分辨率(px)
屏幕像素密度(dpi)
低密度(ldpi)
240x320
120
中密度(mdpi)
320x480
160
高密度(hdpi)
480x800
240
超高密度(xhdpi)
720x1280
320
超超高密度(xxhdpi)
1080x1920
480
进程间通讯:
绑定Service+AIDL->IBinder
AIDL反注册 因为Binder会把客户端传递过来的对象重新序列化成一个新的对象,所以在IPC中无法反注册
//应使用
public class RemoteCallbackList
//内部使用
ArrayMap mCallbacks = new ArrayMap<>();
//反注册
boolean success = mListenerList.unregister(listener);
//特殊的遍历方式
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
if (l != null) {
//TODO
}
}
mListenerList.finishBroadcast();
Binder死亡后重连远程服务
在 onServiceDisconnected中重连远程服务
使用Binder 死亡的DeathRecipient 监听 ,使用linkToDeath给Binder设置死亡代理
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
try {
mBinderPool.asBinder().linkToDeath(mDeathRecipient, 0);
...
} catch (RemoteException e) {
e.printStackTrace();
}
}
...
}
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
mBinderPool.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBinderPool= null;
// TODO:这里重新绑定远程Service
}
};
Binder连接池
Binder权限处理
在AndroidManifest中生命权限
//服务端声明权限
//服务端检测权限
@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
Log.d(TAG, "onbind check=" + check);
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mBinder;
}
//客户端使用权限
在代码中判断包名
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
//检测权限
int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
.....
String packageName = null;
//通过Uid或者Pid获取调用者的包名
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
if (!packageName.startsWith("com.ryg")) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
事件分发
//使用伪代码描述ViewGroup的事件分发
//1. 分发事件
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
//2. 检测自身是否拦截事件
if (onInterceptTouchEvent(ev)){
//3. 拦截事件
consume = onTouchEvent(ev);
}else{
//3. 分发事件给子控件
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
滑动冲突
滑动冲突解决方案
外部拦截方法 在父容器中拦截事件
内部拦截方法 在子控件拦截事件 在父容器中处理子控件未拦截事件
自定义View
onMeasure
SpecMode
MeasureSpec和LayoutParams的对应关系
View的measure过程
ViewGroup 的measure过程 ViewGroup是抽象类,并未重载onMeasure方法,需要子类根据不同的布局特性自定义。
错误用法
layout
layout方法确定View自身的位置,onLayout方法确定所有子元素的位置。
draw过程
自定义View - 绘制一个圆
BUG总结
事件过滤
使用数组拷贝的方式判断当前点击多少次
线程间交互
handler的post消息默认what是0,导当移除使用what=0的消息时,会附带移除post发送的消息
RecyclerView滑动时更新数据
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 21(offset:21.state:20
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3300)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3258)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1803)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1302)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265)
at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1093)
at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:956)
at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:2715)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
at android.view.Choreographer.doCallbacks(Choreographer.java:555)
at android.view.Choreographer.doFrame(Choreographer.java:524)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4921)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
at dalvik.system.NativeStart.main(Native Method)
解决方案:使用DiffUtil解决数据更新
DiffUtil 使用的是 Eugene Myers(尤金梅尔斯) 的差别算法,这个算法本身是不检查元素的移动的。也就是说,有元素的移动它也只是会先标记为删除,然后再标记插入。而如果需要计算元素的移动,它实际上也是在通过 Eugene Myers 算法比对之后,再进行一次移动检查。所以,如果集合本身已经排序过了,可以不进行移动的检查。
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
return oldCar.size();
}
@Override
public int getNewListSize() {
return shoppingCar.getCar().size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldCar.get(oldItemPosition).equals(shoppingCar.getCar().get(newItemPosition));
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return true;
}
//简单使用上述函数方法回调用RecyclerView.Adapter
//onBindViewHolder(holder, position);
// 使用这个函数会调用RecyclerView.Adapter
//public void onBindViewHolder(VH holder, int position, List payloads)
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return null;
}
},true);
diffResult.dispatchUpdatesTo(recyclerView.getAdapter());
RecyclerView封装
@Override
public int getItemCount() {
return mData.size();
}
//复写该方法,确定当前Item的Type
@Override
public int getItemViewType(int position) {
return getDefItemViewType(position);
}
protected int getDefItemViewType(int position) {
//使用代理控制ItemType
if (mMultiTypeDelegate != null) {
return mMultiTypeDelegate.getDefItemViewType(mData, position);
}
return super.getItemViewType(position);
}
@Override
public K onCreateViewHolder(ViewGroup parent, int viewType) {
K baseViewHolder = null;
this.mContext = parent.getContext();
this.mLayoutInflater = LayoutInflater.from(mContext);
baseViewHolder = onCreateDefViewHolder(parent, viewType);
bindViewClickListener(baseViewHolder);
baseViewHolder.setAdapter(this);
return baseViewHolder;
}
protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
int layoutId = mLayoutResId;
//使用代理控制ViewHolder
if (mMultiTypeDelegate != null) {
layoutId = mMultiTypeDelegate.getLayoutId(viewType);
}
return createBaseViewHolder(parent, layoutId);
}
@Override
public void onBindViewHolder(K holder, int position) {
convert(holder, getItem(position));
}
Java线程间通讯
Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等 https://segmentfault.com/a/1190000017766364 锁的类型,Java中的锁是多种类型
公平锁 / 非公平锁
可重入锁 / 不可重入锁
独享锁 / 共享锁
互斥锁 / 读写锁
乐观锁 / 悲观锁
分段锁
比如:在ConcurrentHashMap中使用了一个包含16个锁的数组,
每个锁保护所有散列桶的1/16,
其中第N个散列桶由第(N mod 16)个锁来保护。
假设使用合理的散列算法使关键字能够均匀的分部,
那么这大约能使对锁的请求减少到越来的1/16。
也正是这项技术使得ConcurrentHashMap支持多达16个并发的写入线程。
偏向锁 / 轻量级锁 / 重量级锁
自旋锁
synchronized关键字 重量级锁会让其他申请的线程进入阻塞,性能降低。
volatile关键字 一个变量如果用volatile修饰了,则Java可以确保所有线程看到这个变量的值是一致的,如果某个线程对volatile修饰的共享变量进行更新,那么其他线程可以立马看到这个更新,这就是所谓的线程可见性。 使用volatile关键字必须满足如下两个条件: 对变量的写操作不依赖当前值; 该变量没有包含在具有其他变量的不变式中。 案例:单例模式中的double check,使用volatile关键字是
JDK 1.6 提供的 concurrent 库
1. 线程完成后的回调 使用Callable+Future | CompletionService
2. ReentrantLock
1.6之前 synchronized 关键字效果 相同
1.6之后 ReentrantLock: Lock 接口的实现类
3. ReentrantReadWriteLock
共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。(生产者-消费者)
4. Thead_Communication
1.5 之前
synchronized
this.wait();// 等待
this.notify();//唤醒
1.6 之后
Lock lock = new ReentrantLock();
lock.lock();
lock.unlock();
Condition condition = lock.newCondition();
condition.await();// 等待
condition.signal();//唤醒
5. 信号量
Semaphore sp = new Semaphore(3);
sp.acquire();//获取许可
sp.release();//释放许可
6. 循环栅栏
//只有当3个信号同时到达才会继续往下执行
CyclicBarrier cb = new CyclicBarrier(3);
7. CountdownLatch
CountDownLatch cdOrder = new CountDownLatch(1); // 主线程控制,为0 继续向下执行
CountDownLatch cdAnswer = new CountDownLatch(3); // 子线程控制,为0 继续向下执行
8. 线程间数据交换
Exchanger exchanger = new Exchanger();
9. 阻塞队列
BlockingQueue queue = new ArrayBlockingQueue(3);
10. 同步队列
//SynchronousQueue 内部没有容量,但是由于一个插入操作总是对应一个移除操作,反过来同样需要满足。
BlockingQueue synchronousQueue = new SynchronousQueue();
HandlerThread
ThreadLocal线程本地变量,初始化Loop和MessageQueue
AsyncTask
//线程池
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
算法总结
100G的文件排序 这是一个典型的分治问题,100G的大文件肯定无法一次加载到内存直接排序,所以需要先切分成若干小问题来解决。那么8G内存的计算机一次大概能排多大的数据量,可以在有限的时间内排完呢?也就是100G的大文件要怎么切法,切成多少份比较合适?这个是考察候选人的时间空间复杂度估算能力,需要一定的计算机组织和算法功底,也需要一定实战经验和sense。实际上8G内存的话,操作系统要用掉一部分,如果用Java开发排序程序,大致JVM可用2~4G内存,基于一般的经验值,一次排1G左右的数据应该没有问题(我实际在计算机上干过1G数据的排序,是OK的)。所以100G的文件需要先切分成100份,每份1G,这样每个子文件可以直接加载到内存进行排序。对于1G数据量的字符串排序,采用Java里头提供的快速排序算法是比较合适的。 好,经过有限时间的排序(取决于计算机性能,快的一天内能排完),假定100个1G的文件都已经排好了,相当于现在硬盘上有100个已经排好序的文件,但是我们最终需要的是一个排好序的文件,下面该怎么做?这个时候我们需要把已经解决的子问题组合起来,合并成我们需要的最终结果文件。这个时候该采用什么算法呢?这里考察候选人对外排序和归并排序算法的掌握程度,我们可以将100个排好序的文件进行两两归并排序,这样不断重复,我们就会得到50个排好序的文件,每个大小是2G。然后再两两归并,不断重复,直到最后两个文件归并成目标文件,这个文件就是100G并且是排好序的。因为是外排序+归并排序,每次只需要读取当前索引指向的文件记录到内存,进行比较,小的那个输出到目标文件,内存占用极少。另外,上面的算法是两路归并,也可以采用多路归并,甚至是采用堆排序进行优化,但是总体分治思路没有变化。
时间复杂度
基础排序查找算法
@Test
public void bSearch() {
//13 27 38 49 49 65 76 97
int nums[] = {13, 27, 38, 49, 49, 65, 76, 97};
System.out.println(bInnerSearch(49, nums));
}
public int bInnerSearch(int k, int nums[]) {
int low = 0, high = nums.length - 1;
while (low <= high) {
int mid = (low + high) >> 1;
if (nums[mid] == k) {
return mid;
} else if (nums[mid] > k) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
@Test
public void search() {
int nums[] = {49, 38, 65, 97, 76, 13, 27, 49};
System.out.println(innerSearch(13, nums));
}
public int innerSearch(int k, int nums[]) {
for (int i = 0; i < nums.length; i++) {
if (nums[i] == k) {
return i;
}
}
return -1;
}
@Test
public void selectSort() {
//49 38 65 97 76 13 27 49
//13
//13 27
//13 27 38
//13 27 38 49
//13 27 38 49 49
//13 27 38 49 49 65
//13 27 38 49 49 65 76
//13 27 38 49 49 65 76 97
int i, j, k, temp, nums[] = {49, 38, 65, 97, 76, 13, 27, 49};
for (i = 0; i < nums.length - 1; i++) {
k = i;
for (j = i + 1; j < nums.length; j++) {
if (nums[k] > nums[j]) {
k = j;
}
}
temp = nums[i];
nums[i] = nums[k];
nums[k] = temp;
}
for (int num : nums) {
System.out.print(num + " ");
}
}
@Test
public void quickSort() {
//49 38 65 97 76 13 27 49
//27 38 65 97 76 13 49 49
//27 38 49 97 76 13 65 49
//27 38 13 97 76 49 65 49
//27 38 13 49 76 97 65 49
int nums[] = {49, 38, 65, 97, 76, 13, 27, 49};
innerSort(0, nums.length - 1, nums);
for (int num : nums) {
System.out.print(num + " ");
}
}
public void innerSort(int low, int high, int nums[]) {
if (low < high) {
int i = low, j = high, temp = nums[i];
while (i < j) {
while (i < j && temp <= nums[j]) {
j--;
}
if (i < j) {
nums[i] = nums[j];
i++;
}
while (i < j && temp > nums[i]) {
i++;
}
if (i < j) {
nums[j] = nums[i];
j--;
}
}
nums[i] = temp;
innerSort(low, i - 1, nums);
//innerSort(i + 1, nums.length - 1, nums);
innerSort(i + 1, high, nums);
}
}
@Test
public void bubbleSort() {
//49 38 65 97 76 13 27 49
//38 49 65 76 13 27 49 97
int i, j, temp, nums[] = {49, 38, 65, 97, 76, 13, 27, 49};
for (i = 0; i < nums.length - 1; i++) {
for (j = 0; j < nums.length - 1 - i; j++) {
if (nums[j + 1] < nums[j]) {
temp = nums[j + 1];
nums[j + 1] = nums[j];
nums[j] = temp;
}
}
}
for (int num : nums) {
System.out.print(num + " ");
}
}
@Test
public void insertSort() {
//49 38 65 97 76 13 27 49
//49
//38 49
//38 49 65
//38 49 65 97
//38 49 65 76 97
//13 38 49 65 76 97
//13 27 38 49 65 76 97
//13 27 38 49 49 65 76 97
int i, j, temp, nums[] = {49, 38, 65, 97, 76, 13, 27, 49};
for (i = 1; i < nums.length - 1; i++) {
j = i - 1;
temp = nums[i];
while (j >= 0 && temp < nums[j]) {
//nums[j] = temp; 交换方式错误
nums[j + 1] = nums[j];
j--;
}
nums[j + 1] = temp;
}
for (int num : nums) {
System.out.print(num + " ");
}
}
加密
RSA: 公钥:加密 | 验证 (交换) 私钥:解密 | 签名
AES > DES
Android 媒体播放框架
Android 媒体播放框架MediaSession分析与实践 https://juejin.im/post/5aa0e18851882577b45e91df
性能优化
0. Bitmap缓存
内存缓存
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
磁盘缓存
磁盘缓存需要通过Snapshot来完成,Snapshot只能得到FileInputStream,但FileInputStream无法便捷进行压缩,所以还要获取FileDescriptor来进行压缩
//1.
FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX);
FileDescriptor fileDescriptor = fileInputStream.getFD();
bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor,
reqWidth, reqHeight);
//2.
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fd, null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFileDescriptor(fd, null, options);
网络缓存
0. StrictMode
ThreadPolicy 线程策略检测的内容有 · 自定义的耗时调用 使用 detectCustomSlowCalls() 开启 · 磁盘读取操作 使用 detectDiskReads() 开启 · 磁盘写入操作 使用 detectDiskWrites() 开启 · 网络操作 使用 detectNetwork() 开启
VmPolicy 虚拟机策略检测的内容有 · Activity泄露 使用 detectActivityLeaks() 开启 · 未关闭的Closable对象泄露 使用 detectLeakedClosableObjects() 开启 · 泄露的Sqlite对象 使用 detectLeakedSqlLiteObjects() 开启 · 检测实例数量 使用 setClassInstanceLimit() 开启
1. App启动速度优化
应用的启动分为冷启动、热启动、温启动
统计应用的启动时间
通过ADB命令统计应用的启动时间:adb shell am start -W 首屏Activity。
冷启动优化
//系统
加载启动App;
App启动之后立即展示出一个空白的Window;
创建App的进程;
创建App对象;
启动Main Thread;
创建启动的Activity对象;
//可优化
加载View;
布置屏幕;
进行第一次绘制;
利用提前展示出来的Window,快速展示出来一个界面,给用户快速反馈的体验; 自定义一个背景主题,在Launcher Activity的onCreate super.oncreate()之前设置回自己的Theme
避免在启动时做密集沉重的初始化(Heavy app initialization; App中子线程初始化,或者延后到Launcher Activity中
定位问题:避免I/O操作、反序列化、网络操作、布局嵌套等。
针对5.0以下的机器优化在APP初次启动MultiDex加载时机
开新进程,然后挂起主进程,避免加载过长ANR。进程间通信使用Messenger。Android 5.0 ART虚拟机在APK安装前已经优化。
Android MultiDex初次启动APP优化 https://blog.csdn.net/synaric/article/details/53540760
2. UI性能优化
Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,若未能刷新UI,会出现丢帧,并在Logcat打印drop frames的警告。
I/Choreographer:Skipped 50 frames! The appliction may be doing to much work on its main thread.
检测工具
开发者选项 - 显示GPU过度绘制
检测并发现具体位置过度绘制
蓝>绿>粉红>深红
解决方案: 减少父控件背景绘制
重写onDraw()方法使用canvas.clipRect可解决图层重叠
Hierarchy Viewer
检测并提示各个View的绘制时间
DDMS需要切换到hierarchy模式
选择要检测的ViewGroup点击 Obtain layout times for tree rooted at selected node 绿 > 黄 > 红
BlockCanary(致敬LeakCanary)
原理:
利用loop()中打印的日志检测UI绘制时间 原理:在loop循环messages时,如果设置了logging对象可以在msg.target.dispatchMessage(msg);增加log,可以查看主线程的堆栈信息
//设置mLogging
Looper.getMainLooper().setMessageLogging(new Printer() { ... } );
//获取主线程堆栈信息
StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();
log:
02-21 00:26:26.408 2999-3014/com.zhy.testlp E/TAG:
java.lang.VMThread.sleep(Native Method)
java.lang.Thread.sleep(Thread.java:1013)
java.lang.Thread.sleep(Thread.java:995)
com.zhy.testlp.MainActivity$2.onClick(MainActivity.java:70)
android.view.View.performClick(View.java:4438)
android.view.View$PerformClick.run(View.java:18422)
android.os.Handler.handleCallback(Handler.java:733)
android.os.Handler.dispatchMessage(Handler.java:95)
利用Choreographer打印的日志检测UI绘制时间
Choreographer.getInstance()
.postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long l) {
// 16ms出发一次
}
});
GPU 渲染模式分析 - 在屏幕显示条形图
查看UI性能 在绿色基线下表示该帧流畅
GPU 渲染模式分析 - dumpsys gfxinfo
adb shell dumpsys gfxinfo > C:\Users\86175\Desktop/gfx.txt
将 Profile data in ms: 下的信息导入到excel中将其生成不同的折线图或柱状图
Android Studio GPU Monitor
使用此功能需要在 GPU 渲染模式分析 中选择 in adb shell dumpsys gfxinfo
merge标签
merge标签常用于减少布局嵌套层次,但是只能用于根布局。
include标签和布局性能关系不大,主要用于布局重用,一般和merge标签配合使用
ViewStub标签
//1. @layout/start
//2.
//3.
ViewStub viewStub = (ViewStub) findViewById(R.id.stub);
try {
//如果没有被inflate过,使用inflate膨胀
LinearLayout layout=(LinearLayout)viewStub.inflate();
RatingBar bar=(RatingBar)layout.findViewById(R.id.ratingBar1);
bar.setNumStars(4);
} catch (Exception e) {
//如果使用inflate膨胀报错,就说明已经被膨胀过了,使用setVisibility方法显示
viewStub.setVisibility(View.VISIBLE);
}
TraceView使用
点击 按钮 Start Method Profiling(开启方法分析)和点击 Stop Method Profiling(停止方法分析) 主要关心: Cpu Time/Call :该方法平均占用 CPU 的时间 Real Time/Call :平均执行时间,包括切换、阻塞的时间,>= Cpu Time Calls + Recur Calls/Total :调用、递归次数 展开后关心: Parents:选中方法的调用处 Children:选中方法调用的方法
LeakCanary
原理: 在Application中注册 new ActivityLifecycleCallbacks() {},包装Activity,利用监测机制利用了Java的WeakReference和ReferenceQueue
ANR
traces.txt分析
主线程sleep
线程间通讯
//被同步的锁变量是class对象
private synchronized void testANR() {
SystemClock.sleep(30 * 1000);
}
private synchronized void initView() {
}
ANR-WatchDog
原理:判断ANR的方法其实很简单,我们在子线程里向主线程发消息,如果过了固定时间后,消息仍未处理,则说明已发生ANR了。 Android中ANR的监测与定位 https://blog.csdn.net/u013771867/article/details/78484470 ANR-WatchDog https://github.com/SalomonBrys/ANR-WatchDog
3. 内存优化
GC
分配内存;
确保任何被引用的对象保留在内存中;
回收不能通过引用关系找到的对象的内存.
通过System Class Loader或者Boot Class Loader加载的class对象,通过自定义类加载器加载的class不一定是GC Root
处于激活状态的线程
栈中的对象
方法区类的静态属性引用的对象
方法区中的常量引用的对象
正在被用于同步的各种锁对象
静态变量
单例
动画 | 计时器 | handler 未销毁
内部类
GC_CONCURRENT:当已分配内存达到某一值时,触发并发GC;
GC_FOR_MALLOC:当尝试在堆上分配内存不足时触发的GC;系统必须停止应用程序 并回收内存; GC_HPROF_DUMP_HEAP
GC的流程
Android的进程级别
托管用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)
托管某个 Service,后者绑定到用户正在交互的 Activity
托管正在“前台”运行的 Service(服务已调用 startForeground())
托管正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy())
托管正执行其 onReceive() 方法的 BroadcastReceiver
托管不在前台、但仍对用户可见的 Activity(已调用其 onPause() 方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。
托管绑定到可见(或前台)Activity 的 Service。
服务进程 正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。
后台进程 包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop() 方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。 有关保存和恢复状态的信息,请参阅 Activity文档。
空进程 不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。
常用的内存分析工具
Android Studio Memory Monitor ① GC按钮, 点击执行一次GC操作. ② Dump Java Heap按钮, 点击会在该调试工程的captures目录生成一个类似这样"com.anly.githubapp_2016.09.21_23.42.hprof"命名的hprof文件, 并打开Android Studio的HPROF Viewer显示该文件内容.主要用于查询内存泄漏 ③ Allocation Traking按钮, 点击一次开始, 再次点击结束, 同样会在captrures目录生成一个文件, 类似"com.anly.githubapp_2016.09.21_23.48.alloc", alloc后缀的文件, 并打开Allocation Tracker视图展示该文件内容.主要用于内存跟踪
Dump Java Heap后打开HPROF Viewer的界面
MAT
前提条件:使用 hprof-conv 转换 Android Monitor - Dump Java Heap抓取的文件
Histogram侧重于量的分析 选中Activity - 右键 - 选择Merge Shortest paths to GC Roots - With all references
DominatorTree侧重于关系的分析 列表中的文件左下角有一个小圆点,表示GC Root可达 选中Acitivity - 右键 - 选择 Path To GC Roots - With all references
4. 电量优化
网络请求
PowerManager.WakeLock
WakeLock获取释放成对出现.
使用超时WakeLock[acquire(long timeout)], 以防出异常导致没有释放.
5. 网络优化
Gzip压缩
使用Protocol Buffer代替JSON
网络缓存
配置OkHttp的缓存目录
private static final long CACHE_SIZE = 1024 * 1024 * 50;
@Override
public OkHttpClient.Builder customize(OkHttpClient.Builder builder) {
// set cache dir
File cacheFile = new File(mContext.getCacheDir(), "github_repo");
Cache cache = new Cache(cacheFile, CACHE_SIZE);
builder.cache(cache);
...
return builder;
}
配置Retrofit Request接口的"Cache-Control" Header
@Headers("Cache-Control: public, max-age=180")
@GET("trending?languages[]=java&languages[]=swift&languages[]=objective-c&languages[]=bash&languages[]=python&languages[]=html")
Observable getTrendingRepos();
配置Response中的"Cache-Control" Header OkHttp的拦截器interceptor.
如下拦截器, 做了:
对于request, 当没有网络时, 使用CacheControl.FORCE_CACHE, 即使用缓存. (正常情况使用2.2配置的Cache-Control) 对于response, 有网时, 加入和request一样的Cache-Control; 对于response, 无网, 使用CacheControl.FORCE_CACHE(这种情况较为少见).
private final Interceptor mCacheControlInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//1.
if (!NetworkUtil.isNetworkAvailable(mContext)) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response originalResponse = chain.proceed(request);
//2.
if (NetworkUtil.isNetworkAvailable(mContext)) {
String cacheControl = request.cacheControl().toString();
return originalResponse.newBuilder()
.header("Cache-Control", cacheControl)
.build();
} else {
//3.
return originalResponse.newBuilder()
.header("Cache-Control", CacheControl.FORCE_CACHE.toString())
.build();
}
}
};
Settings - Wifi
WifiSettings
WifiTracker Scanner - mWifiManager.startScan()
AccessPoint
Wifi广播
ConnectivityManager.CONNECTIVITY_ACTION: 网络连接发生了变化的广播, 通常是默认的连接类型已经建立连接或者已经失去连接会触发的广播;
WifiManager.WIFI_STATE_CHANGED_ACTION: WiFi模块硬件状态改变的广播, WiFi开启|关闭;
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION: 扫描到一个热点, 并且此热点达可用状态 会触发此广播; 此时, 你可以通过 wifiManager.getScanResult() 来取出当前所扫描到的 ScanResult; 同时, 你可以从intent中取出一个boolean值; 如果此值为true, 代表着扫描热点已完全成功; 为false, 代表此次扫描不成功, ScanResult 距离上次扫描并未得到更新;
WifiManager.NETWORK_IDS_CHANGED_ACTION: 在网络配置, 保存, 添加, 连接, 断开, 忘记的操作过后, 均会对 WIFI 热点配置形成影响,
WifiManager.SUPPLICANT_STATE_CHANGED_ACTION: 建立连接的热点正在发生变化.
WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION: 广播已配置的网络发生变化, 可由添加, 修改, 删除网络的触发.
WifiManager.LINK_CONFIGURATION_CHANGED_ACTION: WIFI 连接配置发生改变的广播.
WifiManager.NETWORK_STATE_CHANGED_ACTION: WIFI 连接状态发生改变的广播. 可以从 intent 中取得 NetworkInfo, 此时 NetworkInfo 中提供了连接的新状态, 如果连接成功, 可以获取当前连接网络的 BSSID, 和 WifiInfo.
WifiManager.RSSI_CHANGED_ACTION: WIFI 热点信号强度发生变化的广播.
https://www.jianshu.com/p/a0fbb4644b84
动态加载技术 - 插件化技术
访问资源文件
Activity生命周期管理
反射方式
接口方式
//使用
private static final HashMap mPluginClassLoaders = new HashMap();
热修复
类加载方案 采用类加载方案的主要是以腾讯系为主,包括微信的Tinker、QQ空间的超级补丁、手机QQ的QFix、饿了么的Amigo和Nuwa等等。
底层替换方案 采用底层替换方案主要是阿里系为主,包括AndFix、Dexposed、阿里百川、Sophix。 替换ArtMethod结构体中的字段或者替换整个ArtMethod结构体,这就是底层替换方案。
Instant Run方案 借鉴Instant Run的原理的热修复框架有Robust和Aceso。
对比
跨平台
react-native Facebook 出品,JavaScript语言,JSCore引擎,React设计模式,原生渲染
weex Alibaba 出品,JavaScript语言,JS V8引擎,Vue设计模式,原生渲染
flutter Google 出品,Dart语言,Flutter Engine引擎,响应式设计模式,原生渲染 Flutter 主要分为 Framework 和 Engine,我们基于Framework 开发App,运行在 Engine 上。Engine 是 Flutter 的独立虚拟机,由它适配和提供跨平台支持,目前猜测 Flutter 应用程序在 Android 上,是直接运行 Engine 上所以在是不需要Dalvik虚拟机。 得益于 Engine 层,Flutter 甚至不使用移动平台的原生控件, 而是使用自己 Engine 来绘制 Widget (Flutter的显示单元),而 Dart 代码都是通过 AOT 编译为平台的原生代码,所以 Flutter 可以 直接与平台通信,不需要JS引擎的桥接。同时 Flutter 唯一要求系统提供的是 canvas,以实现UI的绘制。
Node.js
事件驱动 非阻塞式 I/O
express 基于 Node.js 平台,快速、开放、极简的 Web 开发框架 接口实现:Routing 路由
SpringMVC
你可能感兴趣的:(android)
AGP 8.0 适配 - 问题列表
zhuzhumouse
gradle android-studio android
不管是基于编译速度,包体积,新特性等那个方面的考虑,大家都应该开始AGP8.0的适配了,下面使我适配过程中遇到的各种问题,这里列出来,给大家的适配做一个参考。问题一:编译失败@TaskAction解决方案:在根目录的setting.gradle或者build.gradle里面添加如下代码:classpath'com.android.tools.build:gradle:8.0.0'原因说明:AGP
一篇文章搞定《Android布局优化》
小孟来码
Android 一篇文章搞定Android android 性能优化
------《一篇文章搞定Android布局优化》前言为什么要进行布局优化?Android绘制原理双缓冲机制布局加载原理布局加载优化的一些方法介绍AsyncLayoutInflater方案X2C方案Compose方案一些常规优化手段优化布局层级及复杂度mergeViewStub避免过度绘制前言在使用ViewPager时,如果我们的适配器使用的是Fragment。Android的绘制优化其实可以分为
Android 第四十二章 Fragment
漂泊的蚂蚁
Android android
一、简介Fragment(碎片)主要用于对ActivityUI模块化,依托Activity存在,当Activity销毁时,Fragment随之销毁二、生命周期onAttach()Fragment和Activity绑定onCreate()初始化onCreateView()创建视图onActivityCreated()Activity已初始化完成onStart()界面可见onResume()获得焦点o
Android 第四十四章 Menu
漂泊的蚂蚁
Android android
publicclassMainActivityextendsAppCompatActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublicboole
Android中Handler的post(Runnable)用法
莫空0000
Android
privateHandlermhandler=newHandler();privatevoidabc(){newThread(newRunnable(){@Overridepublicvoidrun(){try{/*dosomething*///里面执行的事件是被handler放在主线程中执行的,与Run所在线程无关。//.post方法一般用于更新UImhandler.post(newRunnab
gms认证流程_Android P(9.0)GMS认证新要求
weixin_39879651
gms认证流程
本文档主要初步整理AndroidP对GMS认证的新要求一、基本环境主机要求:Java8Python2.7ADB1.0.39VTS环境$sudoapt-getinstallpython-dev$sudoapt-getinstallpython-protobuf$sudoapt-getinstallprotobuf-compiler$sudoapt-getinstallpython-virtualen
Android14 AOSP 允许system分区和vendor分区应用进行AIDL通信
冬瓜神君
android binder
在Android14上,出于种种原因,system分区的应用无法和vendor分区的应用直接通过AIDL的方法进行通信,但是项目的某个功能又需要如此。好在Binder底层其实是支持的,只是在上层进行了屏蔽。修改frameworks/native/libs/binder/BpBinder.cpp文件,找到status_tBpBinder::transact方法,屏蔽相关判断语句或者错误返回即可。如下
Android从源码分析handler.post(runnable),view.post(runnable),runOnUiThread(runnable)执行时机
听者110
Android高级开发系列笔记 Android 线程
大家好,我是听者,耳听心受的听,孙行者的者,感谢大家阅读我的文章。废话不说直接进入主题,不管是Android还是其他语言,线程之间通信都是一个比较“头疼”问题,开发Android的码农应该都知道回到主线程的方式有handler.post(runnable),view.post(runnable),runOnUiThread(runnable)。但是这三种方式的区别以及其执行的时机如何呢?今天就给大
哔哩哔哩APP的 AGP8 升级之旅
锐湃
Gradle android gitee
前言是的,2024年我还在做Android原生开发,没有kmp,没有遥遥领先。本次Android大仓的AGP8升级涵盖多个APP多个业务方,持续3个月;分各个三大步,若干小步完成升级迁移,以下为本次升级踩坑经历。升级与踩坑本次AGP升级计划从7.2.2升级到8.2.2,AGP中最大的变动点是TransformApi接口的废弃、以及默认编译特性的开启。同时也要顺带升级Gradle版本,部分Gradl
Flutter‘s app_plugin_loader Gradle plugin imperatively using the apply script错误解决
江上清风山间明月
Flutter flutter plugin_loader Gradle apply plugin
文章目录问题描述解决方法1.查看当前的`android/build.gradle`2.迁移到`plugins`声明式方法修改前:修改后:3.同步Flutter工具更新4.参考Flutter官方文档补充说明问题描述用Androidstudio开发flutter的andoridapk时编译报如下警告:YouareapplyingFlutter’sapp_plugin_loaderGradleplugi
gradle设置build执行命令_Android开发Gradle高级技巧、自定义Gradle Task、打包完成自动签名、执行Shell脚本...
一张卷纸
先说下为什么有这篇文章吧,最近在写一个应用需要进行系统签名;每次我都需要先将apk生成然后使用sign.jar和系统证书进行签名,非常的麻烦而且tmd还是搞两个系统也就是每次都需要用两个系统证书分别进行签名…一:每次我都需要两个不同的设备的安装包,所以我使用多渠道打包这样每次进行打包都会给我生成两个apk。如下代码:app.gradle,这里需要注意的是多渠道打包,需要指定一个flavorDime
Mac环境jenkins多渠道配置打包Flutter、Android应用
Super-Bin
Flutter android flutter jenkins 移动端
这里写目录标题前言Jenkins安装启动安装插件项目配置General配置源码管理构建构建后操作执行解决问题找不到./gradlew命令解决方法1解决方法2只显示代码,没有正常显示二维码局域网ip无法访问jenkins修改配置可能与nginx的端口占用有关参考前言关于Flutter、Android多渠道配置打包,源码Jenkins安装两种安装方式:各平台官网安装Jenkins步骤mac官网安装je
AGP 8.0 适配 ---- jvm target compatibility
zhuzhumouse
android java gradle
基础知识科普Android工程的GradleJDK,以及代码里面配置的jvmTarget有什么区别?这是两个完全不同的概念:GradleJDK:指的是用于运行Gradle构建系统的JavaDevelopmentKit版本。Gradle是Android项目的构建工具,它负责处理项目的编译、打包、依赖管理等任务。GradleJDK的选择决定了在构建过程中使用的Java版本。jvmTarget:是jav
认识 ADB(Android Debug Bridge,Android SDK 中的一个工具)
我命由我12345
运维 - ADB adb android android-studio android studio android jetpack 安卓 android runtime
一、ADB概述ADB,全称AndroidDebugBridge,是AndroidSDK中的一个工具ADB位于AndroidSDK下platform-tools目录中ADB起到调试桥的作用,ADB可以让开发者通过USB连接安卓设备,并在电脑上执行各种命令,从而方便开发和测试安卓应用程序ADB使开发者能够在电脑上对安卓设备进行全面的操作,包括安装卸载APK、拷贝文件、查看设备硬件信息、查看应用程序占用
android+8.0对应版本,Android各版本适配之8.0
chinhoyoo
android+8.0对应版本
Android8.0透明Activity报错"Onlyfullscreenactivitiescanrequestorientation"1、分析问题首先,我的代码是这样的:style.xmltruetrueadjustPanfalse@color/app_transparent_colortrue//透明true//悬浮AndroidManifest.xml从上面可以看出,我的activity是
android8.0自适应图标,适配8.0+的自适应图标
贫民窟的大富翁
安卓8.0 自适应图标 适配教程 图标背景 XML文件
工具:mt管理器自适应图标是安卓8.0引入的新特性,可以让图标随系统主题变化,保持高度整齐,这个特性其实适配并不难准备工作:抠取图标前景(这个不用我讲了把),提取背景色步骤1:添加图标前景把图标前景文件丢进安装包,丢哪里都行,记住路径就行图片发自简书App打开arsc,在drawable/drawable下新建资源,类型为文本,把图标前景路径输进去,并记下这个资源的arscID比如7F020006
android代码跳转通知权限界面,Android 8.0+通知栏(Notification)适配详解,判断通知权限是否打开,并跳转至通知管理页面...
我是索吹
前不久由于API26(Android8.0)以上的设备没法显示通知栏,因此网上查阅了比较多了资料,得出结论,API26之后引入了通知渠道这么个东西,而后就写了一篇问题对应的博文:Android8.0通知栏(Notification)适配,在模拟上是完美运行,但是当我前两天安装到真机上测试时,咦?怎么又无效了?而后我就想着,是否是通知权限没打开?由于模拟器上不少权限的控制和真机上差异很大,我打开设置
深入理解 Android 混淆规则
nukix
android android
在Android开发中,混淆(Obfuscation)是一种保护代码安全的重要手段,通常通过ProGuard或R8工具来实现。本文将详细介绍Android混淆规则的基本原理、配置方法以及最佳实践,帮助开发者更好地保护应用代码。博主博客https://blog.uso6.comhttps://blog.csdn.net/dxk539687357什么是混淆?混淆是一种通过对代码进行重命名、删除无用代码
Android Studio gradle配置
nukix
android android
settings.gradle配置指定路径module博主博客https://blog.uso6.comhttps://blog.csdn.net/dxk539687357一、正常情况,导入本项目的模块只需要使用include':app',':library'即可。二、但是当需要导入其他项目的模块,可以使用相对路径指定include':app',':library'project(':thirdl
Android 接收 App 安装卸载更新
nukix
android java android
本篇文章主要介绍如何监听APP的安装、卸载和更新的广播。博主博客https://blog.uso6.comhttps://blog.csdn.net/dxk539687357Android8.0以前静态注册在AndroidManifest.xml中加入静态注册即可。Android8.0及以后需要动态注册InstallReceiverinstallReceiver=newInstallReceive
Android中获取so文件来源于哪个库
火龙映天
Android相关 android
Androidapp中可能有很多的.so文件,有时我们不确定这些.so文件都是来源于哪些库的,可以通过在build.gradle中添加代码来统计。具体方法如下:1.在com.android.application模块的build.gradle文件最后添加如下代码://获取所有的.so文件的打包路径tasks.whenTaskAdded{task->if(task.name=='mergeDebug
Android Java创建ViewModel新api
debug_cat
Android应用层开发 android java leetcode
背景项目使用Java,创建ViewModel发现之前旧api不管用了。不要问为什么项目还要用Java,别问。老项目不让升级。ViewModel创建新方式新方式是因为依赖新版本库,其实用旧版本库就回到旧方式了。依赖:deflifecycle_version="2.5.0"//ViewModelimplementation"androidx.lifecycle:lifecycle-viewmodel:
agent和android怎么结合:健康助手,旅游助手,学习助手
ZhangJiQun&MXP
教学 2021 论文 2024大模型以及算力 android 旅游 学习 人工智能 语言模型 自然语言处理 prompt
agent和android怎么结合:健康助手,旅游助手,学习助手创新点智能交互创新:提出全新的agent-Android交互模式,如基于手势、语音、眼动等多模态融合的交互方式。例如让agent能够同时理解用户的语音指令和手势动作,在Android设备上提供更加自然和高效的交互体验,比如在观看视频时,用户可以通过语音和手势结合的方式让agent调整视频播放进度、音量等。个性化服务创新:利用agent
Android15音频进阶之焦点仲裁矩阵(一百零七)
Android系统攻城狮
Android Audio工程师进阶系列 音视频 矩阵 python
简介:CSDN博客专家、《Android系统多媒体进阶实战》一书作者新书发布:《Android系统多媒体进阶实战》优质专栏:Audio工程师进阶系列【原创干货持续更新中……】优质专栏:多媒体系统工程师系列【原创干货持续更新中……】优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门视频实战课
揭秘Linux:从服务器到智能家居,一个操作系统如何改变世界
寒水馨
Linux问题解决方案 服务器 linux 智能家居
揭秘Linux:从服务器到智能家居,一个操作系统如何改变世界文章目录揭秘Linux:从服务器到智能家居,一个操作系统如何改变世界引言:Linux,不仅仅是一个操作系统Linux在企业级应用中的霸主地位云计算与虚拟化:Linux的天下大数据与人工智能:Linux的助推器Web服务器:Apache和Nginx的最佳搭档Linux在移动和嵌入式设备中的崛起Android:Linux的最成功衍生品物联网(
iQOO neo 5精简内置组件
weixin_38627000
安卓 智能手机 安卓
无他!系统自带了太多组件,都用不到,连打开都不曾打开过。下午整理一篇精简组件的列表,各自按照各自的需要进行精简哦。别盲目跟风,要不然手机使用会出问题。精简步骤使用任意刷机工具,开启手机的开发权限,然后adb连接删除组件列表如下:pmuninstall--user0com.android.VideoPlayervivoi视频pmuninstall--user0com.vivo.browservivo
解决华为手机Android系统10.0不能隐藏桌面图标问题,全网唯一
校长团
Android技术 android 安卓 华为 app
在Android系统10.0之后对应用隐藏图标做了严格的限制,Android官网的意思就是,除非是系统应用或者是不含Android四大组件并且没有申请任何权限的app才能隐藏图标。内心一阵独白:这特么的还不如直接说不让隐藏图标!作为Android开发人的我们,能答应么,绝bi不能答应,找办法,全网的搜,大部分都是一个解决方法,就是将图标想方设法弄成全透明的,这个办法有风险不说,实质并未隐藏,在桌面
应用是非正式发布版本, 当前设备不支持安装。
Y X L
Android android gradle android studio
今天后端同学要个安装包想体验下,结果apk发过去后,提示不能安装,具体报错如下。出现这个问题是因为AndroidStudio3.0开始会在debugapk的manifest文件application标签里自动添加android:testOnly="true"属性。该属性导致在IDE中使用Run生成的apk在大部分手机上只能用adbinstall-t来安装。如果使用apk直接安装就会报这个错误。解决
Android 模拟手柄摇杆输入,模拟MotionEvent
BrainZou
Android android 手柄 摇杆 MotionEvent
背景需求是在系统应用模拟摇杆,达到对所有应用都能模拟输入的效果,所以一定会使用到InputManager。但是大多demo和文章模拟摇杆和功能使用都是在同一个应用,所以可以自定义实现此类功能,不需要模拟构造MotionEvent给InputManager相关代码和使用建议先查看Google的对手柄处理文档,对手柄有一定的了解:https://developer.android.com/traini
Android Native 回调 Java/JVM
BrainZou
Android java android 开发语言
背景网络上大部分文章都是JNI的编写,但是一些情况下在so里面,也有Native调用回Java的场景,文章相对较少。而且如果照着一些文章写的,可能会出现写的c的每一行代码都执行了,但是Java层就是没执行等奇奇怪怪的问题。做项目时也遇到过这些坑,特总结如下。部分代码//TestJNI.ktclassTestJNI{init{System.loadLibrary("test")initTest()}
html页面js获取参数值
0624chenhong
html
1.js获取参数值js
function GetQueryString(name)
{
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
var r = windo
MongoDB 在多线程高并发下的问题
BigCat2013
mongodb DB 高并发 重复数据
最近项目用到 MongoDB , 主要是一些读取数据及改状态位的操作. 因为是结合了最近流行的 Storm进行大数据的分析处理,并将分析结果插入Vertica数据库,所以在多线程高并发的情境下, 会发现 Vertica 数据库中有部分重复的数据. 这到底是什么原因导致的呢?笔者开始也是一筹莫 展,重复去看 MongoDB 的 API , 终于有了新发现 :
com.mongodb.DB 这个类有
c++ 用类模版实现链表(c++语言程序设计第四版示例代码)
CrazyMizzz
数据结构 C++
#include<iostream>
#include<cassert>
using namespace std;
template<class T>
class Node
{
private:
Node<T> * next;
public:
T data;
最近情况
麦田的设计者
感慨 考试 生活
在五月黄梅天的岁月里,一年两次的软考又要开始了。到目前为止,我已经考了多达三次的软考,最后的结果就是通过了初级考试(程序员)。人啊,就是不满足,考了初级就希望考中级,于是,这学期我就报考了中级,明天就要考试。感觉机会不大,期待奇迹发生吧。这个学期忙于练车,写项目,反正最后是一团糟。后天还要考试科目二。这个星期真的是很艰难的一周,希望能快点度过。
linux系统中用pkill踢出在线登录用户
被触发
linux
由于linux服务器允许多用户登录,公司很多人知道密码,工作造成一定的障碍所以需要有时踢出指定的用户
1/#who 查出当前有那些终端登录(用 w 命令更详细)
# who
root pts/0 2010-10-28 09:36 (192
仿QQ聊天第二版
肆无忌惮_
qq
在第一版之上的改进内容:
第一版链接:
http://479001499.iteye.com/admin/blogs/2100893
用map存起来号码对应的聊天窗口对象,解决私聊的时候所有消息发到一个窗口的问题.
增加ViewInfo类,这个是信息预览的窗口,如果是自己的信息,则可以进行编辑.
信息修改后上传至服务器再告诉所有用户,自己的窗口
java读取配置文件
知了ing
1,java读取.properties配置文件
InputStream in;
try {
in = test.class.getClassLoader().getResourceAsStream("config/ipnetOracle.properties");//配置文件的路径
Properties p = new Properties()
__attribute__ 你知多少?
矮蛋蛋
C++ gcc
原文地址:
http://www.cnblogs.com/astwish/p/3460618.html
GNU C 的一大特色就是__attribute__ 机制。__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
__attribute__ 书写特征是:
jsoup使用笔记
alleni123
java 爬虫 JSoup
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.7.3</version>
</dependency>
2014/08/28
今天遇到这种形式,
JAVA中的集合 Collectio 和Map的简单使用及方法
百合不是茶
list map set
List ,set ,map的使用方法和区别
java容器类类库的用途是保存对象,并将其分为两个概念:
Collection集合:一个独立的序列,这些序列都服从一条或多条规则;List必须按顺序保存元素 ,set不能重复元素;Queue按照排队规则来确定对象产生的顺序(通常与他们被插入的
杀LINUX的JOB进程
bijian1013
linux unix
今天发现数据库一个JOB一直在执行,都执行了好几个小时还在执行,所以想办法给删除掉
系统环境:
ORACLE 10G
Linux操作系统
操作步骤如下:
第一步.查询出来那个job在运行,找个对应的SID字段
select * from dba_jobs_running--找到job对应的sid
&n
Spring AOP详解
bijian1013
java spring AOP
最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP来解决。一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容。例如,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智。
1.对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用
[Gson六]Gson类型适配器(TypeAdapter)
bit1129
Adapter
TypeAdapter的使用动机
Gson在序列化和反序列化时,默认情况下,是按照POJO类的字段属性名和JSON串键进行一一映射匹配,然后把JSON串的键对应的值转换成POJO相同字段对应的值,反之亦然,在这个过程中有一个JSON串Key对应的Value和对象之间如何转换(序列化/反序列化)的问题。
以Date为例,在序列化和反序列化时,Gson默认使用java.
【spark八十七】给定Driver Program, 如何判断哪些代码在Driver运行,哪些代码在Worker上执行
bit1129
driver
Driver Program是用户编写的提交给Spark集群执行的application,它包含两部分
作为驱动: Driver与Master、Worker协作完成application进程的启动、DAG划分、计算任务封装、计算任务分发到各个计算节点(Worker)、计算资源的分配等。
计算逻辑本身,当计算任务在Worker执行时,执行计算逻辑完成application的计算任务
nginx 经验总结
ronin47
nginx 总结
深感nginx的强大,只学了皮毛,把学下的记录。
获取Header 信息,一般是以$http_XX(XX是小写)
获取body,通过接口,再展开,根据K取V
获取uri,以$arg_XX
&n
轩辕互动-1.求三个整数中第二大的数2.整型数组的平衡点
bylijinnan
数组
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ExoWeb {
public static void main(String[] args) {
ExoWeb ew=new ExoWeb();
System.out.pri
Netty源码学习-Java-NIO-Reactor
bylijinnan
java 多线程 netty
Netty里面采用了NIO-based Reactor Pattern
了解这个模式对学习Netty非常有帮助
参考以下两篇文章:
http://jeewanthad.blogspot.com/2013/02/reactor-pattern-explained-part-1.html
http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf
AOP通俗理解
cngolon
spring AOP
1.我所知道的aop 初看aop,上来就是一大堆术语,而且还有个拉风的名字,面向切面编程,都说是OOP的一种有益补充等等。一下子让你不知所措,心想着:怪不得很多人都和 我说aop多难多难。当我看进去以后,我才发现:它就是一些java基础上的朴实无华的应用,包括ioc,包括许许多多这样的名词,都是万变不离其宗而 已。 2.为什么用aop&nb
cursor variable 实例
ctrain
variable
create or replace procedure proc_test01
as
type emp_row is record(
empno emp.empno%type,
ename emp.ename%type,
job emp.job%type,
mgr emp.mgr%type,
hiberdate emp.hiredate%type,
sal emp.sal%t
shell报bash: service: command not found解决方法
daizj
linux shell service jps
今天在执行一个脚本时,本来是想在脚本中启动hdfs和hive等程序,可以在执行到service hive-server start等启动服务的命令时会报错,最终解决方法记录一下:
脚本报错如下:
./olap_quick_intall.sh: line 57: service: command not found
./olap_quick_intall.sh: line 59
40个迹象表明你还是PHP菜鸟
dcj3sjt126com
设计模式 PHP 正则表达式 oop
你是PHP菜鸟,如果你:1. 不会利用如phpDoc 这样的工具来恰当地注释你的代码2. 对优秀的集成开发环境如Zend Studio 或Eclipse PDT 视而不见3. 从未用过任何形式的版本控制系统,如Subclipse4. 不采用某种编码与命名标准 ,以及通用约定,不能在项目开发周期里贯彻落实5. 不使用统一开发方式6. 不转换(或)也不验证某些输入或SQL查询串(译注:参考PHP相关函
Android逐帧动画的实现
dcj3sjt126com
android
一、代码实现:
private ImageView iv;
private AnimationDrawable ad;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout
java远程调用linux的命令或者脚本
eksliang
linux ganymed-ssh2
转载请出自出处:
http://eksliang.iteye.com/blog/2105862
Java通过SSH2协议执行远程Shell脚本(ganymed-ssh2-build210.jar)
使用步骤如下:
1.导包
官网下载:
http://www.ganymed.ethz.ch/ssh2/
ma
adb端口被占用问题
gqdy365
adb
最近重新安装的电脑,配置了新环境,老是出现:
adb server is out of date. killing...
ADB server didn't ACK
* failed to start daemon *
百度了一下,说是端口被占用,我开个eclipse,然后打开cmd,就提示这个,很烦人。
一个比较彻底的解决办法就是修改
ASP.NET使用FileUpload上传文件
hvt
.net C# hovertree asp.net webform
前台代码:
<asp:FileUpload ID="fuKeleyi" runat="server" />
<asp:Button ID="BtnUp" runat="server" onclick="BtnUp_Click" Text="上 传" />
代码之谜(四)- 浮点数(从惊讶到思考)
justjavac
浮点数 精度 代码之谜 IEEE
在『代码之谜』系列的前几篇文章中,很多次出现了浮点数。 浮点数在很多编程语言中被称为简单数据类型,其实,浮点数比起那些复杂数据类型(比如字符串)来说, 一点都不简单。
单单是说明 IEEE浮点数 就可以写一本书了,我将用几篇博文来简单的说说我所理解的浮点数,算是抛砖引玉吧。 一次面试
记得多年前我招聘 Java 程序员时的一次关于浮点数、二分法、编码的面试, 多年以后,他已经称为了一名很出色的
数据结构随记_1
lx.asymmetric
数据结构 笔记
第一章
1.数据结构包括数据的
逻辑结构、数据的物理/存储结构和数据的逻辑关系这三个方面的内容。 2.数据的存储结构可用四种基本的存储方法表示,它们分别是
顺序存储、链式存储 、索引存储 和 散列存储。 3.数据运算最常用的有五种,分别是
查找/检索、排序、插入、删除、修改。 4.算法主要有以下五个特性:
输入、输出、可行性、确定性和有穷性。 5.算法分析的
linux的会话和进程组
网络接口
linux
会话: 一个或多个进程组。起于用户登录,终止于用户退出。此期间所有进程都属于这个会话期。会话首进程:调用setsid创建会话的进程1.规定组长进程不能调用setsid,因为调用setsid后,调用进程会成为新的进程组的组长进程.如何保证? 先调用fork,然后终止父进程,此时由于子进程的进程组ID为父进程的进程组ID,而子进程的ID是重新分配的,所以保证子进程不会是进程组长,从而子进程可以调用se
二维数组 元素的连续求解
1140566087
二维数组 ACM
import java.util.HashMap;
public class Title {
public static void main(String[] args){
f();
}
// 二位数组的应用
//12、二维数组中,哪一行或哪一列的连续存放的0的个数最多,是几个0。注意,是“连续”。
public static void f(){
也谈什么时候Java比C++快
windshome
java C++
刚打开iteye就看到这个标题“Java什么时候比C++快”,觉得很好笑。
你要比,就比同等水平的基础上的相比,笨蛋写得C代码和C++代码,去和高手写的Java代码比效率,有什么意义呢?
我是写密码算法的,深刻知道算法C和C++实现和Java实现之间的效率差,甚至也比对过C代码和汇编代码的效率差,计算机是个死的东西,再怎么优化,Java也就是和C