Intent和Intent-Filter(二)

上一节主要介绍概念,本节将重点给出演示实例,以便深入理解。

参考连接:

https://developer.android.google.cn/guide/components/intents-filters?hl=zh-cn

https://developer.android.google.cn/guide/components/services?hl=zh-cn

显式 Intent 示例

显式 Intent 是指用于启动某个特定应用组件(例如,应用中的某个特定 Activity 或服务)的 Intent。要创建显式 Intent,请为 Intent 对象定义组件名称 — Intent 的所有其他属性均为可选属性。

例如,如果在应用中构建一个名为 DownloadService、旨在从网页下载文件的服务,则可使用以下代码启动该服务:

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png" 
Intent downloadIntent = new Intent(this, DownloadService.class); 
downloadIntent.setData(Uri.parse(fileUrl)); 
startService(downloadIntent);

Intent(Context, Class) 构造函数分别为应用和组件提供 ContextClass 对象。因此,此 Intent 将显式启动该应用中的 DownloadService 类。如前文所述,Service必须显示启动。

如需了解有关构建和启动服务的详细信息,请参阅服务指南。

隐式 Intent 示例

隐式 Intent 指定能够在可以执行相应操作的设备上调用任何应用的操作。如果您的应用无法执行该操作而其他应用可以,且您希望用户选取要使用的应用,则使用隐式 Intent 非常有用。

例如,如果您希望用户与他人共享您的内容,请使用 ACTION_SEND 操作创建 Intent,并添加指定共享内容的 extra。使用该 Intent 调用 startActivity() 时,用户可以选取共享内容所使用的应用。

注意:可能没有任何应用处理您发送到 startActivity() 的隐式 Intent。或者,由于配置文件限制或管理员执行的设置,可能无法访问应用。如果发生这样的情况,调用失败,应用也会崩溃。要验证 Activity 是否会接收 Intent,请对 Intent 对象调用 resolveActivity()。如果结果为非空,则至少有一个应用能够处理该 Intent,并且可以安全调用 startActivity()。如果结果为空,不要使用该 Intent。如有可能,您应停用发出该 Intent 的功能。以下示例说明如何验证 Intent 是否解析为 Activity。此示例没有使用 URI,但已声明 Intent 的数据类型,用于指定 extra 携带的内容。

// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");

// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}

调用 startActivity() 时,系统将检查已安装的所有应用,确定哪些应用能够处理这种 Intent(即:含 ACTION_SEND 操作并携带“text/plain”数据的 Intent)。如果只有一个应用能够处理,则该应用将立即打开并为其提供 Intent。如果多个 Activity 接受 Intent,则系统将显示一个对话框(如图 2 所示),使用户能够选取要使用的应用。

从上面的话我们可以看出,通过Intent启动其他Activty或者Service,最关键要素的就是ACTION,其次是TYPE和DATA。

强制使用应用选择器

如果有多个应用响应隐式 Intent,则用户可以选择要使用的应用,并将其设置为该操作的默认选项。如果用户可能希望每次使用相同的应用执行某项操作(例如,打开网页时,用户往往倾向于仅使用一种网络浏览器),则选择默认选项的功能十分有用。

但是,如果多个应用可以响应 Intent,且用户可能希望每次使用不同的应用,则应采用显式方式显示选择器对话框。选择器对话框会要求用户选择用于操作的应用(用户无法为该操作选择默认应用)。例如,当应用使用 ACTION_SEND 操作执行“共享”时,用户根据目前的状况可能需要使用另一不同的应用,因此应当始终使用选择器对话框,如图 2 中所示。

图 2. 选择器对话框。

要显示选择器,请使用 createChooser() 创建 Intent,并将其传递给 startActivity(),如下例所示。此示例将显示一个对话框,其中有响应传递给 createChooser() 方法的 Intent 的应用列表,并且将提供的文本用作对话框标题。

Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

接收隐式 Intent

要公布应用可以接收哪些隐式 Intent,请在清单文件中使用 <intent-filter> 元素为每个应用组件声明一个或多个 Intent 过滤器。每个 Intent 过滤器均根据 Intent 的操作、数据和类别指定自身接受的 Intent 类型。仅当隐式 Intent 可以通过 Intent 过滤器之一传递时,系统才会将该 Intent 传递给应用组件。

