Android 5.1 close data flow 数据关闭流程

Database Inspector 与 Room 深度集成:5个提升DAO调试效率的实战技巧 本文详细介绍了如何利用Android Studio的Database Inspector工具与Room框架深度集成,提升DAO调试效率的5个实战技巧。包括从@Query注解直接运行SQL、结合LiveData/Flow观察实时更新、利用离线模式分析崩溃现场、保持数据库连接调试频繁开关场景以及导出数据用于单元测试,帮助开发者更高效地调试数据库操作。 阅读详情
惯例流程图先
dataconnection disconnect.jpg


流程是入口是Settings->Data Usage 的开关,后面会看一下从下拉快速设置的入口进入。

android/packages/apps/Settings/src/com/android/settings/DataUsageSummary.java
关闭数据开关时,会弹出确认窗口
ConfirmDataDisableFragment.show(DataUsageSummary.this);

private View.OnClickListener mDataEnabledListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mBinding) return;
 
final boolean dataEnabled = !mDataEnabled.isChecked();
final String currentTab = mCurrentTab;
if (TAB_MOBILE.equals(currentTab) || currentTab.startsWith(TAB_SIM)) {
if (dataEnabled) {
setMobileDataEnabled(true);
if (mPolicyEditor.getPolicyWarningBytes(mTemplate) == WARNING_DISABLED) {
mPolicyEditor.setPolicyWarningBytes(mTemplate, 2 * GB_IN_BYTES);
}
} else {
// disabling data; show confirmation dialog which eventually
// calls setMobileDataEnabled() once user confirms.
ConfirmDataDisableFragment.show(DataUsageSummary.this);
}
}
 
updatePolicy(false);
}
};

点击确认后即开始关闭数据的后续流程
target.setMobileDataEnabled(false);

/**
* Dialog to request user confirmation before disabling data.
*/
public static class ConfirmDataDisableFragment extends DialogFragment {
public static void show(DataUsageSummary parent) {
if (!parent.isAdded()) return;
 
final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
}
 
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
 
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(R.string.data_usage_disable_mobile);
 
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
if (target != null) {
// TODO: extend to modify policy enabled flag.
target.setMobileDataEnabled(false);
}
}
});
builder.setNegativeButton(android.R.string.cancel, null);
 
return builder.create();
}
}

获得当前TAB对应的SIM卡subId
mTelephonyManager.setDataEnabled(subId[0], enabled);
 
private void setMobileDataEnabled(boolean enabled) {
if (LOGD) Log.d(TAG, "setMobileDataEnabled()");
// How about exposing sub based API like TelephonyManager.setDataEnabled(int subId);
if (mCurrentTab.startsWith(TAB_SIM)) {
int phoneId = multiSimGetCurrentSub();
 
// as per phone, set the individual flag
android.provider.Settings.Global.putInt(getActivity().getContentResolver(),
android.provider.Settings.Global.MOBILE_DATA + phoneId, enabled ? 1 : 0);
 
int[] subId = SubscriptionManager.getSubId(phoneId);
mTelephonyManager.setDataEnabled(subId[0], enabled);
} else {
mTelephonyManager.setDataEnabled(enabled);
mMobileDataEnabled = enabled;
}
updatePolicy(false);
}

android/frameworks/base/telephony/java/android/telephony/TelephonyManager.java
getITelephony().setDataEnabled(subId, enable);
/** @hide */
@SystemApi
public void setDataEnabled(int subId, boolean enable) {
try {
AppOpsManager appOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
if (enable) {
if (appOps.noteOp(AppOpsManager.OP_DATA_CONNECT_CHANGE) != AppOpsManager.MODE_ALLOWED) {
Log.w(TAG, "Permission denied by user.");
return;
}
}
Log.d(TAG, "setDataEnabled: enabled=" + enable);
getITelephony().setDataEnabled(subId, enable);
} catch (RemoteException e) {
Log.e(TAG, "Error calling setDataEnabled", e);
}
}

android/packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java
phone.setDataEnabled(enable);
 
