Android Intent详解...

本文详细介绍了Android中Intent的作用、构成及其解析方式。Intent作为组件间通信的重要手段,可用于启动Activity和服务等。文章通过实例展示了如何构建Intent及Intent过滤器,帮助开发者更好地理解和使用Intent。

 

 
Intent 详解
一、 Intent 作用
Intent 是一个将要执行的动作的抽象的描述,一般来说是作为参数来使用,由 Intent 来协助完成 android 各个组件之间的通讯。比如说调用 startActivity() 来启动一个 activity, 或者由 broadcaseIntent() 来传递给所有感兴趣的 BroadcaseReceiver, 再或者由 startService()/bindservice() 来启动一个后台的 service. 所以可以看出来, intent 主要是用来启动其他的 activity 或者 service ,所以可以将 intent 理解成 activity 之间的粘合剂。
二、 Intent的构成
要在不同的 activity 之间传递数据,就要在 intent 中包含相应的东西,一般来说数据中最基本的应该包括:
          Action 用来指明要实施的动作是什么,比如说 ACTION_VIEW, ACTION_EDIT 等。具体的可以查阅 android SDK-> reference 中的 Android.content.intent 类,里面的 constants 中定义了所有的 action
          Data 要事实的具体的数据,一般由一个 Uri 变量来表示
 
下面是一些简单的例子:
ACTION_VIEW   content://contacts/1   // 显示 identifier 1 的联系人的信息。
ACTION_DIAL content://contacts/1   // 给这个联系人打电话
 
除了 Action data 这两个最基本的元素外, intent 还包括一些其他的元素,
          Category (类别) : 这个选项指定了将要执行的这个 action 的其他一些额外的信息,例如 LAUNCHER_CATEGORY 表示 Intent 的接受者应该在 Launcher 中作为顶级应用出现;而 ALTERNATIVE_CATEGORY 表示当前的 Intent 是一系列的可选动作中的一个,这些动作可以在同一块数据上执行。具体同样可以参考 android SDK-> reference 中的 Android.content.intent 类。以前我也写过一篇于 category 有关的文章,点击这里可以查看。
          Type (数据类型): 显式指定 Intent 的数据类型( MIME )。一般 Intent 的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。
          component (组件): 指定 Intent 的的目标组件的 类名称。通常 Android 会根据 Intent 中包含的其它属性的信息,比如 action data/type category 进行查找,最终找到一个与之匹配的目标组件。但是,如果 component 这个属性有指定的话,将直接使用它指定的组件,而不再执行上述查找过程。指定了这个属性以后, Intent 的其它所有属性都是可选的。
          extras (附加信息),是其它所有附加信息的集合。使用 extras 可以为组件提供扩展信息,比如,如果要执行“发送电子邮件”这个动作,可以将电子邮件的标题、正文等保存在 extras 里,传给电子邮件发送组件。
下面是这些额外属性的几个例子:
ACTION_MAIN with category CATEGORY_HOME // 用来 Launch home screen. 以前我也写过一篇于与之有关的文章, 点击这里可以看到。
ACTION_GET_CONTENT with MIME type vnd.android.cursor.item/phone // 用来列出列表中的所有人的电话号码
综上可以看出, action data/type category extras 一起形成了一种语言,这种语言可以是 android 可以表达出诸如“给张三打电话”之类的短语组合。
三、 intent的解析
应用程序 的组件为了告诉 Android 自己能响应、处理哪些隐式 Intent 请求,可以声明一个甚至多个 Intent Filter 。每个 Intent Filter 描述该组件所能响应 Intent 请求的能力 —— 组件希望接收什么类型的请求行为,什么类型的请求数据。比如之前请求网页浏览器这个例子中,网页浏览器程序的 Intent Filter 就应该声明它所希望接收的 Intent Action WEB_SEARCH_ACTION ,以及与之相关的请求数据是网页地址 URI 格式。如何为组件声明自己的 Intent Filter? 常见的方法是在 AndroidManifest.xml 文件中用属性 < Intent-Filter> 描述组件的 Intent Filter
  前面我们提到,隐式 Intent(Explicit Intents) Intent Filter(Implicit Intents) 进行比较时的三要素是 Intent 的动作、数据以及类别。实际上,一个隐式 Intent 请求要能够传递给目标组件,必要通过这三个方面的检查。如果任何一方面不匹配, Android 都不会将该隐式 Intent 传递给目标组件。接下来我们讲解这三方面检查的具体规则。
  1.动作测试
   Java 代码
 < intent-filter> 元素中可以包括子元素 < action> ,比如:
      < intent-filter> < action android:name=”com.example.project.SHOW_CURRENT” />
 < action android:name=”com.example.project.SHOW_RECENT” />