上面所谓的”应用组件“就是Activity吗?不是,别忘了Android有四大应用组件。读者可以自行回忆有那些应用组件,但是,如前面所说,Service是不支持隐式启动的,所以,一般是指其他两大应用组件,就是Activity和Broadcast,下面是BroadCast在Android Framework中的代码实例,

// listen for events
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
intentFilter.addAction(ALARM_TIMEOUT);
intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);

mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);

从这个Broadcast实例可以看出如果在系统中,系统不仅可以在Manifest中声明,同样也可以在代码中定义,而且可以同时监听多个系统广播。

请注意:显式 Intent 始终会传递给其目标,无论组件声明的 Intent 过滤器如何均是如此。

应用组件应当为自身可执行的每个独特作业声明单独的过滤器。例如,图像库应用中的一个 Activity 可能会有两个过滤器,分别用于查看图像和编辑图像。当 Activity 启动时,将检查 Intent 并根据 Intent 中的信息决定具体的行为(例如,是否显示编辑器控件)。

每个 Intent 过滤器均由应用清单文件中的 <intent-filter> 元素定义,并嵌套在相应的应用组件(例如,<activity> 元素)中。在 <intent-filter> 内部,您可以使用以下三个元素中的一个或多个指定要接受的 Intent 类型:

<action>

name 属性中,声明接受的 Intent 操作。该值必须是操作的文本字符串值,而不是类常量。

          <intent-filter>
            <action android:name="com.android.perftests.core.PERFTEST" />
          </intent-filter>

<data>

使用一个或多个指定数据 URI(schemehostportpath)各个方面和 MIME 类型的属性,声明接受的数据类型。

            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="http" />
                <data android:scheme="https" />
                <data android:host="*" />
            </intent-filter>

 或者

            <intent-filter android:priority="100">
                <action android:name="com.android.cts.action.NEW_AUTHORITY" />
                <data android:host="www.google.com" android:port="80" />
                <data android:host="www.google.com" android:port="8080" />
                <data android:host="mail.google.com" android:port="80" />
                <data android:host="goo.gl" android:port="443" />
                <data android:host="www.google.com" android:port="443" />
            </intent-filter>

或者

            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>

上面这几段代码主要是为了让大家看一下intent-filter中都包含那些内容,具体含义往下看,

<category>

name 属性中,声明接受的 Intent 类别。该值必须是操作的文本字符串值,而不是类常量。

如需要了解更多Intent中各个参数的信息,可以参考,

https://developer.android.google.cn/reference/android/content/Intent?hl=zh-cn,这是一篇专门介绍Intent的文档,对Intent中所有的ACTION、CATEGORY等信息有非常详细的描述。

请注意:要接收隐式 Intent,必须CATEGORY_DEFAULT 类别包括在 Intent 过滤器中。方法 startActivity()startActivityForResult() 将按照其声明 CATEGORY_DEFAULT 类别的方式处理所有 Intent。如果未在 Intent 过滤器中声明此类别,则隐式 Intent 不会解析为您的 Activity。

例如,以下是一个使用包含 Intent 过滤器的 Activity 声明,当数据类型为文本时,系统将接收 ACTION_SEND Intent :

<activity android:name="ShareActivity">
     <intent-filter>
         <action android:name="android.intent.action.SEND"/>
         <category android:name="android.intent.category.DEFAULT"/>
         <data android:mimeType="text/plain"/>
     </intent-filter>
 </activity>

您可以创建一个包括多个 <action><data><category> 实例的过滤器。创建时,需确定组件能够处理这些过滤器元素的任何及所有组合。

如需仅以操作、数据和类别类型的特定组合来处理多种 Intent,则需创建多个 Intent 过滤器。

系统通过将 Intent 与所有这三个元素进行比较,根据过滤器测试隐式 Intent。隐式 Intent 若要传递给组件,必须通过所有这三项测试。如果 Intent 甚至无法匹配其中任何一项测试,则 Android 系统不会将其传递给组件。但是,由于一个组件可能有多个 Intent 过滤器,因此未能通过某一组件过滤器的 Intent 可能会通过另一过滤器。如需了解有关系统如何解析 Intent 的详细信息,请参阅下文的 Intent 解析部分。

