安卓四大组件之ContentProvider
目录
实现步骤
代码分析
onCreate
insert
query
ContextHolder
Cursor
作用与用法
基本步骤:
可能的面试题:为什么使用Cursor?
为什么使用Cursor
使用Cursor的好处
静态内部类实现单例模式
AnndroidManifest.xml配置信息
注释的意义
Room持久化数据库
Room的核心组件
Room的主要功能及优势
TripDAO
Context避免内存泄漏:
实现步骤
1、定义URI
定义一个静态的URI 常量指向数据的位置
public static final Uri DATA_URI = Uri.parse("D:/database");
2、创建一个类继承自ContentProvider,实现必要的CRUD(新建,读取,更新,删除操作)
重写以下方法:
- onCreate():初始化数据库及其他资源
- query(),insert(),delete()和update():分别处理对数据的不同操作。每个方法都接收一个URI参数,该参数用于确定所请求的操作类型和目标数据。
- geType():返回给定的URI内容类型。通常需要根据传入的URI返回响应的MIME类型
3. 配置AndroidManifest.xml
为了让系统知道你的ContentProvider,需要在AndroidManifest.xml文件中注册它。这通常是在<application>标签内完成的:
<providerandroid:name=".TripDataProvider"android:authorities="my.application.TripDataProvider"android:exported="false">
</provider>
android:name: 指向你的ContentProvider类。
android:authorities: 应匹配你在代码中定义的AUTHORITY。
android:exported: 如果你想让其他应用程序也能访问这个提供者,可以将其设置为true。
4. 使用URI进行数据操作
通过ContentResolver对象来执行各种数据操作。
查询所有行程:
Cursor cursor = getContentResolver().query(TripDataProvider.TRIP_DATA_URI, null, null, null, null);
插入新的行程记录:
ContentValues values = new ContentValues();// 设置values...Uri newUri = getContentResolver().insert(TripDataProvider.TRIP_DATA_URI, values);
删除某个ID的行程记录:
String selection = "id=?"; // 删除条件
String[] selectionArgs = {"1"}; // 条件参数,假设删除id为1的行程
int rowsDeleted = getContentResolver().delete(TripDataProvider.TRIP_DATA_URI, selection, selectionArgs);
更新某个行程的信息:
ContentValues updateValues = new ContentValues();// 设置updateValues...String selectionForUpdate = "id=?";String[] selectionArgsForUpdate = {"1"};int updatedRows = getContentResolver().update(TripDataProvider.TRIP_DATA_URI, updateValues, selectionForUpdate, selectionArgsForUpdate);
通过这种方式,就可以用ContentProvider实现跨应用组件的数据共享,并且方便地管理数据。
代码分析
onCreate
@Overridepublic boolean onCreate() {mContext = getContext();mDataBase = TripDatabase.getInstance(mContext);ContextHolder.getInstance().setContext(mContext);return true;}
1. 获取Context 上下文
mContext = getContext();
此处调用了getContext()方法来获取当前的Context。对于ContentProvider而言,getContext()返回的是该提供者运行于的应用程序的上下文环境。这个上下文是ContentProvider与应用程序其他部分进行交互的关键。
2. 初始化数据库
mDataBase = TripDatabase.getInstance(mContext);
此处使用从getContext()获得的Context来初始化TripDatabase实例。TripDatabase是基于Room持久化库构建的数据库类,详见后文。
getInstance(mContext)是一个单例模式的方法,确保在整个应用生命周期内只存在一个数据库实例。这样做有助于节省资源并保持数据的一致性。
3. 设置ContextHolder的Context
ContextHolder.getInstance().setContext(mContext);
调用ContextHolder的getInstance()方法获取其单例实例,然后通过setContext(mContext)方法将之前获取的mContext 上下文对象传递给ContextHolder。 这一步是为了在整个应用程序中共享这个mContext,特别是对于那些可能需要访问应用程序上下文但无法直接获得它的组件来说非常有用。
小结:
获取上下文
初始化数据库对象,根据上下文来获取数据
更新上下文,更新上下文的全局持有器
insert
@Overridepublic Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {int uriType = S_URI_MATCHER.match(uri);if (uriType == TRIP_DATA_PROVIDER) {TripInfo info = formatContentValueToInfo(values);if (info != null) {long id = mDataBase.tripDAO().insertTrip(info);mContext.getContentResolver().notifyChange(uri, null);if (id > 0) {Uri newUri = Uri.parse(uri.toString() + "/" + id + "|" + OPERATION_TYPE_ADD);mContext.getContentResolver().notifyChange(newUri, null);return ContentUris.withAppendedId(uri, id);}}}return null;}
该方法主要用于向数据库中插入一条新的行程(Trip)记录,并通过Uri通知数据的变化。下面是对这段代码的具体分析:
插入操作:该方法接收一个Uri对象和一个可选的ContentValues对象作为参数,代表要插入的数据。
URI匹配:首先使用S_URI_MATCHER.match(uri)来确定传入的Uri类型是否与预定义的TRIP_DATA_PROVIDER相匹配。这一步是为了确保请求的URI是针对行程数据的操作。
数据转换:如果匹配成功,则调用formatContentValueToInfo(values)将ContentValues对象转换为自定义的TripInfo对象,用于后续的数据处理。
数据库插入:使用mDataBase.tripDAO().insertTrip(info)将TripInfo对象插入到数据库中,并返回新插入记录的ID。
tripDao是一个抽象接口,后文有具体的分析
Uri newUri = Uri.parse(uri.toString() + "/" + id + "|" + OPERATION_TYPE_ADD);mContext.getContentResolver().notifyChange(newUri, null);
通知变更:如果插入成功(即返回的ID大于0),则构建一个新的Uri对象,这个新Uri是在原Uri的基础上附加了新记录的ID以及一个操作类型标识(在这个例子中是OPERATION_TYPE_ADD)。然后,分别对原始Uri和新生成的Uri调notifyChange方法,以通知监听这些Uri的观察者数据已经发生变化。
返回结果:最后,如果插入成功,使用ContentUris.withAppendedId(uri, id)构造并返回一个新的Uri,它包含了新插入记录的ID;如果插入失败或info为null,则返回null。
小结:
1、URI匹配:检查传入URI是否对应于形成数据的操作
2、数据类型转换 数据层和业务逻辑层分离
TripInfo info = formatContentValueToInfo(values);
3、 数据插入与通知:对原始Uri进行变更通知,还创建了一个更具体的`Uri`来反映刚刚添加的数据项,并同样发出变更通知。
4. 返回值:如果插入成功返回的Uri包含了新记录的ID,这使得调用者能够直接获取新记录的位置。若插入失败,则返回null;
query
@Nullable@Overridepublic Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {int uriType = S_URI_MATCHER.match(uri);Log.e(TAG, "uri path:" + uri.toString());Cursor cursor = null;if (uriType == TRIP_DATA_PROVIDER) {if (selectionArgs == null) {cursor = mDataBase.tripDAO().findTrips();} else {if (selectionArgs.length >= 1) {String id = selectionArgs[0];cursor = mDataBase.tripDAO().findTripById(Integer.parseInt(id));}}}return cursor;}@Nullable@Overridepublic String getType(@NonNull Uri uri) {return null;}
这query()方法实现,用于根据给定的URI查询数据库并返回结果。
方法分析 :
1. 参数解释 Uri uri: 标识要访问的数据集。
String[] projection: 要返回的列名数组。如果为null,则返回所有列。
String selection: 一个过滤条件,类似于SQL中的WHERE子句。
String[] selectionArgs: 上述selection中占位符的实际值。
String sortOrder: 排序顺序,类似于SQL中的ORDER BY子句。
2. 匹配URI类型
int uriType = S_URI_MATCHER.match(uri);
使用UriMatcher来确定传入的URI对应的资源类型。
在这个例子中,通过S_URI_MATCHER.match(uri)来匹配URI,并根据其类型执行相应的操作。
3. 根据URI类型进行查询
如果uriType等于TRIP_DATA_PROVIDER,表示请求的是行程数据:
如果没有提供selectionArgs(即查询所有行程),则调用mDataBase.tripDAO().findTrips()获取所有行程记录。
如果提供了selectionArgs并且至少包含一个元素,则认为是要根据ID查询特定行程,因此调用mDataBase.tripDAO().findTripById(Integer.parseInt(id))。
5. 返回查询结果
将查询结果封装在Cursor对象中返回。
在这个例子中,query()方法返回的Cursor可以被其他组件(如Activity或Fragment)用来展示或进一步处理从数据库检索到的数据。这使得ContentProvider不仅能够提供对数据的安全访问,还能灵活地支持各种数据操作需求。
delete
update 略
ContextHolder
源码:
public class ContextHolder {private ContextHolder() {}private static class InstanceHolder {private static final ContextHolder INSTANCE = new ContextHolder();}public static ContextHolder getInstance() {return InstanceHolder.INSTANCE;}private Context mContext;public Context getContext() {return LauncherApp.getApplication();}public void setContext(Context mContext) {if (!(mContext instanceof Application)) {throw new RuntimeException("context should be Application context to avoid context leak");}this.mContext = mContext;}
}
这段代码定义了一个名为ContextHolder的类,用于在应用程序的不同部分之间共享和管理全局Context。
以下是对该类各个组成部分的详细分析:
单例模式 :
私有构造函数:ContextHolder的构造函数被声明为private,这意味着它不能从外部直接实例化。
这种设计是实现单例模式的一部分,确保整个应用程序中只有一个ContextHolder实例存在。
静态内部类InstanceHolder:通过使用静态内部类InstanceHolder来持有唯一的ContextHolder实例,这是一种懒汉式单例模式的变体。这种方式利用了Java的类加载机制,在第一次调用getInstance()方法时才加载`InstanceHolder`类,从而延迟了`ContextHolder`实例的创建,实现了懒加载的效果。这样做不仅保证了线程安全,而且避免了同步带来的性能开销。
上下文管理
成员变量mContext:用来存储应用程序的上下文(Context),通常是一个Application对象,以避免内存泄漏的风险。 getContext() 方法:返回当前保存的Context。
LauncherApp.getApplication()来返回一个Context。
public Context getContext() { return LauncherApp.getApplication();
}
setContext(Context mContext) 方法:允许设置ContextHolder持有的Context,但仅当传入的Context是Application类型时才允许设置。
这是为了避免由于持有Activity或Service等生命周期较短的Context而导致的内存泄漏问题。
public void setContext(Context mContext) { if (!(mContext instanceof Application)){ throw new RuntimeException("context should be Application context to avoid context leak"); } this.mContext = mContext;
}
小结:ContextHolder的设计目的是为了提供全局安全访问和共享Context的方式。
采用了单例模式和懒加载策略来确保高效且线程安全的实例管理。通过强制要求Context必须是Application类型,有效地防止了因错误地传递Activity或Service导致的内存泄漏问题
Cursor
作用与用法
Cursor是一个接口,允许你遍历结果集中的行和列,并提取所需的数据,它代表了一个由数据库查询返回的结果集,。
基本步骤:
移动到指定位置:使用moveToFirst(), moveToNext(), moveToLast(), 或者 moveToPosition(int position)等方法在结果集中移动指针。
读取数据:一旦定位到某一行,可以使用如getString(columnIndex), getInt(columnIndex), getLong(columnIndex)等方法读取特定列的数据。
注意,这里的columnIndex是指列在查询结果中的索引,而不是列名。
关闭Cursor:完成数据读取后应调用close()方法释放Cursor占用的资源。 例如,假设我们有一个查询结果`Cursor,可以通过以下方式遍历结果集并读取数据:
if (cursor != null) { while (cursor.moveToNext()) { // 假设第一个列是'id',第二个列是'name' int id = cursor.getInt(cursor.getColumnIndexOrThrow("id")); String name = cursor.getString(cursor.getColumnIndexOrThrow("name")); // 处理id和name... } cursor.close(); // 关闭Cursor以释放资源
}
可能的面试题:为什么使用Cursor?
Cursor是Android中用于表示数据库查询结果集的接口。当你执行一个查询操作时,返回的结果通常是一个Cursor对象,它允许你遍历查询结果中的行和列,并提取所需的数据。以下是使用Cursor的原因及其带来的好处:
为什么使用Cursor
1. 高效处理大数据集:
Cursor允许逐行读取数据,而不是一次性将所有数据加载到内存中。这对于处理大量数据特别有用,因为它减少了内存占用并提高了应用的性能。
2.支持复杂查询结果:
Cursor`可以处理复杂的查询结果,包括多表连接、聚合函数等产生的结果集。它不仅限于简单的单表查询。
3. 灵活的数据访问方式:
使用Cursor,你可以根据需要选择要访问的数据列和行,提供了极大的灵活性。例如,你可以只获取特定的几列数据,而不需要加载整个行的所有列。
4. 与ContentProvider无缝集成:
在Android中,ContentProvider经常用来在不同的应用程序或应用组件之间共享数据。Cursor是ContentProvider返回查询结果的标准方式,这使得不同部分之间的数据交换变得简单且一致。
使用Cursor的好处
1. 节省内存:
如前所述,由于Cursor允许逐行读取数据,因此相比于一次性加载所有数据到内存中,它可以大大减少内存消耗。
2. 简化数据处理逻辑:
Cursor提供了一系列便捷的方法来获取不同类型的数据(如getInt(), getString(), getBlob()等),这使得从查询结果中提取数据变得更加直接和容易。
3. 生命周期管理:
Cursor具有自己的生命周期,可以通过调用close()方法手动关闭它,从而释放相关资源。良好的资源管理有助于避免潜在的内存泄漏问题。
4. 支持异步操作:
虽然`Cursor本身并不直接支持异步操作,但结合Loader框架(例如LoaderManager和AsyncTaskLoader)或Room数据库提供的LiveData支持,可以实现非阻塞的数据加载,提升用户体验。
5. 便于UI更新:
当与CursorAdapter结合使用时,Cursor可以直接作为数据源绑定到UI组件上(如ListView或RecyclerView),使得数据显示更加简便。
Java的类加载机制和懒汉式单例模式虽然涉及的概念不同,但它们之间存在一定的联系,尤其是在实现单例模式时如何确保类实例的唯一性和延迟加载方面。下面将详细解释这两个概念,并探讨它们之间的关系。
静态内部类实现单例模式
public class ContextHolder {private ContextHolder() {// 私有构造函数,防止外部实例化}private static class InstanceHolder {// 静态内部类,只有在调用getInstance方法时才会被加载private static final ContextHolder INSTANCE = new ContextHolder();}public static ContextHolder getInstance() {return InstanceHolder.INSTANCE;}// 其他代码...
}
1.、延迟加载,按需加载:懒汉式单例模式利用了Java类加载机制中的延迟加载特性。具体来说,在单例类首次被真正使用之前(例如调用getInstance()`方法),类不会被加载,因此单例对象也不会被实例化。
2、这是一种更加推荐的懒汉式单例模式实现方式,它不仅实现了延迟加载,而且借助了类加载机制的线程安全性。这种方式通过定义一个静态内部类来持有单例对象,在第一次引用该内部类时才会触发类加载及其实例化操作,从而保证了线程安全且高效的单例模式实现。
关键点解析
1. 私有构造函数:通过将构造函数设为private,防止其他类直接使用`new`关键字来实例化该类的对象,这是单例模式的基础。
2. 静态内部类InstanceHolder:仅当调用ContextHolder.getInstance()方法时,`InstanceHolder`类才会被加载。这是因为Java中静态成员(包括静态内部类)遵循“按需加载”的原则——即直到第一次使用时才会加载静态内容。
3. **INSTANCE变量**:在`InstanceHolder`内部定义了一个静态的`INSTANCE`变量,用于保存单例对象。由于静态内部类只会被加载一次,因此保证了`INSTANCE`的唯一性。
4. 线程安全:利用Java类加载机制的特性,确保了多线程环境下创建单例对象的线程安全性,而无需额外的同步处理。
5. 懒加载:这种方式实现了懒加载,即只有当真正需要使用单例对象时(即调用了getInstance()),才会初始化该对象,而不是在类加载时就进行初始化。
载器才会尝试自己加载。这种方法可以有效避免类的重复加载,同时增强了类的安全性。
对于静态内部类实现单例模式来说,正是利用了Java的“按需加载”特性。静态内部类InstanceHolder不会在`ContextHolder`类加载时就被加载,而是延迟到首次调用getInstance()方法时才加载。这不仅保证了单例对象的唯一性和线程安全性,还实现了懒加载,提高了资源利用率。
AnndroidManifest.xml配置信息
应用主要配置了权限声明、保护广播以及应用组件等信息。
以下是对该文件中包含的内容的分析:
1. 权限声明:
android.permission.ACCESS_NETWORK_STATE和 android.permission.INTERNET:允许应用程序访问网络状态和打开网络连接。
android.permission.BLUETOOTH 和 android.permission.BLUETOOTH_ADMIN:允许应用程序配对和连接蓝牙设备,并进行蓝牙管理操作。
android.permission.READ_EXTERNAL_STORAGE 和 android.permission.WRITE_EXTERNAL_STORAGE:允许应用程序读取和写入外部存储。
android.permission.FOREGROUND_SERVICE`:允许应用程序创建前台服务,这在执行长时间运行的任务时非常有用。
android.permission.ACCESS_WIFI_STATE:允许应用程序访问有关Wi-Fi网络的信息。
2. 保护广播(Protected Broadcasts):
应用程序定义了两个受保护的广播,分别是application.ACTION_MAP_RECV和application.ACTION_SCENE_CARD。
这意味着只有具有特定权限的应用才能发送这些广播动作,增强了应用的安全性。
3. Application节点下的配置:
<uses-library android:name="myApplication.framework" />表示此应用依赖于名为myApplication.framework的库,这可能与框架或功能有关,
Content Provider的配置,即TripDataProvider。
注释的意义
注释(Annotations)在编程中用于为代码添加元数据,这些信息可以被编译器、工具或运行时环境读取和使用。它们本身不会改变程序的执行逻辑,但可以影响编译过程、生成文档、进行代码检查等。你提到的几个注释分别具有以下意义:
1. @NonNull: 这个注释通常用来标明一个方法参数、返回值或变量不能为 null。这有助于静态分析工具识别潜在的空指针异常,并提醒开发者确保该元素在所有情况下都不应为空。
2. @Nullable: 与 @NonNull相反,这个注释表示一个方法参数、返回值或变量可能为 `null`。它提示开发者需要处理这种情况,以避免空指针异常或其他错误。
3. @Dao: 这个注释是特定于Android开发中的Room持久化库的一部分。@Dao注释用于标记接口或抽象类作为数据访问对象(DAO)。DAO提供了访问数据库的方法,如插入、删除、查询等操作。Room是一个官方提供的轻量级ORM(对象关系映射)库,旨在帮助开发者更加方便地创建应用的持久层
Room持久化数据库
Room持久化库是Android Jetpack的一部分,它提供了一个抽象层来访问应用程序的SQLite数据库。通过Room,开发者可以更方便地进行数据库操作,同时避免了直接使用SQLite API时的一些复杂性和潜在错误。
Room的核心组件
1. Entity(实体)
实体代表数据库中的表。每个实体类对应一个数据库表。例如,在您的项目中很可能被标记为@Entity注解,并且包含一些字段,这些字段将映射到表中的列。
@Entity(tableName = "trips")
public class TripInfo {@PrimaryKey(autoGenerate = true)private int id;private String name;// 其他字段...
}
2. DAO (Data Access Object)
DAO是定义用于访问数据库的方法接口。通过DAO,您可以轻松执行CRUD(创建、读取、更新、删除)操作。
在TripDAO接口中,定义了多种方法来进行数据操作,比如插入、删除、查询等。
@Dao
public interface TripDAO {@Insert(onConflict = OnConflictStrategy.REPLACE)long insertTrip(TripInfo info);@Query("SELECT * FROM trips WHERE id = :id")Cursor findTripById(int id);// 更多方法...
}
3. Database
数据库持有者是一个继承自RoomDatabase的抽象类。在这个例子中就是TripDatabase。
它使用@Database注解来指定其所关联的实体列表以及数据库版本号,并提供了获取DAO实例的方法。
@Database(entities = {TripInfo.class}, version = 1)public abstract class TripDatabase extends RoomDatabase {public abstract TripDAO tripDAO();private static volatile TripDatabase INSTANCE;public static TripDatabase getInstance(Context context) {if (INSTANCE == null) {synchronized (TripDatabase.class) {if (INSTANCE == null) {INSTANCE = Room.databaseBuilder(context.getApplicationContext(),TripDatabase.class,"trip.db").allowMainThreadQueries().build();}}}return INSTANCE;}}
Room的主要功能及优势
1. 简化数据库操作:Room通过DAO接口自动生成实现,减少了大量样板代码。只需定义接口和方法签名,Room会在编译期生成具体的实现代码。
2. 类型安全与编译时检查:Room在编译时验证SQL查询语句的有效性,有助于减少运行时错误。如果查询语句有误,编译器会立即提示。
3. 支持LiveData和RxJava:Room能够返回LiveData对象,这样当底层数据库发生变化时,UI可以自动更新。此外,也支持RxJava类型如Flowable、Single等。
4. 事务支持:Room允许在一个事务中执行多个操作,确保要么所有操作都成功,要么全部不执行。
5. 异步查询支持:为了避免阻塞主线程,Room默认不允许在主线程上执行数据库查询。不过,您可以通过.allowMainThreadQueries()选项临时关闭这一限制(仅限于测试环境)。
6. 数据库迁移支持:随着应用的发展,数据库结构可能需要更改。Room提供了易于使用的API来处理数据库版本升级和迁移。
Room持久化库极大地简化了Android应用程序中数据库的操作,提高了开发效率并减少了出错的可能性。结合ContentProvider使用,不仅可以优化内部数据管理,还能增强数据共享能力,满足更多应用场景的需求。
TripDAO
public interface TripDAO {@Insert(onConflict = OnConflictStrategy.REPLACE)public long insertTrip(TripInfo info);@Deletepublic int deleteTrip(TripInfo... info);@Query("DELETE FROM trips WHERE id = :id")public int deleteTrip(int id);@Query("DELETE FROM trips WHERE uuid = :uuid")public int deleteTrip(String uuid);@Query("SELECT * FROM trips WHERE id = :id")public Cursor findTripById(int id);@Query("SELECT * FROM trips")public Cursor findTrips();@Query("SELECT * FROM trips")public List<TripInfo> getAllTrips();@Query("SELECT * FROM trips WHERE tripReallyTime >= :startTime and tripReallyTime <= :endTime")public List<TripInfo> getAllTrips(long startTime,long endTime);@Query("SELECT * FROM trips WHERE id = :id")public TripInfo getTripById(int id);@Query("SELECT * FROM trips WHERE UUID = :uuid")public List<TripInfo> getTripById(String uuid);@Updatepublic int updateTrips(TripInfo info);}
这个TripDAO接口定义了一系列的方法,用于对名为`trips`的数据库表进行操作。它使用了Room持久化库提供的注解来简化SQLite数据库的操作。以下是对每个方法及其用途的详细分析:
接口与注解
@Dao: 标记该接口为一个数据访问对象(DAO),这是Room用来标识可以执行数据库操作的类或接口的关键字。
方法:
1. 插入数据
@Insert(onConflict = OnConflictStrategy.REPLACE)public long insertTrip(TripInfo info);
插入一个新的行程记录到trips表中。如果存在冲突(如主键冲突),则替换现有行。
返回值是新插入行的ID(对于自增主键)。
2. 删除数据
根据对象删除
@Deletepublic int deleteTrip(TripInfo... info);
删除传入的一个或多个`TripInfo`对象对应的记录。返回被删除的行数。
根据ID删除
@Query("DELETE FROM trips WHERE id = :id")public int deleteTrip(int id);
根据给定的ID删除特定行程记录。
根据UUID删除
@Query("DELETE FROM trips WHERE uuid = :uuid")public int deleteTrip(String uuid);
3. 查询数据
根据ID查找单个行程
@Query("SELECT * FROM trips WHERE id = :id")public Cursor findTripById(int id);
返回一个Cursor,指向具有指定ID的行程记录。适用于需要逐行读取结果的情况。
查找所有行程
@Query("SELECT * FROM trips")public Cursor findTrips();
总结
该接口提供了一组全面的方法来管理`trips`表的数据,包括插入、删除、查询和更新操作。通过使用Room提供的注解,这些方法能够以简洁的方式实现复杂的数据库操作,减少了手动编写SQL语句的需求,并提供了类型安全性和编译时检查的支持。这使得开发者可以更加专注于业务逻辑而不是底层的数据库细节。此外,支持返回Cursor和List<TripInfo>类型的查询方法,为不同的应用场景提供了灵活性。
Context避免内存泄漏:
长生命周期对象持有短声明周期对象,避免方法,只有当context是Application类型才赋值
相关文章:
安卓四大组件之ContentProvider
目录 实现步骤 代码分析 onCreate insert query ContextHolder Cursor 作用与用法 基本步骤: 可能的面试题:为什么使用Cursor? 为什么使用Cursor 使用Cursor的好处 静态内部类实现单例模式 AnndroidManifest.xml配置信息 注释的…...
C#中实现XML解析器
XML(可扩展标记语言)是一种广泛用于存储和传输数据的格式,因其具有良好的可读性和可扩展性,在许多领域都有应用。 实现思路: 词法分析 词法分析的目的是将输入的 XML 字符串分解为一个个的词法单元,例如…...
神经符号混合与跨模态对齐:Manus AI如何重构多语言手写识别的技术边界
在全球化数字浪潮下,手写识别技术长期面临"巴别塔困境"——人类书写系统的多样性(从中文象形文字到阿拉伯语连写体)与个体书写风格的随机性,构成了人工智能难以逾越的双重壁垒。传统OCR技术在处理多语言手写场景时,准确率往往不足70%,特别是在医疗处方、古代文…...
TestBrain开源程序是一款集使用AI(如deepseek)大模型自动生成测试用例、和测试用例评审、RAG知识库管理的web平台系统
一、软件介绍 文末提供程序和源码下载 TestBrain开源程序是一款集使用AI(如deepseek)大模型自动生成测试用例、和测试用例评审、RAG知识库管理的web平台系统一个基于LLM的智能测试用例生成平台(功能慢慢丰富中,未来可能将测试相关的所有活动集成到一起),…...
软件工程效率优化:一个分层解耦与熵减驱动的系统框架
软件工程效率优化:一个分层解耦与熵减驱动的系统框架** 摘要 (Abstract) 本报告构建了一个全面、深入、分层的软件工程效率优化框架,旨在超越简单的技术罗列,从根本的价值驱动和熵减原理出发,系统性地探讨提升效率的策略与实践。…...
【金仓数据库征文】- 深耕国产数据库优化,筑牢用户体验新高度
目录 引言 一、性能优化:突破数据处理极限,提升运行效率 1.1 智能查询优化器:精准优化数据检索路径 1.2 并行处理技术:充分释放多核计算潜力 1.3 智能缓存机制:加速数据访问速度 二、稳定性提升:筑牢…...
前端面试常见部分问题,及高阶部分问题
面试中也极有可能让你徒手写代码,无聊的面试问题o( ̄︶ ̄)o 一、HTML/CSS 基础与进阶 常见问题 什么是语义化标签?有哪些常用语义化标签? 答案:语义化标签是指具有明确含义的 HTML 标签,如 <header>、<footer>、<article>、<section> 等。它们有…...
使用 AutoGen 与 Elasticsearch
作者:来自 Elastic Jeffrey Rengifo 学习如何使用 AutoGen 为你的 agent 创建一个 Elasticsearch 工具。 Elasticsearch 拥有与行业领先的生成式 AI 工具和提供商的原生集成。查看我们的网络研讨会,了解如何超越 RAG 基础,或使用 Elastic 向量…...
kafka与flume的整合、spark-streaming
kafka与flume的整合 前期配置完毕,开启集群 需求1: 利用flume监控某目录中新生成的文件,将监控到的变更数据发送给kafka,kafka将收到的数据打印到控制台(三个node01中运行) 1.在kafka中建立topic kafka…...
高级电影感户外街拍人像摄影后期Lr调色教程,手机滤镜PS+Lightroom预设下载!
调色介绍 高级电影感户外街拍人像摄影后期 Lr 调色,是运用 Adobe Lightroom 软件,对户外街拍的人像照片进行后期处理,以塑造出具有电影质感的独特视觉效果。此调色过程借助 Lr 丰富的工具与功能,从色彩、光影、对比度等多维度着手…...
react 常用钩子 hooks 总结
文章目录 React钩子概念图状态管理钩子 state management副作用钩子 effect hooks引用钩子 Ref hooks上下文钩子其他钩子过渡钩子 处理过渡效果性能优化钩子 performance hooksReact 19 新钩子 React钩子概念图 状态管理钩子 state management useState useReducer useSyncEx…...
2025 年导游证报考条件新政策解读与应对策略
2025 年导游证报考政策有了不少新变化,这些变化会对报考者产生哪些影响?我们又该如何应对?下面就为大家详细解读新政策,并提供实用的应对策略。 最引人注目的变化当属中职旅游类专业学生的报考政策。以往,中专学历报考…...
重置 Git 项目并清除提交历史
在某些情况下,你可能需要完全重置一个 Git 项目,清除所有提交历史,然后将当前代码作为全新的初始提交。本文将详细介绍这个过程的操作步骤和注意事项。 重要警告 ⚠️ 注意:以下操作将永久删除项目的所有提交历史、分支和标签。…...
GitHub Copilot (Gen-AI) 很有用,但不是很好
摘要:以下是我在过去三个月中在实际 、 开发中使用 GitHub Copilot Pro 后的想法。由于技术发展迅速,值得注意的是,这些印象是基于我截至 2025 年 3 月的经验。 1 免费试用促使我订阅 GitHub Copilot Pro 我以前读过有关 AI 代码生成器的文…...
K8S Service 原理、案例
一、理论介绍 1.1、3W 法则 1、是什么? Service 是一种为一组功能相同的 pod 提供单一不变的接入点的资源。当 Service 存在时,它的IP地址和端口不会改变。客户端通过IP地址和端口号与 Service 建立连接,这些连接会被路由到提供该 Service 的…...
Base64编码原理:二进制数据与文本的转换技术
🔄 Base64编码原理:二进制数据与文本的转换技术 开发者的数据编码困境 作为开发者,你是否曾遇到这些与Base64相关的挑战: 📊 需要在JSON中传输二进制数据,但不确定如何正确编码🖼️ 想要在HT…...
系统设计(1)—前端—CDN—Nginx—服务集群
简介: 本指南旨涵盖前端、CDN、Nginx 负载均衡、服务集群、Redis 缓存、消息队列、数据库设计、熔断限流降级以及系统优化等模块的核心要点。我们将介绍各模块常见的设计方案与优化策略,并结合电商秒杀、SaaS CRM 系统、支付系统等高并发场景讨论实践技巧…...
Easysearch 基础运维扫盲指南:从 HTTP 到 HTTPS、认证与安全访问全解析
Easysearch 基础运维扫盲指南:从 HTTP 到 HTTPS、认证与安全访问全解析 众所周知,原生 Elasticsearch 默认开启的是 HTTP 明文接口,并且不开启任何身份认证或传输加密。若想启用 TLS/SSL 加密及账号密码验证,通常需要配置繁琐的安…...
在Android中如何使用Protobuf上传协议
在 Android 中使用 Protobuf(Protocol Buffers)主要分为以下几个步骤: ✅ 1. 添加 Protobuf 插件和依赖 在项目的 build.gradle(Project 级)文件中添加 Google 的 Maven 仓库(通常默认已有)&am…...
【数据可视化艺术·应用篇】三维管线分析如何重构城市“生命线“管理?
在智慧城市、能源管理、工业4.0等领域的快速发展中,地下管线、工业管道、电力通信网络等“城市血管”的复杂性呈指数级增长。传统二维管理模式已难以应对跨层级、多维度、动态变化的管线管理需求。三维管线分析技术应运而生,成为破解这一难题的核心工具。…...
2025年的营销趋势-矩阵IP
从 2025 年的营销生态来看,创始人 IP 与智能矩阵的结合确实呈现出颠覆性趋势,这一现象背后隐藏着三个值得深度解析的商业逻辑: 一、创始人 IP 的本质是 "信任货币" 的数字化迁徙 当新能源汽车市场陷入参数混战,雷军将个…...
对接金蝶获取接口授权代码
接口服务信息 using RestSharp; using System.Configuration; using System.Threading.Tasks; public class KingdeeAccessTokenService { private readonly RestClient _client; private readonly KingdeeApiConfig _config; public KingdeeAccessTokenService() …...
探秘 3D 展厅之卓越优势,解锁沉浸式体验新境界
(一)打破时空枷锁,全球触达 3D 展厅的首要优势便是打破了时空限制。在传统展厅中,观众需要亲临现场,且必须在展厅开放的特定时间内参观。而 3D 展厅依托互联网,让观众无论身处世界哪个角落,只…...
prometheus通过Endpoints自定义grafana的dashboard模块
1、prometheus自定义的dashboard模块 文件路径/etc/prometheus/config_out/prometheus-env.yaml - job_name: serviceMonitor/monitoring/pfil/0honor_labels: falsekubernetes_sd_configs:- role: endpointsnamespaces:names:- monitoringrelabel_configs:- source_labels:- …...
java排序算法-计数排序
计数排序的思路 计数排序的基本思路: 确定取值范围: 遍历整个待排序的数组,确定数组中元素的取值范围,找到最小值和最大值。创建计数数组: 创建一个计数数组,其长度为取值范围的大小,用于统计…...
力扣-hot100(滑动窗口最大值)
239. 滑动窗口最大值 困难 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1: 输入:nums […...
每日c/c++题 备战蓝桥杯(P1049 [NOIP 2001 普及组] 装箱问题)
洛谷P1049 装箱问题题解:动态规划在背包问题中的经典应用 题目描述 P1049 装箱问题是一道典型的0-1背包问题变种。题目要求在给定箱子容量V和n个物品体积的情况下,选择若干物品装入箱子,使得箱子的剩余空间最小。最终输出这个最小剩余空间的…...
【尚硅谷Redis6】自用学习笔记
Redis介绍 Redis是单线程 多路IO复用技术(类似黄牛买票) 默认有16个库,用select进行切换 默认端口号为6379 Memcached:多线程 锁(数据类型单一,不支持持久化) 五大常用数据类型 Redis key …...
产品更新丨谷云科技ETLCloud V3.9.2版本发布
谷云科技 ETLCloud 集成平台迎来了每月一次的功能迭代,本月发布版本号为 3.9.2 版本,为用户带来了新的功能、优化改进以及问题修复,以下是详细介绍: 新增组件 本次更新新增了众多实用组件,涵盖了京东和 Shopify 相关…...
Promise并发控制与HTTP请求优化
Promise并发方法对比 #mermaid-svg-tnmGzOkgNUCrbvfI {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-tnmGzOkgNUCrbvfI .error-icon{fill:#552222;}#mermaid-svg-tnmGzOkgNUCrbvfI .error-text{fill:#552222;stroke…...
G1垃圾回收器中YoungGC和MixedGC的区别
在 G1 垃圾回收器中,Mixed GC 和 Young GC 的区别主要体现在以下几个方面: 作用范围 Young GC:仅针对年轻代中的Region进行回收,包括 Eden 区和 Survivor 区的 Region。Mixed GC:会回收所有年轻代的 Region 以及部分…...
Web4.0身份革命:去中心化身份系统的全栈实现路径
去中心化身份(DID)技术栈正在重构数字世界的信任根基,本文从密码学协议、存储网络、验证框架三个维度,解析符合W3C标准的身份系统构建方案。通过Hyperledger Aries架构实践,揭示如何实现跨链身份互通、数据主权控制、零…...
iOS/Flutter混合开发之PlatformView配置与使用
环境:Xcode 16.3、Flutter 3.29.2、Dart 3.7.2。 使用背景:需要在flutter界面中嵌入一个iOS原生控件。 步骤: 1. iOS侧实现: 1.1:PlatformView实现 class FLNativeView: NSObject, FlutterPlatformView {private v…...
Libconfig 修改配置文件里的某个节点
THCommandStatus ( { Status "1"; index 5; }, { Status "2"; index 8; }, { Status "3"; index 7; }, { Status "4"; index 0; } ); 比如这是配置文件的内容ÿ…...
【金仓数据库征文】_AI 赋能数据库运维:金仓KES的智能化未来
AI 赋能数据库运维:金仓KES的智能化未来 🌟嗨,我是LucianaiB! 🌍 总有人间一两风,填我十万八千梦。 🚀 路漫漫其修远兮,吾将上下而求索。 在当今数字经济飞速发展的时代࿰…...
【MySQL】3分钟解决MySQL深度分页问题
什么是深度分页问题?该如何解决呢?这篇文章展开讲讲 什么是深度分页? 当查询结果集非常大时,需要获取靠后页码的数据,比如第1000页、10000页。 如: SELECT * FROM table LIMIT 10000, 10; -- 获取第10001-10010条…...
GitHub 趋势日报 (2025年04月24日)
本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ 📈 今日整体趋势 Top 10 排名项目名称项目描述今日获星总星数语言1kortix-ai/sunaSuna - Open Source Generalist AI Agent⭐ 1105⭐ 3639TypeScript2cloudcommunity/Free-CertificationsA curated …...
一种双模式机器人辅助股骨干骨折钢板植入方法
股骨干骨折是一种常见的高能损伤,微创内固定是首选治疗方法。然而,钢板植入过程中存在不可见、不准确和不稳定等问题。山东大学研究团队提出了一种双模式机器人辅助钢板植入方法,通过神经网络模型规划钢板植入轨迹,然后利用机械臂…...
全球碳化硅晶片市场深度解析:技术迭代、产业重构与未来赛道争夺战(2025-2031)
一、行业全景:从“材料突破”到“能源革命”的核心引擎 碳化硅(SiC)作为第三代半导体材料的代表,凭借其宽禁带(3.26eV)、高临界击穿场强(3MV/cm)、高热导率(4.9W/cmK&…...
IDEA搭建环境的五种方式
一、普通的java项目 File--New--Project 选择Java,jdk选择1.8版本,然后点next 输入项目名和路径名,点击Finish 创建包结构,编写Class类 编写主方法,输出Hello标志完成 二、普通的javaWeb项目 Java Enterprise-- 勾选…...
隐形革命:环境智能如何重构“人-机-境“共生新秩序
引言 在万物互联的时代,环境智能(Ambient Intelligence, AmI)正以“隐形革命者”的姿态重塑人类生活场景。通过分布式传感器、边缘计算与自适应算法的深度融合,AmI构建出能感知、学习并响应人类行为的智慧环境。 本文基于多领域研…...
Mysql唯一性约束
唯一性约束(Unique Constraint)是数据库设计中用于保证表中某一列或多列组合的值具有唯一性的一种规则。它可以防止在指定列中插入重复的数据,有助于维护数据的完整性和准确性。下面从几个方面为你详细解释 作用 确保数据准确性:…...
QuecPython+GNSS:实现快速定位
概述 QuecPython 结合 GNSS(全球导航卫星系统)模块为物联网设备提供开箱即用的定位能力解决方案。该方案支持 GPS/北斗/GLONASS/Galileo 多系统联合定位,为物联网开发者提供从硬件接入到云端服务的全栈式定位解决方案。 优势特点 多体系定…...
【从零开始:自制一个Java消息队列(MQ)】
🚀 从零开始:自制一个Java消息队列(MQ) 在现代分布式系统中,消息队列(Message Queue,MQ)已经成为一个至关重要的组件。它帮助系统在异步处理、负载均衡、解耦等方面提供了强大的支持…...
WHAT - 已阅读书单
指数基金投资指南✅ 我们终将变富 纳瓦尔宝典 围城✅ 许三观卖血记✅ 骆驼祥子✅ 活着 白鹿原✅ 百年孤独 君主论 阿Q正传✅ 蛤蟆先生去看心理医生✅ 思考,快与慢 三体✅ 人类简史:从动物到上帝✅ 明朝那些事✅ 三国演义✅ 中国历代政治得失✅ 资治…...
代码随想录算法训练营第60期第十七天打卡
今天我们继续进入二叉树的下一个章节,今天的内容我在写今天的博客前大致看了一下部分题目难度不算大,那我们就进入今天的题目。 第一题对应力扣编号为654的题目最大二叉树 这道题目的坑相当多,我第一次题目没有看明白就是我不知道到底是如何…...
C 语言数组详解
一、数组的基本概念 在 C 语言中,数组是一种相同数据类型元素的集合,这些元素在内存中连续存储。通过数组,我们可以用一个统一的名字来管理一组相关的数据,并且通过下标(索引)快速访问其中的每一个元素。例…...
ADVB协议同步
关于视频传输,有多种控制时序。协议标准允许设计者选择有限的几个速率的接口来满足 系统设计目标。例如,一些系统使用总线时序发送信息通过line-by-line;在这个案例中, 容器的sof作为vsync同步的点。horizontal line blanding将插入idles,ADV…...
基于LAB颜色空间的增强型颜色迁移算法
本文算法使用Grok完成所有内容,包含算法改进和代码编写,可大大提升代码编写速度,算法改进速度,提供相关idea,提升效率; 概述 本文档描述了一种基于LAB颜色空间的颜色迁移算法,用于将缩略图D的…...
复合材料高置信度 DIC 测量与高级实验技术研讨会邀请函
邀请函 2025年5月8日 上海 中国复合材料学会官网会议通知 您可以点击上方蓝字跳转官网查看官网信息 主办单位: 中国复合材料学会 协办单位: 研索仪器科技(上海)有限公司 数字图像相关技术(Digital Image Correla…...