在接入微信支付的时候,为了传递支付结果的值,就使用了EventBus,那时候只是简单了解了EventBus的使用,现在有时间就来深入研究一下EventBus的实现原理和源码。
EventBus的介绍
可能有部分同学还没有使用过EventBus,那我们首先来了解一下EventBus的作用。 EventBus是一款基于观察者模式的事件发布/订阅框架。简化了应用程序内各组件间、组件与后台线程间的通讯。优点是开销小,优化更优雅,以及将发送者和接受者解耦。如果Activity和Activity进行交互大家都知道,而Fragment和Fragment之间通讯写法五花八门、跨线程/跨界面我们就开始头疼了,那么我们来学习一下EventBus3。
EventBus的主要三要素:
Event:事件。可以是任意类型的对象。
Subscriber:事件订阅者。在EventBus3之前消息处理通过在onEvent、onEventMainTain、onEventBackgroundThread和onEventAsync,他们代表着四种线程模型。在EventBus3之后,事件处理的方法可以按照自己需要命名,但是需要添加一个注解@Subscribe和指定线程模型。
Publisher:事件发布者。可以在任意线程和位置发送事件。
EventBus的四种ThreadMode
POSTING(默认):如果使用事件处理函数指定了线程模型为POSTING,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为POSTING的事件处理函数中精良避免执行耗时操作,因为它会阻塞事件的传递。
MAIN:事件的处理会在UI线程中执行,事件处理的时间不能太长,可能出现ANR。
BACKFROUND:如果事件在UI线程发布的,那么该线程就会在新的线程中运行,如果事件本来就是子线程发布的,那么该事件处理函数直接在发布事件的线程中执行,在此事件处理函数中禁止进行UI更新操作。
ASYNC:无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行,同样,此事件处理函数中禁止进行UI更新操作。
EventBus的基本用法
Android Studio 配置Gradle:
compile 'org.greenrobot:eventbus:3.0.0'
定义一个事件类
public class EventBusEvent {
private String message;
public EventBusEvent (String message) {
this .message = message;
}
public String getMessage () {
return message;
}
public void setMessage (String message) {
this .message = message;
}
}
注册事件
EventBus.getDefault ().register (this)
发送事件
EventBus.getDefault ().post (message)
处理事件
@Subscribe (threadMode = ThreadMode.BACKGROUND)
public void XXXXXX (EventBusEvent message) {
}
取消事件订阅
EventBus.getDefault ().unregister (this)
EventBus源码分析
我们首先从开头注册开始分析EventBus.getDefault()其实就是一个单例。
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault () {
if (defaultInstance == null ) {
synchronized (EventBus.class) {
if (defaultInstance == null ) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
使用双重判断方式,防止并发。接着看看 new EventBus()。
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link #getDefault()}.
*/
public EventBus () {
this (DEFAULT_BUILDER);
}
所在的参DEFAULT_BUILDER,在EventBus.class的全局进行初始化:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
在看一下this调用了EventBus的另一构造方法。使用的是现在常用的Builder模式:
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this , Looper.getMainLooper(), 10 );
backgroundPoster = new BackgroundPoster(this );
asyncPoster = new AsyncPoster(this );
···
}
我们得到EventBus对象后,我们就可以调用register方法。
public void register (Object subscriber) {
Class subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this ) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
先是获取订阅者的class,接着交给subscriberMethodFinder.findSubscriberMethods()处理,返回的是List类型的集合,对于SubscriberMethod类中,主要是用于保存订阅方法的Method对象,线程模式、事件类型、优先级、是否是粘性事件等属性。下面我们直接看看这个方法:
List findSubscriberMethods(Class subscriberClass ) {
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null ) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation" );
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
首先从缓存中查找,如果缓存没有会根据ignoreGeneratedIndex选择如何查找订阅方法,ignoreGeneratedIndex默认false,可以通过EventBusBuilder来设置它的值。我们一般是通过getDefault()获取默认的EventBus对象,也就ignoreGeneratedIndex为false的情况,这时调用的是 findUsingInfo(),我们接着看看这个方法:
private List findUsingInfo(Class subscriberClass ) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null ) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null ) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array ) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
上面用到了FindState这个内部类是保存订阅者的信息,我们看看它的内部结构:
static class FindState {
final List subscriberMethods = new ArrayList<>();
final Map anyMethodByEventType = new HashMap<>();
final Map subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128 );
Class subscriberClass;
Class clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
void initForSubscriber(Class subscriberClass) {
this .subscriberClass = clazz = subscriberClass;
skipSuperClasses = false ;
subscriberInfo = null ;
}
···
}
可以看出,该内部类保存了订阅者及其订阅方法的信息,用Map一一对应来保存,接着利用initForSubscriber()进行初始化,在初始化的时候subscriberInfo设置为null,所以SubscriberMethodFinder的findUsingInfo()里面在初始化后会调用findUsingReflectionInSingleClass()方法。那么我们接着看看这个方法:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz .getDeclaredMethods ()
} catch (Throwable th) {
// Workaround for java.lang .NoClassDefFoundError , see https://github.com /greenrobot/EventBus/issues/149
methods = findState.clazz .getMethods ()
findState.skipSuperClasses = true
}
for (Method method : methods) {
int modifiers = method.getModifiers ()
if ((modifiers & Modifier.PUBLIC ) != 0 && (modifiers & MODIFIERS_IGNORE) == 0 ) {
Class[] parameterTypes = method.getParameterTypes ()
if (parameterTypes.length == 1 ) {
//获取该方法的@Subscribe注解
Subscribe subscribeAnnotation = method.getAnnotation (Subscribe.class )
if (subscribeAnnotation != null) {
Class eventType = parameterTypes[0 ]
if (findState.checkAdd (method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode ()
findState.subscriberMethods .add (new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority (), subscribeAnnotation.sticky ()))
}
}
} else if (strictMethodVerification && method.isAnnotationPresent (Subscribe.class )) {
String methodName = method.getDeclaringClass ().getName () + "." + method.getName ()
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length )
}
} else if (strictMethodVerification && method.isAnnotationPresent (Subscribe.class )) {
String methodName = method.getDeclaringClass ().getName () + "." + method.getName ()
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract" )
}
}
}
这里主要是使用了Java的反射和对注解的解析。首先是通过反射来获取订阅者中的所有方法,并根据方法的类型、参数和注解来找到订阅方法。找到订阅方法后将订阅方法相关信息保存到FindState中。
通过上述的方法,把SubscriberMethods不断逐层返回,返回到EventBus.register(),并开始遍历每一个订阅方法,并调用subscribe(subscriber, subscriberMethod)方法对所有订阅方法进行注册,那么我们接着去这方法的实现:
private void subscribe (Object subscriber, SubscriberMethod subscriberMethod) {
Class eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get (eventType);
if (subscriptions == null ) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0 ; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get (i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break ;
}
}
List> subscribedEvents = typesBySubscriber.get (subscriber);
if (subscribedEvents == null ) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, Object> entry : entries) {
Class candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get (eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
订阅的代码主要就是做两件事,第一件是将我们的订阅方法和订阅者封装到subscriptionsByEventType和typesBySubscriber中;subscriptionsByEventType是我们投递订阅事件的时候,就是根据我们的EventType找到我们的订阅事件,从而去分发事件,处理事件的。typesBySubscriber在调用unregister()的时候,根据订阅者找到EventType,再根据EventType找到订阅事件,从而对订阅者进行解绑。 第二件是如果是粘性事件的话,就立马执行。
分析完事件注册后,到事件发送,我们通过post方法来进行对事件的提交。接下来我们看一下post的方法:
/** Posts the given event to the event bus. */
public void post (Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true ;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset" );
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0 ), postingState);
}
} finally {
postingState.isPosting = false ;
postingState.isMainThread = false ;
}
}
}
代码很清晰,首先是获取一个PostingThreadState,我们看一下PostingThreadState:
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List eventQueue = new ArrayList();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
看出该PostingThreadState封装了当前线程的信息,以及订阅者、订阅事件等。那么我们看一下怎么获取PostingThreadState的:
private final ThreadLocal currentPostingThreadState = new ThreadLocal() {
@Override
protected PostingThreadState initialValue () {
return new PostingThreadState();
}
};
原来 currentPostingThreadState是一个ThreadLocal。我们回到post()方法里,继续是一个while循环,不断从队列中取出时间,调用postSingleEvent()。进去看一下:
private void postSingleEvent (Object event , PostingThreadState postingState) throws Error {
Class eventClass = event .getClass();
boolean subscriptionFound = false ;
if (eventInheritance) {
List> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0 ; h < countTypes; h++) {
Class clazz = eventTypes.get (h);
subscriptionFound |= postSingleEventForEventType(event , postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event , postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this , event ));
}
}
}
eventInheritance表示是否向上查找事件的父类,它的默认值为true,可以通过在EventBusBuilder中来进行配置。当eventInheritance为true时,则通过lookupAllEventTypes找到所有的父类事件并存在List中,然后通过postSingleEventForEventType方法对事件逐一处理,接下来看看postSingleEventForEventType方法:
private boolean postSingleEventForEventType (Object event , PostingThreadState postingState, Class eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized (this ) {
subscriptions = subscriptionsByEventType.get (eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event ;
postingState.subscription = subscription;
boolean aborted = false ;
try {
postToSubscription(subscription, event , postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null ;
postingState.subscription = null ;
postingState.canceled = false ;
}
if (aborted) {
break ;
}
}
return true ;
}
return false ;
}
同步取出该事件对应的subscriptions集合并遍历该集合,将事件的event和subscription传进postToSubscription()方法里。
private void postToSubscription (Subscription subscription, Object event , boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event );
break ;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event );
} else {
mainThreadPoster.enqueue(subscription, event );
}
break ;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event );
} else {
invokeSubscriber(subscription, event );
}
break ;
case ASYNC:
asyncPoster.enqueue(subscription, event );
break ;
default :
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
根据取出的订阅方法的线程模式分别处理。这里我们根据图来理顺一下。
在我们的对应界面的生命周期中,会在onDestory()中对订阅者取消注册。那么我们来看一下unregister()方法。
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister (Object subscriber) {
List> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null ) {
for (Class eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
typesBySubscriber我们在订阅者注册的过程中讲到过这个属性,他根据订阅者找到EventType,然后根据EventType和订阅者来得到订阅事件来对订阅者进行解绑。 看一下unsubscribeByEventType():
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType (Object subscriber, Class eventType) {
List subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null ) {
int size = subscriptions.size();
for (int i = 0 ; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false ;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
逻辑十分清晰,从从subscriptionsByEventType中获取相应的subscriptions 集合,并遍历所有的subscriptions,逐一移除。
总结: 整个EventBus是基于观察者模式而构建的,我们看一下EventBus的核心架构。 EventBus好处很明显,简化我们某些界面、线程间的通讯,实现的代码也很简单。但是由于EventBus的优点导致实现的代码可能分散得较为严重,会出现后期维护较为困难,代码的可读性大大下降。
参考:
【Bugly干货分享】老司机教你 “飙” EventBus 3
EventBus 3.0进阶:源码及其设计模式 完全解析
Android事件总线(二)EventBus3.0源码解析
你可能感兴趣的:(Android)
Cordova插件获取App权限
zl979899
Cordova android
1.github地址:https://github.com/NeoLSN/cordova-plugin-android-permissions/2.安装插件:cordovapluginaddcordova-plugin-android-permissions3.可获取的权限列表4.API方法:varpermissions=cordova.plugins.permissionscheckPermis
[Android]浏览器下载的apk文件无法识别无法安装问题
风浅月明
Android android
在Android电话机上,用浏览器下载apk进行版本更新,出现下载文件没被识别为apk,导致无法安装问题。原来的下载链接:https://mojsetup.obs.cn-southwest-2.myhuaweicloud.com/callphone-release-1.0.4.apk修改后的下载链接:https://mobile.mjsaas.com/callphone-release-1.0.4
android java 串口通信_Android串口通信
婧在机器学习中
android java 串口通信
1.解析SerialPortAPI串口通信例子首先分析一下例子中的类结构:通过类结构可知,最主要的还是在SerialPortJNI.java类,该类写了一些Native方法处理打开与关闭串口接收发送的SerialPort.Java代码如下:packagecom.dwin.navy.serialportapi;importjava.io.FileDescriptor;importandroid.ut
举世无双语音合成系统 VITS 发展历程(2024.3 PAVITS)
u013250861
# 语音
VITS经典项目:FaceBook开源,1000+语言,mms-meta/MMSwenet社区,onnx和android,wenet-e2e/wetts手写模型,huakunyang/SummerTTS30+语言,rhasspy/piperVITS流式推理:
Android系统源代码下载
nmgchfzhzhg
Android学习 Android资料
一、概述众所周知,Android移动操作系统是Google花费了很大的财力、物力及人力的前提下,推广到世界各个角落,以开放源代码的方式(当然也不是完全开放所有),使其在世界范围内迅速漫延开来,到目前虽说只有三四年的时间,火热程度,也不用我细说了,看看业界新闻就明白了。这里我们主要讨论Android系统源代码的下载,这里我们先了解两个工具:1、GitGit是面向Linux内核管理的一个工具,它是一种
Android 应用强制更新策略
linshijun33
Android之路 android 强制更新 自动更新 更新策略
Android应用强制更新的用途十分广泛,特别上刚上线的应用肯定会存在或多或少的bug,特别是涉及移动支付这一块的内容,如果出错了会造成比较大的损失,所以强制更新显得尤为重要。一般来说,强制更新的策略就是:应用启动时请求后台,后台发送应用最新版本的信息(包括应用版本号、名称、更新内容说明、下载包的服务器地址、是否强制更新的标志位)等等。下面我们就将根据以上思路来写实现代码。1.AndroidMan
android应用下载后无法识别apk文件
胡帅君
开发环境&工具 android Android ANDROID 无法安装 无法识别
先说说我遇到的情况.最近在做个项目,用手机自带的浏览器下载apk文件时,提示文件类别无法识别,文件可以下载,但是从浏览器下载后却不能安装,系统始终不能识别apk文件,在ucweb上可以下载并安装,用apk安装器也可以安装,就是手机自带浏览器无法安装;为了方便所有用户下载并正常安装软件只有找到具体原因了。apk文件肯定没问题,那问题就可能出在服务器的设置上,go了下,基本找到了解决问题的思路,这里做
Android 12.0 mtk浏览器Browser下载应用宝app显示bin文件的解决办法
安卓兼职framework应用工程师
android 12.0 Rom定制化系统讲解 android 12.0 Rom定制化高级进阶 android 应用宝 mtk浏览器下载应用宝 应用宝显示bin文件
1.前言在系统12.0的ROM定制化开发中,在开发mtk平台的时候,在系统默认浏览器Browser中发现在下载应用宝app的时候,结果显示的确实bin文件,所以就需要从Browser的下载流程中出发分析相关源码来实现具体的功能2.mtk默认浏览器Browser下载应用宝app显示bin文件的解决办法的核心类/vendor/mediatek/proprietary/packages/apps/Bro
android手机自带浏览器无法识别apk文件
afslc17394
在项目中遇到安卓自带浏览器下载成功,但无法打开已下载的APK文件自动安装的问题,特别是三星系列的手机普遍存在这种问题,google后发现也有朋友遇到类似的问题:http://bbs.csdn.net/topics/390262400引:写了一些android的应用放在网站上让人下载,在某些机型上,三星的android的4.0以上多款机型最普遍,用安卓自带浏览器下载程序,会提示"无法打开文件",导致
Android操作系统详细目录结构
Zengyangtech
Android android 文件管理器 system gmail 音乐 测试
Android操作系统相信大家已经非常熟悉了。该系统没有内置文件管理器,经过安装第三方文件管理器后可以对主系统内存和SD卡上的文件管理。主内存中的文件目录都是由主系统自动生成,那么又有多少人他们目录结构的具体分工呢?下面是本站整理的Android操作系统详细目录结构,供大家交流使用。首先是/system/app文件夹,这里面主要存放的是常规下载的应用程序,可以看到都是以APK格式结尾的文件。在这个
Android已死,备战三个月
2401_89224638
android
性,到公司之后就迫不及待的更新了3.0版本,嗯,还算顺利,只遇到了一个坑,一切都在happy的进行着。什么,你以为我想要写遇到的坑是什么,呵呵哒,我才不会告诉你,等等。。。手里的板砖先放下,一会说还不行吗,今天我们主要来聊聊如何在AndroidStudio3.0上分析内存泄漏,文章的内容很简单,但是自己摸索还是需要一些时间的,所以就在这里记录下来分享给大家。Android高级知识点一、Androi
Android应用ViewDragHelper详解及部分源码浅析
2401_89224638
android
@OverridepublicvoidcomputeScroll(){if(mTopViewDragHelper.continueSettling(true)){invalidate();}}publicvoidcloseDrawer(){if(mIsOpen){mTopViewDragHelper.smoothSlideViewTo(mDrawerView,mDrawerView.getLeft
Android开发欧酷天气
2401_89224638
android
3、关于网页请求写了sendOkHttpRequest方法对网页发送请求publicstaticvoidsendOkHttpRequest(Stringaddress,okhttp3.Callbackcallback){OkHttpClientclient=newOkHttpClient();Requestrequest=newRequest.Builder().url(address).buil
android游戏开发引擎!妈妈再也不用担心我的面试,赶快收藏备战金九银十!_android 三元数组
2401_89224638
android 面试 职场和发展
8.树的深度优先搜索(TreeDFS)9.TwoHeaps10.子集11.经过修改的二叉搜索12.前K个元素13.K路合并14.拓扑排序我们开始吧!1.滑动窗口滑动窗口模式是用于在给定数组或链表的特定窗口大小上执行所需的操作,比如寻找包含所有1的最长子数组。从第一个元素开始滑动窗口并逐个元素地向右滑,并根据你所求解的问题调整窗口的长度。在某些情况下窗口大小会保持恒定,在其它情况下窗口大小会增大或减
Android异步任务与多线程
2401_89224638
android
ANR的根本原因是:应用未在规定的时间内处理AMS指定的任务才会ANR。另外,人眼可以分辨的时间的160毫秒,超过这个时间就可以感到卡顿,所以要控制好这个时间。1.2.3、事件处理原则事件处理的原则:所有可能耗时的操作都放到其他线程去处理。Android中的main线程的事件处理不能太耗时,否则后续的事件无法在5秒内得到响应,就会弹出ANR对话框。那么哪些方法会在main线程执行呢?1)Activ
Android 串口通信
消失的旧时光-1943
android
引言在iot项目中,Android端总会有和硬件通信。通信这里:串口通信,蓝牙通信或者局域网通信。这里讲一下串口通信。什么是串口?“串口”(SerialPort)通常是指一种用于与外部设备进行串行通信的接口。如下是其中一种DB9的形式:更加简单的,还有这样的形式:只要有三条线,TX、RX和GND,或者A、B和GND,就可以去实现通讯。................................
如果后台的Long类型的数据返回是null,那么Android客户端的数据bean的kotlin的Long类型的字段接受到数据后是null空指针吗?
yzpyzp
android kotlin
如果后台的Long类型的数据返回是null,那么Android客户端的数据bean的kotlin的Long类型的字段接受到数据后是null空指针吗?DeepSeekR1思考35秒思考过程好的,用户的问题是关于在Android客户端使用Kotlin处理后台返回的Long类型数据为null时的情况。首先,我需要确认Kotlin中Long类型如何处理可空性。用户提到数据bean中的字段是Kotlin的L
Android Studio Notification通知(day4)
手里的鸡腿突然就不香了
Android Studio
AndroidStudioNotification通知常用的Notification代码片段常用的Notification小编这里给大家介绍常用的五种1、普通通知(这里添加了跳转功能PendingIntent)2、进度条通知3、自定义通知4、列表通知5、大视图通知代码片段有问题私信留言哦(●’◡’●)/***普通通知(涵盖大部分方法,页面跳转)*/publicvoidnotification(){
Android15音频进阶之定位混音线程丢帧问题(一百零八)
Android系统攻城狮
Android Audio工程师进阶系列 音视频 Android15 混音线程丢帧 定位
简介:CSDN博客专家、《Android系统多媒体进阶实战》一书作者新书发布:《Android系统多媒体进阶实战》优质专栏:Audio工程师进阶系列【原创干货持续更新中……】优质专栏:多媒体系统工程师系列【原创干货持续更新中……】优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门视频实战课
Android Loader 机制
村里小码农
Android应用开发
1Loader介绍Loader是Android3.0提供的新的异步api,Loader可用于进行异步操作,其实它内部采用的也是类似AsycnTask的异步任务机制,那么既然有了AsyncTask为什么还要有Loader呢?因为AysncTask有一个缺点,当Activity或者Fragment因为为屏幕旋转等因素被销毁重建后,如果处理不当就会造成任务和数据丢失,每次重建都要重新启动一个AsyncT
php过滤文字中的表情字符和mysql服务端对emoji的支持
angzhan5306
php 移动开发 数据库
1.过滤emoji表情的原因在我们的项目开发中,emoji表情是个麻烦的东西,即使我们可以能存储,也不一定能完美显示,因为它的更新速度很快:在iOS以外的平台上,例如PC或者android。如果你需要显示emoji,就得准备一大堆emoji图片并使用第三方前端类库才行。即便如此,还是可能因为emoji图片不够全而出现无法显示的情况在大多数业务场景下,emoji也不是非要不可的。我们可以适当地考虑干
Unity打包APK报错 using a newer Android Gradle plugin to use compileSdk = 35
[奋斗不止]
unity-android unity Unity apk 报错
Unity打包APK报错usinganewerAndroidGradleplugintousecompileSdk=35三个报错信息如下第一个WARNING:WerecommendusinganewerAndroidGradleplugintousecompileSdk=35ThisAndroidGradleplugin(7.1.2)wastesteduptocompileSdk=32Thiswa
Android仿人人客户端(v5(2)
2401_87555477
android harmonyos 华为
@OverrideprotectedvoidsetupView(){mTopNavbar=(TopNavbar)findViewById(R.id.rl_top_navbar);mWebView=(WebView)findViewById(R.id.wv_auth);mWebView.setVerticalScrollBarEnabled(false);mWebView.setHorizontal
android学习,android后端服务器的搭建
2401_84413531
程序员 android 学习 服务器
那么在市场紧缩以及大前端的趋势下,我们移动端程序员如何突破职业瓶颈,保持个人的核心竞争力呢?一、硬技能:专业的技术知识1.Java/Kotlin开发语言Java常用数据结构:ArrayList、Vector、CopyOnWriteArrayList、HaspMap、ConcurrentHashMap、HashTable等使用场景。JVM虚拟机包括Java内存管理,GC垃圾回收机制,类加载机制。推荐
Android从零开始搭建MVVM架构(1)————DataBinding
2401_87555477
android 架构 java
type=“android.view.View.OnClickListener”/>1.2、和别名alias的使用这里我们先定义同名的2个类User。放在不同包里。publicclassUser{privateStringname;privateintage;publicUser(Stringname,intage){this.name=name;this.age=age;}}之前我们的标签就可以
spacedesk 使用教程
念九_ysl
windows
spacedesk是一款免费的软件,它允许你将一个设备用作另一个设备的扩展屏幕,支持Windows、Android和iOS等多平台设备。下面是如何使用Spacedesk的基本教程:1.安装Spacedesk在主机(WindowsPC)上安装:访问Spacedesk官网:https://www.spacedesk.net。在主页上下载并安装SpacedeskDriver。安装后,启动Spacedes
Android 数据加载与分页业务
抹香鲸的弟弟蓝鲸
android kotlin android java
1,前言这篇文章写的是数据加载和分页。用到的库有'androidx.lifecycle:lifecycle-service:2.4.0''androidx.lifecycle:lifecycle-extensions:2.2.0''androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'io.reactivex.rxjava3:rxjava:3.0.4io
Android输入事件传递流程系统源码级解析
古苏
android
1.硬件层到Linux内核设备节点:触摸事件由内核驱动捕获,写入/dev/input/eventX。关键结构体:input_event(包含时间戳、类型、代码、值)。2.Native层处理(system_server进程)2.1EventHub路径:frameworks/native/services/inputflinger/EventHub.cpp职责:通过epoll监听设备节点,读取原始事件
Jenkins 构建 Unity打包APK
[奋斗不止]
jenkins Unity jenkins unity 批处理 编辑器
Jenkins构建Unity打包APK一、创建一个Pipeline任务在项目跟目录创建Pipeline脚本jenkins_scripts\Pipeline\android_master_pipeline脚本如下//AndroidMaster打包apkpipeline{agentanystages{stage('TestParameter'){steps{script{//shell脚本目录ANDR
带经纬度的水印相机_水印今日相机安卓版下载-水印相机拍照时间地点app下载v2.8.8.14-西西软件下载...
健康菌
带经纬度的水印相机
水印相机拍照时间地点app是一款可以定位的拍照软件,在这里拍摄的照片都可以自动添加,照片拍摄时间和地点还有经纬度,不管多久都可以知道照片的故事,需要的伙伴,赶紧来西西下载吧!水印相机拍照时间地点app简介:记录宝宝出生天数,工程管理、外出考勤、综合执法都在用!2018年iPhone获奖相机App,登录上线Android版本!300万人都在用的相机App!今天相机,最美的视频画面,最丰富的记录元素,
eclipse maven
IXHONG
eclipse
eclipse中使用maven插件的时候,运行run as maven build的时候报错
-Dmaven.multiModuleProjectDirectory system propery is not set. Check $M2_HOME environment variable and mvn script match.
可以设一个环境变量M2_HOME指
timer cancel方法的一个小实例
alleni123
多线程 timer
package com.lj.timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class MyTimer extends TimerTask
{
private int a;
private Timer timer;
pub
MySQL数据库在Linux下的安装
ducklsl
mysql
1.建好一个专门放置MySQL的目录
/mysql/db数据库目录
/mysql/data数据库数据文件目录
2.配置用户,添加专门的MySQL管理用户
>groupadd mysql ----添加用户组
>useradd -g mysql mysql ----在mysql用户组中添加一个mysql用户
3.配置,生成并安装MySQL
>cmake -D
spring------>>cvc-elt.1: Cannot find the declaration of element
Array_06
spring bean
将--------
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3
maven发布第三方jar的一些问题
cugfy
maven
maven中发布 第三方jar到nexus仓库使用的是 deploy:deploy-file命令
有许多参数,具体可查看
http://maven.apache.org/plugins/maven-deploy-plugin/deploy-file-mojo.html
以下是一个例子:
mvn deploy:deploy-file -DgroupId=xpp3
MYSQL下载及安装
357029540
mysql
好久没有去安装过MYSQL,今天自己在安装完MYSQL过后用navicat for mysql去厕测试链接的时候出现了10061的问题,因为的的MYSQL是最新版本为5.6.24,所以下载的文件夹里没有my.ini文件,所以在网上找了很多方法还是没有找到怎么解决问题,最后看到了一篇百度经验里有这个的介绍,按照其步骤也完成了安装,在这里给大家分享下这个链接的地址
ios TableView cell的布局
张亚雄
tableview
cell.imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:[indexPath row]]];
CGSize itemSize = CGSizeMake(60, 50);
&nbs
Java编码转义
adminjun
java 编码转义
import java.io.UnsupportedEncodingException;
/**
* 转换字符串的编码
*/
public class ChangeCharset {
/** 7位ASCII字符,也叫作ISO646-US、Unicode字符集的基本拉丁块 */
public static final Strin
Tomcat 配置和spring
aijuans
spring
简介
Tomcat启动时,先找系统变量CATALINA_BASE,如果没有,则找CATALINA_HOME。然后找这个变量所指的目录下的conf文件夹,从中读取配置文件。最重要的配置文件:server.xml 。要配置tomcat,基本上了解server.xml,context.xml和web.xml。
Server.xml -- tomcat主
Java打印当前目录下的所有子目录和文件
ayaoxinchao
递归 File
其实这个没啥技术含量,大湿们不要操笑哦,只是做一个简单的记录,简单用了一下递归算法。
import java.io.File;
/**
* @author Perlin
* @date 2014-6-30
*/
public class PrintDirectory {
public static void printDirectory(File f
linux安装mysql出现libs报冲突解决
BigBird2012
linux
linux安装mysql出现libs报冲突解决
安装mysql出现
file /usr/share/mysql/ukrainian/errmsg.sys from install of MySQL-server-5.5.33-1.linux2.6.i386 conflicts with file from package mysql-libs-5.1.61-4.el6.i686
jedis连接池使用实例
bijian1013
redis jedis连接池 jedis
实例代码:
package com.bijian.study;
import java.util.ArrayList;
import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoo
关于朋友
bingyingao
朋友 兴趣爱好 维持
成为朋友的必要条件:
志相同,道不合,可以成为朋友。譬如马云、周星驰一个是商人,一个是影星,可谓道不同,但都很有梦想,都要在各自领域里做到最好,当他们遇到一起,互相欣赏,可以畅谈两个小时。
志不同,道相合,也可以成为朋友。譬如有时候看到两个一个成绩很好每次考试争做第一,一个成绩很差的同学是好朋友。他们志向不相同,但他
【Spark七十九】Spark RDD API一
bit1129
spark
aggregate
package spark.examples.rddapi
import org.apache.spark.{SparkConf, SparkContext}
//测试RDD的aggregate方法
object AggregateTest {
def main(args: Array[String]) {
val conf = new Spar
ktap 0.1 released
bookjovi
kernel tracing
Dear,
I'm pleased to announce that ktap release v0.1, this is the first official
release of ktap project, it is expected that this release is not fully
functional or very stable and we welcome bu
能保存Properties文件注释的Properties工具类
BrokenDreams
properties
今天遇到一个小需求:由于java.util.Properties读取属性文件时会忽略注释,当写回去的时候,注释都没了。恰好一个项目中的配置文件会在部署后被某个Java程序修改一下,但修改了之后注释全没了,可能会给以后的参数调整带来困难。所以要解决这个问题。
&nb
读《研磨设计模式》-代码笔记-外观模式-Facade
bylijinnan
java 设计模式
声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
/*
* 百度百科的定义:
* Facade(外观)模式为子系统中的各类(或结构与方法)提供一个简明一致的界面,
* 隐藏子系统的复杂性,使子系统更加容易使用。他是为子系统中的一组接口所提供的一个一致的界面
*
* 可简单地
After Effects教程收集
cherishLC
After Effects
1、中文入门
http://study.163.com/course/courseMain.htm?courseId=730009
2、videocopilot英文入门教程(中文字幕)
http://www.youku.com/playlist_show/id_17893193.html
英文原址:
http://www.videocopilot.net/basic/
素
Linux Apache 安装过程
crabdave
apache
Linux Apache 安装过程
下载新版本:
apr-1.4.2.tar.gz(下载网站:http://apr.apache.org/download.cgi)
apr-util-1.3.9.tar.gz(下载网站:http://apr.apache.org/download.cgi)
httpd-2.2.15.tar.gz(下载网站:http://httpd.apac
Shell学习 之 变量赋值和引用
daizj
shell 变量引用 赋值
本文转自:http://www.cnblogs.com/papam/articles/1548679.html
Shell编程中,使用变量无需事先声明,同时变量名的命名须遵循如下规则:
首个字符必须为字母(a-z,A-Z)
中间不能有空格,可以使用下划线(_)
不能使用标点符号
不能使用bash里的关键字(可用help命令查看保留关键字)
需要给变量赋值时,可以这么写:
Java SE 第一讲(Java SE入门、JDK的下载与安装、第一个Java程序、Java程序的编译与执行)
dcj3sjt126com
java jdk
Java SE 第一讲:
Java SE:Java Standard Edition
Java ME: Java Mobile Edition
Java EE:Java Enterprise Edition
Java是由Sun公司推出的(今年初被Oracle公司收购)。
收购价格:74亿美金
J2SE、J2ME、J2EE
JDK:Java Development
YII给用户登录加上验证码
dcj3sjt126com
yii
1、在SiteController中添加如下代码:
/**
* Declares class-based actions.
*/
public function actions() {
return array(
// captcha action renders the CAPTCHA image displ
Lucene使用说明
dyy_gusi
Lucene search 分词器
Lucene使用说明
1、lucene简介
1.1、什么是lucene
Lucene是一个全文搜索框架,而不是应用产品。因此它并不像baidu或者googleDesktop那种拿来就能用,它只是提供了一种工具让你能实现这些产品和功能。
1.2、lucene能做什么
要回答这个问题,先要了解lucene的本质。实际
学习编程并不难,做到以下几点即可!
gcq511120594
数据结构 编程 算法
不论你是想自己设计游戏,还是开发iPhone或安卓手机上的应用,还是仅仅为了娱乐,学习编程语言都是一条必经之路。编程语言种类繁多,用途各 异,然而一旦掌握其中之一,其他的也就迎刃而解。作为初学者,你可能要先从Java或HTML开始学,一旦掌握了一门编程语言,你就发挥无穷的想象,开发 各种神奇的软件啦。
1、确定目标
学习编程语言既充满乐趣,又充满挑战。有些花费多年时间学习一门编程语言的大学生到
Java面试十问之三:Java与C++内存回收机制的差别
HNUlanwei
java C++ finalize() 堆栈 内存回收
大家知道, Java 除了那 8 种基本类型以外,其他都是对象类型(又称为引用类型)的数据。 JVM 会把程序创建的对象存放在堆空间中,那什么又是堆空间呢?其实,堆( Heap)是一个运行时的数据存储区,从它可以分配大小各异的空间。一般,运行时的数据存储区有堆( Heap)和堆栈( Stack),所以要先看它们里面可以分配哪些类型的对象实体,然后才知道如何均衡使用这两种存储区。一般来说,栈中存放的
第二章 Nginx+Lua开发入门
jinnianshilongnian
nginx lua
Nginx入门
本文目的是学习Nginx+Lua开发,对于Nginx基本知识可以参考如下文章:
nginx启动、关闭、重启
http://www.cnblogs.com/derekchen/archive/2011/02/17/1957209.html
agentzh 的 Nginx 教程
http://openresty.org/download/agentzh-nginx-tutor
MongoDB windows安装 基本命令
liyonghui160com
windows安装
安装目录:
D:\MongoDB\
新建目录
D:\MongoDB\data\db
4.启动进城:
cd D:\MongoDB\bin
mongod -dbpath D:\MongoDB\data\db
&n
Linux下通过源码编译安装程序
pda158
linux
一、程序的组成部分 Linux下程序大都是由以下几部分组成: 二进制文件:也就是可以运行的程序文件 库文件:就是通常我们见到的lib目录下的文件 配置文件:这个不必多说,都知道 帮助文档:通常是我们在linux下用man命令查看的命令的文档
二、linux下程序的存放目录 linux程序的存放目录大致有三个地方: /etc, /b
WEB开发编程的职业生涯4个阶段
shw3588
编程 Web 工作 生活
觉得自己什么都会
2007年从学校毕业,凭借自己原创的ASP毕业设计,以为自己很厉害似的,信心满满去东莞找工作,找面试成功率确实很高,只是工资不高,但依旧无法磨灭那过分的自信,那时候什么考勤系统、什么OA系统、什么ERP,什么都觉得有信心,这样的生涯大概持续了约一年。
根本不是自己想的那样
2008年开始接触很多工作相关的东西,发现太多东西自己根本不会,都需要去学,不管是asp还是js,
遭遇jsonp同域下变作post请求的坑
vb2005xu
jsonp 同域post
今天迁移一个站点时遇到一个坑爹问题,同一个jsonp接口在跨域时都能调用成功,但是在同域下调用虽然成功,但是数据却有问题. 此处贴出我的后端代码片段
$mi_id = htmlspecialchars(trim($_GET['mi_id ']));
$mi_cv = htmlspecialchars(trim($_GET['mi_cv ']));
贴出我前端代码片段:
$.aj