intent可以显式和隐式调用,显示就是直接setClass等,目的确切,隐式就是通过设置参数,通过系统搜索来确认具体要调用的是哪个。
Intent实现了Parceable,它里面的注意参数有:
private String mAction;
private Uri mData;
private String mType;
private String mPackage;
private ComponentName mComponent;
private int mFlags;
private HashSet<String> mCategories;
private Bundle mExtras;
private Rect mSourceBounds;
我们可以通过set、get函数来设置和读取这些内容,如setAction、getAction等。
ACTION:
动作,系统有些默认的常量:
如ACTION_EDIT、ACTION_VIEW、ACTION_INSERT等,具体可以查询文档
DATA:
数据(URI和MIME数据),不同的动作会有不同的数据,如ACTION_EDIT,那我们的数据就是可以用来编辑的URI资源。
如在联系人应用中,一个指向某联系人的URI可能为:content://contacts/1。对于不同的动作,其URI数据的类型是不同的(可以设置type属性指定特定类型数据),如ACTION_EDIT指定Data为文件URI,打电话为tel:URI,访问网络为http:URI,而由content provider提供的数据则为content: URIs。
mExtras附属数据:
Bundle类型,可以通过putExtras来保存数据,也可以通过getIntExtra来获取。如果要执行“发送电子邮件”这个动作,可以将电子邮件的标题、正文等保存在extras里,传给电子邮件发送组件。
mComponent:可以通过setClassName来实现不同apk之间的activity调用。指定Intent的的目标组件的类名称。通常 Android会根据Intent 中包含的其它属性的信息,比如action、data/type、category进行查找,最终找到一个与之匹配的目标组件。但是,如果 component这个属性有指定的话,将直接使用它指定的组件,而不再执行上述查找过程。
mCategories种类:可以通过addCategory和getCategories来设置和读取。例如 LAUNCHER_CATEGORY 表示Intent 的接受者应该在Launcher中作为顶级应用出现;而ALTERNATIVE_CATEGORY表示当前的Intent是一系列的可选动作中的一个,这些动作可以在同一块数据上执行。
CATEGORY_BROWSABLE 目标activity可以使用浏览器来显示-例如图片或电子邮件消息.
CATEGORY_GADGET 该activity可以被包含在另外一个装载小工具的activity中.
CATEGORY_HOME 该activity显示主屏幕,也就是用户按下Home键看到的界面.
CATEGORY_LAUNCHER 该activity可以作为一个任务的第一个activity,并且列在应用程序启动器中.
CATEGORY_PREFERENCE 该activity是一个选项面板.
FLAG:
这里我们要注意如果我们是在BroadcastReceiver或者service中 启动一个新的Activity, 不要忘记i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Intent intent = new Intent(this,xxx.class);
//如果activity在task存在,拿到最顶端,不会启动新的Activity,这个标志一般不是由程序代码设置的,如在launchMode中设置singleTask模式时系统帮你设定
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
//如果activity在task存在,将Activity之上的所有Activity结束掉
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//默认的跳转类型,将Activity放到一个新的Task中
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//如果设置,当这个Activity位于历史stack的顶端运行时,不再启动一个新的。
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
如果设置,这将在Task的Activity stack中设置一个还原点,当Task恢复时,需要清理Activity。也就是说,下一次Task带着FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。
这在你的程序有分割点的时候很有用。例如,一个e-mail应用程序可能有一个操作是查看一个附件,需要启动图片浏览Activity来显示。这个Activity应该作为e-mail应用程序Task的一部分,因为这是用户在这个Task中触发的操作。然而,当用户离开这个Task,然后从主画面选择e-mail app,我们可能希望回到查看的会话中,但不是查看图片附件,因为这让人困惑。通过在启动图片浏览时设定这个标志,浏览及其它启动的Activity在下次用户返回到mail程序时都将全部清除。
FLAG_ACTIVITY_NO_HISTORY
如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。这也可以通过设置noHistory特性。
FLAG_ACTIVITY_REORDER_TO_FRONT
如果在Intent中设置,并传递给Context.startActivity(),这个标志将引发已经运行的Activity移动到历史stack的顶端。
例如,假设一个Task由四个Activity组成:A,B,C,D。如果D调用startActivity()来启动Activity B,那么,B会移动到历史stack的顶端,现在的次序变成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP标志也设置的话,那么这个标志将被忽略
mType类型:
显式指定Intent的数据类型(MIME)。一般Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。
Intent解析流程:
Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。在这个解析过程中,Android是通过Intent的action、type、category这三个属性来进行判断的,判断方法如下:
如果Intent指明定了action,则目标组件的IntentFilter的action列表中就必须包含有这个action,否则不能匹配;
如果Intent没有提供type,系统将从data中得到数据类型。和action一样,目标组件的数据类型列表中必须包含Intent的数据类型,否则不能匹配。
如果Intent中的数据不是content: 类型的URI,而且Intent也没有明确指定它的type,将根据Intent中数据的scheme (比如 http: 或者mailto:) 进行匹配。同上,Intent 的scheme必须出现在目标组件的scheme列表中。
如果Intent指定了一个或多个category,这些类别必须全部出现在组建的类别列表中。比如Intent中包含了两个类别:LAUNCHER_CATEGORY 和 ALTERNATIVE_CATEGORY,解析得到的目标组件必须至少包含这两个类别。
示例:
<action android:name="com.example.project.SHOW_CURRENT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/mpeg" android:scheme="http" . . . />
<data android:scheme="http" android:type="video/*" />
IntentFilter:对intent进行过滤,除了在xml中定义外,我们在广播中也用到这个。
<activity android:name="NotesList" android:label="@string/title_notes_list">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
</activity>
这里我们看到
vnd.android.cursor.item(一条记录或者内容)和vnd.android.cursor.dir(全部记录或者内容)
private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
registerReceiver(mReceiver, intentFilter);
private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
}
}
}
IntentService:这个是service的一个子类,我们看它的源码其实可以知道,它其实是通过HandlerThread来实现独立线程处理数据,避免在UI中
处理较耗时的内容出现ANR。
这是一个基于消息的服务,每次启动该服务并不是马上处理你的工作,而是首先会创建对应的Looper,Handler并且在MessageQueue中添加的附带客户Intent的Message对象,当Looper发现有Message的时候接着得到Intent对象通过在onHandleIntent((Intent)msg.obj)中调用你的处理程序.处理完后即会停止自己的服务.意思是Intent的生命周期跟你的处理的任务是一致的.所以这个类用下载任务中非常好,下载任务结束后服务自身就会结束退出.
IntentService是一个通过Context.startService(Intent)启动可以处理异步请求的Service,使用时你只需要继承IntentService和重写其中的onHandleIntent(Intent)方法接收一个Intent对象,在适当的时候会停止自己(一般在工作完成的时候). 所有的请求的处理都在一个工作线程中完成,它们会交替执行(但不会阻塞主线程的执行),一次只能执行一个请求
Intent intent = new Intent("iteye.dyingbleed.DownloadService");
intent.putExtra("url", url); //添加下载地址
startService(intent);
package lizhen.apk;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.app.IntentService;
import android.content.Intent;
public class DownloadService extends IntentService {
private ExecutorService pool;
public DownloadService() {
super("DownloadService");
}
@Override
public void onCreate() {
super.onCreate();
pool = Executors.newCachedThreadPool();
}
@Override
protected void onHandleIntent(Intent intent) {
String url = intent.getStringExtra("url");
pool.execute(new DownloadTask(url));
}
@Override
public void onDestroy() {
super.onDestroy();
pool.shutdown();
}
private class DownloadTask implements Runnable {
private final String url;
public DownloadTask(String url) {
this.url = url;
}
@Override
public void run() {
// TODO 此處省略具體實現
}
}
}
这里呢,如果我们要实现与主线程进行数据交互,可以在
onHandleIntent里面通过广播等方式进行传输。
PendingIntent:
可以看作是对Intent的包装。供当前App之外的其他App调用。有点“被动”或是“Callback”的意思,但不是严格意义上的“被动”或是“Callback”。总之,当前App不能用它马上启动它所包裹的Intent。而是在外部App执行这个PendingIntent时,间接地、实际地调用里面的Intent。PendingIntent主要持有的信息是它所包装的Intent和当前App的Context。正由于PendingIntent中保存有当前App的Context,使它赋予外部App一种能力,使得外部App可以如同当前App一样的执行PendingIntent里的Intent,就算在执行时当前App已经不存在了,也能通过存在PendingIntent里的Context照样执行Intent。
常和alermanger 和notificationmanager一起使用。
下面我们来看个notification的示例,理解下:
添加一个notification按照下列步骤:
1.获取一个notificationManager
NotificationManager m_NotificationManager=(NotificationManager)this.getSystemService(NOTIFICATION_SERVICE);
2、定义一个notification
Notification m_Notification=new Notification();
3.设置notification的各种属性
//设置通知在状态栏显示的图标
m_Notification.icon=R.drawable.icon;
//当我们点击通知时显示的内容
m_Notification.tickerText="Button1 通知内容.....";
通知时发出的默认声音
m_Notification.defaults=Notification.DEFAULT_SOUND;
//设置通知显示的参数
Intent m_Intent=new Intent(this,Activity.class);
PendingIntent m_PendingIntent=PendingIntent.getActivity(NotificationDemo.this, 0, m_Intent, 0);
m_Notification.setLatestEventInfo(NotificationDemo.this, title, content,m_PendingIntent );
这里还要注意
m_Notification.fullScreenIntent = m_PendingIntent;这个notification会马上执行
//具体可以看通话的notificationMgr里面的updateInCallNotification
m_Notification.contentIntent = m_PendingIntent;
4.执行notification
//这个可以理解为开始执行这个通知
m_NotificationManager.notify(notify_id,m_Notification);
这里我们就知道,当notificationManager发送这个notify的时候,可能设置这个notify的activity已经消失,
但是它还是能过根据PendingIntent来实现intent的功能,跳转到
NotificationDemo这个activity中。其实就是实现了一个异步的过程。
Intent实现了Parceable,它里面的注意参数有:
private String mAction;
private Uri mData;
private String mType;
private String mPackage;
private ComponentName mComponent;
private int mFlags;
private HashSet<String> mCategories;
private Bundle mExtras;
private Rect mSourceBounds;
我们可以通过set、get函数来设置和读取这些内容,如setAction、getAction等。
ACTION:
动作,系统有些默认的常量:
如ACTION_EDIT、ACTION_VIEW、ACTION_INSERT等,具体可以查询文档
DATA:
数据(URI和MIME数据),不同的动作会有不同的数据,如ACTION_EDIT,那我们的数据就是可以用来编辑的URI资源。
如在联系人应用中,一个指向某联系人的URI可能为:content://contacts/1。对于不同的动作,其URI数据的类型是不同的(可以设置type属性指定特定类型数据),如ACTION_EDIT指定Data为文件URI,打电话为tel:URI,访问网络为http:URI,而由content provider提供的数据则为content: URIs。
mExtras附属数据:
Bundle类型,可以通过putExtras来保存数据,也可以通过getIntExtra来获取。如果要执行“发送电子邮件”这个动作,可以将电子邮件的标题、正文等保存在extras里,传给电子邮件发送组件。
mComponent:可以通过setClassName来实现不同apk之间的activity调用。指定Intent的的目标组件的类名称。通常 Android会根据Intent 中包含的其它属性的信息,比如action、data/type、category进行查找,最终找到一个与之匹配的目标组件。但是,如果 component这个属性有指定的话,将直接使用它指定的组件,而不再执行上述查找过程。
mCategories种类:可以通过addCategory和getCategories来设置和读取。例如 LAUNCHER_CATEGORY 表示Intent 的接受者应该在Launcher中作为顶级应用出现;而ALTERNATIVE_CATEGORY表示当前的Intent是一系列的可选动作中的一个,这些动作可以在同一块数据上执行。
CATEGORY_BROWSABLE 目标activity可以使用浏览器来显示-例如图片或电子邮件消息.
CATEGORY_GADGET 该activity可以被包含在另外一个装载小工具的activity中.
CATEGORY_HOME 该activity显示主屏幕,也就是用户按下Home键看到的界面.
CATEGORY_LAUNCHER 该activity可以作为一个任务的第一个activity,并且列在应用程序启动器中.
CATEGORY_PREFERENCE 该activity是一个选项面板.
FLAG:
这里我们要注意如果我们是在BroadcastReceiver或者service中 启动一个新的Activity, 不要忘记i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Intent intent = new Intent(this,xxx.class);
//如果activity在task存在,拿到最顶端,不会启动新的Activity,这个标志一般不是由程序代码设置的,如在launchMode中设置singleTask模式时系统帮你设定
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
//如果activity在task存在,将Activity之上的所有Activity结束掉
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//默认的跳转类型,将Activity放到一个新的Task中
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//如果设置,当这个Activity位于历史stack的顶端运行时,不再启动一个新的。
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
如果设置,这将在Task的Activity stack中设置一个还原点,当Task恢复时,需要清理Activity。也就是说,下一次Task带着FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。
这在你的程序有分割点的时候很有用。例如,一个e-mail应用程序可能有一个操作是查看一个附件,需要启动图片浏览Activity来显示。这个Activity应该作为e-mail应用程序Task的一部分,因为这是用户在这个Task中触发的操作。然而,当用户离开这个Task,然后从主画面选择e-mail app,我们可能希望回到查看的会话中,但不是查看图片附件,因为这让人困惑。通过在启动图片浏览时设定这个标志,浏览及其它启动的Activity在下次用户返回到mail程序时都将全部清除。
FLAG_ACTIVITY_NO_HISTORY
如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。这也可以通过设置noHistory特性。
FLAG_ACTIVITY_REORDER_TO_FRONT
如果在Intent中设置,并传递给Context.startActivity(),这个标志将引发已经运行的Activity移动到历史stack的顶端。
例如,假设一个Task由四个Activity组成:A,B,C,D。如果D调用startActivity()来启动Activity B,那么,B会移动到历史stack的顶端,现在的次序变成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP标志也设置的话,那么这个标志将被忽略
mType类型:
显式指定Intent的数据类型(MIME)。一般Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。
Intent解析流程:
Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。在这个解析过程中,Android是通过Intent的action、type、category这三个属性来进行判断的,判断方法如下:
如果Intent指明定了action,则目标组件的IntentFilter的action列表中就必须包含有这个action,否则不能匹配;
如果Intent没有提供type,系统将从data中得到数据类型。和action一样,目标组件的数据类型列表中必须包含Intent的数据类型,否则不能匹配。
如果Intent中的数据不是content: 类型的URI,而且Intent也没有明确指定它的type,将根据Intent中数据的scheme (比如 http: 或者mailto:) 进行匹配。同上,Intent 的scheme必须出现在目标组件的scheme列表中。
如果Intent指定了一个或多个category,这些类别必须全部出现在组建的类别列表中。比如Intent中包含了两个类别:LAUNCHER_CATEGORY 和 ALTERNATIVE_CATEGORY,解析得到的目标组件必须至少包含这两个类别。
示例:
<action android:name="com.example.project.SHOW_CURRENT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/mpeg" android:scheme="http" . . . />
<data android:scheme="http" android:type="video/*" />
IntentFilter:对intent进行过滤,除了在xml中定义外,我们在广播中也用到这个。
<activity android:name="NotesList" android:label="@string/title_notes_list">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
</activity>
这里我们看到
vnd.android.cursor.item(一条记录或者内容)和vnd.android.cursor.dir(全部记录或者内容)
private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
registerReceiver(mReceiver, intentFilter);
private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
}
}
}
IntentService:这个是service的一个子类,我们看它的源码其实可以知道,它其实是通过HandlerThread来实现独立线程处理数据,避免在UI中
处理较耗时的内容出现ANR。
这是一个基于消息的服务,每次启动该服务并不是马上处理你的工作,而是首先会创建对应的Looper,Handler并且在MessageQueue中添加的附带客户Intent的Message对象,当Looper发现有Message的时候接着得到Intent对象通过在onHandleIntent((Intent)msg.obj)中调用你的处理程序.处理完后即会停止自己的服务.意思是Intent的生命周期跟你的处理的任务是一致的.所以这个类用下载任务中非常好,下载任务结束后服务自身就会结束退出.
IntentService是一个通过Context.startService(Intent)启动可以处理异步请求的Service,使用时你只需要继承IntentService和重写其中的onHandleIntent(Intent)方法接收一个Intent对象,在适当的时候会停止自己(一般在工作完成的时候). 所有的请求的处理都在一个工作线程中完成,它们会交替执行(但不会阻塞主线程的执行),一次只能执行一个请求
Intent intent = new Intent("iteye.dyingbleed.DownloadService");
intent.putExtra("url", url); //添加下载地址
startService(intent);
package lizhen.apk;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.app.IntentService;
import android.content.Intent;
public class DownloadService extends IntentService {
private ExecutorService pool;
public DownloadService() {
super("DownloadService");
}
@Override
public void onCreate() {
super.onCreate();
pool = Executors.newCachedThreadPool();
}
@Override
protected void onHandleIntent(Intent intent) {
String url = intent.getStringExtra("url");
pool.execute(new DownloadTask(url));
}
@Override
public void onDestroy() {
super.onDestroy();
pool.shutdown();
}
private class DownloadTask implements Runnable {
private final String url;
public DownloadTask(String url) {
this.url = url;
}
@Override
public void run() {
// TODO 此處省略具體實現
}
}
}
这里呢,如果我们要实现与主线程进行数据交互,可以在
onHandleIntent里面通过广播等方式进行传输。
PendingIntent:
可以看作是对Intent的包装。供当前App之外的其他App调用。有点“被动”或是“Callback”的意思,但不是严格意义上的“被动”或是“Callback”。总之,当前App不能用它马上启动它所包裹的Intent。而是在外部App执行这个PendingIntent时,间接地、实际地调用里面的Intent。PendingIntent主要持有的信息是它所包装的Intent和当前App的Context。正由于PendingIntent中保存有当前App的Context,使它赋予外部App一种能力,使得外部App可以如同当前App一样的执行PendingIntent里的Intent,就算在执行时当前App已经不存在了,也能通过存在PendingIntent里的Context照样执行Intent。
常和alermanger 和notificationmanager一起使用。
下面我们来看个notification的示例,理解下:
添加一个notification按照下列步骤:
1.获取一个notificationManager
NotificationManager m_NotificationManager=(NotificationManager)this.getSystemService(NOTIFICATION_SERVICE);
2、定义一个notification
Notification m_Notification=new Notification();
3.设置notification的各种属性
//设置通知在状态栏显示的图标
m_Notification.icon=R.drawable.icon;
//当我们点击通知时显示的内容
m_Notification.tickerText="Button1 通知内容.....";
通知时发出的默认声音
m_Notification.defaults=Notification.DEFAULT_SOUND;
//设置通知显示的参数
Intent m_Intent=new Intent(this,Activity.class);
PendingIntent m_PendingIntent=PendingIntent.getActivity(NotificationDemo.this, 0, m_Intent, 0);
m_Notification.setLatestEventInfo(NotificationDemo.this, title, content,m_PendingIntent );
这里还要注意
m_Notification.fullScreenIntent = m_PendingIntent;这个notification会马上执行
//具体可以看通话的notificationMgr里面的updateInCallNotification
m_Notification.contentIntent = m_PendingIntent;
4.执行notification
//这个可以理解为开始执行这个通知
m_NotificationManager.notify(notify_id,m_Notification);
这里我们就知道,当notificationManager发送这个notify的时候,可能设置这个notify的activity已经消失,
但是它还是能过根据PendingIntent来实现intent的功能,跳转到
NotificationDemo这个activity中。其实就是实现了一个异步的过程。