注意:使用 Intent 过滤器时,无法安全地防止其他应用启动组件。尽管 Intent 过滤器将组件限制为仅响应特定类型的隐式 Intent,但如果开发者确定您的组件名称,则其他应用有可能通过使用显式 Intent 启动您的应用组件。如果必须确保只有您自己的应用才能启动您的某一组件,请勿在您的清单中声明 Intent 过滤器。而是将该组件的 exported 属性设置为 "false"

两个意思,

1. 不在Manifest中声明任何IntentFilter;

2. 将该组件的exported属性设置为false;

同样,为了避免无意中运行不同应用的 Service,请始终使用显式 Intent 启动您自己的服务。

请注意:对于所有 Activity,您必须在清单文件中声明 Intent 过滤器。但是,广播接收器的过滤器可以通过调用 registerReceiver() 动态注册。稍后,您可以使用 unregisterReceiver() 注销该接收器。这样一来,应用便可仅在应用运行时的某一指定时间段内侦听特定的广播。前面已经给过例子了,再看一次,

            // listen for events
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(ALARM_WAKEUP);
            intentFilter.addAction(ALARM_TIMEOUT);
            intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
            intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
            intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
            intentFilter.addAction(Intent.ACTION_SCREEN_ON);
            intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
             intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
            mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);

过滤器示例

为演示一些 Intent 过滤器的行为,请参阅从社交共享应用的清单文件截取的示例:

<activity android:name="MainActivity">
     <!-- This activity is the main entry, should appear in app launcher -->
     <intent-filter>
         <action android:name="android.intent.action.MAIN" />
         <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
 </activity>
 <activity android:name="ShareActivity">
     <!-- This activity handles "SEND" actions with text data -->
     <intent-filter>
         <action android:name="android.intent.action.SEND"/>
         <category android:name="android.intent.category.DEFAULT"/>
         <data android:mimeType="text/plain"/>
     </intent-filter>
     <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
     <intent-filter>
         <action android:name="android.intent.action.SEND"/>
         <action android:name="android.intent.action.SEND_MULTIPLE"/>
         <category android:name="android.intent.category.DEFAULT"/>
         <data android:mimeType="application/vnd.google.panorama360+jpg"/>
         <data android:mimeType="image/*"/>
         <data android:mimeType="video/*"/>
     </intent-filter>
 </activity>

第一个 Activity MainActivity 是应用的主要入口点。当用户最初使用启动器图标启动应用时,该 Activity 将打开:

  • ACTION_MAIN 操作指示这是主要入口点,且不要求输入任何 Intent 数据。
  • CATEGORY_LAUNCHER 类别指示此 Activity 的图标应放入系统的应用启动器。如果 <activity> 元素未使用 icon 指定图标,则系统将使用 <application> 元素中的图标。

这两个元素必须配对使用,Activity 才会显示在应用启动器中。

第二个 Activity ShareActivity 旨在便于共享文本和媒体内容。尽管用户可以通过从 MainActivity 导航进入此 Activity,但也可以从发出隐式 Intent(与两个 Intent 过滤器之一匹配)的另一应用中直接进入 ShareActivity

请注意:MIME 类型 application/vnd.google.panorama360+jpg 是一个指定全景照片的特殊数据类型,您可以使用 Google panorama API 对其进行处理。

使用待定 Intent

PendingIntent 对象是 Intent 对象的包装器。PendingIntent 的主要目的是授权外部应用使用包含的 Intent,就像是它从您应用本身的进程中执行的一样。

待定 Intent 的主要用例包括:

由于每个 Intent 对象均设计为由特定类型的应用组件进行处理(ActivityServiceBroadcastReceiver),因此还必须基于相同的考虑因素创建 PendingIntent。使用待定 Intent 时,应用不会使用调用(如 startActivity())执行该 Intent。相反,通过调用相应的创建器方法创建 PendingIntent 时,您必须声明所需的组件类型:

除非您的应用正在从其他应用中接收 PendingIntent,否则上述用于创建 PendingIntent 的方法可能是您所需的唯一 PendingIntent 方法。

如何理解?

每种方法均会提取当前的应用 Context、您要封装的 Intent 以及一个或多个指定应如何使用该 Intent 的标志(例如,是否可以多次使用该 Intent)。

