当前位置: 首页 > news >正文

第5章 使用Intent和IntentFilter通信

第5章 使用Intent和IntentFilter通信

本章要点

  • 理解Intent对于Android应用的作用
  • 使用Intent启动系统组件
  • Intent的Component属性的作用
  • Intent的Action属性的作用
  • Intent的Category属性的作用
  • 为指定Action、Category的Intent配置对应的intent-filter
  • Intent的Data属性
  • Intent的Type属性
  • 为指定Data、Type的Intent配置对应的intent-filter
  • Intent的Extra属性
  • Intent的Flag属性

在前面介绍Activity时,我们已经多次使用了Intent。当一个Activity需要启动另一Activity时,程序并没有直接告诉系统要启动哪个Activity,而是通过Intent来表达自己的意图:需要启动哪个Activity。Intent的中文意思即为“意图”。

此时,可能会有读者产生疑问,假如甲Activity需要启动另一Activity,为什么不直接使用一个类似于startActivity(Class activityClass)的方法呢?这样岂不是更简单明了?实际上,这种方式虽然简洁,但却明显背离了Android的设计理念。Android使用Intent来封装程序的“调用意图”,无论是启动Activity、Service组件,还是BroadcastReceiver,Android都采用统一的Intent对象来封装这些“启动意图”,这为开发者提供了一致的编程模型。

此外,使用Intent还有一个好处:在某些情况下,应用程序可能只想启动具有某种特征的组件,而不想与某个具体组件耦合。如果直接使用startActivity(Class activityClass)这种方法来启动特定组件,势必造成一种硬编码耦合,这不利于实现更高层次的解耦。

如果读者曾有过Spring MVC、Struts2等MVC框架的编程经验,那么一定可以很好地理解Intent的设计。当MVC框架的控制器处理完用户请求后,并不会直接返回特定的视图页面,而是返回一个逻辑视图名。开发者可以在配置文件中将该逻辑视图映射到任意的物理视图资源。Android系统中的Intent设计有点类似于MVC框架中的逻辑视图设计。

总之,Intent封装了Android应用程序中启动某个组件的“意图”。不仅如此,Intent还是应用程序组件之间通信的重要媒介。正如在前面示例中所见,两个Activity可以将需要交换的数据封装成Bundle对象,然后通过Intent来携带这个Bundle对象,从而实现两个Activity之间的数据交换。

5.1 Intent对象简述

Android的应用程序包含三种重要组件:Activity、Service、BroadcastReceiver。应用程序采用一致的方式来启动这些组件——都是依靠Intent来启动的。Intent封装了程序想要启动组件的意图,并可用于与被启动的组件交换信息。

表5.1 使用Intent启动不同组件的方法

组件类型启动方法
ActivitystartActivity(Intent intent)
startActivityForResult(Intent intent, int requestCode)
ServiceComponentName startService(Intent service)
boolean bindService(Intent service, ServiceConnection conn, int flags)
BroadcastReceiversendBroadcast(Intent intent)
sendBroadcast(Intent intent, String receiverPermission)
sendOrderedBroadcast(Intent intent, String receiverPermission)
sendStickyBroadcast(Intent intent)
sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

在上一章中,我们已经介绍了如何使用Intent来启动Activity的示例。至于如何使用Intent来启动其他两种组件,后续章节中会有相关示例,此处暂不深入介绍。

Intent对象大致包含Component、Action、Category、Data、Type、Extra和Flag这七种属性,其中Component用于明确指定需要启动的目标组件,而Extra则用于“携带”需要交换的数据。

接下来,我们将详细介绍Intent对象各属性的作用。

5.2 Intent 的属性及intent-filter 配置

Intent代表了Android应用的启动“意图”,Android 应用将会根据Intent来启动指定组件,至于到底启动哪个组件,则取决于Intent的各属性。下面将详细介绍Intent的各属性值,以及Android如何根据不同属性值来启动相应的组件。

5.2.1 Component 属性

Intent的Component属性需要接受一个ComponentName对象,ComponentName对象包含如下几个构造器:

  • ComponentName(String pkg, String cls):创建pkg所在包下的cls类所对应的组件。
  • ComponentName(Context pkg, String cls):创建pkg所对应的包下的cls类所对应的组件。
  • ComponentName(Context pkg, Class<?> cls):创建pkg所对应的包下的cls类所对应的组件。

这些构造器的本质是相同的,这说明创建一个ComponentName需要指定包名和类名,这样就可以唯一地确定一个组件类,从而应用程序即可根据给定的组件类启动特定的组件。

除此之外,Intent还包含了如下三个方法:

  • setClass(Context packageContext, Class<?> cls):设置该Intent将要启动的组件对应的类。
  • setClassName(Context packageContext, String className):设置该Intent将要启动的组件对应的类名。
  • setClassName(String packageName, String className):设置该Intent将要启动的组件对应的类名。

提示:
Android应用的Context代表了访问该应用环境信息的接口,而Android应用的包名则作为应用的唯一标识。因此,Android应用的Context对象与该应用的包名有对应关系。上面三个setClass()方法正是通过Context或String指定组件的包名和实现类。

指定Component属性的Intent已经明确了它将要启动哪个组件,因此这种Intent也被称为显式Intent;没有指定Component属性的Intent被称为隐式Intent——隐式Intent没有明确指定要启动哪个组件,应用将会根据Intent指定的规则去启动符合条件的组件,但具体是哪个组件则不确定。

提示:
例如,一个女孩子有找男朋友的意图,此时有两种方式来表达她的意图:

  1. 她已经芳心暗许,明确地告诉父母“我要找‘梁山伯’做男朋友”,这种方式被称为显式Intent。
  2. 她告诉父母,要找“高”“富”“帅”做男朋友,至于到底是谁不重要,只要符合这三个条件即可,这种方式被称为隐式Intent。

下面的示例程序示范了如何通过显式Intent(指定了Component属性)来启动另一个Activity。

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button bn = findViewById(R.id.bn);//为bn按钮绑定事件监听器bn.setOnClickListener(view -> {// 创建一个ComponentName对象ComponentName comp = new ComponentName(MainActivity.this, SecondActivity.class);Intent intent = new Intent();// 为Intent设置Component属性intent.setComponent(comp);startActivity(intent);});}
}

上面程序中的三行代码用于创建ComponentName对象,并将该对象设置成Intent对象的Component属性,这样应用程序即可根据该Intent的“意图”去启动指定组件。

实际上,上面三行代码完全可以简化为如下形式:

// 根据指定组件类来创建Intent
Intent intent = new Intent(MainActivity.this, SecondActivity.class);

从上面的代码可以看出,当需要为Intent设置Component属性时,实际上Intent已经提供了一个简化的构造器,这样方便程序直接指定启动其他组件。

当程序通过Intent的Component属性(明确指定了启动哪个组件)启动特定组件时,被启动组件几乎不需要使用<intent-filter>元素进行配置。