/**
* Set mobile data enabled
* Used by the user through settings etc to turn on/off mobile data
*
* @param enable {@code true} turn turn data on, else {@code false}
*/
@Override
public void setDataEnabled(int subId, boolean enable) {
enforceModifyPermission();
int phoneId = mSubscriptionController.getPhoneId(subId);
log("getDataEnabled: subId=" + subId + " phoneId=" + phoneId);
Phone phone = PhoneFactory.getPhone(phoneId);
if (phone != null) {
log("setDataEnabled: subId=" + subId + " enable=" + enable);
phone.setDataEnabled(enable);
} else {
loge("setDataEnabled: no phone for subId=" + subId);
}
}

android/frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java
mDcTracker.setDataEnabled(enable);
@Override
public void setDataEnabled(boolean enable) {
mDcTracker.setDataEnabled(enable);
}
  android/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java    

/**
* Modify {@link android.provider.Settings.Global#MOBILE_DATA} value.
*/
public void setDataEnabled(boolean enable) {
Message msg = obtainMessage(DctConstants.CMD_SET_USER_DATA_ENABLE);
msg.arg1 = enable ? 1 : 0;
sendMessage(msg);
}
onSetUserDataEnabled(enabled);
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
log("DISCONNECTED_CONNECTED: msg=" + msg);
DcAsyncChannel dcac = (DcAsyncChannel) msg.obj;
mDataConnectionAcHashMap.remove(dcac.getDataConnectionIdSync());
dcac.disconnected();
break;
}
...
case DctConstants.CMD_SET_USER_DATA_ENABLE: {
final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
onSetUserDataEnabled(enabled);
break;
}
...
onCleanUpAllConnections
protected void onSetUserDataEnabled(boolean enabled) {
synchronized (mDataEnabledLock) {
if (mUserDataEnabled != enabled) {
mUserDataEnabled = enabled;
Settings.Global.putInt(mPhone.getContext().getContentResolver(),
Settings.Global.MOBILE_DATA + mPhone.getPhoneId(), enabled ? 1 : 0);
if (getDataOnRoamingEnabled() == false &&
mPhone.getServiceState().getRoaming() == true) {
if (enabled) {
notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
} else {
notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
}
}
 
if (enabled) {
onTrySetupData(Phone.REASON_DATA_ENABLED);
} else {
onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);//1
}
}
}
}

android/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
这个类比较复杂,我们只看本次重点。
cleanUpAllConnections(true, cause);
/**
* Cleanup all connections.
*
* TODO: Cleanup only a specified connection passed as a parameter.
* Also, make sure when you clean up a conn, if it is last apply
* logic as though it is cleanupAllConnections
*
* @param cause for the clean up.
*/
 
@Override
protected void onCleanUpAllConnections(String cause) {
cleanUpAllConnections(true, cause);
}

下面这里会循环多次。for(ApnContext apnContext : mApnContexts.values()) 
/**
* If tearDown is true, this only tears down a CONNECTED session. Presently,
* there is no mechanism for abandoning an CONNECTING session,
* but would likely involve cancelling pending async requests or
* setting a flag or new state to ignore them when they came in
* @param tearDown true if the underlying DataConnection should be
* disconnected.
* @param reason reason for the clean up.
* @return boolean - true if we did cleanup any connections, false if they
* were already all disconnected.
*/
protected boolean cleanUpAllConnections(boolean tearDown, String reason) {
if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
boolean didDisconnect = false;
boolean specificdisable = false;
 
if (!TextUtils.isEmpty(reason)) {
specificdisable = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED);
}
 
for (ApnContext apnContext : mApnContexts.values()) {
if (apnContext.isDisconnected() == false) didDisconnect = true;
if (specificdisable) {
if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
if (DBG) log("ApnConextType: " + apnContext.getApnType());
apnContext.setReason(reason);
cleanUpConnection(tearDown, apnContext);//2
}
} else {
// TODO - only do cleanup if not disconnected
apnContext.setReason(reason);
cleanUpConnection(tearDown, apnContext);
}
}
 
stopNetStatPoll();
stopDataStallAlarm();
 
// TODO: Do we need mRequestedApnType?
mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
 
log("cleanUpConnection: mDisconnectPendingCount = " + mDisconnectPendingCount);
if (tearDown && mDisconnectPendingCount == 0) {
notifyDataDisconnectComplete();
notifyAllDataDisconnected();
}
 
