热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android自定义开机向导踩坑

开机向导简介在Android设备第一次上电或者进行恢复出厂设置后第一次启动时运行的应用.用于对Android设备进行语言,网络等相关设置.Android源码中的开机向导本文都是基于

开机向导简介

在Android设备第一次上电或者进行恢复出厂设置后第一次启动时运行的应用.用于对Android设备进行语言,网络等相关设置.

Android源码中的开机向导

本文都是基于Android 8.0 系统源码来说明的.

DefaultActivity.java

在系统目录 packages\apps 之下有个 Provision 项目就是开机向导.但是里面只有一个简单的 DefaultActivity.来看下源码有什么内容.

public class DefaultActivity extends Activity {@Overrideprotected void onCreate(Bundle icicle) {super.onCreate(icicle);// Add a persistent setting to allow other apps to know the device has been provisioned.Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);//1Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);// remove this activity from the package manager.PackageManager pm = getPackageManager();ComponentName name = new ComponentName(this, DefaultActivity.class);//2pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);//3// terminate the activity.finish();}}
}

  1. 在第1个注释的代码行中有个关键字 Settings.Global.DEVICE_PROVISIONED 是配置全局设置告诉其他应用设备已经进行过初始化设置.

  2. 在第2个注释的代码行中的构造函数 ComponentName(Context pkg,Class cls) 需要传递2个参数,一个是上下文对象,另一个是class对象.这里是第一个坑,下面再讲.

  3. 在第3个注释的代码行中 setComponentEnabledSetting(ComponentName componentName,int newState,int flags) 是来设置组件的状态的API,以下是对参数的说明:

  • ComponentName 组件名

  • newState 组件新状态有以下三个状态:

    不可用状态:COMPONENT_ENABLED_STATE_DISABLED 可用状态:COMPONENT_ENABLED_STATE_ENABLED 默认状态:COMPONENT_ENABLED_STATE_DEFAULT

  • flag 行为标签

内容很简单,只有几行代码.主要是配置一个全局参数告诉其它应用已经设置并设置组件状态为不可用.

AndroidManifest.xml

再看下 AndroidManifest.xml 文件里的内容:

"http://schemas.android.com/apk/res/android"package="com.android.provision">"com.android.provision" />"android.permission.WRITE_SETTINGS" />"android.permission.WRITE_SECURE_SETTINGS" />"DefaultActivity"android:excludeFromRecents="true">"1">"android.intent.action.MAIN" />"android.intent.category.HOME" />"android.intent.category.DEFAULT" />"android.intent.category.SETUP_WIZARD" />

AndroidManifest文件中是对 DefaultActivity 的声明.有两个关键点需要注意:

  1. android:priority 属性,这个属性一般会用在 ActivityBroadcastReceiver 中,用来定义 Activity 或者 BroadcastReceiver 启动的优先级.范围在 -1000~1000 之间.值越大优先级越高.在 Activity 中使用时只有隐式调用才起作用,显示调用无效. (这是第二个坑点)

  2. android.intent.category.HOME 这个 category 是用来标记为桌面程序,例如系统中的Launcher应用.用于在系统启动之后启动该应用.

项目中遇到的坑点与解决方法

在上文中提到过遇到的两个坑点,一个是构造函数 ComponentName(Context pkg,Class cls) 参数使用错误导致的问题,一个是 android:priority 属性使用的问题.先说后面这个问题.

android:priority

上文说过 priority 属性的值越大优先级越高,就能优先启动.

在开机向导的APP(下文都称作 SetupWizard)中的配置:

".activity.MainActivity">"9">"android.intent.action.MAIN" />"android.intent.category.HOME" />"android.intent.category.DEFAULT" />"android.intent.category.SETUP_WIZARD" />

priority 的值设置为9,而 Launcher APP中没有声明 priority 则默认为0.所以在理论上应该是在系统启动的时候会优先启动 SetupWizard APP.但是结果却是弹出一个选项框如下图.

让我们二选一启动.这显然不是想要的效果.猜想或许是因为 LauncherAPP中没有设置 priority 属性的原因. 故此将 Launcher 应用的优先级修改为1后再次编译运行.其结果依然是二选一.反复修改两个优先级的值依旧无效.经同事提醒将 Launcher 应用的优先级设置为负数再试试.没想到就能正常进入到 SetupWizard 应用中; 猜想是不是使用的设备的系统对其优先级有修改,将正数的大小比较给做了处理.而对正负数的大小比较无影响. 因此解决方法是将 Launcher 应用的 priority 属性设置为负数就能解决此问题.

当非特权app任何priority > 0的设置都不起作用。无论设置任何正数值会被恢复0。因为开机启动APP被放置在 vendor/app 目录之下,但并不真正属于系统应用,其priority属性无法生效。因此根本解决方法是将其放置在 system/priv-app/Provision 之下,覆盖系统只带的 Provision.apk 开机向导app即可。并不需要将 Launcher.apk 的priority值设为负数。


ComponentName(Context pkg,Class cls)

在所有流程走完之后会调用如下方法:

public static void finishSetUpWizard(Context context) {Settings.Global.putInt(context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);PackageManager pm = context.getPackageManager();ComponentName name = new ComponentName(context, context.getClass());pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);
}

