概述
在Android开发中,监听数据变化是实现响应式UI的关键技术。本指南深入介绍Android平台的数据监听机制,包括ContentObserver、LiveData、Room等现代化解决方案,帮助开发者构建数据驱动的应用。
学习目标
- 掌握ContentProvider和ContentObserver机制
- 理解Android数据观察者模式
- 学习Room数据库的数据监听
- 掌握LiveData的使用方法
- 了解FileObserver文件系统监听
- 掌握数据监听的性能优化
ContentProvider基础
ContentProvider是什么?
ContentProvider是Android四大组件之一,用于在不同应用之间共享数据。它提供了标准的接口来操作数据,并支持数据变化通知机制。
创建ContentProvider
// Java实现
public class MyContentProvider extends ContentProvider {
private static final String AUTHORITY = "com.example.provider";
private static final String TABLE_NAME = "users";
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/" + TABLE_NAME);
private static final int USERS = 1;
private static final int USER_ID = 2;
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI(AUTHORITY, TABLE_NAME, USERS);
uriMatcher.addURI(AUTHORITY, TABLE_NAME + "/#", USER_ID);
}
private DatabaseHelper dbHelper;
@Override
public boolean onCreate() {
dbHelper = new DatabaseHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case USERS:
cursor = db.query(TABLE_NAME, projection, selection,
selectionArgs, null, null, sortOrder);
break;
case USER_ID:
String id = uri.getLastPathSegment();
cursor = db.query(TABLE_NAME, projection, "_id = ?",
new String[]{id}, null, null, sortOrder);
break;
}
// 设置通知URI
if (cursor != null) {
cursor.setNotificationUri(getContext().getContentResolver(), uri);
}
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
long id = db.insert(TABLE_NAME, null, values);
if (id > 0) {
Uri newUri = ContentUris.withAppendedId(CONTENT_URI, id);
// 通知数据变化
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count = 0;
switch (uriMatcher.match(uri)) {
case USERS:
count = db.update(TABLE_NAME, values, selection, selectionArgs);
break;
case USER_ID:
String id = uri.getLastPathSegment();
count = db.update(TABLE_NAME, values, "_id = ?", new String[]{id});
break;
}
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count = 0;
switch (uriMatcher.match(uri)) {
case USERS:
count = db.delete(TABLE_NAME, selection, selectionArgs);
break;
case USER_ID:
String id = uri.getLastPathSegment();
count = db.delete(TABLE_NAME, "_id = ?", new String[]{id});
break;
}
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case USERS:
return "vnd.android.cursor.dir/vnd.example.users";
case USER_ID:
return "vnd.android.cursor.item/vnd.example.user";
default:
return null;
}
}
}
// Kotlin实现
class MyContentProvider : ContentProvider() {
companion object {
private const val AUTHORITY = "com.example.provider"
private const val TABLE_NAME = "users"
val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY/$TABLE_NAME")
private const val USERS = 1
private const val USER_ID = 2
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, TABLE_NAME, USERS)
addURI(AUTHORITY, "$TABLE_NAME/#", USER_ID)
}
}
private lateinit var dbHelper: DatabaseHelper
override fun onCreate(): Boolean {
dbHelper = DatabaseHelper(context!!)
return true
}
override fun query(
uri: Uri,
projection: Array<String>?,
selection: String?,
selectionArgs: Array<String>?,
sortOrder: String?
): Cursor? {
val db = dbHelper.readableDatabase
val cursor = when (uriMatcher.match(uri)) {
USERS -> db.query(
TABLE_NAME, projection, selection,
selectionArgs, null, null, sortOrder
)
USER_ID -> {
val id = uri.lastPathSegment
db.query(
TABLE_NAME, projection, "_id = ?",
arrayOf(id), null, null, sortOrder
)
}
else -> null
}
cursor?.setNotificationUri(context?.contentResolver, uri)
return cursor
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
val db = dbHelper.writableDatabase
val id = db.insert(TABLE_NAME, null, values)
return if (id > 0) {
val newUri = ContentUris.withAppendedId(CONTENT_URI, id)
context?.contentResolver?.notifyChange(newUri, null)
newUri
} else {
null
}
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<String>?
): Int {
val db = dbHelper.writableDatabase
val count = when (uriMatcher.match(uri)) {
USERS -> db.update(TABLE_NAME, values, selection, selectionArgs)
USER_ID -> {
val id = uri.lastPathSegment
db.update(TABLE_NAME, values, "_id = ?", arrayOf(id))
}
else -> 0
}
if (count > 0) {
context?.contentResolver?.notifyChange(uri, null)
}
return count
}
override fun delete(
uri: Uri,
selection: String?,
selectionArgs: Array<String>?
): Int {
val db = dbHelper.writableDatabase
val count = when (uriMatcher.match(uri)) {
USERS -> db.delete(TABLE_NAME, selection, selectionArgs)
USER_ID -> {
val id = uri.lastPathSegment
db.delete(TABLE_NAME, "_id = ?", arrayOf(id))
}
else -> 0
}
if (count > 0) {
context?.contentResolver?.notifyChange(uri, null)
}
return count
}
override fun getType(uri: Uri): String? {
return when (uriMatcher.match(uri)) {
USERS -> "vnd.android.cursor.dir/vnd.example.users"
USER_ID -> "vnd.android.cursor.item/vnd.example.user"
else -> null
}
}
}
在AndroidManifest.xml中注册
<provider
android:name=".MyContentProvider"
android:authorities="com.example.provider"
android:exported="false"
android:grantUriPermissions="true" />
ContentObserver详解
ContentObserver基础
ContentObserver用于监听ContentProvider中数据的变化:
// Java实现
public class MyContentObserver extends ContentObserver {
private Context context;
private Handler handler;
public MyContentObserver(Context context, Handler handler) {
super(handler);
this.context = context;
this.handler = handler;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// 数据发生变化时调用
Log.d("ContentObserver", "数据发生变化");
// 处理数据变化
handleDataChange();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
// 数据发生变化时调用(带URI)
Log.d("ContentObserver", "数据发生变化: " + uri.toString());
handleDataChange(uri);
}
private void handleDataChange() {
// 查询最新数据
Cursor cursor = context.getContentResolver().query(
MyContentProvider.CONTENT_URI,
null, null, null, null
);
if (cursor != null) {
// 处理数据
while (cursor.moveToNext()) {
// 读取数据
}
cursor.close();
}
}
private void handleDataChange(Uri uri) {
// 根据URI处理特定数据变化
}
}
// Kotlin实现
class MyContentObserver(
private val context: Context,
handler: Handler
) : ContentObserver(handler) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
Log.d("ContentObserver", "数据发生变化")
handleDataChange()
}
override fun onChange(selfChange: Boolean, uri: Uri?) {
super.onChange(selfChange, uri)
Log.d("ContentObserver", "数据发生变化: ${uri?.toString()}")
uri?.let { handleDataChange(it) }
}
private fun handleDataChange() {
context.contentResolver.query(
MyContentProvider.CONTENT_URI,
null, null, null, null
)?.use { cursor ->
while (cursor.moveToNext()) {
// 读取数据
}
}
}
private fun handleDataChange(uri: Uri) {
// 根据URI处理特定数据变化
}
}
注册和注销ContentObserver
// Java
public class MainActivity extends AppCompatActivity {
private MyContentObserver contentObserver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建ContentObserver
Handler handler = new Handler(Looper.getMainLooper());
contentObserver = new MyContentObserver(this, handler);
// 注册ContentObserver
getContentResolver().registerContentObserver(
MyContentProvider.CONTENT_URI,
true, // notifyForDescendants
contentObserver
);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销ContentObserver
if (contentObserver != null) {
getContentResolver().unregisterContentObserver(contentObserver);
}
}
}
// Kotlin
class MainActivity : AppCompatActivity() {
private lateinit var contentObserver: MyContentObserver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 创建ContentObserver
val handler = Handler(Looper.getMainLooper())
contentObserver = MyContentObserver(this, handler)
// 注册ContentObserver
contentResolver.registerContentObserver(
MyContentProvider.CONTENT_URI,
true, // notifyForDescendants
contentObserver
)
}
override fun onDestroy() {
super.onDestroy()
// 注销ContentObserver
contentResolver.unregisterContentObserver(contentObserver)
}
}
数据变化监听实现
完整示例:联系人数据监听
class ContactsObserverActivity : AppCompatActivity() {
private lateinit var contactsObserver: ContentObserver
private lateinit var contactsAdapter: ContactsAdapter
private val contacts = mutableListOf<Contact>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contacts_observer)
setupRecyclerView()
setupContactsObserver()
loadContacts()
// 请求权限
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.READ_CONTACTS
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_CONTACTS),
PERMISSION_REQUEST_CODE
)
}
}
private fun setupRecyclerView() {
contactsAdapter = ContactsAdapter(contacts)
recyclerView.apply {
layoutManager = LinearLayoutManager(this@ContactsObserverActivity)
adapter = contactsAdapter
}
}
private fun setupContactsObserver() {
contactsObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
Log.d(TAG, "联系人数据发生变化")
loadContacts()
}
}
// 注册观察者
contentResolver.registerContentObserver(
ContactsContract.Contacts.CONTENT_URI,
true,
contactsObserver
)
}
private fun loadContacts() {
lifecycleScope.launch(Dispatchers.IO) {
val contactsList = mutableListOf<Contact>()
contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
arrayOf(
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER
),
null,
null,
ContactsContract.Contacts.DISPLAY_NAME + " ASC"
)?.use { cursor ->
while (cursor.moveToNext()) {
val id = cursor.getLong(0)
val name = cursor.getString(1)
val hasPhone = cursor.getInt(2) > 0
if (hasPhone) {
val phoneNumber = getPhoneNumber(id)
contactsList.add(Contact(id, name, phoneNumber))
}
}
}
withContext(Dispatchers.Main) {
contacts.clear()
contacts.addAll(contactsList)
contactsAdapter.notifyDataSetChanged()
}
}
}
private fun getPhoneNumber(contactId: Long): String? {
var phoneNumber: String? = null
contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER),
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
arrayOf(contactId.toString()),
null
)?.use { cursor ->
if (cursor.moveToFirst()) {
phoneNumber = cursor.getString(0)
}
}
return phoneNumber
}
override fun onDestroy() {
super.onDestroy()
contentResolver.unregisterContentObserver(contactsObserver)
}
data class Contact(
val id: Long,
val name: String,
val phoneNumber: String?
)
companion object {
private const val TAG = "ContactsObserver"
private const val PERMISSION_REQUEST_CODE = 100
}
}
系统设置监听
class SettingsObserverActivity : AppCompatActivity() {
private lateinit var brightnessObserver: ContentObserver
private lateinit var volumeObserver: ContentObserver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings_observer)
setupBrightnessObserver()
setupVolumeObserver()
}
private fun setupBrightnessObserver() {
brightnessObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
val brightness = getBrightness()
Log.d(TAG, "屏幕亮度变化: $brightness")
updateBrightnessUI(brightness)
}
}
contentResolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS),
false,
brightnessObserver
)
}
private fun setupVolumeObserver() {
volumeObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
Log.d(TAG, "音量变化: $volume")
updateVolumeUI(volume)
}
}
contentResolver.registerContentObserver(
Settings.System.CONTENT_URI,
true,
volumeObserver
)
}
private fun getBrightness(): Int {
return try {
Settings.System.getInt(
contentResolver,
Settings.System.SCREEN_BRIGHTNESS
)
} catch (e: Settings.SettingNotFoundException) {
0
}
}
private fun updateBrightnessUI(brightness: Int) {
tvBrightness.text = "当前亮度: $brightness"
seekBarBrightness.progress = brightness
}
private fun updateVolumeUI(volume: Int) {
tvVolume.text = "当前音量: $volume"
seekBarVolume.progress = volume
}
override fun onDestroy() {
super.onDestroy()
contentResolver.unregisterContentObserver(brightnessObserver)
contentResolver.unregisterContentObserver(volumeObserver)
}
companion object {
private const val TAG = "SettingsObserver"
}
}
Room数据库集成
Room与LiveData结合
Room数据库原生支持LiveData,可以自动监听数据变化:
// Entity
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true)
val id: Long = 0,
val name: String,
val email: String,
val age: Int
)
// DAO
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAllUsers(): LiveData<List<User>>
@Query("SELECT * FROM users WHERE id = :userId")
fun getUserById(userId: Long): LiveData<User>
@Insert
suspend fun insertUser(user: User)
@Update
suspend fun updateUser(user: User)
@Delete
suspend fun deleteUser(user: User)
@Query("SELECT * FROM users WHERE name LIKE :query")
fun searchUsers(query: String): LiveData<List<User>>
}
// Database
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database"
).build()
INSTANCE = instance
instance
}
}
}
}
// ViewModel
class UserViewModel(application: Application) : AndroidViewModel(application) {
private val userDao = AppDatabase.getDatabase(application).userDao()
val allUsers: LiveData<List<User>> = userDao.getAllUsers()
fun insertUser(user: User) {
viewModelScope.launch(Dispatchers.IO) {
userDao.insertUser(user)
}
}
fun updateUser(user: User) {
viewModelScope.launch(Dispatchers.IO) {
userDao.updateUser(user)
}
}
fun deleteUser(user: User) {
viewModelScope.launch(Dispatchers.IO) {
userDao.deleteUser(user)
}
}
fun searchUsers(query: String): LiveData<List<User>> {
return userDao.searchUsers("%$query%")
}
}
// Activity
class UserListActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
private lateinit var userAdapter: UserAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_list)
setupRecyclerView()
setupViewModel()
setupFab()
}
private fun setupRecyclerView() {
userAdapter = UserAdapter { user ->
// 处理用户点击
showUserDetail(user)
}
recyclerView.apply {
layoutManager = LinearLayoutManager(this@UserListActivity)
adapter = userAdapter
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
}
}
private fun setupViewModel() {
viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
// 观察数据变化
viewModel.allUsers.observe(this) { users ->
// 数据变化时自动更新UI
userAdapter.submitList(users)
if (users.isEmpty()) {
tvEmpty.visibility = View.VISIBLE
recyclerView.visibility = View.GONE
} else {
tvEmpty.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
}
}
private fun setupFab() {
fabAdd.setOnClickListener {
showAddUserDialog()
}
}
private fun showAddUserDialog() {
val dialogView = layoutInflater.inflate(R.layout.dialog_add_user, null)
val etName = dialogView.findViewById<EditText>(R.id.et_name)
val etEmail = dialogView.findViewById<EditText>(R.id.et_email)
val etAge = dialogView.findViewById<EditText>(R.id.et_age)
AlertDialog.Builder(this)
.setTitle("添加用户")
.setView(dialogView)
.setPositiveButton("添加") { _, _ ->
val name = etName.text.toString()
val email = etEmail.text.toString()
val age = etAge.text.toString().toIntOrNull() ?: 0
if (name.isNotEmpty() && email.isNotEmpty()) {
val user = User(name = name, email = email, age = age)
viewModel.insertUser(user)
}
}
.setNegativeButton("取消", null)
.show()
}
private fun showUserDetail(user: User) {
// 显示用户详情
}
}
Room数据库迁移监听
class DatabaseMigrationObserver {
companion object {
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
// 添加新列
database.execSQL("ALTER TABLE users ADD COLUMN phone TEXT")
// 通知迁移完成
Log.d("Migration", "数据库从版本1迁移到版本2")
}
}
val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
// 创建新表
database.execSQL("""
CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
userId INTEGER NOT NULL,
title TEXT NOT NULL,
content TEXT,
createdAt INTEGER NOT NULL,
FOREIGN KEY(userId) REFERENCES users(id) ON DELETE CASCADE
)
""")
}
}
}
}
// 配置数据库迁移
val database = Room.databaseBuilder(
context,
AppDatabase::class.java,
"app_database"
)
.addMigrations(
DatabaseMigrationObserver.MIGRATION_1_2,
DatabaseMigrationObserver.MIGRATION_2_3
)
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
Log.d("Database", "数据库创建完成")
}
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
Log.d("Database", "数据库打开")
}
})
.build()
LiveData与数据观察
自定义LiveData
class NetworkLiveData(private val context: Context) : LiveData<NetworkInfo>() {
private val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
postValue(NetworkInfo(true, getNetworkType()))
}
override fun onLost(network: Network) {
super.onLost(network)
postValue(NetworkInfo(false, NetworkType.NONE))
}
}
override fun onActive() {
super.onActive()
val networkRequest = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
// 立即更新当前状态
val isConnected = connectivityManager.activeNetworkInfo?.isConnected ?: false
postValue(NetworkInfo(isConnected, getNetworkType()))
}
override fun onInactive() {
super.onInactive()
connectivityManager.unregisterNetworkCallback(networkCallback)
}
private fun getNetworkType(): NetworkType {
val activeNetwork = connectivityManager.activeNetworkInfo
return when (activeNetwork?.type) {
ConnectivityManager.TYPE_WIFI -> NetworkType.WIFI
ConnectivityManager.TYPE_MOBILE -> NetworkType.MOBILE
else -> NetworkType.NONE
}
}
data class NetworkInfo(
val isConnected: Boolean,
val type: NetworkType
)
enum class NetworkType {
WIFI, MOBILE, NONE
}
}
// 使用
class MainActivity : AppCompatActivity() {
private lateinit var networkLiveData: NetworkLiveData
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
networkLiveData = NetworkLiveData(this)
networkLiveData.observe(this) { networkInfo ->
when {
!networkInfo.isConnected -> {
showNoConnectionDialog()
}
networkInfo.type == NetworkLiveData.NetworkType.MOBILE -> {
showMobileDataWarning()
}
}
}
}
}
MediatorLiveData组合多个数据源
class UserProfileViewModel : ViewModel() {
private val userId = MutableLiveData<Long>()
// 用户基本信息
private val userInfo: LiveData<User> = userId.switchMap { id ->
userRepository.getUserById(id)
}
// 用户帖子
private val userPosts: LiveData<List<Post>> = userId.switchMap { id ->
postRepository.getPostsByUserId(id)
}
// 用户关注者
private val followers: LiveData<List<User>> = userId.switchMap { id ->
userRepository.getFollowers(id)
}
// 组合多个数据源
val userProfile = MediatorLiveData<UserProfile>().apply {
addSource(userInfo) { user ->
value = value?.copy(user = user) ?: UserProfile(user = user)
}
addSource(userPosts) { posts ->
value = value?.copy(posts = posts) ?: UserProfile(posts = posts)
}
addSource(followers) { followersList ->
value = value?.copy(followers = followersList) ?: UserProfile(followers = followersList)
}
}
fun loadUserProfile(id: Long) {
userId.value = id
}
data class UserProfile(
val user: User? = null,
val posts: List<Post> = emptyList(),
val followers: List<User> = emptyList()
)
}
FileObserver文件监听
监听文件系统变化
class LogFileObserver(
private val logDirectory: File,
private val onLogFileChanged: (String) -> Unit
) : FileObserver(logDirectory.absolutePath, ALL_EVENTS) {
override fun onEvent(event: Int, path: String?) {
path?.let {
when (event) {
CREATE -> {
Log.d(TAG, "文件创建: $path")
if (path.endsWith(".log")) {
onLogFileChanged("新日志文件: $path")
}
}
DELETE -> {
Log.d(TAG, "文件删除: $path")
}
MODIFY -> {
Log.d(TAG, "文件修改: $path")
if (path.endsWith(".log")) {
readLatestLogEntry(File(logDirectory, path))
}
}
MOVED_TO -> {
Log.d(TAG, "文件移入: $path")
}
}
}
}
private fun readLatestLogEntry(file: File) {
try {
val lines = file.readLines()
if (lines.isNotEmpty()) {
onLogFileChanged("最新日志: ${lines.last()}")
}
} catch (e: Exception) {
Log.e(TAG, "读取日志文件失败", e)
}
}
companion object {
private const val TAG = "LogFileObserver"
}
}
// 使用
class LogMonitorActivity : AppCompatActivity() {
private lateinit var fileObserver: LogFileObserver
private val logEntries = mutableListOf<String>()
private lateinit var logAdapter: ArrayAdapter<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_log_monitor)
setupListView()
setupFileObserver()
}
private fun setupListView() {
logAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, logEntries)
listView.adapter = logAdapter
}
private fun setupFileObserver() {
val logDir = File(getExternalFilesDir(null), "logs")
if (!logDir.exists()) {
logDir.mkdirs()
}
fileObserver = LogFileObserver(logDir) { message ->
runOnUiThread {
logEntries.add(0, "${Date()}: $message")
logAdapter.notifyDataSetChanged()
}
}
fileObserver.startWatching()
}
override fun onDestroy() {
super.onDestroy()
fileObserver.stopWatching()
}
}
监听下载目录
class DownloadObserver(
private val context: Context
) : FileObserver(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath,
CREATE or DELETE or MOVED_TO
) {
private val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
override fun onEvent(event: Int, path: String?) {
path?.let {
when (event) {
CREATE, MOVED_TO -> {
if (isMediaFile(path)) {
scanMediaFile(path)
notifyNewDownload(path)
}
}
DELETE -> {
notifyFileDeleted(path)
}
}
}
}
private fun isMediaFile(filename: String): Boolean {
val mediaExtensions = listOf(".jpg", ".jpeg", ".png", ".mp4", ".mp3", ".pdf")
return mediaExtensions.any { filename.endsWith(it, ignoreCase = true) }
}
private fun scanMediaFile(filename: String) {
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
filename
)
MediaScannerConnection.scanFile(
context,
arrayOf(file.absolutePath),
null
) { path, uri ->
Log.d(TAG, "媒体扫描完成: $path")
}
}
private fun notifyNewDownload(filename: String) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"下载通知",
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("文件下载完成")
.setContentText(filename)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setAutoCancel(true)
.build()
notificationManager.notify(filename.hashCode(), notification)
}
private fun notifyFileDeleted(filename: String) {
Log.d(TAG, "文件被删除: $filename")
}
companion object {
private const val TAG = "DownloadObserver"
private const val CHANNEL_ID = "download_channel"
}
}
性能优化
1. 避免过度监听
class OptimizedContentObserver : ContentObserver(Handler(Looper.getMainLooper())) {
private var lastUpdateTime = 0L
private val updateThreshold = 1000L // 1秒内的变化只处理一次
override fun onChange(selfChange: Boolean, uri: Uri?) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastUpdateTime < updateThreshold) {
return
}
lastUpdateTime = currentTime
// 处理数据变化
handleDataChange(uri)
}
private fun handleDataChange(uri: Uri?) {
// 批量处理数据变化
}
}
2. 使用合适的通知粒度
class GranularContentProvider : ContentProvider() {
companion object {
private const val AUTHORITY = "com.example.provider"
// 定义不同粒度的URI
val USERS_URI = Uri.parse("content://$AUTHORITY/users")
val USER_DETAIL_URI = Uri.parse("content://$AUTHORITY/users/detail")
val USER_STATS_URI = Uri.parse("content://$AUTHORITY/users/stats")
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<String>?
): Int {
val count = // 执行更新
// 根据更新内容通知不同的URI
when {
values?.containsKey("name") == true -> {
context?.contentResolver?.notifyChange(USER_DETAIL_URI, null)
}
values?.containsKey("score") == true -> {
context?.contentResolver?.notifyChange(USER_STATS_URI, null)
}
else -> {
context?.contentResolver?.notifyChange(uri, null)
}
}
return count
}
}
3. 批量操作优化
class BatchOperationProvider : ContentProvider() {
override fun applyBatch(
operations: ArrayList<ContentProviderOperation>
): Array<ContentProviderResult> {
val database = dbHelper.writableDatabase
val results = arrayListOf<ContentProviderResult>()
database.beginTransaction()
try {
for (operation in operations) {
results.add(operation.apply(this, results, results.size))
}
database.setTransactionSuccessful()
// 批量操作完成后统一通知
context?.contentResolver?.notifyChange(CONTENT_URI, null)
} finally {
database.endTransaction()
}
return results.toTypedArray()
}
}
最佳实践
1. 生命周期管理
class LifecycleAwareObserver(
private val lifecycle: Lifecycle,
private val contentResolver: ContentResolver,
private val uri: Uri,
private val onChange: () -> Unit
) : LifecycleObserver {
private val contentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
onChange()
}
}
init {
lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startObserving() {
contentResolver.registerContentObserver(uri, true, contentObserver)
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopObserving() {
contentResolver.unregisterContentObserver(contentObserver)
}
}
// 使用
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LifecycleAwareObserver(
lifecycle,
contentResolver,
MyContentProvider.CONTENT_URI
) {
// 处理数据变化
loadData()
}
}
}
2. 错误处理
class RobustContentObserver(
private val context: Context,
handler: Handler
) : ContentObserver(handler) {
override fun onChange(selfChange: Boolean, uri: Uri?) {
try {
handleChange(uri)
} catch (e: SecurityException) {
Log.e(TAG, "没有权限访问: $uri", e)
requestPermission()
} catch (e: Exception) {
Log.e(TAG, "处理数据变化时出错", e)
// 可以考虑上报错误
}
}
private fun handleChange(uri: Uri?) {
// 安全地处理数据变化
context.contentResolver.query(
uri ?: return,
null, null, null, null
)?.use { cursor ->
// 处理数据
}
}
private fun requestPermission() {
// 请求必要的权限
}
companion object {
private const val TAG = "RobustContentObserver"
}
}
3. 测试策略
@RunWith(AndroidJUnit4::class)
class ContentObserverTest {
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
private lateinit var context: Context
private lateinit var contentResolver: ContentResolver
@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
contentResolver = context.contentResolver
}
@Test
fun testContentObserverNotification() {
var notificationCount = 0
val latch = CountDownLatch(1)
val observer = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
notificationCount++
latch.countDown()
}
}
contentResolver.registerContentObserver(
TestContentProvider.CONTENT_URI,
true,
observer
)
// 触发数据变化
contentResolver.insert(TestContentProvider.CONTENT_URI, ContentValues().apply {
put("name", "Test")
})
// 等待通知
assertTrue(latch.await(5, TimeUnit.SECONDS))
assertEquals(1, notificationCount)
contentResolver.unregisterContentObserver(observer)
}
}
调试技巧
1. 日志记录
class DebugContentObserver(
private val tag: String,
handler: Handler
) : ContentObserver(handler) {
private var changeCount = 0
override fun onChange(selfChange: Boolean, uri: Uri?) {
changeCount++
Log.d(tag, """
ContentObserver触发 #$changeCount
URI: $uri
Self Change: $selfChange
Thread: ${Thread.currentThread().name}
Time: ${SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()).format(Date())}
""".trimIndent())
super.onChange(selfChange, uri)
}
}
2. 监控工具
object ContentObserverMonitor {
private val activeObservers = mutableMapOf<String, ObserverInfo>()
fun registerObserver(name: String, uri: Uri, observer: ContentObserver) {
activeObservers[name] = ObserverInfo(uri, observer, System.currentTimeMillis())
Log.d("Monitor", "注册Observer: $name for $uri")
}
fun unregisterObserver(name: String) {
activeObservers.remove(name)?.let { info ->
val duration = System.currentTimeMillis() - info.registrationTime
Log.d("Monitor", "注销Observer: $name, 活跃时间: ${duration}ms")
}
}
fun getActiveObservers(): List<String> {
return activeObservers.map { (name, info) ->
"$name -> ${info.uri}"
}
}
data class ObserverInfo(
val uri: Uri,
val observer: ContentObserver,
val registrationTime: Long
)
}
常见问题
问题1:ContentObserver不触发
原因:
- URI不匹配
- 没有调用notifyChange
- Observer未正确注册
解决方案:
// 确保URI匹配
contentResolver.registerContentObserver(
Uri.parse("content://com.example.provider/users"),
true, // notifyForDescendants = true
observer
)
// 确保在数据变化时通知
override fun insert(uri: Uri, values: ContentValues?): Uri? {
val id = // 插入数据
val newUri = ContentUris.withAppendedId(uri, id)
// 必须调用notifyChange
context?.contentResolver?.notifyChange(newUri, null)
return newUri
}
问题2:内存泄漏
原因:
- Observer未注销
- 使用非静态内部类
解决方案:
class SafeActivity : AppCompatActivity() {
private var contentObserver: ContentObserver? = null
override fun onStart() {
super.onStart()
contentObserver = createObserver()
contentResolver.registerContentObserver(uri, true, contentObserver!!)
}
override fun onStop() {
super.onStop()
contentObserver?.let {
contentResolver.unregisterContentObserver(it)
}
}
private fun createObserver(): ContentObserver {
// 使用静态类或独立类,避免持有Activity引用
return object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
// 处理变化
}
}
}
}
问题3:性能问题
原因:
- 频繁触发onChange
- 在onChange中执行耗时操作
解决方案:
class ThrottledContentObserver(
handler: Handler
) : ContentObserver(handler) {
private val pendingChanges = mutableSetOf<Uri>()
private val throttleRunnable = Runnable {
processPendingChanges()
}
override fun onChange(selfChange: Boolean, uri: Uri?) {
uri?.let { pendingChanges.add(it) }
handler.removeCallbacks(throttleRunnable)
handler.postDelayed(throttleRunnable, THROTTLE_DELAY)
}
private fun processPendingChanges() {
val changes = pendingChanges.toList()
pendingChanges.clear()
// 批量处理变化
GlobalScope.launch(Dispatchers.IO) {
handleChanges(changes)
}
}
private suspend fun handleChanges(uris: List<Uri>) {
// 异步处理
}
companion object {
private const val THROTTLE_DELAY = 500L
}
}
相关文章
Android组件通信
- Android Activity生命周期与组件通信完全指南 - Activity基础
- 广播机制 - BroadcastReceiver
- 服务 - Service组件
数据存储
- 内容提供者 - ContentProvider详解
- SharedPreferences - 轻量级存储
架构组件
总结
数据变化监听是Android应用开发中的重要技术,通过合理使用ContentObserver、LiveData、Room等技术,可以构建响应式的用户界面。
🎯 核心要点
- ContentObserver:监听ContentProvider数据变化
- LiveData:生命周期感知的可观察数据
- Room + LiveData:自动化的数据库监听
- FileObserver:文件系统变化监听
- 性能优化:避免过度监听,合理使用通知粒度
🛠️ 开发建议
- 注意生命周期管理,及时注销Observer
- 使用现代架构组件简化开发
- 合理设计数据变化通知粒度
- 异步处理耗时操作