return didDisconnect;
}

每一次进来后if(apnContext.isDisconnected()) 的判断结果是不一样的,apnContext={mApnType=default mState=CONNECTED mApnType为其他时,其mState一般是IDLE的
protected void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
 
if (apnContext == null) {
if (DBG) log("cleanUpConnection: apn context is null");
return;
}
 
DcAsyncChannel dcac = apnContext.getDcAc();
if (DBG) {
log("cleanUpConnection: E tearDown=" + tearDown + " reason=" + apnContext.getReason() +
" apnContext=" + apnContext);
}
if (tearDown) {//3
if (apnContext.isDisconnected()) {
// The request is tearDown and but ApnContext is not connected.
// If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
apnContext.setState(DctConstants.State.IDLE);
if (!apnContext.isReady()) {
if (dcac != null) {
dcac.tearDown(apnContext, "", null);
}
apnContext.setDataConnectionAc(null);
}
} else {
// Connection is still there. Try to clean up.
if (dcac != null) {//4
if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
boolean disconnectAll = false;
if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())) {
// CAF_MSIM is this below condition required.
// if (PhoneConstants.APN_TYPE_DUN.equals(PhoneConstants.APN_TYPE_DEFAULT)) {
if (teardownForDun()) {
if (DBG) log("tearing down dedicated DUN connection");
// we need to tear it down - we brought it up just for dun and
// other people are camped on it and now dun is done. We need
// to stop using it and let the normal apn list get used to find
// connections for the remaining desired connections
disconnectAll = true;
}
}
if (DBG) {
log("cleanUpConnection: tearing down" + (disconnectAll ? " all" :""));
}
Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, apnContext);//4
if (disconnectAll) {
apnContext.getDcAc().tearDownAll(apnContext.getReason(), msg);
} else {
apnContext.getDcAc()
.tearDown(apnContext, apnContext.getReason(), msg);
}
apnContext.setState(DctConstants.State.DISCONNECTING);
mDisconnectPendingCount++;
}
} else {
// apn is connected but no reference to dcac.
// Should not be happen, but reset the state in case.
apnContext.setState(DctConstants.State.IDLE);
mPhone.notifyDataConnection(apnContext.getReason(),
apnContext.getApnType());
}
}
} else {
// force clean up the data connection.
if (dcac != null) dcac.reqReset();
apnContext.setState(DctConstants.State.IDLE);
mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
apnContext.setDataConnectionAc(null);
}
 
if (mOmhApt != null) {
mOmhApt.clearActiveApnProfile();
}
 
// Make sure reconnection alarm is cleaned up if there is no ApnContext
// associated to the connection.
if (dcac != null) {
cancelReconnectAlarm(apnContext);
}
 
setupDataForSinglePdnArbitration(apnContext.getReason());
 
if (DBG) {
log("cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason() +
" apnContext=" + apnContext + " dcac=" + apnContext.getDcAc());
}
}