但是结束后会再次启动 SetupWizard 应用,但是会在启动上次结束的 Activity 时崩溃重新启动. 例如我在网络设置 NetworkSettingActivity 中设置网络成功后结束整个应用,调用 finishSetUpWizard(mContext) 后.并没有预期结束当前应用继而启动 Launcher 应用.再次启动 SetupWizard 应用时继续走流程,发现在启动 NetworkSettingActivity 时异常崩溃.发现该现象与 setComponentEnabledSetting(componentName,newState,flags) API的作用类似. 深入了解当 newState 参数设置为 COMPONENT_ENABLED_STATE_DISABLED 时当前组件 NetworkSettingActivity 会从PM中移除,而无法再次启动.就如我们启动时会报异常 android.content.ActivityNotFoundException: Unable to find explicit activity.

但是我们预期的是结束当前应用后继而启动 Launcher.现在却是重新启动 SetupWizard 应用且不能开启上次结束时调用了 finishSetUpWizard 方法的 Activity.发现和我们预期效果不同的是禁止唤起的 Activity 不同.如果我们禁止唤起 MainActivitySetupWizard 应用不就不会再次启动了吗.因此修改 finishSetUpWizard(Context context) 方法中 ComponentName(Context pkg,Class cls) 的cls参数,将 SetupWizard 应用的入口 MainActivity 传递进去.修改后的方法:

public static void finishSetUpWizard(Context context) {Settings.Global.putInt(context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);PackageManager pm = context.getPackageManager();ComponentName name = new ComponentName(context, MainActivity.class);pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);
}

再次编译启动,流程设置完毕后正常结束 SetupWizard 并启动 Launcher 应用.踩坑结束.

转:https://juejin.im/post/5cc165946fb9a031ec6d2ab8



推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 在一对一直播源码使用过程中,有时会出现软键盘切换闪屏问题,就是当切换表情的时候屏幕会跳动,因此要对一对一直播源码表情面板无缝切换进行优化。 ... [详细]
  • 2021年最详细的Android屏幕适配方案汇总
    1Android屏幕适配的度量单位和相关概念建议在阅读本文章之前,可以先阅读快乐李同学写的文章《Android屏幕适配的度量单位和相关概念》,这篇文章 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • 今天就跟大家聊聊有关怎么在Android应用中实现一个换肤功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根 ... [详细]
  • 开发笔记:(002)spring容器中bean初始化销毁时执行的方法及其3种实现方式
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了(002)spring容器中bean初始化销毁时执行的方法及其3种实现方式相关的知识,希望对你有一定的参考价值。 ... [详细]
  • http头_http头部注入
    1、http头部注入分析1、原理 ... [详细]
  • charles3.11.1抓https包
    结论先行:用的是安卓测试机,没加固之前的生产环境的安装包,可以抓到https请求加固之后的包【也就是要上应用市场的包】,抓不到https请求电脑上的操作:1.安装证书【电脑上安装了 ... [详细]
author-avatar
手浪用户2502876054
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有