程序的SecondActivity也很简单,它的界面布局中只包含一个简单的文本框,用于显示该Activity对应的Intent的Component属性的包名、类名。该Activity的代码如下:

public class SecondActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);TextView show = findViewById(R.id.show);// 获取该Activity对应的Intent的Component属性ComponentName comp = getIntent().getComponent();// 显示该ComponentName对象的包名、类名show.setText("组件包名为:" + comp.getPackageName() + "\n组件类名为:" + comp.getClassName());}
}

运行上面的程序,通过第一个Activity中的按钮进入第二个Activity中,将看到如图5.1所示的界面。

5.2.2 Action、Category 属性与intent-filter配置

Intent的Action、Category属性的值都是一个普通的字符串,其中Action代表该Intent所要完成的一个抽象“动作”,而Category则用于为Action增加额外的附加类别信息。通常Action属性会与Category属性结合使用。

Action要完成的只是

一个抽象动作,这个动作具体由哪个组件(或许是Activity,或许是BroadcastReceiver)来完成,Action这个字符串本身并不管。比如Android提供的标准Action:Intent.ACTION_VIEW,它只表示一个抽象的查看操作,但具体查看什么、启动哪个Activity来查看,Intent.ACTION_VIEW并不知道,这取决于Activity的<intent-filter>配置。只要某个Activity的<intent-filter>配置中包含了该ACTION_VIEW,该Activity就有可能被启动。

提示:
有过Struts2开发经验的读者都知道,当Struts2的Action处理用户请求结束后,Action并不会直接指定“跳转”到哪个Servlet(通常是JSP,但JSP的本质就是Servlet)。Action的处理方法只是返回一个普通字符串,然后在配置文件中配置该字符串对应到哪个Servlet。Struts2采用这种设计思路是为了把Action与呈现视图的Servlet分离开。类似地,Intent通过指定Action属性(属性值其实就是一个普通字符串),就可以把该Intent与具体的Activity分离,从而提供高层次的解耦。

下面通过一个简单的示例来示范Action属性(就是普通字符串)的作用。下面程序的第一个Activity非常简单,它只包括一个普通按钮,当用户单击该按钮时,程序会“跳转”到第二个Activity——但第一个Activity指定跳转的Intent时,并不以“硬编码”的方式指定要跳转的目标Activity,而是为Intent指定Action属性。

public class MainActivity extends Activity {public static final String CRAZYIT_ACTION = "org.crazyit.intent.action.CRAZYIT_ACTION";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button bn = findViewById(R.id.bn);// 为bn按钮绑定事件监听器bn.setOnClickListener(view -> {// 创建Intent对象Intent intent = new Intent();// 为Intent设置Action属性(属性值就是一个普通字符串)intent.setAction(MainActivity.CRAZYIT_ACTION);startActivity(intent);});}
}

上面程序中的代码指定了根据Intent来启动Activity,但该Intent并未以“硬编码”的方式指定要启动哪个Activity,从上面程序中无法看出该程序将要启动哪个Activity。那么到底程序会启动哪个Activity呢?这取决于Activity配置中<intent-filter>元素的配置。

<intent-filter>元素是AndroidManifest.xml文件中<activity>元素的子元素,前面已经介绍过,<activity>元素用于为应用程序配置Activity,<activity><intent-filter>子元素则用于配置该Activity所能“响应”的Intent。

<intent-filter>元素里通常可包含如下子元素:

  • 0-N个<action>子元素。
  • 0-N个<category>子元素。
  • 0-1个<data>子元素。

提示:
<intent-filter>元素也可以是<service><receiver>两个元素的子元素,用于表明它们可以响应的Intent。

<action><category>子元素的配置非常简单,它们都可指定android:name属性,该属性的值就是一个普通字符串。

<activity>元素的<intent-filter>子元素里包含多个<action>子元素(相当于指定了多个字符串)时,就表明该Activity能响应Action属性值为其中任意一个字符串的Intent。

还是借用前面介绍的女孩子找男朋友的例子,可以把Intent中的Action、Category属性理解为她对男朋友的要求,每个Intent只能指定一个Action“要求”,但可以指定多个Category“要求”;IntentFilter(使用<intent-filter>元素配置)则用于声明该组件(比如Activity、Service、BroadcastReceiver)能满足的要求,每个组件可以声明自己满足多个Action要求、多个Category要求。只要某个组件能满足的要求大于、等于Intent所指定的要求,那么该Intent就能启动该组件。

由于上面的程序指定启动Action属性为MainActivity.CRAZYIT_ACTION常量(常量值为org.crazyit.intent.action.CRAZYIT_ACTION)的Activity,这就要求被启动的Activity对应的配置元素的<intent-filter>元素里至少包括一个如下的<action>子元素:

<action android:name="org.crazyit.intent.action.CRAZYIT_ACTION" />

需要指出的是,一个Intent对象最多只能包括一个Action属性,程序可调用Intent的setAction(String str)方法来设置Action属性值;但一个Intent对象可以包括多个Category属性,程序可调用Intent的addCategory(String str)方法来为Intent添加Category属性。当程序创建Intent时,该Intent默认启动Category属性值为Intent.CATEGORY_DEFAULT常量(常量值为android.intent.category.DEFAULT)的组件。

因此,虽然上面程序中的代码并未指定目标Intent的Category属性,但该Intent已有一个值为android.intent.category.DEFAULT的Category属性值,因此被启动Activity对应的配置元素的<intent-filter>元素里至少还包括一个如下的<category>子元素:

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

下面是被启动的Activity的完整配置。

<activity android:name=".SecondActivity" android:label="第二个Activity"><intent-filter><!-- 指定该Activity能响应Action属性值为指定字符串的Intent --><action android:name="org.crazyit.intent.action.CRAZYIT_ACTION" /><!-- 指定该Activity能响应Action属性值为helloWorld的Intent --><action android:name="helloWorld" /><!-- 指定该Activity能响应Category属性值为DEFAULT的Intent --><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</activity>

上面Activity配置的代码指定该Activity能响应具有指定Action属性值、指定Category属性值的Intent。

上面配置代码中配置了一个实现类为SecondActivity的Activity,因此程序还应该提供这个Activity代码。代码如下。

public class SecondActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);TextView show = findViewById(R.id.show);// 获取该Activity对应的Intent的Action属性String action = getIntent().getAction();// 显示Action属性show.setText("Action为:" + action);}
}

上面的程序代码很简单,它只是在启动时把启动该Activity的Intent的Action属性值显示在指定文本框内。

运行上面的程序,并单击程序中的“启动指定Action、默认Category对应的Activity”按钮,将看到如图5.2所示的界面。

接下来的示例程序将会示范Category属性的用法。