android/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
sendMessage
/**
* Tear down the connection through the apn on the network.
*
* @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
* With AsyncResult.userObj set to the original msg.obj.
*/
public void tearDown(ApnContext apnContext, String reason, Message onCompletedMsg) {
if (DBG) {
log("tearDown: apnContext=" + apnContext
+ " reason=" + reason + " onCompletedMsg=" + onCompletedMsg);
}
sendMessage(DataConnection.EVENT_DISCONNECT,//7
new DisconnectParams(apnContext, reason, onCompletedMsg));
}
android/frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
这个也挺复杂的 维护了一个状态机 StateMachine
tearDownData(dp);
/**
* The state machine is connected, expecting an EVENT_DISCONNECT.
*/
private class DcActiveState extends State {
...
 
@Override
public boolean processMessage(Message msg) {
...
case EVENT_DISCONNECT: {
DisconnectParams dp = (DisconnectParams) msg.obj;
if (DBG) {
log("DcActiveState: EVENT_DISCONNECT dp=" + dp
+ " dc=" + DataConnection.this);
}
if (mApnContexts.contains(dp.mApnContext)) {
if (DBG) {
log("DcActiveState msg.what=EVENT_DISCONNECT RefCount="
+ mApnContexts.size());
}
 
if (mApnContexts.size() == 1) {
mApnContexts.clear();
mDisconnectParams = dp;
mConnectionParams = null;
dp.mTag = mTag;
tearDownData(dp);
transitionTo(mDisconnectingState);
} else {
mApnContexts.remove(dp.mApnContext);
notifyDisconnectCompleted(dp, false);
}
} else {
log("DcActiveState ERROR no such apnContext=" + dp.mApnContext
+ " in this dc=" + DataConnection.this);
notifyDisconnectCompleted(dp, false);
}
retVal = HANDLED;
break;
}

mPhone.mCi.deactivateDataCall
/**
* TearDown the data connection when the deactivation is complete a Message with
* msg.what == EVENT_DEACTIVATE_DONE and msg.obj == AsyncResult with AsyncResult.obj
* containing the parameter o.
*
* @param o is the object returned in the AsyncResult.obj.
*/
private void tearDownData(Object o) {
int discReason = RILConstants.DEACTIVATE_REASON_NONE;
if ((o != null) && (o instanceof DisconnectParams)) {
DisconnectParams dp = (DisconnectParams)o;
 
if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)) {
discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
} else if (TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
}
}
if (mPhone.mCi.getRadioState().isOn()
|| (mPhone.getServiceState().getRilDataRadioTechnology()
== ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN )) {
if (DBG) log("tearDownData radio is on, call deactivateDataCall");
mPhone.mCi.deactivateDataCall(mCid, discReason,
obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
} else {
if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
AsyncResult ar = new AsyncResult(o, null, null);
sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, ar));
}
}
调用到RIL.java中的方法
android/frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java



--------------
Android数据库加密实战:SQLCipher集成与数据迁移指南 在移动应用开发中,数据安全是保障用户隐私和商业机密的核心要求。SQLite作为Android平台默认的本地数据库,其存储的明文数据面临被直接读取的风险。通过AES-256等加密算法对数据库文件进行透明加密,可以在不改变数据访问逻辑的前提下,有效防止数据泄露。这种技术对于金融、社交、企业办公等涉及敏感信息的应用场景具有重要价值。本文以SQLCipher为例,深入解析其页级加密与完整性校验原理,并提供从项目集成、密码安全管理到数据迁移的完整工程实践方案,帮助开发者构建更安全的本地存储体系。 阅读详情

相关推荐

Unity串口通信实战:5分钟接入硬件数据驱动虚拟世界

串口通信(Serial Communication)是计算机与外部硬件设备进行数据交换的基础技术之一,其原理是通过串行接口按位顺序传输数据,实现稳定可靠的点对点通信。在物联网、工业控制和嵌入式系统领域,串口通信因其简单、成熟和低成本而成为连接传感器、控制器与上位机软件的首选方案。对于Unity开发者而言,掌握硬件通信能力能将虚拟的3D世界与现实物理设备联动,极大拓展了应用场景,从游戏外设控制、VR/AR交互到数字孪生和工业仿真均可实现。本文以备受好评的**SerialPortUtilityPro**插件为核

weixin_30245867的博客 332

【Android】之 Flow 的使用和浅析

Google 推荐在 MVVM 架构中使用 Kotlin Flow,可见其发展前景是相当好的。Kotlin Flow 可以用于替换 Rxjava,也可以用于替换 LiveData,功能十分强大,它是 Kotlin 协程库提供的一部分功能,因此,如果我们项目中已经引用了 Kotlin 协程,则不需要额外引入 Flow 相关的依赖。在协程中,挂起函数最多仅能返回一个值,而数据流 Flow 可按顺序发出多个值,例如,我们可以通过数据流从数据库中实时接收更新。数据流使用挂起函数通过异步方式生成和使用值,也就是说,数

yang553566463的博客 1万+

安卓USB转串口开发实战:从原理到应用,打造移动串口调试工具