如需了解有关使用待定 Intent 的详细信息,请参阅通知应用微件 API 指南等手册中每个相应用例的相关文档。示例如下,

    public class ExampleAppWidgetProvider extends AppWidgetProvider {

        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            final int N = appWidgetIds.length;

            // Perform this loop procedure for each App Widget that belongs to this provider
            for (int i=0; i<N; i++) {
                int appWidgetId = appWidgetIds[i];

                // Create an Intent to launch ExampleActivity
                Intent intent = new Intent(context, ExampleActivity.class);
                PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

                // Get the layout for the App Widget and attach an on-click listener
                // to the button
                RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
                views.setOnClickPendingIntent(R.id.button, pendingIntent);

                // Tell the AppWidgetManager to perform an update on the current app widget
                appWidgetManager.updateAppWidget(appWidgetId, views);
            }
        }
    }
    

Intent 解析

当收到隐式 Intent 以启动 Activity 时,系统会根据以下三个方面将该 Intent 与 Intent 过滤器进行比较,搜索与该 Intent 最为匹配的 Activity:

  • 操作。
  • 数据(URI 和数据类型)。
  • 类别。

下文根据应用的清单文件中的 Intent 过滤器声明,描述 Intent 如何与相应的组件匹配。

注:Activity中的 Intent - Filter一般声明在Manifest中,但是 Broadcast 的 Intent - Filter一般声明在代码中,如下 Framework 中接收系统广播的,通过下面的代码也可以看出,一次 Intent-Filter可以加任意多个action,Manifest中同样如此。

// listen for events
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
intentFilter.addAction(ALARM_TIMEOUT);
intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
                 
intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);

mNetworkConnectivityHandler.registerNetworkCallbacks();

操作测试

要指定接受的 Intent 操作,Intent -Filter既可以不声明任何 <action> 元素,也可以声明多个此类元素,如下例所示:

<intent-filter>
     <action android:name="android.intent.action.EDIT" />
     <action android:name="android.intent.action.VIEW" />
     ...
 </intent-filter>

要通过此过滤器,您在 Intent 中指定的操作必须与过滤器中列出的某一操作匹配。

如果该过滤器未列出任何操作,则 Intent 没有任何匹配项,因此所有 Intent 均无法通过测试。但是,如果 Intent 未指定操作,则只要过滤器内包含至少一项操作,就可以通过测试。

类别测试

要指定接受的 Intent 类别,Intent - Filter既可以不声明任何 <category> 元素,也可以声明多个此类元素,如下例所示:

<intent-filter>
     <category android:name="android.intent.category.DEFAULT" />
     <category android:name="android.intent.category.BROWSABLE" />
     ...
 </intent-filter>

若要使 Intent 通过类别测试,则 Intent 中的每个类别均必须与过滤器中的类别匹配。反之则未必然,Intent -Filter 声明的类别可以超出 Intent 中指定的数量,且 Intent 仍会通过测试。因此,不含类别的 Intent 应当始终会通过此测试,无论过滤器中声明何种类别均是如此。

请注意:Android 会自动将 CATEGORY_DEFAULT 类别应用于传递给 startActivity()startActivityForResult() 的所有隐式 Intent。如需 Activity 接收隐式 Intent,则必须将 "android.intent.category.DEFAULT" 的类别包括在其 Intent 过滤器中(如上文的 <intent-filter> 示例所示)。

数据测试

要指定接受的 Intent 数据,Intent 过滤器既可以不声明任何 <data> 元素,也可以声明多个此类元素,如下例所示:

<intent-filter>
     <data android:mimeType="video/mpeg" android:scheme="http" ... />
     <data android:mimeType="audio/mpeg" android:scheme="http" ... />
     ...
 </intent-filter>

每个 <data> 元素均可指定 URI 结构和数据类型(MIME 媒体类型)。URI 的每个部分都是一个单独的属性:schemehostportpath

<scheme>://<host>:<port>/<path>

下例所示为这些属性的可能值:

content://com.example.project:200/folder/subfolder/etc

在此 URI 中,架构是 content,主机是 com.example.project,端口是 200,路径是 folder/subfolder/etc