public class MainActivity extends Activity {// 定义一个Action常量public static final String CRAZYIT_ACTION = "org.crazyit.intent.action.CRAZYIT_ACTION";// 定义一个Category常量public static final String CRAZYIT_CATEGORY = "org.crazyit.intent.category.CRAZYIT_CATEGORY";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button bn = findViewById(R.id.bn);bn.setOnClickListener(view -> {Intent intent = new Intent();// 设置Action属性intent.setAction(MainActivity.CRAZYIT_ACTION);// 添加Category属性intent.addCategory(MainActivity.CRAZYIT_CATEGORY);startActivity(intent);});}
}

上面程序指定了该Intent的Action属性值为org.crazyit.intent.action.CRAZYIT_ACTION字符串,并为该Intent添加了字符串为org.crazyit.intent.category.CRAZYIT_CATEGORY的Category属性。这意味着上面的程序所要启动的目标Activity里应该包含如下<action>子元素和<category>子元素:

<!-- 指定该Activity能响应action为指定字符串的Intent -->
<action android:name="org.crazyit.intent.action.CRAZYIT_ACTION" />
<!-- 指定该Activity能响应category为指定字符串的Intent -->
<category android:name="org.crazyit.intent.category.CRAZYIT_CATEGORY" />
<!-- 指定该Activity能响应category为DEFAULT的Intent -->
<category android:name="android.intent.category.DEFAULT" />

下面是程序要启动的目标Activity所对应的配置代码。

<activity android:name=".SecondActivity" android:label="第二个Activity"><intent-filter><!-- 指定该Activity能响应action为指定字符串的Intent --><action android:name="org.crazyit.intent.action.CRAZYIT_ACTION" /><!-- 指定该Activity能响应category为指定字符串的Intent --><category android:name="org.crazyit.intent.category.CRAZYIT_CATEGORY" /><!-- 指定该Activity能响应category为DEFAULT的Intent --><category android:name="android.intent.category.DEFAULT"/></intent-filter>
</activity>

上面配置Activity时也指定该Activity的实现类为SecondActivity,该实现类的代码如下:

public class SecondActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);TextView show = findViewById(R.id.show);// 获取该Activity对应的Intent的Action属性String action = getIntent().getAction();// 显示Action属性show.setText("Action为:" + action);TextView cate = findViewById(R.id.cate);// 获取该Activity对应的Intent的Category属性Set<String> cates = getIntent().getCategories();// 显示Category属性cate.setText("Category属性为:" + cates);}
}

上面的程序代码也很简单,它只是在启动时把启动该Activity的Intent的Action、Category属性值分别显示在不同的文本框内。运行上面的程序,并单击程序中的“启动指定Action、指定Category对应的Activity”按钮,将看到如图5.3所示的界面。

5.2.3 指定Action、Category调用系统Activity

Intent代表了启动某个程序组件的“意图”,实际上Intent对象不仅可以启动本应用内程序组件,也可以启动Android系统的其他应用的程序组件,包括系统自带的程序组件(只要权限允许)。

实际上,Android内部提供了大量标准的Action、Category常量,其中用于启动Activity的标准的Action常量及对应的字符串如表5.2所示。

表5.2 启动Activity的标准的Action常量及对应的字符串

Action 常量对应字符串简单说明
ACTION_MAINandroid.intent.action.MAIN应用程序入口
ACTION_VIEWandroid.intent.action.VIEW查看指定数据
ACTION_ATTACH_DATAandroid.intent.action.ATTACH_DATA指定某块数据将被附加到其他地方
ACTION_EDITandroid.intent.action.EDIT编辑指定数据
ACTION_PICKandroid.intent.action.PICK从列表中选择某项,并返回所选数据
ACTION_CHOOSERandroid.intent.action.CHOOSER显示一个Activity选择器
ACTION_GET_CONTENTandroid.intent.action.GET_CONTENT让用户选择数据,并返回所选数据
ACTION_DIALandroid.intent.action.DIAL显示拨号面板
ACTION_CALLandroid.intent.action.CALL直接向指定用户打电话
ACTION_SENDandroid.intent.action.SEND向其他人发送数据
ACTION_SENDTOandroid.intent.action.SENDTO向其他人发送消息
ACTION_ANSWERandroid.intent.action.ANSWER应答电话
ACTION_INSERTandroid.intent.action.INSERT插入数据
ACTION_DELETEandroid.intent.action.DELETE删除数据
ACTION_RUNandroid.intent.action.RUN运行数据
ACTION_SYNCandroid.intent.action.SYNC执行数据同步
ACTION_PICK_ACTIVITYandroid.intent.action.PICK_ACTIVITY用于选择Activity
ACTION_SEARCHandroid.intent.action.SEARCH执行搜索
ACTION_WEB_SEARCHandroid.intent.action.WEB_SEARCH执行Web搜索
ACTION_FACTORY_TESTandroid.intent.action.FACTORY_TEST工厂测试的入口点
ACTION_ANSWERandroid.intent.action.ANSWER应答电话

标准的Category常量及对应的字符串如表5.3所示。

表5.3 标准的Category常量及对应的字符串

Category 常量对应字符串简单说明
CATEGORY_DEFAULTandroid.intent.category.DEFAULT默认的Category
CATEGORY_BROWSABLEandroid.intent.category.BROWSABLE指定该Activity能被浏览器安全调用
CATEGORY_TABandroid.intent.category.TAB指定Activity作为TabActivity的Tab页
CATEGORY_LAUNCHERandroid.intent.category.LAUNCHERActivity显示顶级程序列表
CATEGORY_INFOandroid.intent.category.INFO用于提供包信息
CATEGORY_HOMEandroid.intent.category.HOME设置该Activity随系统启动而运行
CATEGORY_PREFERENCEandroid.intent.category.PREFERENCE该Activity是参数面板
CATEGORY_TESTandroid.intent.category.TEST该Activity是一个测试
CATEGORY_CAR_DOCKandroid.intent.category.CAR_DOCK指定手机被插入汽车底座(硬件)时运行该Activity
CATEGORY_DESK_DOCKandroid.intent.category.DESK_DOCK指定手机被插入桌面底座(硬件)时运行该Activity
CATEGORY_CAR_MODEandroid.intent.category.CAR_MODE设置该Activity可在车载环境下使用

表5.2、表5.3所列出的都只是部分较为常用的Action常量、Category常量。关于Intent所提供的全部Action常量、Category常量,应参考Android API文档中关于Intent的说明。

下面将以两个实例来介绍Intent系统Action、系统Category的用法。

实例:查看并获取联系人电话
这个实例将在程序中提供一个按钮,用户单击该按钮时会显示系统的联系人列表,当用户单击指定联系人之后,程序将会显示该联系人的名字、电话。

这个程序非常有用,比如我们要开发一个发送短信的程序,当用户编写短信完成之后,可能需要浏览联系人列表,并从联系人列表中选出短信接收人,那就可以用到该程序了。