< action android:name=”com.example.project.SHOW_PENDING” />
 < /intent-filter> 
  一条 < intent-filter> 元素至少应该包含一个 < action> ,否则任何 Intent 请求都不能和该 < intent-filter> 匹配。如果 Intent 请求的 Action < intent-filter> 中个某一条 < action> 匹配,那么该 Intent 就通过了这条 < intent-filter> 的动作测试。如果 Intent 请求或 < intent-filter> 中没有说明具体的 Action 类型,那么会出现下面两种情况。 
(1) 如果 < intent-filter> 中没有包含任何 Action 类型,那么无论什么 Intent 请求都无法和这条 < intent- filter> 匹配 ;
(2) 反之,如果 Intent 请求中没有设定 Action 类型,那么只要 < intent-filter> 中包含有 Action 类型,这个 Intent 请求就将顺利地通过 < intent-filter> 的行为测试。 
    2.类别测试
   Java 代码
 < intent-filter> 元素可以包含 < category> 子元素,比如:
      < intent-filter . . . >
      < category android:name=”android.Intent.Category.DEFAULT” />
      < category android:name=”android.Intent.Category.BROWSABLE” />
< /intent-filter> 
  只有当 Intent 请求中所有的 Category 与组件中某一个 IntentFilter < category> 完全匹配时,才会让该 Intent 请求通过测试, IntentFilter 中多余的 < category> 声明并不会导致匹配失败。一个没有指定任何类别测试的 IntentFilter 仅仅只会匹配没有设置类别的 Intent 请求。 
    3.数据测试
  数据在 < intent-filter> 中的描述如下:  Java 代码 
< intent-filter . . . >
      < data android:type=”video/mpeg” android:scheme=”http” . . . />
      < data android:type=”audio/mpeg” android:scheme=”http” . . . />
< /intent-filter> 
   元素指定了希望接受的 Intent 请求的数据 URI 和数据类型, URI 被分成三部分来进行匹配: scheme authority path 。其中,用 setData() 设定的 Inteat 请求的 URI 数据类型和 scheme 必须与 IntentFilter 中所指定的一致。若 IntentFilter 中还指定了 authority path ,它们也需要相匹配才会通过测试。
 4.简单例子说明
  讲解完 Intent 基本概念之后,接下来我们就使用 Intent 激活 Android 自带的电话拨号程序,通过这个实例你会发现,使用 Intent 并不像其概念描述得那样难。最终创建 Intent 的代码如下所示。
   Intent i = new