<data> 元素中,上述每个属性均为可选,但存在线性依赖关系:

  • 如果未指定架构,则会忽略主机。
  • 如果未指定主机,则会忽略端口。
  • 如果未指定架构和主机,则会忽略路径。

将 Intent 中的 URI 与过滤器中的 URI 规范进行比较时,它仅与过滤器中包含的部分 URI 进行比较。例如:

  • 如果过滤器仅指定架构,则具有该架构的所有 URI 均与该过滤器匹配。
  • 如果过滤器指定架构和权限,但未指定路径,则具有相同架构和权限的所有 URI 都会通过过滤器,无论其路径如何均是如此。
  • 如果过滤器指定架构、权限和路径,则仅具有相同架构、权限和路径的 URI 才会通过过滤器。

请注意:路径规范可以包含星号通配符 (*),因此仅需部分匹配路径名即可。

数据测试会将 Intent 中的 URI 和 MIME 类型与过滤器中指定的 URI 和 MIME 类型进行比较。规则如下:

  1. 仅当过滤器未指定任何 URI 或 MIME 类型时,不含 URI 和 MIME 类型的 Intent 才会通过测试。
  2. 对于包含 URI 但不含 MIME 类型(既未显式声明,也无法通过 URI 推断得出)的 Intent,仅当其 URI 与过滤器的 URI 格式匹配、且过滤器同样未指定 MIME 类型时,才会通过测试。
  3. 仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时,包含 MIME 类型但不含 URI 的 Intent 才会通过测试。
  4. 仅当 MIME 类型与过滤器中列出的类型匹配时,同时包含 URI 类型和 MIME 类型(通过显式声明,或可以通过 URI 推断得出)的 Intent 才会通过测试的 MIME 类型部分。如果 Intent 的 URI 与过滤器中的 URI 匹配,或者如果 Intent 具有 content:file: URI 且过滤器未指定 URI,则 Intent 会通过测试的 URI 部分。换言之,如果过滤器只是列出 MIME 类型,则假定组件支持 content:file: 数据。

 

请注意:如果 Intent 指定 URI 或 MIME 类型,则数据测试会在 <intent-filter> 中没有 <data> 元素时失败。

最后一条规则,即规则 (d),反映出对组件能够从文件中或内容提供程序处获得本地数据的预期。因此,其过滤器只能列出数据类型,不需要显式命名 content:file: 架构。以下是一个典型示例,说明 <data> 元素向 Android 指出,组件可从内容提供程序处获得并显示图像数据:

 

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

由于大部分可用数据均由内容提供程序分发,因此指定数据类型(而非 URI)的过滤器也许最为常见。

另一常见的配置是具有架构和数据类型的过滤器。例如,下文中的 <data> 元素向 Android 指出,组件可从网络中检索视频数据以执行操作:

<intent-filter>
    <data android:scheme="http" android:mimeType="video/*" />
    ...
</intent-filter>

 【注】为什么需要这么多的匹配规则?

其主要目的就是要过滤掉不符合要求的 Intent,而只对符合规则的 Intent 进行响应。这些规则由 Intent-Filter 指定。大部分内部开发可以在Intent中设置的较为详细,但是外部开发(通过Intent引用外部应用)一般制定类型就可以了。

Intent 匹配

通过 Intent 过滤器匹配 Intent,这不仅有助于发现要激活的目标组件,还有助于发现设备上组件集的相关信息。例如,主页应用通过使用指定 ACTION_MAIN 操作和 CATEGORY_LAUNCHER 类别的 Intent 过滤器查找所有 Activity,以此填充应用启动器。IntentFilter 类文档所述,只有当 Intent 中的操作和类别与过滤器匹配时,匹配才会成功。

您的应用可以使用类似于主页应用的方式使用 Intent 匹配。PackageManager 提供一整套 query...() 方法来返回所有能够接受特定 Intent 的组件。此外,还会提供一系列类似的 resolve...() 方法来确定响应 Intent 的最佳组件。例如,queryIntentActivities() 将返回能够执行作为参数传递的 Intent 中列出的所有 Activity,而 queryIntentServices() 则可返回类似的一系列服务。这两种方法均不会激活组件;而只是列出能够响应的组件。对于广播接收器,有一种类似的方法:queryBroadcastReceivers()

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值