该程序的界面布局代码如下。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_horizontal"android:orientation="vertical"><!--显示联系人姓名的文本框--><TextViewandroid:id="@+id/show"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="10dp"android:textSize="18sp" /><!-- 显示联系人电话号码的文本框--><TextViewandroid:id="@+id/phone"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="10dp"android:textSize="18sp" /><Buttonandroid:id="@+id/bn"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="查看联系人" />
</LinearLayout>

上面的界面布局中包含了两个文本框、一个按钮,其中按钮用于浏览系统的联系人列表并选择其中的联系人。两个文本框分别用于显示联系人的名字和电话号码。

由于该程序会用到系统的联系人应用,因此读者在运行该程序之前应该先进入Android系统自带的Contacts程序,并通过该程序添加几个联系人。

该程序的Activity代码如下。

public class MainActivity extends Activity {private static final int PICK_CONTACT = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button bn = findViewById(R.id.bn);// 为bn按钮绑定事件监听器bn.setOnClickListener(view -> {// 运行时获取读取联系人信息的权限requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, 0x133);});}@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {if (requestCode == 0x133) {// 如果用户同意授权访问if (permissions.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {// 创建IntentIntent intent = new Intent();// 设置Intent的Action属性intent.setAction(Intent.ACTION_PICK);// 设置Intent的Type属性intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);// 启动Activity,并希望获取该Activity的结果startActivityForResult(intent, PICK_CONTACT);}}}@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case PICK_CONTACT:if (resultCode == Activity.RESULT_OK) {// 获取返回的数据Uri contactData = data.getData();CursorLoader cursorLoader= new CursorLoader(this, contactData, null, null, null, null);// 查询联系人信息Cursor cursor = cursorLoader.loadInBackground();// 如果查询到指定的联系人if (cursor != null && cursor.moveToFirst()) {String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));// 获取联系人的名字String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));String phoneNumber = "此联系人暂未输入电话号码";// 根据联系人查询该联系人的详细信息Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactId, null, null);if (phones != null && phones.moveToFirst()) {// 取出电话号码phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));}// 关闭游标if (phones != null) phones.close();TextView show = findViewById(R.id.show);// 显示联系人的名称show.setText(name);TextView phone = findViewById(R.id.phone);// 显示联系人的电话号码phone.setText(phoneNumber);// 关闭游标if (cursor != null) cursor.close();}}break;}}
}

提示:
该实例使用了后面章节中关于ContentProvider的知识,如果读者一时看不懂这个程序,可以参考后面章节的讲解进行理解。

由于上面程序需要查看系统联系人信息,因此要获取系统对应的权限。首先需要在AndroidManifest.xml文件中增加如下代码来获取权限。

<!--请求获取读取联系人的权限-->
<uses-permission android:name="android.permission.READ_CONTACTS" />

最新的Android要求在获取系统资源时不能仅靠在AndroidManifest.xml文件中申请权限,而是在App运行时动态请求获取权限。因此,上面按钮的单击事件处理代码中的权限代码只是在运行时动态获取权限。

当用户对应用授权完成后,系统会自动激发Activity的onRequestPermissionsResult方法,因此上面Activity重写了该方法来处理用户的授权结果:只有当用户授权本Activity访问Contacts(通讯录)时,本Activity才会继续定义actionIntent.ACTION_PICK、指定type的Intent,并通过该Intent启动Activity,如上面Activity中的代码所示。

运行上面的程序,单击程序界面中的“查看联系人”按钮,该应用将会向用户请求授权。如果用户同意访问联系人信息,程序将会显示如图5.4所示的界面。

在图5.4所示的联系人列表中单击某个联系人,系统将会自动返回上一个Activity,程序会在上一个Activity中显示所选联系人的名字和电话号码,如图5.5所示。

上面的Intent对象除设置了Action属性之外,还设置了Type属性,关于Intent的Type属性的作用,5.2.4节将会进行更详细的介绍。