串口通信是嵌入式系统、物联网设备和工业控制中基础且广泛使用的数据交换方式,它通过异步串行传输实现设备间的可靠通信。其核心原理基于特定的波特率、数据位和停止位配置,确保数据帧的准确收发。在技术价值上,串口通信协议简单、硬件成本低、抗干扰能力强,是连接微控制器、传感器和上位机的经典桥梁。随着移动设备的普及,将串口通信能力扩展到安卓平台,利用其USB Host功能,可以构建出灵活、便携的调试与数据采集解决方案。这尤其适用于现场调试、物联网网关和智能硬件开发等场景。通过集成CH340、FT232等常见USB转串口芯

weixin_30696427的博客 290

两种关闭数据流的方式以及注意事项

     虽然在java程序在结束时自动关闭所有打开的流,但是当使用完流后,显示的关闭所有打开的流仍然是一个好习惯。一个被打开的流有可能会用尽系统的资源,这取决于平台和实现。如果没有将流关闭,另一个程序试图打开另一个流时,可能会得到不需要的资源。   从JDK1.7后,有两种关闭数据流的方法,分别如下:   1) 使用close()显式地关闭流,部分代码如下: File...

weixin_30680385的博客 1042

Android点餐系统实战:离线下单、扫码核销与厨房实时同步

Android点餐系统是移动应用开发中典型的高可靠性业务场景,涉及状态管理、本地缓存、弱网容错与多端协同等核心问题。其技术本质是基于MVVM与Clean Architecture的分层设计,依托Room实现数据持久化与离线优先策略,结合WorkManager保障任务可靠执行,并通过CameraX+ML Kit或ZXing支持鲁棒扫码能力。在餐饮SaaS、智慧餐厅等垂直领域,该方案可直接落地为商用级App,兼顾性能、可维护性与OEM兼容性。本文聚焦真实后厨环境下的Android原生工程实践,覆盖扫码下单、订单

weixin_33860528的博客 228

笔记——Oracle19c

Oracle阅读笔记

weixin_63819458的博客 1348

Android-Flow 使用和源码解析

数据流以协程为基础构建,可提供多个值。从概念上来讲,数据流是可通过异步方式进行计算处理的一组数据序列。所发出值的类型必须相同。例如,Flow 是发出整数值的数据流。在协程中,与仅返回单个值的挂起函数相反,数据流可按顺序发出多个值。数据流包含三个实体:创建数据流有两种常用方式: 第一就是试用数据流构建函数, 第二就是利用扩展方法将原有类型转换成Flow这里比较常用的函数是 和 其实 是简化版的 函数,内部帮我们调用了 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JE

ShiShouFeng 1879

Docker篇——Docker常用命令大全!史上最全面的Docker命令,涵盖常规命令、镜像基本命令、容器基本命令以及常用其他命令,一篇文章速通!

本篇文章介绍的关于DOcker的命令非常全面,Docker常见的命令介绍完毕,涉及到的命令比较繁杂,希望大家可以收藏并坚持学习。后续还会持续更新相关技术点,敬请期待

我热爱学习,也乐于分享。无论是科技前沿的洞见,还是生活中的小技巧,我都会在这里与你分享。我相信,知识就是力量,而分享则能让这份力量得到更好的传递。 5746

浅谈Java如何正确关闭流