Intent(Intent.ACTION_DIAL,Uri.parse(”tel://13800138000″));
  创建好 Intent 之后,你就可以通过它告诉 Android 希望启动新的 Activity 了。
   startActivity(i);
   Activity 启动后显示界面如下:
 
 
三、Intent的构造函数
公共构造函数:
1 Intent() 空构造函数
2 Intent(Intent o) 拷贝构造函数
3 Intent(String action) 指定 action 类型的构造函数
4 Intent(String action, Uri uri) 指定 Action 类型和 Uri 的构造函数, URI 主要是结合程序之间的数据共享 ContentProvider
5 Intent(Context packageContext, Class<?> cls) 传入组件的构造函数,也就是上文提到的
6 Intent(String action, Uri uri, Context packageContext, Class<?> cls) 前两种结合体
Intent 有六种构造函数, 3 4 5 是最常用的,并不是其他没用!
Intent(String action, Uri uri)  action 就是对应在 AndroidMainfest.xml 中的 action 节点的 name 属性值。在 Intent 类中定义了很多的 Action Category 常量。
示例代码二:
   1: Intent intent = new Intent(Intent.ACTION_EDIT, null);
   2: startActivity(intent);
示例代码二是用了第四种构造函数,只是 uri 参数为 null 。执行此代码的时候,系统就会在程序主配置文件 AndroidMainfest.xml 中寻找
<action android:name="android.intent.action.EDIT" /> 对应的 Activity ,如果对应为多个 activity 具有 <action android:name="android.intent.action.EDIT" /> 此时就会弹出一个 dailog 选择 Activity ,如下图:
  如果是用示例代码一那种方式进行发送则不会有这种情况。
三、利用 Intent Activity 之间传递数据
Main 中执行如下代码:
   1: Bundle bundle = new Bundle();
   2: bundle.putStringArray("NAMEARR", nameArr);
   3: Intent intent = new Intent(Main.this, CountList.class);
   4: intent.putExtras(bundle);
   5: startActivity(intent);
CountList 中,代码如下:
   1: Bundle bundle = this.getIntent().getExtras();
   2: String[] arrName = bundle.getStringArray("NAMEARR");
以上代码就实现了 Activity 之间的数据传递!
 
Android intent intent-filter

  
5. 总结说明
  这篇文章是我刚开始学习 Android 时看到的,当时理解的不是很深入,现在再回头看这篇文章总结的很详细,在这里与大家分享。
   1 ,掉 web 浏览器
   Java 代码
   Uri myBlogUri = Uri.parse("http://kuikui.javaeye.com");
   returnIt = new Intent(Intent.ACTION_VIEW, myBlogUri);
 
   2 ,地图
   Java 代码
   Uri mapUri = Uri.parse("geo:38.899533,-77.036476");
   returnIt = new Intent(Intent.ACTION_VIEW, mapUri);
 
   3 ,调拨打电话界面
   Java 代码
   Uri telUri = Uri.parse("tel:100861");
   returnIt = new Intent(Intent.ACTION_DIAL, telUri);
 
   4 ,直接拨打电话
   Java 代码
   Uri callUri = Uri.parse("tel:100861");
   returnIt = new Intent(Intent.ACTION_CALL, callUri);
 
   5 ,卸载
   Java 代码
   Uri uninstallUri = Uri.fromParts("package", "xxx", null);
   returnIt = new Intent(Intent.ACTION_DELETE, uninstallUri);
 
   6 ,安装
   Java 代码
   Uri installUri = Uri.fromParts("package", "xxx", null);
   returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);
 
   7 ,播放
   Java 代码
   Uri playUri = Uri.parse("file:///sdcard/download/everything.mp3");
   returnIt = new Intent(Intent.ACTION_VIEW, playUri);
 
   8 ,掉用发邮件
   Java 代码
   Uri emailUri = Uri.parse("mailto:shenrenkui@gmail.com");
   returnIt = new Intent(Intent.ACTION_SENDTO, emailUri);
 
   9 ,发邮件
   Java 代码
   returnIt = new Intent(Intent.ACTION_SEND);
   String[] tos = { "shenrenkui@gmail.com" };
   String[] ccs = { "shenrenkui@gmail.com" };
   returnIt.putExtra(Intent.EXTRA_EMAIL, tos);
   returnIt.putExtra(Intent.EXTRA_CC, ccs);
   returnIt.putExtra(Intent.EXTRA_TEXT, "body");
   returnIt.putExtra(Intent.EXTRA_SUBJECT, "subject");
   returnIt.setType("message/rfc882");
   Intent.createChooser(returnIt, "Choose Email Client");
  
   10 ,发短信
  Java 代码
   Uri smsUri = Uri.parse("tel:100861");
   returnIt = new Intent(Intent.ACTION_VIEW, smsUri);
   returnIt.putExtra("sms_body", "shenrenkui");
   returnIt.setType("vnd.android-dir/mms-sms");
 
   11 ,直接发邮件
   Java 代码
   Uri smsToUri = Uri.parse("smsto://100861");
   returnIt = new Intent(Intent.ACTION_SENDTO, smsToUri);
   returnIt.putExtra("sms_body", "shenrenkui");
 
   12 ,发彩信
   Java 代码
   Uri mmsUri = Uri.parse("content://media/external/images/media/23");
   returnIt = new Intent(Intent.ACTION_SEND);
   returnIt.putExtra("sms_body", "shenrenkui");
   returnIt.putExtra(Intent.EXTRA_STREAM, mmsUri);
   returnIt.setType("image/png");
 
  用获取到的 Intent 直接调用 startActivity(returnIt) ok 了。
 
下面,以 Android SDK 中的便笺例子来说明, Intent 如何定义及如何被解析。这个应用可以让用户浏览便笺列表、查看每一个便笺的详细信息。

Manifest.xml
Xml 代码   
1.         <manifest  xmlns:android = "http://schemas.android.com/apk/res/android"    
2.         package = "com.google.android.notepad" >    
3.              <application  android:icon = "@drawable/app_notes"    
4.         android:label = "@string/app_name" >    
5.              <provider  class = "NotePadProvider"    
6.         android:authorities = "com.google.provider.NotePad"  />    
7.              <activity  class = ".NotesList" ="@string/title_notes_list" >    
8.                <intent-filter>    
9.                  <action  android:value = "android.intent.action.MAIN" />    
10.               <category  android:value = "android.intent.category.LAUNCHER"  />    
11.             </intent-filter>    
12.             <intent-filter>    
13.               <action  android:value = "android.intent.action.VIEW" />    
14.               <action  android:value = "android.intent.action.EDIT" />    
15.               <action  android:value = "android.intent.action.PICK" />    
16.               <category  android:value = "android.intent.category.DEFAULT"  />    
17.               <type  android:value = "vnd.android.cursor.dir/vnd.google.note"  />    
18.             </intent-filter>    
19.             <intent-filter>    
20.               <action  android:value = "android.intent.action.GET_CONTENT"  />    
21.               <category  android:value = "android.intent.category.DEFAULT"  />    
22.               <type  android:value = "vnd.android.cursor.item/vnd.google.note"  />    
23.             </intent-filter>    
24.           </activity>    
25.           <activity  class = ".NoteEditor" ="@string/title_note" >    
26.             <intent-filter  android:label = "@string/resolve_edit" >    
27.               <action  android:value = "android.intent.action.VIEW" />    
28.               <action  android:value = "android.intent.action.EDIT" />    
29.               <category  android:value = "android.intent.category.DEFAULT"  />    
30.               <type  android:value = "vnd.android.cursor.item/vnd.google.note"  />    
31.             </intent-filter>    
32.             <intent-filter>    
33.               <action  android:value = "android.intent.action.INSERT" />    
34.               <category  android:value = "android.intent.category.DEFAULT"  />    
35.               <type  android:value = "vnd.android.cursor.dir/vnd.google.note"  />    
36.             </intent-filter>    
37.           </activity>    
38.           <activity  class = ".TitleEditor" ="@string/title_edit_title"   
39.      android:theme = "@android:style/Theme.Dialog" >    
40.             <intent-filter  android:label = "@string/resolve_title" >    
41.               <action  android:value = "com.google.android.notepad.action.EDIT_TITLE" />    
42.               <category  android:value = "android.intent.category.DEFAULT"  />    
43.               <category  android:value = "android.intent.category.ALTERNATIVE"  />    
44.               <category  android:value = "android.intent.category.SELECTED_ALTERNATIVE" />    
45.               <type  android:value = "vnd.android.cursor.item/vnd.google.note"  />    
46.             </intent-filter>    
47.           </activity>    
48.      </application>    
49.      </manifest>   
 

例子中的第一个 Activity com.google.android.notepad.NotesList ,它是应用的主入口,提供了三个功能,分别由三个 intent-filter 进行描述:
        1
、第一个是进入便笺应用的顶级入口( action android.app.action.MAIN )。类型为 android.app.category.LAUNCHER 表明这个 Activity 将在 Launcher 中列出。
        2
、第二个是,当 type vnd.android.cursor.dir/vnd.google.note (保存便笺记录的目录) 时,可以查看可用的便笺( action android.app.action.VIEW ),或者让用户选择一个便笺并返回给调用者( action android.app.action.PICK )。
        3
、第三个是,当 type vnd.android.cursor.item/vnd.google.note 时,返回给调用者一个用户选择的便笺( action android.app.action.GET_CONTENT ),而用户却不需要知道便笺从哪里读取的。 有了这些功能,下面的 Intent 就会被解析到 NotesList 这个 activity

    * { action=android.app.action.MAIN }
:与此 Intent 匹配的 Activity ,将会被当作进入应用的顶级入口。  

    * { action=android.app.action.MAIN, category=android.app.category.LAUNCHER }
:这是目前 Launcher 实际使用的 Intent ,用于生成 Launcher 的顶级列表。  

    * { action=android.app.action.VIEW data=content://com.google.provider.NotePad/notes }
:显示 "content://com.google.provider.NotePad/notes" 下的所有便笺的列表,使用者可以遍历列表,并且察看某便笺的详细信息。  

    * { action=android.app.action.PICK data=content://com.google.provider.NotePad/notes }
:显示 "content://com.google.provider.NotePad/notes" 下的便笺列表,让用户可以在列表中选择一个,然后将选择的便笺的 URL 返回给调用者。  

    * { action=android.app.action.GET_CONTENT type=vnd.android.cursor.item/vnd.google.note }
:和 上面的 action pick Intent 类似,不同的是这个 Intent 允许调用者(在这里指要调用 NotesList 的某个 Activity )指定 它们需要返回的数据类型,系统会根据这个数据类型查找合适的 Activity (在这里系统会找到 NotesList 这个 Activity ),供用户选择便笺。  


        
第二个 Activity com.google.android.notepad.NoteEditor ,它为用户显示一条便笺,并且允许 用户修改这个便笺。它定义了两个 intent-filter ,所以具有两个功能。第一个功能是,当数据类型为 vnd.android.cursor.item/vnd.google.note 时,允许用户查看和修改一个便签( action android.app.action.VIEW android.app.action.EDIT )。第二个功能是,当数据类型为 vnd.android.cursor.dir/vnd.google.note ,为调用者显示一个新建便笺的界面,并将新建的便笺插 入到便笺列表中( action android.app.action.INSERT )。

      
有了这两个功能,下面的 Intent 就会被解析到 NoteEditor 这个 activity

    * { action=android.app.action.VIEW data=content://com.google.provider.NotePad/notes/{ID} }
:向用户显示标识为 ID 的便笺。

    * { action=android.app.action.EDIT data=content://com.google.provider.NotePad/notes/{ID} }
:允许用户编辑标识为 ID 的便笺。  

    * { action=android.app.action.INSERT data=content://com.google.provider.NotePad/notes }
:在 “content://com.google.provider.NotePad/notes” 这个便笺列表中创建一个新的空便笺,并允许用 户编辑这个便签。当用户保存这个便笺后,这个新便笺的 URI 将会返回给调用者。  


        
最后一个 Activity com.google.android.notepad.TitleEditor ,它允许用户编辑便笺的标题。它可以被实现为 一个应用可以直接调用(在 Intent 中明确设置 component 属性)的类,不过这里我们将为你提供一个在现有的数据上发布可选操作的方法。在这个 Activity 的唯一的 intent-filter 中,拥有一个私有的 action com.google.android.notepad.action.EDIT_TITLE ,表明允许用户编辑便笺的标题。和前面的 view edit 动作一样,调用这个 Intent 的时候,也必须指定具体的便笺( type vnd.android.cursor.item/vnd.google.note )。不同的是,这里显示和编 辑的只是便笺数据中的标题。
      
除了支持缺省类别( android.intent.category.DEFAULT ),标题编辑器还支持另外两个标准类别: android.intent.category.ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE 。实现了这两个类别之后,其它 Activity 就可以调用 queryIntentActivityOptions(ComponentName, Intent[], Intent, int) 查询这个 Activity 提供的 action ,而不需要了解它的具体实现;或者调用 addIntentOptions(int, int, ComponentName, Intent[], Intent, int, Menu.Item[]) 建立动态菜单。需要说明的是,在这个 intent-filter 中有一个明确的名称(通过 android:label= "@string/resolve_title" 指定),在用户浏览数据的时候,如果这个 Activity 是数据的一个可选操作,指定明确的名称可以为用 户提供一个更好控制界面。
      
有了这个功能,下面的 Intent 就会被解析到 TitleEditor 这个 Activity

    * { action=com.google.android.notepad.action.EDIT_TITLE data=content://com.google.provider.NotePad/notes/{ID} }
:显示并且允许用户编辑标识为 ID 的便笺的标题。
 
 
 
 
<think> 我们正在分析一条来自Android SystemUI的日志,特别是关于QuickSettings(快速设置)的QSTileHostHelper发出的"PACKAGE changed"信息。 日志内容如下: "SystemUi--QuickSettings的QSTileHostHelper提示的PACKAGE changed信息含义,pkgName=com.facebook.lite, isMultiAppfalse ,replaceDataBundle[{android.intent.extra.REMOVED_FOR_ALL_USERS=true, ...}]" 我们需要解析这条日志的含义,特别是它反映的系统行为。 步骤1:理解日志来源 - SystemUI:Android系统用户界面进程,负责状态栏、导航栏、快速设置面板等。 - QuickSettings:快速设置面板,用户可以通过下拉通知栏访问。 - QSTileHostHelper:一个帮助类,用于管理快速设置中的磁贴(tiles)。磁贴代表一些快捷设置开关,比如Wi-Fi、蓝牙等。 步骤2:理解“PACKAGE changed”事件 - 当系统中有应用包(package)发生变化时(如安装、更新、卸载),系统会广播相应的Intent(如ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED等)。 - SystemUI中的QSTileHostHelper会监听这些广播事件,以便在应用包发生变化时更新快速设置面板中的磁贴。例如,如果某个应用被卸载,而该应用提供了自定义的磁贴,那么该磁贴应该被移除。 步骤3:分析具体参数 - pkgName=com.facebook.lite:发生变化的包名,即Facebook Lite应用。 - isMultiApp=false:表示这个应用不是多开应用(如工作档案中的应用)。在Android中,多开应用通常是指在不同的用户或配置文件下的应用实例。 - replaceDataBundle:包含额外信息的Bundle,它描述了包变更的具体情况。这个Bundle实际上是Intent的extras。 步骤4:解析replaceDataBundle中的键值对 根据提供的Bundle内容: - `android.intent.extra.REMOVED_FOR_ALL_USERS=true`:表示这个包被从所有用户(包括主用户和其他用户)中移除。通常,卸载应用时,如果是多用户设备,可以选择只卸载当前用户或所有用户。这里为true表示所有用户都移除了。 - `android.intent.extra.DONT_KILL_APP=false`:表示在卸载时,系统会杀死该进程。如果为true,则不会立即杀死进程(通常用于更新场景)。 - `android.intent.extra.UID=10308`:被卸载应用的Linux用户ID(UID)。每个应用在安装时都会被分配一个唯一的UID。 - `android.intent.extra.SYSTEM_UPDATE_UNINSTALL=false`:表示这不是由于系统更新而卸载。系统更新卸载指的是更新系统应用时替换旧版本。 - `android.intent.extra.USER_INITIATED=true`:表示卸载是由用户发起的(例如通过设置界面手动卸载),而不是由系统或其他应用自动触发。 - `android.intent.extra.DATA_REMOVED=true`:表示除了卸载应用本身外,应用的数据也被移除了(通常用户卸载时会选择同时删除数据)。 - `android.intent.extra.user_handle=0`:用户句柄,0代表主用户(即设备拥有者)。 步骤5:综合解释 这条日志表明:用户(主用户)主动发起的卸载操作,将应用`com.facebook.lite`从设备上所有用户中移除,并且同时删除了该应用的数据。卸载操作不是系统更新的一部分,并且系统会在卸载后杀死该应用进程。 对于QuickSettings的影响: - 如果`com.facebook.lite`应用在快速设置面板中注册了自定义磁贴(例如,Facebook Lite可能提供快速分享或通知的磁贴),那么当应用被卸载后,QSTileHostHelper会接收到这个包移除事件,并从快速设置面板中移除与该应用相关的磁贴。 步骤6:潜在问题 - 如果卸载后磁贴仍然存在,可能是由于SystemUI没有及时更新,可以尝试重启SystemUI或重启设备。 - 如果磁贴没有被正确移除,可能是由于卸载过程中出现异常,或者在设计上该磁贴并不与特定应用绑定(但日志明确指向这个包)。 结论: 这条日志记录了SystemUI的快速设置模块收到一个应用包(com.facebook.lite)被卸载的事件,并根据这个事件更新快速设置面板(移除与该应用相关的磁贴)。卸载是由用户发起的,移除了应用和数据,并且影响了所有用户。 相关问题: §§相关问题§§ 1. Android的快速设置面板是如何动态管理磁贴的? 2. 用户在多用户设备(如工作档案)上卸载应用时,系统会有哪些不同的处理? 3. 如何自定义一个快速设置磁贴,以及卸载应用时磁贴如何被移除? 4. 为什么有些应用卸载后快速设置磁贴仍残留?如何解决? 注意:在回答中我们没有使用数学公式,因此不需要使用$$...$$格式。但根据用户要求,如果有数学内容则需要使用。本次回答没有数学内容。 </think> ### SystemUI QuickSettings的QSTileHostHelper日志分析 #### 1. **日志含义解析** 日志内容: `SystemUi--QuickSettings的QSTileHostHelper提示的PACKAGE changed信息含义,pkgName=com.facebook.lite, isMultiApp=false, replaceDataBundle[{...}]` - **QSTileHostHelper**:SystemUI中管理快速设置面板磁贴(Quick Settings Tiles)的核心模块[^1] - **PACKAGE changed**:系统检测到应用包状态变更事件(安装/更新/卸载) - **pkgName=com.facebook.lite**:目标应用为Facebook Lite - **isMultiApp=false**:**非多用户场景**(仅在当前用户生效) - **replaceDataBundle**:事件附加数据包,包含关键操作标识 #### 2. **replaceDataBundle参数详解** ```plaintext android.intent.extra.REMOVED_FOR_ALL_USERS=true android.intent.extra.DONT_KILL_APP=false android.intent.extra.UID=10308 android.intent.extra.SYSTEM_UPDATE_UNINSTALL=false android.intent.extra.USER_INITIATED=true android.intent.extra.DATA_REMOVED=true android.intent.extra.user_handle=0 ``` | 参数 | 值 | 含义 | |------|----|------| | `REMOVED_FOR_ALL_USERS` | `true` | **从所有用户卸载**(多用户设备全局移除) | | `DONT_KILL_APP` | `false` | **立即终止进程**(非静默卸载) | | `UID` | `10308` | 应用唯一身份标识符 | | `SYSTEM_UPDATE_UNINSTALL` | `false` | 非系统更新触发的卸载 | | `USER_INITIATED` | `true` | **用户主动操作**(非系统自动卸载) | | `DATA_REMOVED` | `true` | **删除用户数据**(清理应用私有目录) | | `user_handle` | `0` | 主用户(设备拥有者) | #### 3. **系统行为与影响** 此日志表明: 1. **用户通过系统设置**主动卸载了Facebook Lite应用 2. **触发QuickSettings磁贴清理**: - QSTileHostHelper检测到包移除事件 - 自动移除与该应用关联的快速设置磁贴(如自定义快捷开关) 3. 卸载过程**彻底清除应用数据**(`DATA_REMOVED=true`) 4. **多用户支持**:因`REMOVED_FOR_ALL_USERS=true`,所有用户档案下的实例均被移除 #### 4. **技术实现原理** 当应用被卸载时,系统发送广播: ```java // 系统发送卸载广播 Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED); intent.putExtra(Intent.EXTRA_USER_HANDLE, 0); intent.putExtra(Intent.EXTRA_DATA_REMOVED, true); ... sendBroadcast(intent); ``` QSTileHostHelper通过`PackageMonitor`监听广播: ```java public class QSTileHostHelper extends PackageMonitor { @Override public void onPackageRemoved(String packageName, int uid) { removeCustomTileForPackage(packageName); // 清理关联磁贴 } } ``` #### 5. **异常场景排查** 若磁贴未自动移除,可能原因: - SystemUI进程未及时响应广播(需重启SystemUI) - 磁贴未正确关联包名(开发错误) - 系统级Bug(需检查Android版本已知问题) --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值