实例:返回系统Home桌面
本实例将会提供一个按钮,当用户单击该按钮时,系统将会返回Home桌面,就像单击模拟器右边的按钮一样。这也需要通过Intent来实现,程序为Intent设置合适的Action、Category属性,并根据该Intent来启动Activity即可返回Home桌面。该实例程序如下。

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button bn = findViewById(R.id.bn);bn.setOnClickListener(view -> {// 创建Intent对象Intent intent = new Intent();// 为Intent设置Action、Category属性intent.setAction(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_HOME);startActivity(intent);});}
}

上面程序中的代码设置了Intent的Action属性值为"android.intent.action.MAIN"字符串、Category属性值为"android.intent.category.HOME"字符串,满足该Intent的Activity其实就是Android系统的Home桌面。因此,运行上面的程序时单击“返回桌面”按钮,即可返回Home桌面。

5.2.4 Data、Type属性与intent-filter配置

Data属性通常用于向Action属性提供操作的数据。Data属性接受一个Uri对象,该Uri对象通常通过如下形式的字符串来表示:

content://com.android.contacts/contacts/1
tel:123

Uri字符串通常满足如下格式:

scheme://host:port/path

例如上面给出的content://com.android.contacts/contacts/1,其中content是scheme部分,com.android.contacts是host部分,port部分被省略了,/contacts/1是path部分。

Type属性用于指定该Data属性所指定Uri对应的MIME类型,这种MIME类型可以是任何自定义的MIME类型,只要符合abc/xyz格式的字符串即可。

Data属性与Type属性的关系比较微妙,这两个属性会相互覆盖,例如:

  • 如果为Intent先设置Data属性,后设置Type属性,那么Type属性将会覆盖Data属性。
  • 如果为Intent先设置Type属性,后设置Data属性,那么Data属性将会覆盖Type属性。
  • 如果希望Intent既有Data属性,也有Type属性,则应该调用Intent的setDataAndType方法。

下面的示例演示了Intent的Data属性与Type属性互相覆盖的情形,该示例的界面布局文件很简单,只是定义了三个按钮,并为三个按钮绑定了事件处理函数。

下面是该示例的Activity代码。

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void overrideType(View source) {Intent intent = new Intent();// 先为Intent设置Type属性intent.setType("abc/xyz");// 再为Intent设置Data属性,覆盖Type属性intent.setData(Uri.parse("lee://www.fkjava.org:888/test"));Toast.makeText(this, intent.toString(), Toast.LENGTH_LONG).show();}public void overrideData(View source) {Intent intent = new Intent();// 先为Intent设置Data属性intent.setData(Uri.parse("lee://www.fkjava.org:888/mypath"));// 再为Intent设置Type属性,覆盖Data属性intent.setType("abc/xyz");Toast.makeText(this, intent.toString(), Toast.LENGTH_LONG).show();}public void dataAndType(View source) {Intent intent = new Intent();// 同时设置Intent的Data、Type属性intent.setDataAndType(Uri.parse("lee://www.fkjava.org:888/mypath"), "abc/xyz");Toast.makeText(this, intent.toString(), Toast.LENGTH_LONG).show();}
}

上面的三个事件监听方法分别为Intent设置了Data、Type属性,第一个事件监听方法先设置了Type属性,再设置了Data属性,这将导致Data属性覆盖Type属性,单击按钮激发该事件监听方法,将可以看到如图5.6所示的Toast输出。

从图5.6可以看出,此时的Intent只有Data属性,Type属性被覆盖了。

第二个事件监听方法先设置了Data属性,再设置了Type属性,这将导致Type属性覆盖Data属性,单击按钮激发该事件监听方法,将可以看到如图5.7所示的输出。

第三个事件监听方法同时设置了Data、Type属性,这样该Intent中才会同时具有Data、Type属性。

在AndroidManifest.xml文件中为组件声明Data、Type属性都通过<data>元素,<data>元素的格式如下:

<data android:mimeType=""android:scheme=""android:host=""android:port=""android:path=""android:pathPrefix=""android:pathPattern="" />

上面的<data>元素支持如下属性:

  • mimeType:用于声明该组件所能匹配的Intent的Type属性。
  • scheme:用于声明该组件所能匹配的Intent的Data属性的scheme部分。
  • host:用于声明该组件所能匹配的Intent的Data属性的host部分。
  • port:用于声明该组件所能匹配的Intent的Data属性的port部分。
  • path:用于声明该组件所能匹配的Intent的Data属性的path部分。
  • pathPrefix:用于声明该组件所能匹配的Intent的Data属性的path前缀。
  • pathPattern:用于声明该组件所能匹配的Intent的Data属性的path字符串模板。

Intent的Type属性用于指定该Intent的要求,对应组件中<intent-filter>元素的<data>子元素的mimeType属性必须与此相同,才能启动该组件。

Intent的Data属性略有差异,程序员为Intent指定Data属性时,Data属性的Uri对象实际上可分为scheme、host、port和path部分,此时并不要求被启动组件

<intent-filter><data>子元素的android:schemeandroid:hostandroid:portandroid:path完全满足。

Data属性的“匹配”过程有些差别,它会先检查<intent-filter><data>子元素,然后:

  • 如果目标组件的<data>子元素只指定了android:scheme属性,那么只要Intent的Data属性的scheme部分与android:scheme属性值相同,即可启动该组件。
  • 如果目标组件的<data>子元素只指定了android:schemeandroid:host属性,那么只要Intent的Data属性的scheme、host部分与android:schemeandroid:host属性值相同,即可启动该组件。
  • 如果目标组件的<data>子元素指定了android:schemeandroid:hostandroid:port属性,那么只要Intent的Data属性的scheme、host、port部分与android:schemeandroid:hostandroid:port属性值相同,即可启动该组件。

提示:
如果<data>子元素只有android:port属性,没有指定android:host属性,那么android:port属性将不会起作用。

  • 如果目标组件的<data>子元素只指定了android:schemeandroid:hostandroid:path属性,那么只要Intent的Data属性的scheme、host、path部分与android:schemeandroid:hostandroid:path属性值相同,即可启动该组件。

如果<data>子元素只有android:path属性,没有指定android:host属性,那么android:path属性将不会起作用。

  • 如果目标组件的<data>子元素指定了android:schemeandroid:hostandroid:portandroid:path属性,那么就要求Intent的Data属性的scheme、host、port、path部分依次与android:schemeandroid:hostandroid:portandroid:path属性值相同,才可启动该组件。

下面的示例测试了Intent的Data属性与<data>元素配置的关系,该示例依次配置了如下5个Activity。

<activity android:icon="@drawable/ic_scheme" android:name=".SchemeActivity" android:label="指定scheme的Activity"><intent-filter><action android:name="xx" /><category android:name="android.intent.category.DEFAULT" /><!-- 只要Intent的Data属性的scheme是lee,即可启动该Activity --><data android:scheme="lee" /></intent-filter>
</activity><activity android:icon="@drawable/ic_host" android:name=".SchemeHostPortActivity" android:label="指定scheme、host、port的Activity"><intent-filter><action android:name="xx" /><category android:name="android.intent.category.DEFAULT" /><!-- 只要Intent的Data属性的scheme是lee,且host是www.fkjava.org,port是8888,即可启动该Activity --><data android:scheme="lee" android:host="www.fkjava.org" android:port="8888" /></intent-filter>
</activity><activity android:icon="@drawable/ic_sp" android:name=".SchemeHostPathActivity" android:label="指定scheme、host、path的Activity"><intent-filter><action android:name="xx" /><category android:name="android.intent.category.DEFAULT" /><!-- 只要Intent的Data属性的scheme是lee,且host是www.fkjava.org,path是/mypath,即可启动该Activity --><data android:scheme="lee" android:host="www.fkjava.org" android:path="/mypath" /></intent-filter>
</activity><activity android:icon="@drawable/ic_path" android:name=".SchemeHostPortPathActivity" android:label="指定scheme、host、port、path的Activity"><intent-filter><action android:name="xx" /><category android:name="android.intent.category.DEFAULT" /><!-- 需要Intent的Data属性的scheme是lee,且host是www.fkjava.org,port是8888,path是/mypath,才可启动该Activity --><data android:scheme="lee" android:host="www.fkjava.org" android:port="8888" android:path="/mypath" /></intent-filter>
</activity><activity android:icon="@drawable/ic_type" android:name=".SchemeHostPortPathTypeActivity" android:label="指定scheme、host、port、path、type的Activity"><intent-filter><action android:name="xx" /><category android:name="android.intent.category.DEFAULT" /><!-- 需要Intent的Data属性的scheme是lee,且host是www.fkjava.org,port是8888,path是/mypath,type是abc/xyz,才可启动该Activity --><data android:scheme="lee" android:host="www.fkjava.org" android:port="8888" android:path="/mypath" android:mimeType="abc/xyz" /></intent-filter>
</activity>

在上面的配置文件中配置了5个Activity,这5个Activity的实现类都非常简单,它们都仅在界面上显示一个TextView,并不显示其他内容。关于这5个Activity的<data>子元素配置说明如下:

  • 第1个Activity:只要Intent的Data属性的scheme是lee,即可启动该Activity。
  • 第2个Activity:只要Intent的Data属性的scheme是lee,且host是www.fkjava.org,port是888,即可启动该Activity。
  • 第3个Activity:只要Intent的Data属性的scheme是lee,且host是www.fkjava.org,path是/mypath,即可启动该Activity。
  • 第4个Activity:需要Intent的Data属性的scheme是lee,且host是www.fkjava.org,port是888,path是/mypath,才可启动该Activity。
  • 第5个Activity:需要Intent的Data属性的scheme是lee,且host是www.fkjava.org,port是8888,path是/mypath,type是abc/xyz,才可启动该Activity。

上面配置Activity的<intent-filter>元素时,<action>子元素的name属性是随意指定的,这也是必需的。如果希望<data>子元素能正常起作用,至少要配置一个<action>子元素,但该子元素的android:name属性值可以是任意的字符串。

下面是第1个启动Activity的方法。

public void scheme(View source) {Intent intent = new Intent();// 只设置Intent的Data属性intent.setData(Uri.parse("lee://www.crazyit.org:1234/test"));startActivity(intent);
}

上面Intent的Data属性,只有scheme为lee,也就是只有第1个Activity符合条件,因此通过该方法启动Activity时,将可以看到启动如图5.8所示的Activity。

下面是第2个启动Activity的方法。

public void schemeHostPort(View source) {Intent intent = new Intent();// 只设置Intent的Data属性intent.setData(Uri.parse("lee://www.fkjava.org:8888/test"));startActivity(intent);
}

上面Intent的Data属性,scheme为lee,因此第1个Activity符合条件;且该Intent的Data属性的host为www.fkjava.org,port为888,因此第2个Activity也符合条件。通过该方法启动Activity时,将可以看到启动如图5.9所示的选择Activity界面。

下面是第3个启动Activity的方法。

public void schemeHostPath(View source) {Intent intent = new Intent();// 只设置Intent的Data属性intent.setData(Uri.parse("lee://www.fkjava.org:1234/mypath"));startActivity(intent);
}

上面Intent的Data属性,scheme为lee,因此第1个Activity符合条件;且该Intent的Data属性的host为www.fkjava.org,path为/mypath,因此第3个Activity也符合条件。通过该方法启动Activity时,将可以看到启动如图5.10所示的选择Activity界面。

下面是第4个启动Activity的方法。

public void schemeHostPortPath(View source) {Intent intent = new Intent();// 只设置Intent的Data属性intent.setData(Uri.parse("lee://www.fkjava.org:8888/mypath"));startActivity(intent);
}

上面Intent的Data属性,scheme为lee,因此第1个Activity符合条件;且该Intent的Data属性的host为www.fkjava.org,port为888,因此第2个Activity符合条件;该Intent的Data属性的host为www.fkjava.org,path为/mypath,因此第3个Activity符合条件;该Intent的Data属性的host为www.fkjava.org,port为888,path为/mypath,因此第

4个Activity也符合条件。通过该方法启动Activity时,将可以看到启动如图5.11所示的选择Activity界面。

下面是第5个启动Activity的方法。

public void schemeHostPortPathType(View source) {Intent intent = new Intent();// 同时设置Intent的Data、Type属性intent.setDataAndType(Uri.parse("lee://www.fkjava.org:8888/mypath"), "abc/xyz");startActivity(intent);
}

上面的Intent不仅指定了Data属性,也指定了Type属性,此时符合条件的只有第5个Activity。通过该方法启动Activity时,将可以看到启动如图5.12所示的Activity。

实例:使用Action、Data属性启动系统Activity
一旦为Intent同时指定了Action、Data属性,Android就可根据指定的数据类型来启动特定的应用程序,并对指定数据执行相应的操作。

下面是几个常见的Action属性、Data属性的组合:

  • ACTION_VIEW content://com.android.contacts/contacts/1:显示标识为1的联系人的信息。
  • ACTION_EDIT content://com.android.contacts/contacts/1:编辑标识为1的联系人的信息。
  • ACTION_DIAL content://com.android.contacts/contacts/1:显示向标识为1的联系人拨号的界面。
  • ACTION_VIEW tel:123:显示向指定号码123拨号的界面。
  • ACTION_DIAL tel:123:显示向指定号码123拨号的界面。
  • ACTION_VIEW content://contacts/people/:显示所有联系人列表的信息,通过这种组合可以非常方便地查看系统联系人。

本实例程序示范通过同时为Intent指定Action、Data属性来启动特定程序并操作相应的数据。下面程序的界面很简单,只包含3个按钮,其中一个按钮用于浏览指定网页;一个按钮用于编辑指定联系人信息;另一个按钮用于呼叫指定号码。

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button bn = findViewById(R.id.bn);// 为bn按钮添加一个监听器bn.setOnClickListener(view -> {// 创建IntentIntent intent = new Intent();// 为Intent设置Action属性intent.setAction(Intent.ACTION_VIEW);// 设置Data属性intent.setData(Uri.parse("http://www.crazyit.org"));startActivity(intent);});Button edit = findViewById(R.id.edit);// 为edit按钮添加一个监听器edit.setOnClickListener(view -> {// 创建IntentIntent intent = new Intent();// 为Intent设置Action属性(动作为:编辑)intent.setAction(Intent.ACTION_EDIT);// 设置Data属性intent.setData(Uri.parse("content://com.android.contacts/contacts/1"));startActivity(intent);});Button call = findViewById(R.id.call);// 为call按钮添加一个监听器call.setOnClickListener(view -> {// 创建IntentIntent intent = new Intent();// 为Intent设置Action属性(动作为:拨号)intent.setAction(Intent.ACTION_DIAL);// 设置Data属性intent.setData(Uri.parse("tel:13800138000"));startActivity(intent);});}
}