1.在try中关流,而没在finally中关流 try { OutputStream out = new FileOutputStream(""); // ...操作流代码 out.close(); } catch (Exception e) { e.printStackTrace(); } 正确写法: OutputStream out = null; try { out = new FileOutputStream(""); // ...操作流代码 } catch (Exception e)

学习 记录 总结 分享 9732

手把手教你用Deep Single-Image Portrait Relighting实现人像光影重塑(附GitHub代码)

本文提供了一份详细的实战指南,手把手教你利用Deep Single-Image Portrait Relighting技术实现人像光影重塑。文章从环境搭建、核心原理拆解出发,逐步指导构建简易重光照流程,并探讨了使用预训练模型进行效果优化的进阶方法,最后附上避坑指南与常见问题排查,帮助开发者快速上手这一AI图像处理技术。

weixin_29065015的博客 316

实战演练 | ABAQUS显式动力学分析在金属切削仿真中的应用

本文详细介绍了如何利用ABAQUS显式动力学分析进行金属切削过程的二维仿真。通过从零开始的完整流程,包括几何建模、材料本构与失效定义、网格划分、接触设置、边界条件施加,到结果后处理与切削力提取,为工程师和研究人员提供了实用的仿真方法,以优化刀具设计和加工工艺。

purple的博客 930

IO流之节点流(字符流)和数据流关闭

输sql教程入流----Reader 1 public class Reader { 2 public static void main(String[] args) throws Exception { 3 //获取字符输入流的地址 4 FileReader reader = new FileReader("D:\\reader.txt"); 5 //定义读取到的长度 6 int len = 0; 7

chinaherolts2008的博客 327

Android Room数据库优化与实战技巧

SQLite作为Android平台默认的关系型数据库引擎,其原生API存在类型不安全、样板代码多等痛点。Room作为Jetpack组件提供的ORM框架,通过编译时生成类型安全的SQL代码,显著提升了开发效率和可靠性。其核心原理是利用注解处理器在编译阶段验证SQL语法,并自动处理对象关系映射。在工程实践中,Room完美适配Kotlin协程和Flow,支持分页库Paging3集成,特别适合处理复杂数据持久化场景。以电商订单系统为例,通过合理设计实体类主键(如采用UUID替代自增ID)和关联表结构,配合TypeC

weixin_34345753的博客 422

YOLOv11的嗅觉传感器数据关联-(特殊场景下的气味-视觉联合分析)

摘要 本文探讨了YOLOv11与嗅觉传感器融合的多模态感知技术。通过分析YOLOv11的架构特性与嗅觉传感器的数据特点,提出了三种融合策略:数据级、特征级和决策级融合。系统采用硬件同步方案实现时空对齐,包含视觉采集模块、嗅觉传感模块和边缘计算单元。环境配置基于Python 3.9和PyTorch 2.0,支持实时多模态数据处理。该技术可应用于环境监测、工业检测等场景,突破单一视觉感知的局限性。

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 625

98DX3236内核编译过程小结

0.条件(1)Marvell技术支持提供的压缩文件:GNU EABI:arm-mv5sft-linux-gnueabi_SW3.2.tar.bz2.tar.bz2armebv7-marvell-linux-gnueabi-softfp_i686_201201.tar.bz2CPSS代码:Cpss-PP-DxCh-4.2_012.zipLinux内核文件:lsp-4.2_012.zipLSP-Uni...

snaking616 1803

基于Kotlin协程与MVVM的Android文件管理器开源项目实践

在Android开发中,文件管理与系统交互是常见的核心需求,涉及存储权限、异步操作与UI状态管理等关键技术点。其原理在于通过合理的架构设计,将数据获取、业务逻辑与界面展示分离,以提升代码的可维护性与可测试性。现代Android开发通过Kotlin协程、Flow以及Jetpack组件(如ViewModel、Room)等技术栈,能够高效处理IO密集型任务,并实现响应式数据流。这类技术的工程价值在于构建高性能、低延迟的应用,同时确保良好的用户体验。典型的应用场景包括本地文件浏览、应用信息管理、批量操作处理等系统工

weixin_30271335的博客 437

【异常】XXL-job触发调度:address:http://192.168.x.x:x/ code:500 msg:xxl-rpc remoting error(connect timed out)

XXL-JOB调度任务失败分析:调度中心(10.254.234.80)与执行器(192.168.x.x)连接超时,返回500错误。主要原因是网络不通或执行器异常:1.网络不通可能由防火墙或网段隔离导致;2.执行器未启动或端口未监听;3.地址配置错误。解决方案:1.关闭代理使用内网;2.测试网络连通性和端口可用性;3.检查执行器进程状态和端口监听情况;4.核对执行器地址配置;5.必要时配置防火墙规则放行端口。

奶爸的编程之路,也就一周冷个三天 414

Kotlin Flow combine 源码剖析:一个 Channel 如何优雅合并多条流

zip 是严格按顺序一一配对(第 1 个配第 1 个,第 2 个配第 2 个),任一上游慢下来整体就等着;combine 是各自取最新值,谁快谁多都能跟上。要「配对」用 zip,要「组合状态」用 combine

Android的专栏 339

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Kotlin 1.9 空安全进阶:5个真实项目中的NPE陷阱与解决方案

本文深入探讨Kotlin 1.9在Android开发中的空安全进阶实践,解析5个真实项目中常见的NPE陷阱及其解决方案。通过协程作用域取消、Flow操作等场景的案例分析,帮助开发者掌握防御性编程技巧,提升代码健壮性。特别适合Kotlin开发者面试准备及项目实战参考。

weixin_30546189的博客 426

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Android文件下载完整性校验:从哈希原理到SHA-256流式实现

在移动应用开发中,数据完整性校验是确保文件传输可靠性的核心技术。其基本原理是通过密码学哈希函数为数据生成唯一的“数字指纹”,在传输前后比对指纹是否一致,从而验证数据未被篡改或损坏。哈希算法如MD5、SHA-1因存在碰撞漏洞已不适用于安全场景,而SHA-256在安全性与计算效率间取得了最佳平衡,成为当前移动开发的主流选择。从技术价值看,完整性校验不仅是防御网络传输错误、CDN缓存污染的基础手段,更是构建用户信任、防止因文件损坏导致应用崩溃的关键工程实践。在Android开发中,结合流式处理技术,可在文件下载过

weixin_34378969的博客 424

android-drag-FlowLayout开发实战:自定义Adapter与数据绑定

android-drag-FlowLayout是一款强大的Android可拖拽流式布局库,它允许开发者轻松实现支持拖拽排序的标签云、兴趣选择器等交互界面。本文将详细介绍如何通过自定义Adapter与数据绑定,充分发挥该库的拖拽功能。 ## 一、认识DragAdapter:数据与视图的桥梁 DragAdapter是连接数据源与视图展示的核心组件,它负责创建Item视图并绑定数据。与传统Adapt

gitblog_00705的博客 774

Kotlin 进阶语法与 Android 实战理解

本文总结了Kotlin进阶特性在Android开发中的核心应用:泛型与in/out型变提升复用和类型安全;inline、reified保留类型信息并优化性能;sealed、委托、DSL简化状态与构建;协程与Flow实现结构化并发和响应式UI。紧扣实际场景,助你写出更安全、简洁、

trebleZ的博客 278

Android开发者的高性能数据库选择:ObjectBox核心优势与实战指南

在移动应用开发中,数据持久化是构建流畅用户体验的基础。传统的关系型数据库如SQLite虽然稳定,但在处理复杂对象模型和高并发读写时,常面临性能瓶颈和繁琐的ORM转换。ObjectBox作为一种面向对象的NoSQL数据库,从底层存储引擎重新设计,通过零拷贝架构和高效的二进制序列化,实现了远超SQLite的读写速度。其技术价值在于将数据模型直接映射为编程语言中的对象,消除了对象-关系映射的开销,同时提供了简洁的Fluent API和内置的响应式数据观察机制。这使得它在需要**极速读写**和**实时UI更新**的

weixin_34296641的博客 340

Android Room数据库实战:从CRUD到高级优化

数据持久化是移动应用开发的基础需求,而ORM框架通过对象关系映射技术简化了数据库操作。Room作为Android Jetpack组件,通过编译时SQL验证和注解驱动开发,显著提升了SQLite的使用效率。其核心原理是将Java/Kotlin对象自动映射到数据库表,开发者只需定义Entity和DAO接口,Room会在编译期生成具体实现代码。这种设计既保证了类型安全,又能减少60%以上的样板代码。在电商、社交等需要本地缓存的场景中,Room配合Flow/LiveData可以实现高效的响应式数据流。本文通过用户管

weixin_30367169的博客 419

Kotlin Flow 生命周期管理 launch、launchWhenStarted、repeatOnLifecycle,StateFlow 粘性状态共享与 SharedFlow 非粘性事件分发

Kotlin Flow 生命周期管理 launch、launchWhenStarted、repeatOnLifecycle,StateFlow 粘性状态共享与 SharedFlow 非粘性事件分发

OuO-1 的博客 434
上一篇: Android 5.1 open data flow 数据开启流程
下一篇: Android Studio 系列(三)使用Version Control管理多仓库多分支源码
十橙
博客等级 码龄24年 312粉丝 95原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值