Android应用独立语言管理架构设计与系统级权限集成方案
【免费下载链接】Language-SelectorLanguage Selector let users select individual app languages (Android 13+)项目地址: https://gitcode.com/gh_mirrors/la/Language-Selector
Android 13及以上系统引入了应用级语言设置功能,但部分定制ROM(如MIUI)未提供此功能的用户界面。Language Selector项目通过系统级权限集成和Shizuku框架实现了高性能分布式应用语言管理解决方案,解决了Android多语言环境管理的技术痛点。
系统架构设计与核心模块
权限层架构:Shizuku系统服务桥接
项目采用Shizuku框架作为系统服务访问的桥梁,实现了对Android LocaleManager API的权限访问。核心权限管理模块位于UserService类中,通过ILocaleManager接口与系统Locale服务进行交互:
class UserService : IUserService.Stub() { var LOCALE_MANAGER: ILocaleManager? = null fun requiresLocaleManager() { if (LOCALE_MANAGER != null) return val localeBinder = SystemServiceHelper.getSystemService("locale") LOCALE_MANAGER = ILocaleManager.Stub.asInterface(localeBinder) } override fun setApplicationLocales(packageName: String?, locales: LocaleList?) { requiresLocaleManager() val currentUser = ActivityManager.getCurrentUser() LOCALE_MANAGER!!.setApplicationLocales(packageName, currentUser, locales, true) } }该架构设计实现了系统级权限的安全访问,通过Shizuku提供的Binder机制与Android系统服务进行通信,确保了对LocaleManager API的完整控制权。
语言环境管理模块
LocaleManager类负责处理语言环境的解析和管理,通过Java的Locale API获取系统支持的所有语言环境:
class LocaleManager { val localeList = ArrayList<LocaleRegion>() init { val locales = Locale.getAvailableLocales() val localeListMap = mutableMapOf<String, LocaleRegion>() for (locale in locales) { val languageName = locale.capDisplayName() val languageTag = locale.toLanguageTag() val language = locale.getDisplayLanguage(locale).replaceFirstChar { it.uppercaseChar() } val existingLocale = localeListMap[language] if (existingLocale != null) { val singleLocale = SingleLocale(languageName, languageTag) existingLocale.locales.add(singleLocale) continue } localeListMap[language] = LocaleRegion(language, arrayListOf()) } localeList.addAll(localeListMap.values) localeList.sortBy { it.language } } }应用语言设置界面架构:展示LocaleManager模块与系统Locale服务的交互流程,包含语言环境解析、分类管理和排序策略
依赖注入与组件管理
项目采用Hilt进行依赖注入管理,Modules.kt文件定义了核心组件的单例模式:
@InstallIn(SingletonComponent::class) @Module object Modules { @Singleton @Provides fun provideLocaleManager(): LocaleManager { return LocaleManager() } }ViewModel架构设计与状态管理
主界面状态管理
MainScreenVm采用响应式状态管理架构,结合Kotlin协程实现异步数据加载:
@HiltViewModel class MainScreenVm @Inject constructor( val app: Application ) : ViewModel() { private val _uiState = MutableStateFlow(MainScreenState()) val uiState: StateFlow<MainScreenState> = _uiState.asStateFlow() fun fillListOfApps(getAlsoSystemApps: Boolean = false) { _uiState.value.listOfApps.clear() viewModelScope.launch(Dispatchers.IO) { val packageList = getInstalledPackages(getAlsoSystemApps).map { it } val sortedList = packageList.sortedBy { app.packageManager.getLabel(it).lowercase() } _uiState.value.listOfApps.addAll(sortedList) _uiState.update { it.copy(isLoading = false) } } } }应用信息状态模型
AppInfoState定义了应用语言管理的核心数据模型:
data class AppInfoState( val appIcon: Drawable? = null, val appName: String = "", val appPackage: String = "", val currentLanguage: String = "", val listOfSuggestedLanguages: MutableList<SingleLocale> = mutableStateListOf(), val listOfPinnedLanguages: MutableList<SingleLocale> = mutableStateListOf(), val selectedLanguage: Int = -1, val listOfAllLanguages: MutableList<LocaleRegion> = mutableStateListOf(), )快速设置磁贴(QS Tile)实现方案
系统集成架构
QSTile类实现了Android快速设置磁贴功能,提供了实时应用语言切换能力:
class QSTile : TileService() { private var isLoaded = false private val locales = mutableListOf<SingleLocale>() override fun onClick() { if (!this::targetPackage.isInitialized) return UserServiceProvider.run { val currentLocale = getApplicationLocales(targetPackage.packageName) val nextLocale = getNextSingleLocale(currentLocale) val localeList = if (nextLocale.languageTag.isEmpty()) LocaleList() else LocaleList(nextLocale.toLocale()) setApplicationLocales(targetPackage.packageName, localeList) updateTile() } } }系统快捷设置面板集成架构:展示QSTile与系统服务的交互流程,包含实时语言切换和状态同步机制
语言切换算法
QSTile实现了循环切换算法,确保语言切换的连贯性和用户体验:
private fun getNextSingleLocale(localeList: LocaleList): SingleLocale { if (locales.isEmpty()) throw Exception("getNextSingleLocale() should be not called with empty MutableList<SingleLocale> locales") if (localeList.isEmpty) return locales[1] for (i in 0 until locales.size) { val thisLocale = locales[i] if (localeList[0].toLanguageTag() == thisLocale.languageTag) { if (i == locales.size - 1) { return locales.first() } return locales[i + 1] } } return locales.first() }系统服务集成与权限管理
多服务管理器架构
项目实现了对Android系统多个服务的集成访问:
var ACTIVITY_MANAGER: IActivityManager? = null fun requiresActivityManager() { if (ACTIVITY_MANAGER != null) return val am = SystemServiceHelper.getSystemService("activity") ACTIVITY_MANAGER = IActivityManager.Stub.asInterface(am) } override fun forceStopPackage(packageName: String?) { requiresActivityManager() val currentUser = ActivityManager.getCurrentUser() ACTIVITY_MANAGER!!.forceStopPackage(packageName, currentUser) }运行时任务管理
通过ActivityTaskManager获取当前运行应用信息:
var ACTIVITY_TASK_MANAGER: IActivityTaskManager? = null fun requiresActivityTaskManager() { if (ACTIVITY_TASK_MANAGER != null) return val am = SystemServiceHelper.getSystemService("activity_task") ACTIVITY_TASK_MANAGER = IActivityTaskManager.Stub.asInterface(am) } override fun getFirstRunningTaskPackage(): String { requiresActivityTaskManager() val runningTask = try { ACTIVITY_TASK_MANAGER!!.getTasks(1, false, false, -1).first() } catch (e: NoSuchMethodError) { Log.w( BuildConfig.APPLICATION_ID, "getTasks failed, trying again without displayId, error: ${e.stackTraceToString()}" ) ACTIVITY_TASK_MANAGER!!.getTasks(1, false, false).first() } return runningTask.topActivity?.packageName ?: "" }性能优化与兼容性策略
语言列表加载优化
项目采用延迟加载和缓存策略优化语言列表性能:
- 按需加载:仅在需要时解析Locale.getAvailableLocales()
- 内存缓存:LocaleManager实例采用单例模式,避免重复解析
- 异步处理:ViewModel使用协程进行异步数据加载
版本兼容性处理
针对不同Android版本提供兼容性支持:
override fun setApplicationLocales(packageName: String?, locales: LocaleList?) { requiresLocaleManager() val currentUser = ActivityManager.getCurrentUser() if (Build.VERSION.SDK_INT == 33 && Build.VERSION.RELEASE_OR_CODENAME != "UpsideDownCake") { LOCALE_MANAGER!!.setApplicationLocales(packageName, currentUser, locales) return } LOCALE_MANAGER!!.setApplicationLocales(packageName, currentUser, locales, true) }部署与构建配置
Gradle依赖管理
项目采用现代Android开发工具链,通过libs.versions.toml进行依赖版本管理:
[versions] shizuku = "13.1.1" hilt = "2.46" navigation-compose = "2.5.3" compose-bom = "2023.05.00" [libraries] shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation-compose" }AndroidManifest配置
应用声明了必要的系统权限和服务:
<service android:name=".QSTile" android:exported="true" android:icon="@drawable/qs_tile" android:label="@string/app_name" android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> <meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE" android:value="true" /> <intent-filter> <action android:name="android.service.quicksettings.action.QS_TILE" /> </intent-filter> </service>技术架构总结
Language Selector项目通过以下技术架构实现了高性能应用语言管理:
- 权限层:Shizuku框架提供系统服务访问权限
- 服务层:UserService封装LocaleManager、ActivityManager等系统服务
- 业务层:LocaleManager处理语言环境,ViewModel管理应用状态
- UI层:Jetpack Compose构建现代化用户界面
- 系统集成层:QSTile提供快捷设置磁贴功能
该架构设计确保了系统级权限的安全访问、语言环境的高效管理以及用户体验的流畅性,为Android应用独立语言设置提供了完整的解决方案。
【免费下载链接】Language-SelectorLanguage Selector let users select individual app languages (Android 13+)项目地址: https://gitcode.com/gh_mirrors/la/Language-Selector
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考