运行上面的程序,单击第一个按钮,该按钮被单击时启动Intent(Action=Intent.ACTION_VIEW, Data=http://www.crazyit.org)对应的Activity,将看到打开www.crazyit.org的界面。

单击第二个按钮,该按钮被单击时启动Intent(Action=Intent.ACTION_EDIT, Data=content://com.android.contacts/contacts/1)对应的Activity,将看到编辑标识为1的联系人界面。

单击第三个按钮,该按钮被单击时启动Intent(Action=Intent.ACTION_DIAL, Data=tel:13800138000)对应的Activity,将看到程序向13800138000拨号的界面。

5.2.5 Extra属性

Intent的Extra属性通常用于在多个Action之间进行数据交换,Intent的Extra属性值应该是一个Bundle对象,Bundle对象就像一个Map对象,它可以存入多个key-value对,这样就可以通过Intent在不同Activity之间进行数据交换了。关于Extra属性的用法前面已有示例,此处不再赘述。

5.2.6 Flag属性

Intent的Flag属性用于为该Intent添加一些额外的控制标志,Intent可调用addFlags()方法来添加控制标志。

前面介绍启用ActionBar的程序图标返回主Activity时已经用到了Flag属性。比如前面介绍的Intent.FLAG_ACTIVITY_CLEAR_TOP标志可用于清除当前Activity栈中的Activity。

除此之外,Intent还包含了如下常用的Flag标志:

  • FLAG_ACTIVITY_BROUGHT_TO_FRONT:如果通过该Flag启动的Activity已经存在,下次再次启动时,将只是把该Activity带到前台。例如,现在Activity栈中有Activity A,此时以该标志启动Activity B(即Activity B是以FLAG_ACTIVITY_BROUGHT_TO_FRONT标志启动的),然后在Activity B中启动Activity C、D,如果此时在Activity D中再启动Activity B,将直接把Activity栈中的Activity B带到前台。此时Activity栈中情形是Activity A、C、D、B。
  • FLAG_ACTIVITY_CLEAR_TOP:该Flag相当于加载模式中的singleTask,通过这种Flag启动的Activity将会把要启动的Activity之上的Activity全部弹出Activity栈。例如,Activity栈中包含A、B、C、D四个Activity,如果采用该Flag从Activity D跳转到Activity B,那么此时Activity栈中只包含A、B两个Activity。
  • FLAG_ACTIVITY_NEW_TASK:默认的启动标志,该标志控制重新创建一个新的Activity。
  • FLAG_ACTIVITY_NO_ANIMATION:该标志控制启动Activity时不使用过渡动画。
  • FLAG_ACTIVITY_NO_HISTORY:该标志控制被启动的Activity将不会保留在Activity栈中。例如,Activity栈中原来有A、B、C三个Activity,此时在Activity C中以该Flag启动Activity D,Activity D再启动Activity E,此时Activity栈中只有A、B、C、E四个Activity,Activity D不会保留在Activity栈中。
  • FLAG_ACTIVITY_REORDER_TO_FRONT:该Flag控制如果当前已有Activity,则直接将该Activity带到前台。例如,现在Activity栈中有A、B、C、D四个Activity,如果使用FLAG_ACTIVITY_REORDER_TO_FRONT标志来启动Activity B,那么启动后的Activity栈中情形为A、C、D、B。
  • FLAG_ACTIVITY_SINGLE_TOP:该Flag相当于加载模式中的singleTop模式。例如,原来Activity栈中有A、B、C、D四个Activity,在Activity D中再次启动Activity D,Activity栈中依然还是A、B、C、D四个Activity。

Android为Intent提供了大量的Flag,每个Flag都有其特定的功能,具体请参考关于Intent的API文档。

5.3 本章小结

本章主要介绍了Android系统中Intent的功能和用法。当Android应用需要启动某个组件时,总需要借助于Intent来实现。不管是启动Activity,还是启动Service、BroadcastReceiver组件,Android系统都是由Intent来实现的。简单地说,Android使用Intent封装了应用程序的“启动意图”,但这种“意图”并未直接与任何程序组件耦合,通过这种方式即可很好地提高系统的可扩展性和可维护性。学习本章需要重点掌握Intent的Component、Action、Category、Data、Type各属性的功能与用法,并掌握如何在AndroidManifest.xml文件中配置<intent-filter>元素。

相关文章:

第5章 使用Intent和IntentFilter通信

第5章 使用Intent和IntentFilter通信 本章要点 理解Intent对于Android应用的作用使用Intent启动系统组件Intent的Component属性的作用Intent的Action属性的作用Intent的Category属性的作用为指定Action、Category的Intent配置对应的intent-filterIntent的Data属性Intent的Typ…...

AI产品经理需要了解的算法知识

这篇文章给大家系统总结一下AI产品经理需要了解的算法知识。 1、自然语言生成&#xff08;NLG&#xff09; 自然语言生成&#xff08;Natural Language Generation&#xff0c;简称NLG&#xff09;是一种人工智能技术&#xff0c;它的目标是将计算机的数据、逻辑或算法产生的…...

OD C卷 - 结对编程

结对编程&#xff08;200&#xff09; 某部门计划通过结队编程来进行项目开发&#xff0c;部门有N名员工&#xff0c;每个员工有唯一的职级&#xff0c;三个员工为一组&#xff0c;结队分组规则如下&#xff1a;选出序号为i,j,k的员工&#xff0c;职级分别为level[i], level[j…...

AcWing 723. PUM

输入两个整数 NN 和 MM&#xff0c;构造一个 NN 行 MM 列的数字矩阵&#xff0c;矩阵中的数字从第一行到最后一行&#xff0c;按从左到右的顺序依次为 1,2,3,…,NM1,2,3,…,NM。 矩阵构造完成后&#xff0c;将每行的最后一个数字变为 PUM。 输出最终矩阵。 输入格式 共一行…...

编译安装php7.4.33正确开启opcache,不只是去掉opcache.enable=1前面的分号

编译安装php7.4.33正确开启opcache&#xff0c;不只是去掉opcache.enable1前面的分号 要成功开启 必须增加zend_extensionopcache.so 请看下面的样例&#xff1a; [opcache] zend_extensionopcache.so; Determines if Zend OPCache is enabled opcache.enable1; Determines …...

ComfyUI - 在服务器中部署 AIGC 绘画的 ComfyUI 工具 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/141140498 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 ComfyU…...

MySQL中的distinct和group by哪个效率更高?

前言 大家好&#xff0c;我是月夜枫~~ 一、distinct和group by的区别 1.1.作用方式和应用场景 ‌group by和‌distinct的主要区别在于它们的作用方式和应用场景。 group by用于对数据进行分组和聚合操作&#xff0c;通常与聚合函数&#xff08;如COUNT、SUM、AVG等&#xf…...

STM32F103C8T6单片机原理图设计(PCB板)

先了解了以下stm32f103c8t6的引脚使用&#xff1a; 对比过一些原理图&#xff0c;我发现除了特定协议的引脚功能&#xff0c;只要功能正确&#xff0c;可以自己选择连接对应的引脚。可以根据使用的LED/BEEP/DHT11/BH1750/ESP8266等模块的功能对应相应的引脚&#xff1a; WIFI(…...

Qt自定义控件:关于大佬“飞扬青云“的自定义UI控件的使用教程(MinGw,MSVC)

前言 最近在搞自定义控件&#xff0c;无意间发现大佬飞扬青云的开源项目&#xff0c;Qt/C编写超精美自定义控件 这里先贴出大佬项目地址和博客 码云&#xff1a;wwlzq5/qucsdk (gitee.com)&#xff08;旧版下载地址Qt4.7到Qt5.14&#xff09; github&#xff1a;https://git…...

关于RCE

什么是RCE&#xff1f; RCE漏洞&#xff0c;可以让攻击者直接向后台服务器远程注入操作系统命令或者代码&#xff0c;从而控制后台系统。也就是远程命令执行。命令执行是在目标服务器上任意执行系统命令。它属于高危漏洞之一&#xff0c;也属于代码执行的范畴。命令执行漏洞与…...

深入理解npm:从入门到精通

1. npm 简介 npm&#xff08;Node Package Manager&#xff09;是 Node.js 生态系统中的核心组件&#xff0c;它不仅是一个包管理器&#xff0c;还是一个强大的开发工具和庞大的开源社区。自2010年首次发布以来&#xff0c;npm 已经成为世界上最大的软件注册表&#xff0c;拥有…...

Docker入门教程:10分钟掌握基础

Docker入门教程&#xff1a;10分钟掌握基础 1. Docker是什么&#xff1f; Docker是一种容器化技术&#xff0c;它允许开发者打包他们的应用以及应用的依赖环境到一个可移植的容器中&#xff0c;这个容器可以在任何支持Docker的操作系统上运行。 2. Docker的基本组成 镜像&a…...

NLP实验-基于预训练模型的文本分类

使用BERT及其变体实现AclImdb情感分类 前言数据集介绍【Hugging Face】使用方法和如何挑选一个自己需要的模型 基于BERT预训练模型的本文分类数据预处理载入文本标记器将数据转化为模型可以接受的格式训练模型加载模型 基于RoBerta预训练模型的文本分类基于DeBerta预训练模型的…...

Table API SQL的概念和通用API

目录 表API和SQL程序的结构 创建表环境(Create a TableEnvironment) 在目录中创建表(Create Tables in the Catalog) 临时表与永久表(Temporary vs Permanent tables) 遮蔽(Shadowing) 创建表(Create a Table) 连接器表(Connector Tables) 虚拟表(Virtual…...

【网络】UDP和TCP之间的差别和回显服务器

文章目录 UDP 和 TCP 之间的差别有连接/无连接可靠传输/不可靠传输面向字节流/面向数据报全双工/半双工 UDP/TCP API 的使用UDP APIDatagramSocket构造方法方法 DatagramPacket构造方法方法 回显服务器&#xff08;Echo Server&#xff09;1. 接收请求2. 根据请求计算响应3. 将…...

Electron:摄像头录制和屏幕录制

摄像头录制 main.js const { app, BrowserWindow} require(electron)let mainWin null const createWindow () > {mainWin new BrowserWindow({width: 800,height: 600,title: 自定义菜单,webPreferences: {// 允许渲染进程使用nodejsnodeIntegration: true,// 允许渲…...

【uniapp】vue3+vite配置tailwindcss

安装 npm install autoprefixer tailwindcss uni-helper/vite-plugin-uni-tailwind -Dautoprefixer &#xff1a;自动管理浏览器前缀的插件&#xff0c;可以解析css文件并且添加前缀到css内容里。uni-helper/vite-plugin-uni-tailwind: 将 Tailwind CSS 框架集成到使用 Vite 作…...

从源码到应用:医疗陪诊系统与在线问诊小程序开发详解

在数字化医疗时代&#xff0c;医疗陪诊系统与在线问诊小程序的开发成为了医疗机构和技术公司关注的焦点。接下来&#xff0c;小编将与您一同深入了解。 一、医疗陪诊系统的核心功能 医疗陪诊系统旨在为患者提供更贴心的医疗服务&#xff0c;通过专业人员陪同患者完成就医过程。…...

mysql数据库中decimal数据类型比较大小

在MySQL中&#xff0c;DECIMAL数据类型用于存储精确的数值&#xff0c;它非常适合用于需要高精度计算的场景&#xff0c;如金融应用。当我们需要在MySQL数据库中比较DECIMAL类型数据的大小时&#xff0c;可以使用标准的比较运算符&#xff0c;如>, <, >, <, 和 &l…...

掌控库存,简化管理 — InvenTree 开源库存管理系统

InvenTree &#xff1a;简化您的库存管理&#xff0c;让效率和控制力触手可及。- 精选真开源&#xff0c;释放新价值。 概览 InvenTree&#xff0c;一款专为精细化库存管理而设计的开源系统&#xff0c;以其高效和灵活性在众多库存管理工具中脱颖而出。它以Python和Django框架…...

Linux---项目自动化构建工具-make/Makefile

一、背景 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的 规则来指定&#xff0c;哪些文件需要先编译&#xff0c;哪些文件…...

嘉立创EDA个人学习笔记1(PCB板介绍)

前言 本篇文章属于嘉立创EDA的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 嘉立创EDA-PCB设计零基础入门课程&#xff08;54集全&#xff09;_…...

(转)Restful接口设计(1)

.representational&#xff1a;代表性的 URI&#xff08;Universal Resouce Identifier&#xff09;&#xff1a;Universal &#xff1a;普遍的;共同的。Identifier&#xff1a;标识符。统一资源标识符。 31-RESTful接口介绍-02_哔哩哔哩_bilibili 31-RESTful接口介绍-03_哔哩…...

Python进阶之3D图形

Python进阶之3D图形 在数据可视化中&#xff0c;2D图形通常可以满足大多数需求。然而&#xff0c;对于一些复杂的数据或分析&#xff0c;3D图形可以提供更多的视角和洞察。在Python中&#xff0c;使用 Matplotlib 和 Plotly 等库可以轻松创建各种3D图形。本文将介绍如何使用这…...

机器学习深度学习中的搜索算法浅谈

机器学习&深度学习中的搜索算法浅谈 搜索算法是计算机科学中的核心算法&#xff0c;用于在各种数据结构&#xff08;如数组、列表、树、图等&#xff09;中查找特定元素或信息。这些算法不仅在理论上具有重要意义&#xff0c;还在实际应用中扮演着关键角色。本文将详细探讨…...

基于IMX8M_plus+FPGA+AI监护仪解决方案

监护仪是一种以测量和控制病人生理参数&#xff0c;并可与已知设定值进行比较&#xff0c;如果出现超标可发出警报的装置或系统。 &#xff08;1&#xff09;监护仪主要采集测量人体生理参数&#xff0c;心电、血压、血氧、体温等需要采集处理大量的数据&#xff0c;系统需要多…...

仿RabbitMq实现简易消息队列正式篇(路由匹配篇)

TOC 目录 路由匹配模块 代码展示 路由匹配模块 决定了一条消息是否能够发布到指定的队列 在每个队列根交换机的绑定信息中&#xff0c;都有一个binding_key&#xff08;在虚拟机篇有说到&#xff09;这是队列发布的匹配规则 在每条要发布的消息中&#xff0c;都有一个rout…...

一套完整的NVR网络硬盘录像机解决方案和NVR程序源码介绍

随着网络技术的发展&#xff0c;视频数据存储的需求激增&#xff0c;促使硬盘录像机&#xff08;DVR&#xff09;逐渐演变为具备网络功能的网络视频录像机&#xff08;NVR&#xff09;。NVR&#xff0c;即网络视频录像机&#xff0c;负责网络视音频信号的接入、存储、转发、解码…...

2024年人工智能固态硬盘采购容量预计超过45 EB

根据TrendForce发布的最新市场报告&#xff0c;人工智能&#xff08;AI&#xff09;服务器客户在过去两个季度显著增加了对企业级固态硬盘&#xff08;SSD&#xff09;的订单。为了满足AI应用中不断增长的SSD需求&#xff0c;上游供应商正在加速工艺升级&#xff0c;并计划在20…...

Java的反射原理

反射允许程序在运行时检查或修改其类、接口、字段和方法的行为。反射主要通过java.lang.reflect包中的类和接口实现&#xff0c;它主要用于以下目的&#xff1a; 在运行时分析类的能力&#xff1a;通过反射&#xff0c;可以在运行时检查类的结构&#xff0c;比如它的方法、构造…...