• Intent----android中的伟大邮差


          在android中,intent就像是一个邮差,辛勤高效的在各个组件之间来回穿梭。我们可以通过它启动一个Activity或者Service,或者是发送给广播组件,又或者是与后台的Service进行通信。所谓的Intent,字面意思就是"意图,目的",在android中的定义就是一个动作的抽象描述,类似于接口是抽象的行为协议一样,但这两者在实现上是不同的东西。

          即使是好的邮差,如果没有邮递地址,依然无法正确的将货物送到指定的地方。这个完全交给系统来处理,它会帮Intent寻找合适的邮递地址,像是发送给Activity的Intent就会准确的发送给该Activity,发送个各个广播的Intent就会发送给指定的广播,从不会出错。

    一.Intent的组成

          Intent所包含的信息主要包括两个方面:action和data。

          action就是所要执行的动作,像是我们经常在根文件中声明的Activity的动作:ACTION_MAIN,表示应用程序的入口。action通常是一个大写表示的字符串,用来简要的描述动作,当然,我们也可以自定义自己应用的Intent,不过在使用的时候必须指定全称,也就是包名。

          正如方法签名包含了参数和返回值一样,action的名字也必须尽量包含了该action的信息,所以在自定义自己的action的时候,名字是非常重要的,必须尽可能的与其他的action区分开来,而且还要将该名字与intent的其他部分紧密的结合在一起,简而言之,不是单独的定义action,而是要定义其他组件能够处理的完整协议。

          我们可以通过setAction()来设置action和通过getAction()来获取action。

           一般的action像是这样:

    public static final String ACTION_DIAL = "android.intent.action.DIAL"

          data是我们的动作所要操作的数据,通常是以Uri的形式表示。data并不是单独的出现,它们经常和action成对出现,像是这样:ACTION_VIEW content://contacts/people/1,就是用于展现手机中通讯录标识为1的人的信息,而且还提示了数据的存放地点是在设备上,并且是由content provider所控制。

          当一个Intent被发送给适合的组件时,我们除了要知道data的Uri之外,还必须知道数据的类型,也就是MIME type。我们可以显式地指明data的Uri和MIME type,可以通过setData()指明数据的uri,setType()指明数据的MIME type,setDataAndType()指明数据的Uri和MIME type,getData()用于获取Uri,而getType()用于获取type。

          除了上面的主要属性外,Intent还可以包含其他信息:category,type,component和extras。

          category给出了action执行的其他信息,像是CATEGORY_LAUNCHER表示目标Activity是应用程序中最优先被执行的Activity,也就是所谓的top-level Activity。还有CATEGORY_HOME,表示的就是手机开机启动后或者按下HOME键后显示的Activity,CATEGORY_BROWSABLE表示的就是能通过在网页浏览器中点击链接而激活的Activity,而CATEGORY_PREFERENCE表示Activity是用于设置的Activity。

          type是显式的指明data的MIME type,一旦指明了type,就只能使用该类型的data。

          component是显式的指明了使用Intent的组件,这时其他属性的设置就是可选的,因为它们的设置都是为了方便寻找合适的组件。

          extras正如字面意思,就是额外的信息,它们使用Bundle类型进行数据传递,也就是键值对,像是time-zone代表的就是新的时间区域。通常它代表的是Intent的扩展信息,像是我们的action如果能够发送邮件,那么我们可以将邮件的标题和内容放在extras里面。

          像是这样:

    Bundle bundle = new Bundle();
    intent.putExtras(bundle);

         flags的作用像是告诉系统应该如何启动Activity,或者在启动后应该如何处理该Activity等。

     二.Intent的使用

          Intent可以分为两类:显式的Intent和隐式的Intent。

          显式的Intent指定了使用Intent的组件,所以它并不需要其他信息。通常它使用在应用程序的内部,像是启动应用程序的其他Activity或者内部定义的Service。

          隐式的Intent并没有指定组件,所以它需要更加详细的信息以确保系统能够发送给正确的组件。但是这些还是不够的,组件本身还是要提供信息来确保它接收到正确的Intent,这就是intent-filter的作用。我们经常在根文件中注册一个组件的时候,指明它的intent-filter,像是在程序中使用广播组件的时候,我们可以显式的指明intent-filter,像是这样:registerReceiver(BroadcastReceiver, IntentFilter)。

          我们在声明intent-filter的时候,通常只需要指定三个属性:action,data和category,但是一般情况下我们有时候也不会完整的设定这三个属性,像是只设定action和category,那么这时的filter就只会匹配没有data的intent。在intent-filter中的data还进一步划分成几个属性:type,scheme,authority和path。type就是我们熟悉的MIME type,我们简单的讲解一下其他我们不熟悉的属性。

           所谓的sheme就是我们上面讲的content:Uri等信息,值得注意的是,如果我们只是声明了scheme而没有声明type的话,那么filter就只会匹配没有type的Intent,而像是content:Uri是不会被匹配的,因为content provider在存储的时候是有保存MIME type的信息,所以如果没有提供type的话,是无法从里面提取出data。如果我们只提供type而没有提供scheme的话,那就表示匹配没有Uri或者包含content:Uri和file:Uri的Intent。如果scheme和type都没有,那么匹配的将会是没有data或者type的Intent。值得注意的是,当我们需要指定authority的时候,authority的scheme列表和我们的intent-filter的scheme列表必须相同,而path则需要包括authority和scheme列表。

          我们来一个例子:

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

           其中,content就是scheme,com.example.project就是host,而200是port,path就是folder/subfolder/etc,host和port加起来就是URI authority,但注意,如果host没有指定,那么port也是没有必要的。

          以上介绍的属性都是可选的,但这并不意味着它们是独立的:如果想要声明一个authority,那么scheme就是必要的。如果想要声明一个path,scheme和authority都是必要的。

           我们再来看一个例子:

    <intent-filter>
         <data android:mimeType="audio/mpeg"/>
    </intent-filter>

          这里我们可以使用audio/*,这就表示audio的所有子类型都可以匹配。

          像是这样:

    <data android:scheme="http" android:type="video/*"/>

          表示我们的数据类型是video,然后可以通过网络来获取。

          在实际的应用中,我们使用隐式的Intent比较多,之所以使用隐式的Intent,是因为我们想要让系统来决定哪个组件是最适合该Intent的,通常系统会比人更加清楚什么才是最适合的,只要我们给出的信息足够详细。

           最好的情况就是我们在注册一个组件的时候,如果希望它接收Intent,那么就必须在根文件中声明它的intent-filter,这样无论是显式的还是隐式的Intent它都能接收到,如果没有,那么它只会接收显式的Intent。

          IntentFilter并不像它的名字一样,具有过滤保护的作用,因为它无法阻止显式的Intent发送给组件,它只能阻止隐式的Intent发送给该组件。

          一个intent-filter可以包含多个action:

    <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>

          这个filter声明了很多action,它允许用户view或者edit目录,也允许从目录中pick一个note出来。
          值得注意的是,filter声明了DEFAULT这个category。之所以声明为DEFAULT,是因为Context.startActivity()和Activity.startActivityForResult()这两个方法都默认Intent包含DEFAULT这个category,除了两个例外情况:显式的声明目标activity的名字和包含action-MAIN和category-LAUNCHER的Intent。

          这里还有一个特别的东西:data的mimeType。我们看到,这里的的mimeType声明为vnd.android.cursor.dir/vnd.google.note,说明我们可以从Content Provider中获取Cursor并进一步获取Note Pad的数据。

          最后发送给Activity的Intent大概像是这样子:

          {action:android.intent.action.VIEW data:content://com.google.provider.NotePad/notes}

          {action:android.intent.action.PICK data:content://com.google.provider.NotePad/notes}

          {action:android.intent.action.EDIT data:content://com.google.provider.NotePad/notes}

          当然,这里的Intent并不是完整的,还得考虑一下Activity的情况。如果该Activity用于展示所有的数据,Intent就是上面这样子, 但如果只是单独作用于一条数据的Activity,就算intent-filter是一样的,实际上的Intent应该像是这样子:

          {action:android.intent.action.VIEW data:content://com.google.provider.NotePad/notes/ID}

           再来看一个例子:

    <intent-filter android:label="@string/resolve_title">
         <action android:name="com.android.notepad.action.EDIT_TITLE"/>
         <category android:name="android.intent.category.DEFAULT"/>
         <category android:name="android.intent.category.ALTERNATIVE"/>
         <category android:name="android.intent.category.SELECTED_ALTERNATIVE"/>
         <data android:mimeType="vnd.android.cursor.item/vnd.google.note"/>
    </intent-filter>

          这个Filter声明的action是修改title。为了能够支持DEFAULT这个category,我们还必须支持其他两个标准的categories:ALTERNATIVE和SELECTED_ALTERNATIVE。这个Intent大概就是这样子:
          {action:com.android.notepad.action.EDIT_TITLE data:content://com.google.provider.NotePad/notes/ID}

          android系统本身就定义了大量的标准action和category,如果有需要的话,可以自己查阅官方文档。

         接下来我们就针对常见的使用场景来分析如何使用Intent。

    1.Activity的跳转:

    Intent intent = new Intent(Activity1.this, Activity2.class);
    startActivity(intent);

        关于Activity的跳转,还有一个知识点需要补充。我们知道,在android中有一个Task(栈),专门用于存放Activity,它遵循的是"先进后出"的原则,但如果我们不想要遵循这个原则,想要取出指定的Activity,我们就可以利用一个东西:Flag,也就是上面提到的Flag:

    Intent intent = new Intent(Activity1.this, Activity2.class);
    //如果Activity在Task中存在,并且是在最顶端,不会启动新的Activity
    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已经运行到Task,再次跳转不会再运行这个Activity
    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    2.向另一个Activity传递数据:

    Intent intent = new Intent(Activity1.this, Activity2.class);
    Bundle bundle = new Bundle();
    bundle.putString("name", "Jack");
    intent.putExtras(bundle);
    startActivity(intent);

        至于接收数据:

    Bundle bundle = new Bundle();
    String name = bundle.getString("name");

    3.向上一个Activity返回结果,这种情况针对的是startActivityForResult(intent, requestCode)启动的Activity:

    Intent intent = getIntent();
    Bundle bundle = new Bundle();
    bundle.putString("name", "Jack");
    intent.putExtras(bundle);
    setResult(RESULT_OK, intent);

    4.回调上一个Activity的结果处理函数:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
         super.onActivityResult(requestCode, resultCode, data);
         if(requestCode == REQUEST_CODE){
              if(resultCode == RESULT_OK){
                   String temp = null;
                   Bundle bundle = data.getExtras();
                   ....
              }
         }
    }

         接下来就是一些调用官方应用程序的使用场景:
    1.显示网页:

    Uri uri = Uri.parse("http://google.com");  
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
    startActivity(intent); 

    2.显示地图:

    Uri uri = Uri.parse("geo:38.899533,-77.036476");  
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);   
    startActivity(intent);   

    3.路径规划:

    Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=startLat%20startLng&daddr=endLat%20endLng&hl=en");  
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
    startActivity(intent);  

    4.打电话:
    (1)叫出拨号程序:

    Uri uri = Uri.parse("tel:0800000123");  
    Intent intent = new Intent(Intent.ACTION_DIAL, uri);  

    (2)直接打电话:

    Uri uri = Uri.parse("tel:0800000123");  
    Intent intent = new Intent(Intent.ACTION_CALL, uri);  
    startActivity(intent);  

         这些都要在根文件中声明权限:<user-permission id="android.permission.CALL_PHONE"/>
    5.发送SMS(短信)和MMS(彩信):

    //调用短信程序 
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
    intent.putExtra("content", "你好");   
    it.setType("vnd.android-dir/mms-sms");  
    startActivity(intent); 
    //发送短信
    Uri uri = Uri.parse("smsto://0800000123");  
    Intent intent = new Intent(Intent.ACTION_SENDTO, uri);  
    intent.putExtra("content", "你好");  
    startActivity(intent); 
    //发送彩信
    Uri uri = Uri.parse("content://media/external/images/media/23");  
    Intent intent = new Intent(Intent.ACTION_SEND);   
    intent.putExtra("content", "。。。");   
    intent.putExtra(Intent.EXTRA_STREAM, uri);  
    intent.setType("image/png");   
    startActivity(intent);  

    6.发送Email:

    Uri uri = Uri.parse("mailto:xxx@abc.com");  
    Intent intent = new Intent(Intent.ACTION_SENDTO, uri);  
    startActivity(intent);  
    
    
    Intent intent = new Intent(Intent.ACTION_SEND);  
    intent.putExtra(Intent.EXTRA_EMAIL, "me@abc.com");  
    intent.putExtra(Intent.EXTRA_TEXT, "The email body text");  
    intent.setType("text/plain");  
    startActivity(Intent.createChooser(intent, "Choose Email Client"));  
    
    
    Intent intent=new Intent(Intent.ACTION_SEND);    
    String[] tos={"me@abc.com"};    
    String[] ccs={"you@abc.com"};    
    intent.putExtra(Intent.EXTRA_EMAIL, tos);    
    intent.putExtra(Intent.EXTRA_CC, ccs);    
    intent.putExtra(Intent.EXTRA_TEXT, "The email body text");    
    intent.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");    
    intent.setType("message/rfc822");    
    startActivity(Intent.createChooser(intent, "Choose Email Client")); 
    
    
    //传送附件
    Intent intent = new Intent(Intent.ACTION_SEND);  
    intent.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");  
    intent.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/mysong.mp3");  sendIntent.setType("audio/mp3");  
    startActivity(Intent.createChooser(intent, "Choose Email Client")); 

    7.播放多媒体:

    Uri uri = Uri.parse("file:///sdcard/song.mp3");  
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
    intent.setType("audio/mp3");  
    startActivity(intent); 
    
    Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");  
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
    startActivity(intent); 

    8.Market相关:

    //寻找某个应用 
    Uri uri = Uri.parse("market://search?q=pname:pkg_name"); 
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
    startActivity(intent);
      
    //显示某个应用的相关信息 
    Uri uri = Uri.parse("market://details?id=app_id");  
    Intent intent = new Intent(Intent.ACTION_VIEW, uri); 
    startActivity(intent); 
     

    9.Uninstall应用程序:

    Uri uri = Uri.fromParts("package", strPackageName, null); 
    Intent intent = new Intent(Intent.ACTION_DELETE, uri);   
    startActivity(intent);  

        

         

         

         

         

         

         

         

       

  • 相关阅读:
    洛谷 P3128 [ USACO15DEC ] 最大流Max Flow —— 树上差分
    洛谷 P3953 [ NOIP 2017 ] 逛公园 —— 最短路DP
    bzoj 3231 [ Sdoi 2008 ] 递归数列 —— 矩阵乘法
    bzoj 1024 [ SCOI 2009 ] 生日快乐 —— 递归
    hdu 5823 color II —— 子集DP
    bzoj 1093 [ ZJOI 2007 ] 最大半连通子图 —— 拓扑+DP
    洛谷 P3959 NOIP2017 宝藏 —— 状压搜索
    最短路(模板
    线段树 扫描线
    Dijkstra算法
  • 原文地址:https://www.cnblogs.com/wenjiang/p/3256042.html
Copyright © 2020-2023  润新知