版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ithink213/article/details/46891031
Intent
简介
Android中使用Intent来调用组件,Android中的组件包括Activity,Service,Broadcast Receiver,Content Provider。Android将多种理念融入到了Intent的概念中。可以使用Intent从一个应用程序中调用外部应用程序,可以使用Intent从应用程序调用内部或者外部组件,可以使用Intent触发时间,可以使用Intent发出警报等等。由上述可知,intent是具有相关数据负载的操作。
简单来说,Intent是你可以告诉Android要执行(或调用)的一种操作。Android调用的操作取决于该操作所注册的内容。
例如,编写一个Activity:BasicViewActivity
public class BasicViewActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.basic_view);
}
}
basic_view布局指向了/res/layout/目录下的布局文件。我们可以在应用程序的描述文件中注册此活动,使其可以被其他应用程序调用。
注册代码如下:
<activity android:name=".BasicViewActivity"
android:label="Basic View Tests" >
<intent-filter>
<action android:name="com.zxn.intent.action.ShowBasicView" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
注册之后我们就可以使用Intent来调用此BasciViewActivity
public static void invokeBasicActivity(Activity activity) {
String actionName = "com.zxn.intent.action.ShowBasicView";
Intent intent = new Intent(actionName);
activity.startActivity(intent);
}
Android中可用的Intent
以上是我们使用Intent启动另外一个组件的过程,也是最基本的用法。我们也可以使用Intent启动Android一些自带的程序。
扫描二维码关注公众号,回复:
4719633 查看本文章
例如:
public class IntentsUtils {
// 使用浏览器打开一个uri
public static void invokeWebBrowser(Activity activity) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
activity.startActivity(intent);
}
public static void invokeWebSearch(Activity activity) {
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.setData(Uri.parse("http://www.baidu.com"));
activity.startActivity(intent);
}
// 打开拨号界面
public static void dial(Activity activity) {
Intent intent = new Intent(Intent.ACTION_DIAL);
activity.startActivity(intent);
}
// 拨打一个电话
public static void call(Activity activity) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:904-905-5646"));
activity.startActivity(intent);
}
// 使用一个地图程序打开指定位置
public static void showMapAtLatLong(Activity activity) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("geo:0,0?z=4&q=business+near+city"));
activity.startActivity(intent);
}
// 启动某个应用获取返回的数据
public static void invokePick(Activity activity) {
Intent pickIntent = new Intent(Intent.ACTION_PICK);
pickIntent.setData(Uri.parse("content://com.google.provider.NotePad/notes"));
activity.startActivityForResult(pickIntent, 1);
}
// 启动某个应用程序获取返回的数据
public static void invokeGetContent(Activity activity) {
Intent pickIntent = new Intent(Intent.ACTION_GET_CONTENT);
pickIntent.setType("vnd.android.cursor.item/vnd.google.note");
activity.startActivityForResult(pickIntent, 2);
}
}
创建一个简单的菜单,以便我们调用上面这些代码。如下:
public class MainActivity extends ActionBarActivity {
private final static String tag = "MainActivity";
// Initialize this in onCreateOptions
Menu myMenu = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.setupButton();
this.setupEditText();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
super.onCreateOptionsMenu(menu);
this.myMenu = menu;
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
try {
handleMenus(item);
} catch (Throwable t) {
Log.d(tag, t.getMessage(), t);
throw new RuntimeException("error", t);
}
return true;
}
private void handleMenus(MenuItem item) {
this.appendMenuItemText(item);
if(item.getItemId() == R.id.menu_clear) {
this.emptyText();
} else if(item.getItemId() == R.id.menu_basic_view) {
IntentsUtils.invokeBasicActivity(this);
} else if(item.getItemId() == R.id.menu_show_browser) {
IntentsUtils.invokeWebBrowser(this);
} else if(item.getItemId() == R.id.menu_dial) {
IntentsUtils.dial(this);
} else if(item.getItemId() == R.id.menu_call) {
IntentsUtils.call(this);
} else if(item.getItemId() == R.id.menu_map) {
IntentsUtils.showMapAtLatLong(this);
} else if(item.getItemId() == R.id.menu_testPick) {
IntentsUtils.invokePick(this);
} else if(item.getItemId() == R.id.menu_testGetContent) {
IntentsUtils.invokeGetContent(this);
}
}
private TextView getTextView() {
TextView tv = (TextView)this.findViewById(R.id.textViewId);
return tv;
}
public void appendText(String text) {
TextView tv = (TextView)this.findViewById(R.id.textViewId);
tv.setText(tv.getText() + text);
}
public void appendMenuItemText(MenuItem menuItem) {
String title = menuItem.getTitle().toString();
TextView tv = (TextView)this.findViewById(R.id.textViewId);
tv.setText(tv.getText() + "\n" + title + ":" + menuItem.getItemId());
}
private void emptyText() {
TextView tv = (TextView)this.findViewById(R.id.textViewId);
tv.setText("");
}
private void dial() {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
}
private void setupButton() {
Button b = (Button)this.findViewById(R.id.button1);
b.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
parentButtonClicked(v);
}
});
}
private void parentButtonClicked(View v) {
this.appendText("\nbutton clicked");
this.dialUsingEditText();
}
private void dialWithNumber(String tel) {
String telUriString = "tel:" + tel;
Log.d(tag, telUriString);
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(telUriString));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
}
private void dialUsingEditText() {
EditText etext = (EditText)this.findViewById(R.id.editTextId);
String text = etext.getText().toString();
if(PhoneNumberUtils.isGlobalPhoneNumber(text) == true) {
dialWithNumber(text);
}
}
private EditText getEditText() {
EditText etext = (EditText)this.findViewById(R.id.editTextId);
return etext;
}
private void setupEditText() {
EditText etext = this.getEditText();
etext.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
}
protected void onActivityResult(int requestCode, int resultCode, Intent outputIntent) {
super.onActivityResult(requestCode, resultCode, outputIntent);
IntentsUtils.parseResult(this, requestCode, resultCode, outputIntent);
}
}
Intent的组成
另一种确定Intent用途的方式是查看Intent对象包含的内容。Intent包含操作、数据URI、extra数据元素的键值映射,以及一个显示类名。
1.Intent和数据URI
我们在Intent中可以添加名为data的参数,这个参数指向一个URI,根据URI的不同,从而打开不同的界面。
举个栗子:打开一个指定电话的拨号页面。那么我们不仅要指定传递intent的操作,还要指定intent的数据。代码如下:
public static void call(Activity activity) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:904-905-5646"));
activity.startActivity(intent);
}
以上代码中,Intent.ACTION_CALL是我们要执行的动作,打开拨号界面。下面的setData是为该Intent设置数据,这里的数据并非是真正的数据,而是指向数据的指针。数据部分是一个表示URI的字符串,这个URI包含可被推断的数据。
2.一般操作
在这里,我们需要注意一点,操作和数据并非是一一对应的关系,在以上例子中,我们执行Intent.ACTION_CALL传输的数据是一个电话号码,在以下例子中并非是这样。
public static void invokeWebBrowser(Activity activity) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
activity.startActivity(intent);
}
这个程序只是仅仅打开一个view,并没有告诉系统打开一个浏览器,Android如何知道该调用什么样的Activity来响应Intent呢?在这种情况下,Android不仅依赖于一般操作,还依赖于URI的性质。如何区分不同的data呢?这个时候就需要Intent-flater了,intent过滤器,顾名思义,就是对传递过来的intent进行过滤,从而得出我们想要的。来看看我们这个Activity所注册的描述信息:
<activity......>
<span style="white-space:pre"> </span><intent-filter>
<span style="white-space:pre"> </span><action android:name="android.intent.action.VIEW" />
<span style="white-space:pre"> </span><data android:scheme="http" />
<span style="white-space:pre"> </span><data android:scheme="https" />
<span style="white-space:pre"> </span></intent-filter>
</activity>
在intent-filter中,我们注册了一个action和data,分别对应操作和数据,action中的“android.intent.action.VIEW“在Intent文件中对应的就是ACTION_VIEW
Intent.java:
public static final String ACTION_VIEW = "android.intent.action.VIEW";
data部分我们定义了android:scheme的属性为http和https。这样intent-filter就能过滤请求为http和https的URI。
intent-filter的data子节点的子元素和特性包括:host、mimeType、path、pathPattern、pathPrefix、port、scheme。具体每种的意义可以参考android官网。
mimeType类型是一个经常用到的类型。例如:我们可以查看一组笔记:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
<intent-filter>
也可以查看单个笔记
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
<intent-filter>
3.使用extra信息
除了操作和数据外,intent还包含extra额外信息,extra可以向intent提供更多的信息。extra以键值对的形式表示,键名称通常是以包名开头,值可以是任何基本的数据类型或任意对象,只要他实现了andorid.os.pracelable接口即可。extra使用andorid.os.bundle表示。
可以使用以下方法访问extra bundle
// Get the Bundle from an Intent
Bundle extraBundle = intent.getExtras();
// Place a bundle in an intent
Bundle anotherBundle = new Bundle();
// populate the bundle with key/value pairs
...
// and then set the bundle on the Intent
intent.putExtras(anotherBudnle);
我们可以使用以下方法向extra添加数据:
putExtra(String name, boolean value);
putExtra(String name, int value);
putExtra(String name, double value);
putExtra(String name, String value);
putExtra(String name, int[] values);
putExtra(String name, float[] values);
putExtra(String name, Serializable value);
putExtra(String name, Parcelable value);
putExtra(String name, Bundle value);
putExtra(String name, Intent anotherIntent);
putIntegerArrayListExtra(String name, ArrayList arrayList);
putParcelableArrayListExtra(String name, ArrayList arrayList);
putStringArrayListExtra(String name, ArrayList arrayList);
4.使用组件直接调用活动
我们可以直接指定Activity的ComponentName来访问Activity。具体如下:
setComponent(ComponentName name);
setClassName(String packageName, String classNameInThatPackage);
setClassName(Context context, String classNameInThatContext);
setClass(Context context, Class classObjectInThatContext);
ComponentName将一个包名和类名包装在一起。例如,以下代码调用系统contacts活动:
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.andorid.contacts", "com.android.aontacts.DialContactsEntryActivity"));
startActivity(intent);
也可以直接使用类名,不构造ComponentName,例如:
Intent directIntent = new Intent(activity, BasicViewActivity.class);
activity.start(directIntent);
别忘记,在AndroidManifest.xml中注册Activity
<activity android:name=".BasicViewActivity"
android:label="Test Activity">
这里我们不需要intent-filter,因为这种类型的Intent是显式Intent,显式Intent指定了一个完全限定的Android组件,所以会忽略掉该Intent的其他部分。
5.Intent类别
可以将活动分为不同的类别,以便根据类别名来搜索。比如,我们定义一个启动页面:
<activity
<span style="white-space:pre"> </span>android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
其中android.intent.category.LAUNCHER则将这个页面定义为启动页面。
更多类别可以访问android官网查看:
http://developer.android.com/intl/zh-cn/reference/android/content/Intent.html。
之后我们就可以使用一下代码进行访问:
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.adCategory(Intent.CATEGORY_LAUNCHER);
PackageManager pm = getPackageManager();
List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);
借助于PackagerManager就可以找到与Intent相匹配的活动。
6.Intent解析规则
Android使用多种策略,基于Intent过滤器来将Intent和他们的目标活动相匹配。Intent分为显式和隐式。如果设置了名称,则这个Intent就是显式Intent,对于显式Intent,重要的是组件名称,Intent的所有其他方面或者特性都会被忽略。如果没有指定名称的Intent就是隐式Intent,解析隐式Intent目标的规则非常多。
基本规则是,传的Intent的操作,类别和数据特征必须匹配Intent过滤器中指定的特征。与Intent不同,一个Intent过滤器可以指定多个操作,类别和数据特征。这意味着一个Intent过滤器可以满足多个Intent的需求,也就是说一个活动响应多个Intent。但是,“匹配”的含义在操作,数据特征和类别之间各不相同。我们看看隐式Intent的每部分匹配条件。
1.操作
如果Intent有一个操作,Intent过滤器必须将这个操作添加到操作列表,或者不包含任何操作。如果Intent过滤器没有定义任何操作,则该Intent过滤器可以匹配所有传入的Intent操作。相应,如果Intent过滤器中定义了一个或多个操作,则至少一个操作与传入的Intent操作匹配。
2.数据
如果Intent中没有指定数据类型,他将不匹配包含任何数据或者数据特征的传入的Intent,他将仅查找没有指定数据类型的Intent。
在过滤器中,缺少数据和缺少操作的情况是相反的。如果过滤器没有操作,将匹配所有内容。如果过滤器中没有数据,将都不匹配。
3.数据类型
要匹配一种数据类型,传入Intent的数据类型必须是Intent过滤器中指定的数据类型之一。Intent中的数据类型必须存在于Intent过滤器中。
传入Intent的数据类型可以通过以下两种方式来确定。第一种:如果数据URI是一个内容或者文件URI,ContentProvider或者Android将确定类型。第二种:查看
Intent的显式数据类型,这种方式要生效,传入的Intent不应该设置数据URI,因为当对Intent调用setType()是会自动设置他。
作为MIME类型范围的一部分,Android还支持使用星号(*)作为子类型来涵盖所有可能的子类型。
4.数据模式
要匹配数据模式,传入Intent的数据模式必须是Intent过滤器中的指定的模式之一,换句话说,传入的数据模式必须存在于Intent过滤器中。
5.数据授权
如果过滤器中没有数据授权,则可以匹配任何传入的数据URI的授权,如果在过滤器中指定了授权,那么一种模式和授权应该与传入的Intent的数据URI想匹配。
6.数据路径
如果过滤器中没有数据路径,则可以匹配任何传入的数据URI的路径,如果在过滤器中指定了路径,那么一种模式,授权和路径应该与传入的Intent的数据URI想匹配。
换句话说,模式,授权和路径协同验证传入Intent的URI,所以path、authority和scheme不是孤立工作的,而是协同工作的。