精华内容
下载资源
问答
  • gradle in action

    热门讨论 2014-04-05 17:15:03
    Gradle in action 正式版 Part 1: Introducing Gradle Chapter 1. Introduction to project automation Chapter 2. Next-generation builds with Gradle Chapter 3. Building a Gradle project by example Part 2...
  • R.in.Action.2nd.Edition.2015.5.pdf

    千次下载 热门讨论 2015-09-21 12:35:23
    R.in.Action.2nd.Edition.2015.5.pdf R语言实战第二版
  • Android中action启动方法大全

    千次阅读 多人点赞 2019-01-17 15:11:13
    1、Intent简介 Intent的中文意思是“意图,目的”的意思,可以理解为不同组件之间通信的...Intent在由以下几个部分组成:动作(action),数据(data),分类(Category),类型(Type),组件(Component),和扩...

    1、Intent简介

    Intent的中文意思是“意图,目的”的意思,可以理解为不同组件之间通信的“媒介”或者“信使”。  
    目标组件一般要通过Intent来声明自己的条件,一般通过组件中的<intent-filter>元素来过滤。  
    Intent在由以下几个部分组成:动作(action),数据(data),分类(Category),类型(Type),组件(Component),和扩展信息(Extra)。  
    Intent在寻找目标组件的时候有两种方法:

    第一,通过组件名称直接指定;第二,通过Intent Filter过滤指定  
    Intent启动不同组件的方法  
    组件名称  方法名称    
    Activity  startActivity()  startActivityForResult()    
    Service  startService()  bindService()    
    Broadcasts  sendBroadcast()  sendOrderedBroadcast()  sendStickyBroadcast()  
      
    常见的Activity Action Intent常量  
    常量名称   常量值  意义  
    ACTION_MAIN  android.intent.action.MAIN   应用程序入口  
    ACTION_VIEW  android.intent.action.VIEW  显示数据给用户  
    ACTION_ATTACH_DATA  android.intent.action.ATTACH_DATA  指明附加信息给其他地方的一些数据  
    ACTION_EDIT  android.intent.action.EDIT  显示可编辑的数据    
    ACTION_PICK  android.intent.action.PICK  选择数据    
    ACTION_CHOOSER  android.intent.action.CHOOSER  显示一个Activity选择器    
    ACTION_GET_CONTENT  android.intent.action.GET_CONTENT  获得内容    
    ACTION_DIAL  android.intent.action.GET_CONTENT  显示打电话面板    
    ACITON_CALL  android.intent.action.DIAL  直接打电话    
    ACTION_SEND  android.intent.action.SEND  直接发短信    
    ACTION_SENDTO  android.intent.action.SENDTO  选择发短信    
    ACTION_ANSWER  android.intent.action.ANSWER  应答电话    
    ACTION_INSERT  android.intent.action.INSERT  插入数据    
    ACTION_DELETE  android.intent.action.DELETE  删除数据    
    ACTION_RUN  android.intent.action.RUN  运行数据    
    ACTION_SYNC  android.intent.action.SYNC  同步数据    
    ACTION_PICK_ACTIVITY  android.intent.action.PICK_ACTIVITY  选择Activity    
    ACTION_SEARCH  android.intent.action.SEARCH  搜索    
    ACTION_WEB_SEARCH  android.intent.action.WEB_SEARCH  Web搜索    
    ACTION_FACTORY_TEST  android.intent.action.FACTORY_TEST  工厂测试入口点       
      
      
    常见的BroadcastIntent Action常量  BroadcastIntent   
    Action字符串常量  描述    
    ACTION_TIME_TICK  系统时间每过一分钟发出的广播    
    ACTION_TIME_CHANGED  系统时间通过设置发生了变化    
    ACTION_TIMEZONE_CHANGED  时区改变    
    ACTION_BOOT_COMPLETED  系统启动完毕    
    ACTION_PACKAGE_ADDED  新的应用程序apk包安装完毕    
    ACTION_PACKAGE_CHANGED  现有应用程序apk包改变    
    ACTION_PACKAGE_REMOVED  现有应用程序apk包被删除    
    ACTION_UID_REMOVED  用户id被删除       
      
      
    Intent的Action和Data属性匹配    
    Action属性  Data属性  说明    
    ACTION_VIEW  content://contacts/people/1  显示id为1的联系人信息    
    ACTION_DIAL  content://contacts/people/1  将id为1的联系人电话号码显示在拨号界面中    
    ACITON_VIEW  tel:123  显示电话为123的联系人信息    
    ACTION_VIEW  http://www.google.com  在浏览器中浏览该网站    
    ACTION_VIEW  file://sdcard/mymusic.mp3  播放MP3    
    ACTION_VIEW  geo:39.2456,116.3523  显示地图  
       
      
    常见的Category常量  
    Category字符串常量  描述  
    CATEGORY_BROWSABLE  目标Activity能通过在网页浏览器中点击链接而激活(比如,点击浏览器中的图片链接)    
    CATEGORY_GADGET  表示目标Activity可以被内嵌到其他Activity当中    
    CATEGORY_HOME  目标Activity是HOME Activity,即手机开机启动后显示的Activity,或按下HOME键后显示的Activity    
    CATEGORY_LAUNCHER  表示目标Activity是应用程序中最优先被执行的Activity    
    CATEGORY_PREFERENCE  表示目标Activity是一个偏爱设置的Activity  
      
      
    常见的Extra常量  
    Extra键值字符串常量  描述  
    EXTRA_BCC  装有邮件密送地址的字符串数组    
    EXTRA_CC  装有邮件抄送地址的字符串数组    
    EXTRA_EMAIL  装有邮件发送地址的字符串数组    
    EXTRA_INTENT  使用ACTION_PICK_ACTIVITY动作时装有Intent选项的键    
    EXTRA_KEY_EVENT  触发该Intent的案件的KeyEvent对象    
    EXTRA_PHONE_NUMBER  使用拨打电话相关的Action时,电话号码字符串的键,类型为String    
    EXTRA_SHORTCUT_ICON  使用ACTION_CREATE_SHORTCUT在HomeActivity创建快捷方式时,对快捷方式的描述信息。  
    其中ICON和ICON_RESOURCE描述的是快捷方式的图标,类型分别为Bitmap和ShortcutIconResource。INTENT描述的是快捷方式相对应的Intent对象。NAME描述的是快捷方式的名字。    
    EXTRA_SHORTCUT_ICON_RESOURCE  EXTRA_SHORTCUT_INTENT  EXTRA_SHORTCUT_NAME  EXTRA_SUBJECT  描述信息主题的键    
    EXTRA_TEXT  使用ACTION_SEND动作时,用来描述要发送的文本信息,类型为CharSequence    
    EXTRA_TITLE  使用ACTION_CHOOSER动作时,描述对话框标题的键,类型为CharSequence    
    EXTRA_UID  使用ACTION_UID_REMOVED动作时,描述删除的用户id的键,类型为int      
      
      
    Android.telephony包中的类    
    类名  描述    
    CellLocation  表示设备位置的抽象类    
    PhoneNumberFormattingTextWather  监视一个TextView控件,如果有电话号码输入,则用formatNumber()方法处理电话号码    
    PhoneNumberUtils  包含各种处理电话号码字符串的使用工具    
    PhoneStateListener  监视手机中电话状态变化的监听类    
    ServiceState  包含电话状态和相关的服务信息    
    TelephonyManager  提供对手机中电话服务信息的访问  
      
       
    与短信服务相关的类主要在包android.telephony.gsm中  
    类名  描述  
    GsmCellLocation  表示GSM手机的基站位置    
    SmsManager  管理各种短信操作    
    SmsMessage  表示具体的短信    
      
      
    2、.Intent的用法
    (1)用Action跳转  
    1、使用Action跳转,如果有一个程序的AndroidManifest.xml中的某一个 Activity的IntentFilter段中 定义了包含了相同的Action那么这个Intent就与这个目标Action匹配。如果这个IntentFilter段中没有定义 Type,Category,那么这个 Activity就匹配了。但是如果手机中有两个以上的程序匹配,那么就会弹出一个对话可框来提示说明。   
    Action 的值在Android中有很多预定义,如果你想直接转到你自己定义的Intent接收者,你可以在接收者的IntentFilter 中加入一个自定义的Action值(同时要设定 Category值为"android.intent.category.DEFAULT"),在你的Intent中设定该值为Intent的 Action,就直接能跳转到你自己的Intent接收者中。因为这个Action在系统中是唯一的。  
    2,data/type,你可以用Uri 来做为data,比如Uri uri = Uri.parse(http://www.google.com);  
    Intent i = new Intent(Intent.ACTION_VIEW,uri);手机的Intent分发过程中,会根据http://www.google.com 的scheme判断出数据类型type 。手机的Brower则能匹配它,在Brower的Manifest.xml中的IntenFilter中 首先有ACTION_VIEW Action,也能处理http:的type,  
    3, 至于分类Category,一般不要去在Intent中设置它,如果你写Intent的接收者,就在Manifest.xml的Activity的 IntentFilter中包含android.category.DEFAULT,这样所有不设置 Category(Intent.addCategory(String c);)的Intent都会与这个Category匹配。  
    4,extras(附 加信息),是其它所有附加信息的集合。使用extras可以为组件提供扩展信息,比如,如果要执行“发送电子邮件”这个动 作,可以将电子邮件的标题、正文等保存在extras里,传给电子邮件发送组件。  
    (2)用类名跳转  
        Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描 述,Android则根据此Intent的描述, 负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。Intent在这里起着实现调用者与被调用者之间的解耦作用。Intent传递过程中,要找 到目标消费者(另一个Activity,IntentReceiver或Service),也就是Intent的响 应者。  
    Intent intent = new Intent();  
    intent.setClass(context, targetActivy.class);  
    //或者直接用 Intent intent = new Intent(context, targetActivity.class);  
      
    startActivity(intent);  
    不过注意用类名跳转,需要在AndroidManifest.xml中申明activity    
    <activity android:name="targetActivity"></activity>  
    2.几种Intent的用法  
    android 中intent是经常要用到的。不管是页面牵转,还是传递数据,或是调用外部程序,系统功能都要用到intent。在做了一些intent的例子之后,整理了一下intent,希望对大家有用。由于intent内容太多,不可能真的写全,难免会有遗落,以后我会随时更新。如果你们有疑问或新的intent内容,希望交流。   


    3、intent大全:   
    1.从google搜索内容   
    Intent intent = new Intent();   
    intent.setAction(Intent.ACTION_WEB_SEARCH);   
    intent.putExtra(SearchManager.QUERY,"searchString")   
    startActivity(intent);   
      
    2.浏览网页   
    Uri uri = Uri.parse("http://www.google.com");   
    Intent it  = new Intent(Intent.ACTION_VIEW,uri);   
    startActivity(it);   
      
    3.显示地图   
    Uri uri = Uri.parse("geo:38.899533,-77.036476");   
    Intent it = new Intent(Intent.Action_VIEW,uri);   
    startActivity(it);   
      
    4.路径规划   
    Uri uri = Uri.parse("http://maps.google.com/maps?f=dsaddr=startLat%20startLng&daddr=endLat%20endLng&hl=en");   
    Intent it = new Intent(Intent.ACTION_VIEW,URI);   
    startActivity(it);   
      
    5.拨打电话   
    Uri uri = Uri.parse("tel:xxxxxx");   
    Intent it = new Intent(Intent.ACTION_DIAL, uri);     
    startActivity(it);   
      
    6.调用发短信的程序   
    Intent it = new Intent(Intent.ACTION_VIEW);      
    it.putExtra("sms_body", "The SMS text");      
    it.setType("vnd.android-dir/mms-sms");      
    startActivity(it);   
      
    7.发送短信   
    Uri uri = Uri.parse("smsto:0800000123");      
    Intent it = new Intent(Intent.ACTION_SENDTO, uri);      
    it.putExtra("sms_body", "The SMS text");      
    startActivity(it);   
    String body="this is sms demo";   
    Intent mmsintent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("smsto", number, null));   
    mmsintent.putExtra(Messaging.KEY_ACTION_SENDTO_MESSAGE_BODY, body);   
    mmsintent.putExtra(Messaging.KEY_ACTION_SENDTO_COMPOSE_MODE, true);   
    mmsintent.putExtra(Messaging.KEY_ACTION_SENDTO_EXIT_ON_SENT, true);   
    startActivity(mmsintent);   
      
    8.发送彩信   
    Uri uri = Uri.parse("content://media/external/images/media/23");      
    Intent it = new Intent(Intent.ACTION_SEND);      
    it.putExtra("sms_body", "some text");      
    it.putExtra(Intent.EXTRA_STREAM, uri);      
    it.setType("image/png");      
    startActivity(it);   
    StringBuilder sb = new StringBuilder();   
    sb.append("file://");   
    sb.append(fd.getAbsoluteFile());   
    Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mmsto", number, null));   
    // Below extra datas are all optional.   
    intent.putExtra(Messaging.KEY_ACTION_SENDTO_MESSAGE_SUBJECT, subject);   
    intent.putExtra(Messaging.KEY_ACTION_SENDTO_MESSAGE_BODY, body);   
    intent.putExtra(Messaging.KEY_ACTION_SENDTO_CONTENT_URI, sb.toString());   
    intent.putExtra(Messaging.KEY_ACTION_SENDTO_COMPOSE_MODE, composeMode);   
    intent.putExtra(Messaging.KEY_ACTION_SENDTO_EXIT_ON_SENT, exitOnSent);   
    startActivity(intent);   
      
    9.发送Email   
    Uri uri = Uri.parse("mailto:xxx@abc.com");   
    Intent it = new Intent(Intent.ACTION_SENDTO, uri);   
    startActivity(it);   
    Intent it = new Intent(Intent.ACTION_SEND);      
    it.putExtra(Intent.EXTRA_EMAIL, "me@abc.com");      
    it.putExtra(Intent.EXTRA_TEXT, "The email body text");      
    it.setType("text/plain");      
    startActivity(Intent.createChooser(it, "Choose Email Client"));   
    Intent it=new Intent(Intent.ACTION_SEND);        
    String[] tos={"me@abc.com"};        
    String[] ccs={"you@abc.com"};        
    it.putExtra(Intent.EXTRA_EMAIL, tos);        
    it.putExtra(Intent.EXTRA_CC, ccs);        
    it.putExtra(Intent.EXTRA_TEXT, "The email body text");        
    it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");        
    it.setType("message/rfc822");        
    startActivity(Intent.createChooser(it, "Choose Email Client"));      
      
    Intent it = new Intent(Intent.ACTION_SEND);      
    it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");      
    it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/mysong.mp3");      
    sendIntent.setType("audio/mp3");      
    startActivity(Intent.createChooser(it, "Choose Email Client"));   
      
    10.播放多媒体     
    Intent it = new Intent(Intent.ACTION_VIEW);   
    Uri uri = Uri.parse("file:///sdcard/song.mp3");   
    it.setDataAndType(uri, "audio/mp3");   
    startActivity(it);   
    Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");      
    Intent it = new Intent(Intent.ACTION_VIEW, uri);      
    startActivity(it);   
      
    11.uninstall apk   
    Uri uri = Uri.fromParts("package", strPackageName, null);      
    Intent it = new Intent(Intent.ACTION_DELETE, uri);      
    startActivity(it);   
      
    12.install apk   
    Uri installUri = Uri.fromParts("package", "xxx", null);   
    returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);   
      
    13. 打开照相机   
    <1>Intent i = new Intent(Intent.ACTION_CAMERA_BUTTON, null);   
        this.sendBroadcast(i);   
    <2>long dateTaken = System.currentTimeMillis();   
        String name = createName(dateTaken) + ".jpg";   
        fileName = folder + name;   
        ContentValues values = new ContentValues();   
        values.put(Images.Media.TITLE, fileName);   
        values.put("_data", fileName);   
        values.put(Images.Media.PICASA_ID, fileName);   
        values.put(Images.Media.DISPLAY_NAME, fileName);   
        values.put(Images.Media.DESCRIPTION, fileName);   
        values.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, fileName);   
        Uri photoUri = getContentResolver().insert(   
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);   
           
        Intent inttPhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);   
        inttPhoto.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);   
        startActivityForResult(inttPhoto, 10);   
      
    14.从gallery选取图片   
    Intent i = new Intent();   
    i.setType("image/*");   
    i.setAction(Intent.ACTION_GET_CONTENT);   
    startActivityForResult(i, 11);   
      
    15. 打开录音机   
    Intent mi = new Intent(Media.RECORD_SOUND_ACTION);   
    startActivity(mi);   
      
    16.显示应用详细列表         
    Uri uri = Uri.parse("market://details?id=app_id");           
    Intent it = new Intent(Intent.ACTION_VIEW, uri);           
    startActivity(it);           
    //where app_id is the application ID, find the ID            
    //by clicking on your application on Market home            
    //page, and notice the ID from the address bar        
      
    刚才找app id未果,结果发现用package name也可以   
    Uri uri = Uri.parse("market://details?id=<packagename>");   
    这个简单多了   
      
    17寻找应用         
    Uri uri = Uri.parse("market://search?q=pname:pkg_name");           
    Intent it = new Intent(Intent.ACTION_VIEW, uri);           
    startActivity(it);   
    //where pkg_name is the full package path for an application         
      
    18打开联系人列表   
        <1>              
        Intent i = new Intent();   
        i.setAction(Intent.ACTION_GET_CONTENT);   
        i.setType("vnd.android.cursor.item/phone");   
        startActivityForResult(i, REQUEST_TEXT);   
      
        <2>   
        Uri uri = Uri.parse("content://contacts/people");   
        Intent it = new Intent(Intent.ACTION_PICK, uri);   
        startActivityForResult(it, REQUEST_TEXT);   
      
    19 打开另一程序   
    Intent i = new Intent();   
    ComponentName cn = new ComponentName("com.yellowbook.android2",   
            "com.yellowbook.android2.AndroidSearch");   
    i.setComponent(cn);   
    i.setAction("android.intent.action.MAIN");   
    startActivityForResult(i, RESULT_OK);   
      
    20.调用系统编辑添加联系人(高版本SDK有效):  
    Intent it = newIntent(Intent.ACTION_INSERT_OR_EDIT);  
    it.setType("vnd.android.cursor.item/contact");  
    //it.setType(Contacts.CONTENT_ITEM_TYPE);  
    it.putExtra("name","myName");  
    it.putExtra(android.provider.Contacts.Intents.Insert.COMPANY,  "organization");  
    it.putExtra(android.provider.Contacts.Intents.Insert.EMAIL,"email");  
    it.putExtra(android.provider.Contacts.Intents.Insert.PHONE,"homePhone");  
    it.putExtra(android.provider.Contacts.Intents.Insert.SECONDARY_PHONE,  
                   "mobilePhone");  
    it.putExtra(  android.provider.Contacts.Intents.Insert.TERTIARY_PHONE,  
                   "workPhone");  
    it.putExtra(android.provider.Contacts.Intents.Insert.JOB_TITLE,"title");  
    startActivity(it);  
       
    21.调用系统编辑添加联系人(全有效):  
    Intent intent = newIntent(Intent.ACTION_INSERT_OR_EDIT);  
    intent.setType(People.CONTENT_ITEM_TYPE);  
    intent.putExtra(Contacts.Intents.Insert.NAME, "My Name");  
    intent.putExtra(Contacts.Intents.Insert.PHONE, "+1234567890");  
    intent.putExtra(Contacts.Intents.Insert.PHONE_TYPE,Contacts.PhonesColumns.TYPE_MOBILE);  
    intent.putExtra(Contacts.Intents.Insert.EMAIL, "com@com.com");  
    intent.putExtra(Contacts.Intents.Insert.EMAIL_TYPE,                    Contacts.ContactMethodsColumns.TYPE_WORK);  
    startActivity(intent);

    展开全文
  • LuceneInAction(第2版)_中文版

    千次下载 热门讨论 2012-07-12 09:52:59
    内容简介  《Lucene实战(第2版)》基于Apache的Lucene 3.0,从Lucene核心、Lucene应用、案例分析3个方面详细系统地介绍了Lucene,包括认识Lucene、建立索引、为应用程序添加搜索功能、高级搜索技术、扩展搜索、使用...
  • ROS学习笔记六:action-server/action-client 以下是最后一种通信机制action-server/action-client,尽管与service/client通信机制很像,它与service/client通信机制还是有很关键的不同点的: 那就是多对多的...

    ROS学习笔记六:action-server/action-client

    以下是最后一种通信机制action-server/action-client,尽管与service/client通信机制很像,它与service/client通信机制还是有很关键的不同点的:

    • 那就是多对多的action-server与action-client的交流机制
      service/client通信机制中service和client通信是单对单的,当client发送一个“请求”后,只有当它接收到service的“响应”信息才能执行其他操作,可以理解为这是一种单通道的封闭式通信,而
      action-server与action-client通信就不受这种限制,action-client对server发出一个“请求”后,还可以执行其他操作。

    与service/client通信相似的是:

    • action-client需要知道它出发的“请求”的接收者的名称(serviceName)
    • action-server也需要知道“响应”信息的接收对象是“谁”

    当然,action-servers和action-clients的框架里面还有很多功能和变量,可以在http://wiki.ros.org/actionlib 中查看,这里只列出一个简单的应用。

    一.创建一个action-server的包

    ①依然使用catkin_simple功能来创建package
    cs_create_pkg example_action_server roscpp actionlib

    ②然后在新建的package中创建一个**action**package用来存储action messages的信息,方法与创建service messages一样。
    cd example_action_server
    mkdir action

    ③惯例,在package.xml文件中添加两行生成消息文件的语句:

    <build_depend>message_generation</build_depend>
    <run_depend>message_runtime</run_depend>

    二.定义action-server消息

    与之前定义msg消息和service消息很相似,都需要建立一个对应格式的消息文件

    • publishers/subscribers的消息文件格式是*.msg
    • service/client的消息文件格式是*.srv
    • action-server/action-client的消息文件格式是*.action

    其中,service messages比起简单的messages要复杂一些,而action的消息跟service的消息很类似,也有分多个区域,总体上要稍微复杂一点
    service消息有两个消息区域

    • request
    • response

    action消息有三个消息区域

    • goal
    • result
    • feedback

    以下是本例要创建的一个action消息
    ①先进入到action的文件夹,创建一个名称为demo.action的文件:
    cd action
    touch demo.action

    ②然后写入如下内容:

    #goal definition
    int32 input
    ---
    #result definition
    int32 output
    int32 goal_stamp
    ---
    #feedback
    int32 fdbk

    三个消息区域通过三个连字符分隔开,最上面的是goad,中间的是result,最下面的feedback,“#”开头的注释语句不是必须的,可以省略。
    注意,顺序不可以打乱,严格按照以上顺序排列

    ③跟着就可以进行编译生成action消息类型的头文件了
    catkin_make
    生成的头文件在

    ~/ros_ws/devel/include/packageName

    这里的packageName是example_action_server,里面有7个头文件,如下图
    action messages
    其中,以demoAction.h为主,其他六个头文件都包含在该头文件中,要调用该包中的action消息类型,只需要知道demoAction.h这个头文件的名称即可。


    三.创建一个action server节点

    以下是一个action server节点的代码:

    #include<ros/ros.h> //惯例添加
    #include <actionlib/server/simple_action_server.h> //这是一个library里面一个做好的包(simple_action_server)里面的头文件
    #include<example_action_server/demoAction.h> //这个头文件是重点,在上一部分生成的action消息的头文件
    
    int g_count = 0;
    bool g_count_failure = false;
    
    class ExampleActionServer {
    private:
    
        ros::NodeHandle nh_;  // 节点句柄
    
        actionlib::SimpleActionServer<example_action_server::demoAction> as_;//创建一个名称为"as_"的“SimpleActionServer”,SimpleActionServer是一个在actionlib里面的类(在actionlib的包中有定义),消息类型为example_action_server::demoAction,即上部分定义的action消息类型
    
        // 以下三个是与client交流传递的消息类型
        example_action_server::demoGoal goal_; // 接收clients发送的goal message的变量
        example_action_server::demoResult result_; // 保存result结果信息的变量,当完成goad的请求后将结果信息发送给client
        example_action_server::demoFeedback feedback_; //在这个例子中没有用到feedback信息,它起一个反馈作用,将一定信息反馈给client,具体参数根据实际需要设定,如 as_.publishFeedback(feedback_); ,就可以将变量“feedback_”发送给client
    
    
    public:
        ExampleActionServer(); //在类定义之外定义构造函数的主体
    
        ~ExampleActionServer(void) {
        }
        // Action接口
        void executeCB(const actionlib::SimpleActionServer<example_action_server::demoAction>::GoalConstPtr& goal);
    };
    
    ExampleActionServer::ExampleActionServer() :
       as_(nh_, "example_action", boost::bind(&ExampleActionServer::executeCB, this, _1),false)
    //执行上述构造函数:成员as_将通过指定的节点句柄(nh_)来实例化, 并在接收到goad消息后执行一个函数(executeCB),这个函数是类(ExampleActionServer)中的一个成员方法(member method)
    // 在这里定义了server的名称叫为“example_action”,这个名称很重要,server和client通信需要知道这个名字
    //“this”会告诉“boost::bind”这个函数是类的成员
    //“_1”被“boost::bind”用来通知“simple action server”,回调函数(executeCB)只取一个数值
    //“false”的意思是暂时不启动这个server(在这个构造函数中的后面会启动server)
    {
        ROS_INFO("in constructor of exampleActionServer...");
    
        as_.start(); //在上述的初始化参数使用false,先不启动action server,到这里才启动,就是要确保server的实例化对象要先完成之后再启动这个实例化对象,避免出现意外状况
    }
    
    //下面是具体的executeCBD(成员方法)的执行函数
    //example_action_server::demoAction上部分定义的action消息类型
    void ExampleActionServer::executeCB(const actionlib::SimpleActionServer<example_action_server::demoAction>::GoalConstPtr& goal) {
    
    //result消息由两个成员组成,一个是“input”,它保存的是clients发送过来的goal消息,另一个是“goal_stamp”,记录接收goal消息的次数
        g_count++; // 从server启动开始追踪被执行的goal消息数量
        result_.output = g_count; // 在这里使用在类中定义过的成员变量result_
        result_.goal_stamp = goal->input;
    
        //检测clients和server是否保持同步状态
        if (g_count != goal->input) {
            ROS_WARN("hey--mismatch!");
            ROS_INFO("g_count = %d; goal_stamp = %d", g_count, result_.goal_stamp);
            g_count_failure = true; //set a flag to commit suicide
            ROS_WARN("informing client of aborted goal");
            as_.setAborted(result_); // 这个函数的意思是放弃这个goad并发送result消息通知client
        }
        else {
             as_.setSucceeded(result_); // 这个函数的意思是成功执行这个goad并发送result消息通知client
        }
    }
    
    int main(int argc, char** argv) {
        ros::init(argc, argv, "demo_action_server_node"); //初始化,命名该节点
    
        ROS_INFO("instantiating the demo action server: ");
    
        ExampleActionServer as_object; // 创建类(ExampleActionServer)的一个实例化对象
    
        ROS_INFO("going into spin");
        // 从这里开始,所有的工作都是在action server中的成员方法(executeCB())完成的
        // 在action server(example_action)下有五个topic: cancel, feedback, goal, result, status
        while (!g_count_failure && ros::ok()) {
            ros::spinOnce(); 
        }
    
        return 0;
    }
    

    四.创建一个action client节点

    在和action server同一个package中建立一个example_aciton_client.cpp文件,以下是一个action client节点的代码:

    #include<ros/ros.h>
    #include <actionlib/client/simple_action_client.h> //与server一样,client也需要使用actionlib library里面的头文件,只是使用的是simple_action_client.h
    #include<example_action_server/demoAction.h> //上一部分定义的action messages文件
    
    
    //下面这个函数是非必要的,当goal完成时就会调用这个函数一次,但这也是一种访问server中“result”消息的便捷方式
    void doneCb(const actionlib::SimpleClientGoalState& state,
            const example_action_server::demoResultConstPtr& result) {
        ROS_INFO(" doneCb: server responded with state [%s]", state.toString().c_str());
        int diff = result->output - result->goal_stamp;
        ROS_INFO("got result output = %d; goal_stamp = %d; diff = %d", result->output, result->goal_stamp, diff);
    }
    
    int main(int argc, char** argv) {
        ros::init(argc, argv, "demo_action_client_node"); //初始化,命名节点
        int g_count = 0;//这是一个与server兼容的goal消息的对象
    
        example_action_server::demoGoal goal;实例化一个类型为“example_action_server::demoGoal”的对象
    
        actionlib::SimpleActionClient<example_action_server::demoAction> action_client("example_action", true);
        //action_client是“actionlib::SimpleActionClient”实例化的对象,使用的消息类型是“example_action_server::demoAction”
        // “example_action”是server的名字,在action server节点中命名的
        //参数“true”是为了说明新的client将作为一个单线程运行
    
        // 以下开始连接server:
        ROS_INFO("waiting for server: ");
        bool server_exists = action_client.waitForServer(ros::Duration(5.0)); //等待5秒,若括号内不设置时间,会一直等待
        // 有一点很奇怪,好像并没有等地5秒,但是如果server没有运行却会很快会有返回值
    
        if (!server_exists) {
            ROS_WARN("could not connect to server; halting");
            return 0; // 输出一个警报消息,这部分也是非必要的
        }
    
    
        ROS_INFO("connected to action server"); // 成功连接
    
        while (true) {
            // 开始给goal消息赋值
            g_count++;
            goal.input = g_count; // 这只是一个按序列编号发送的goal消息
            //action_client.sendGoal(goal); // 只发送goal消息
            action_client.sendGoal(goal, &doneCb); // 在发送goal消息时可以使用一个额外的回调函数来检测goal的执行情况
            //    action_client.sendGoal(goal, &doneCb, &activeCb, &feedbackCb); //可以同时调用多个回调函数
    
            bool finished_before_timeout = action_client.waitForResult(ros::Duration(5.0));//等待result消息5秒,同样,去掉括号内的数值,会一直等待
    
        //若没有接收到result消息,则发出警报
            if (!finished_before_timeout) {
                ROS_WARN("giving up waiting on result for goal number %d", g_count);
                return 0;
            } else {
                //否则,就说明server反馈了result消息给我们
            }
    
        }
    
        return 0;
    }
    

    这里的client和server是在同一个package里面的,很多时候同一个server有很多个clients,而且都是在不同的package,这时候就需要在package.xml文件里面添加action messages包的依赖项,同时还要在client节点的文件中加上action messages的头文件。

    还需要在CMakeLists.txt文件中添加:
    ①Boost所在package的依赖项

    find_package(Boost REQUIRED COMPONENTS system thread)

    ②生成两个节点的可执行程序的命令:

    cs_add_executable(example_action_server src/example_action_server.cpp)
    cs_add_executable(example_action_client src/example_action_client.cpp)

    最后进行编译就完成了
    catkin_make


    五.运行action-server/action-client节点

    运行节点前,请确保已经运行了roscore

    分别在两个不同的terminal中启动server和client:
    rosrun example_action_server example_action_server
    rosrun example_action_server example_action_client

    在client的端口会有如下输出:

    [ INFO] [1521442053.251384877]:  doneCb: server responded with state [SUCCEEDED]
    [ INFO] [1521442053.251542525]: got result output = 2242; goal_stamp = 2242; diff = 0
    [ INFO] [1521442053.254128683]:  doneCb: server responded with state [SUCCEEDED]
    [ INFO] [1521442053.254174911]: got result output = 2243; goal_stamp = 2243; diff = 0
    [ INFO] [1521442053.257378829]:  doneCb: server responded with state [SUCCEEDED]
    [ INFO] [1521442053.257444954]: got result output = 2244; goal_stamp = 2244; diff = 0
    [ INFO] [1521442053.264676774]:  doneCb: server responded with state [SUCCEEDED]
    [ INFO] [1521442053.264862612]: got result output = 2245; goal_stamp = 2245; diff = 0
    [ INFO] [1521442053.267605141]:  doneCb: server responded with state [SUCCEEDED]
    [ INFO] [1521442053.267730561]: got result output = 2246; goal_stamp = 2246; diff = 0
    

    当节点运行后,可以查看当前有几个topic正在运行:
    rostopic list
    会有如下输出:

    /example_action/cancel
    /example_action/feedback
    /example_action/goal
    /example_action/result
    /example_action/status
    /rosout
    /rosout_agg

    可知有5个新的topic在example_action下被运行,可以通过查看命令观察它们输出:
    例如查看goal的消息:
    rostopic echo example_action/goal
    会有如下输出:

    ---
    header: 
      seq: 63929
      stamp: 
        secs: 1521442598
        nsecs: 441791983
      frame_id: ''
    goal_id: 
      stamp: 
        secs: 1521442598
        nsecs: 441792381
      id: /demo_action_client_node-63930-1521442598.441792381
    goal: 
      input: 63930
    ---
    header: 
      seq: 63930
      stamp: 
        secs: 1521442598
        nsecs: 443889821
      frame_id: ''
    goal_id: 
      stamp: 
        secs: 1521442598
        nsecs: 443890098
      id: /demo_action_client_node-63931-1521442598.443890098
    goal: 
      input: 63931

    在这个例程里面使用的“simple”action-server library 不能同时处理多个goals,也不能让它们排成队列,一个接一个处理,通俗地讲就是只有一个坑,被人占了就没地儿去了,所以选用action-server library时要考虑清楚想要的特性是什么。

    展开全文
  • 基于GITHUB ACTION的定时任务,真香!

    千次阅读 多人点赞 2020-12-06 18:23:59
    创建 hello GitHub aciton 工作流 首先,先写一个最简单的GitHub action的工作流来直观的看看其需要什么要素,其实很大部分的内容在GitHub action的官方文档里将的很清楚了,GITHUB ACTION官方文档 进入自己的GitHub...

    我本身就有一台自用的服务器(其实也不算,就是一个性能很弱的nas主机)每天24小时运行着,上面跑的系统是ubuntu 18,最近发现了一种玩法,可以把自己写的脚本,或者github上白嫖来的脚本,以定时任务的方式放在上面跑,然后每天就会自动做一些任务感觉挺方便的。目前是把一些签到类的脚本和定时推送消息的脚本放在上面,当然,还有很多的玩法,只要有自己的需求和强大的脚本哈。

    前不久在github上瞎逛的时候,我想找找现在有没有什么有趣的脚本可以白嫖哈,发现很多开发者在进行脚本运行说明的时候,都会带上使用GITHUB ACTION的方式来跑。之前我是没有了解过这方面的内容的,在我看来其实挺神奇的,为什么呢?因为执行脚本那必然是需要一台服务器来运行的,也就是会占用服务器的资源,换句话说,难道GitHub可以免费给开发者提供用于运行脚本的服务器资源吗?答案是:没错。那这,如果能够充分的利用这个平台,肯定可以产生很多玩法的,事实也是如此,GitHub action 甚至有个属于自己的市场了,所以,这方面可以折腾下,玩好了那就真香!

    什么是GitHub action

    我不能很精确的描述它的定义,也不想照搬官方文档对它的定义。简单说说我的理解,字面意思翻译过来就是GitHub动作,加点动词,那就是GitHub帮你做一些(一系列)动作。GitHub action产生的初衷是用于CI的,也就是持续集成。我们知道写完代码,或者改完bug,首先要编译,然后测试,测试没问题可能就是部署了,当然,我说的过程很简单,实际开发过程可能是更复杂的。这些过程实际上都是开发人员手动做的,每次都一样,老实说有点机械和重复。如果能够做到,开发人员修改完代码,程序会自动的进行构建,编译,然后跑测试用例,生成测试报告,如果测试没有问题,然后就部署运行。那么这将是一件十分惬意的事吧,其实现在已经有很多成熟的方案了,我了解到的关于java的就有maven,它是一个专门用于构建java工程的工具,能够一条龙的做到这些过程。

    CI和这些构造工具有什么不同吗?其实我还不清楚,只能说一点简单的理解,太菜了哈。现阶段我觉得他们的核心思想是一致的,也就是加快程序开发效率。CI可以从更宏观的角度去描述程序的集成过程,而构建工具可以只是CI的一个环节。CI一般而言都是一个任务流或者说流水线,在这个过程中一个个任务被顺序或者并行执行。

    怎么用GitHub action

    想要使用GitHub action的功能,首先需要在GitHub上创建一个公开仓库,GitHub action对公开仓库是不收费的。

    创建 hello GitHub aciton 工作流

    首先,先写一个最简单的GitHub action的工作流来直观的看看其需要什么要素,其实很大部分的内容在GitHub action的官方文档里将的很清楚了,GITHUB ACTION官方文档

    进入自己的GitHub 公开仓库,点击ACTION按钮

    点击创建一个新的工作流

    选择自己来写工作流文件,不从GitHub已经提供的工作流模板中做选择。因为是第一次写,所以自己先写可以更直观的了解一些GitHub acton 的基本概念和要素,后面再根据自己的需求在对应的模板中做选择,这样可以提高写action的效率。

    接下来就来到了这个页面,GitHub自动给我们生成了这个main.yml文件,里头已经填充上一个工作流所必须的基本要素了。

    这个文件可以好好说明一下。首先是文件的位置,GitHub默认给我们创建的位置是基于项目下的.github/workflows/目录中,其实这是GitHub action执行工作流的指定路径,要想让GitHub识别并执行工作流的话,对应的工作流文件是必须要放在这个目录下的。

    当然最终要的是这个文件的内容了,我将其拷贝出来并做一些精简,如下。

    # name属性用来指定这个工作流的名字
    name: HELLO GITHUB ACITON
    
    # 这个部分用来指定能够触发工作流执行的事件
    on:
      # 当对分支main进行push操作的时候,这个工作流就被触发了
      push:
        branches: [ main ]
    
    
    
    # 工作流是由一个或多个的jobs构成的,在jobs里来说明要交给GitHub aciton执行的任务
    jobs:
      # 这个jobs中的一个任务,名字叫build(随便怎么取)
      build:
        # 用来指定这个任务在什么操作系统上跑(服务器是GitHub免费提供的)
        runs-on: ubuntu-latest
    
        # 指出这个build任务的步骤
        steps:
    
          # 步骤,这里只写了一个步骤,目的是输出hello github acition
          - name: Run a one-line script
            run: echo Hello, github action!
    
    

    一个最最基本的GitHub action工作流就写好了,当进行push操作的时候就会触发。

    触发的结果可以点击action按钮进行查看

    接下来点击工作流: hello github aciton

    可以看到右侧显示了触发这一个工作流执行的事件,我这里进行了一次push 到main分支的操作,所以就触发了这个工作流。想看到工作流执行的详情,那就再点击到具体的事件就可以了。

    因为我这个工作流十分的简单,唯一的任务就是build,点击build可以看到执行的过程。一个任务的执行一般都是由set up job + steps + complete job构成的,steps前后一般是GitHub action做一些环境的配置,steps部分就是主要由我们写的任务了。我在这个工作流中只写了一个step,也就是 在控制台打印hello,github action!,可以看到它被正确的执行了。

    GitHub action 中的一些基本概念

    刚才直观地通过一个例子来说明了一下GitHub action是怎么执行的,接下来把GitHub action的一些基本概念给汇总下,这里主要引用了阮一峰老师的文章。

    GitHub Actions 有一些自己的术语。

    (1)workflow (工作流程):持续集成一次运行的过程,就是一个 workflow。

    (2)job (任务):一个 workflow 由一个或多个 jobs 构成,含义是一次持续集成的运行,可以完成多个任务。

    (3)step(步骤):每个 job 由多个 step 构成,一步步完成。

    (4)action (动作):每个 step 可以依次执行一个或多个命令(action)。

    GitHub Actions 的配置文件叫做 workflow 文件,存放在代码仓库的.github/workflows目录。

    workflow 文件采用 YAML 格式,文件名可以任意取,但是后缀名统一为.yml,比如foo.yml。一个库可以有多个 workflow 文件。GitHub 只要发现.github/workflows目录里面有.yml文件,就会自动运行该文件。

    我们要写的主要文件也就是workflow文件,其中可以配置的字段非常多,主要介绍下一些常用的字段,更多的可以查看官方文档

    还是直接给出一个完整的worflow文件,字段直接用注解备注了。

    #字段1 name:用来指定workflow的名称,必备
    name: Greeting from Mona
    #字段2 on: 用来指定触发 workflow 的条件,通常是某些事件。
    # 也可以是数组,如 on: [push, pull_request]
    on: push
    #字段3 jobs: workflow文件的主体是jobs字段,表示要执行的一项或多项任务
    jobs:
    #字段4: jobs.job_id: job_id是自定义的,比如这里写my_job
      my-job:
      # 字段5: jobs.job_id.name: 用来指定这个job的说明
        name: My Job
        # 字段6: jobs.job_id.runs-on: 用来指定这个job要在什么环境跑,也是必备的
        runs-on: ubuntu-latest
        # 字段7: jobs.job_id.steps: steps字段指定每个 Job 的运行步骤,可以包含一个或多个步骤。steps是一个数组,每个元素是一个step
        steps:
        # 字段8: name,具体的一个step的名字
        - name: Print a greeting
        # 字段9: env,具体的一个step会用到的环境变量
          env:
            MY_VAR: Hi there! My name is
            FIRST_NAME: Mona
            MIDDLE_NAME: The
            LAST_NAME: Octocat
            #字段10: run,具体的一个步骤运行的命令或者 action。
          run: |
            echo $MY_VAR $FIRST_NAME $MIDDLE_NAME $LAST_NAME.
    

    通过workflow示例文件也可以看出GitHub action中涉及到的基本概念的关系:在一个step中说明要做的事,比如执行指令或者使用他人的action, 一个job由多个step构成,jobs由多个job构成,而workflow的主体是jobs。workflow就是通过这样的层次组织来实现工作流的执行。

    GitHub action执行定时任务脚本,真香

    利用GitHub action的特点,写一点脚本让GitHub帮我们定时运行,这点想想就很有意思。

    定时推送天气信息到邮箱

    使用GitHub action来推送天气到邮箱

    还是给出workflow的完整文件,并在上面做注释

    name: 'GitHub Actions Weather Bot'
     
    on:
      push:
      schedule:
      # 定时任务,在每天的5点推送天气信息到邮箱
        - cron: '0 21 * * *'
    
    jobs:
      bot:
        runs-on: ubuntu-latest
        steps:
        # 首先copy git仓库到虚拟机上
          - name: 'Checkout codes'
            uses: actions/checkout@v1
        # 执行仓库中的脚本文件
          - name: 'Get Weather'
            run: bash ./weather.sh
          # 获取格式化的日期并存入GitHub内置的环境变量中
          - name: 'Get Date'
            run: echo "REPORT_DATE=$(TZ=':Asia/Shanghai' date '+%Y-%m-%d %T')" >> $GITHUB_ENV
          # 使用现成的发送邮箱的GitHub action来发送邮件
          - name: 'Send mail'
            uses: dawidd6/action-send-mail@master
            with:
              # 这些是发送邮件需要配置的参数,更多详细的说明请访问具体的仓库
              server_address: smtp.qq.com
              server_port: 465
              # 这些sectret的环境变量需要配置在setting中的secret下
              username: ${{ secrets.MAIL_USERNAME }}
              password: ${{ secrets.MAIL_PASSWORD }}
              subject: 天气预报 (${{env.REPORT_DATE}})
              body: file://result.html
              to: 1113794717@qq.com
              from: GitHub Actions
              content_type: text/html
    

    下面是脚本文件的内容,目的其实就是获取天气信息并生成文件

    #!/bin/bash
    set -eux
    
    CITY=Shenzhen
    LANGUAGE="zh-CN"
    UNIT=m
    UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
    
    curl \
      -H "Accept-Language: $LANGUAGE" \
      -H "User-Agent: $UA" \
      -o result.html \
      wttr.in/$CITY?format=4\&$UNIT
    

    接下来再说明下怎么配置一些比较私密的信息,比如账号、密码之类的

    在具体的项目下,点击setting

    找到secrets

    新增一个secret

    这些新增的secret是可以在GitHub action中使用的,比如上面用于发送天气信息到邮箱的工作流中就用到了。

    定时执行bilibili签到、投币脚本

    这是我的有关哔哩哔哩的基本仓库,欢迎使用,easy-bilibili

    还是贴出完整的workflow文件,并做些说明

    name: easy-bilibili-daily-task
    on:
      push:
        branches: [ master ]
      schedule:
        # 定时任务,在每天的5点执行任务
      	- cron: '0 21 * * *'
    jobs:
      start:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v2
          - name: Set up JDK 1.8
            uses: actions/setup-java@v1
            with:
              java-version: 1.8
          - name: use cache to speed up the time of build
            uses: actions/cache@v2
            with:
              path: ~/.m2/repository
              key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
          - name: execute the task
            env:
              # 这些参数可以使用电脑打开哔哩哔哩web版获取到,然后将对应的值放入secrets中
              sessdata: ${{ secrets.SESSDATA }}
              userId: ${{ secrets.USERID }}
              bill_jct: ${{ secrets.BILL_JCT }}
              # 配置这个参数可以将消息推送到微信(关注server酱)
              sckey: ${{ secrets.SCKEY }}
            run: mvn compile exec:java -Dexec.mainClass="com.hhdd.Main" -Dexec.args="${sessdata} ${userId} ${bill_jct} ${sckey}"
    

    这个工作流里比较特别的地方,使用了一个cache动作来加速maven的构建。

    maven构建的程序依赖很多jar包,如果每次都重新下载这些jar包将很花费时间和网络资源。用了cache就能够将需要的jar包存储起来,存在GitHub提供的服务器上,下次还需要这些依赖的时候直接去取,而不需要重新到网络上下载,这就大大加快了构建的过程。为了能够准确的找到自己存储在GitHub服务器上的cache,使用这个cache动作的时候会生成一个key,生成key的方式多种多样,围绕的核心准则是能够准确的找到cache并且当依赖发生变化的时候能够及时的更新cache。这里用到的是官方提供的生成方式 ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }},已经足够好用了。

    工作流的最后一步是执行整个maven工程的主类,也就是Main,需要传入一些参数,参数的来源是secret。

    来看看执行结果

    成功进行观看视频和投币,哈哈。

    最后

    简单写了点GitHub action的用法和玩法,我用它来推送天气消息和哔哩哔哩的签到任务。其实如果足够有心,你也可以这么做,还是挺方便有趣的。GitHub action能做的东西当然远不止这些,何况说,它的核心是用来CI的,其实我这种玩法有玩坏的嫌疑哈,要了解更多当然得看github action的官方文档了。GitHub上还有挺多有意思的脚本,如果配合上这个GitHub action还挺不错的哈,比如:

    1. BILIBILI-HELPER (b站签到)
    2. jd_base(jd签到)

    这是我的个人博客地址:https://huanglusong.github.io/
    有任何问题,只要是对技术热爱,喜欢倒腾的小伙伴欢迎加入qq群:624017389,一起交流,本人主要是搞java开发的,希望能和大家共同学习,齐头共进。

    参考文章:

    1. GitHub Actions 入门教程_阮一峰
    2. GitHub Actions 教程:定时发送天气邮件
    3. GITHUB ACTION 官方文档
    展开全文
  • 目录引言ACTION_DOWN与ACTION_UP的传递机制ACTION_CANCEL的作用FLAG_DISALLOW_INTERCEPT的作用 引言 关于Android事件分发机制网上相关的文章很多,多数都是一些较为基础并且重复的内容。本文将从源码带领大家探究...

    引言

    关于Android事件分发机制网上相关的文章很多,多数都是一些较为基础并且重复的内容。本系列将从源码带领大家探究一些事件分发机制的“细枝末节”。但是在此之前,还是简单重复一下基础内容。即事件分发的三个重要方法:
    事件传递给当前view时,dispatchTouchEvent方法会被调用。在方法内部会判断是否拦截事件onInterceptTouchEvent及如何处理事件onTouchEvent
    一个完整的事件序列以Down开始,中间经过一个或者多个Move,最后以Up结束。
    用一张图来总结ViewGroup的Down事件传递机制:

    True
    True
    Flase
    True
    False
    False
    True
    False
    dispatchTouchEvent
    onInterceptTouchEvent
    mOnTouchListener.onTouch
    End
    子view dispatchTouchEvent
    End
    onTouchEvent
    End
    上层view或者Activity处理

    事件Down传递给当前ViewGroup时,首先回调用dispatchTouchEvent方法,该方法内部会通过onInterceptTouchEvent方法判断是否拦截该Down事件。

    如果拦截,则会首先判断ViewGroup是否设置了mOnTouchListener并且onTouch方法是否返回true,如果满足,则该事件处理结束。如果不满足,则会交给ViewGroup的onTouchEvent来处理,如果onTouchEvent返回true,则该事件处理结束;如果返回false,表示当前ViewGroup无法处理该事件,那么该事件回传递给上层View或者Activity来处理。
    如果不拦截,则事件会交给子View来处理,回调用子View的dispatchTouchEvent方法,在子View的dispatchTouchEvent方法内部,也会判断是否设置了mOnTouchListener并且onTouch方法是否返回true,如果满足,则该事件处理结束。如果不满足,则会交给View的onTouchEvent来处理,如果onTouchEvent返回true,则该事件处理结束;如果返回False,则会继续走到父View的mOnTouchListener.onTouch判断逻辑中。

    以上说的是Down事件的传递机制,我们知道如果一个View处理的Down事件,那么Move和Up事件也会自动交给它处理,那么这一过程是如何实现的呢?

    ACTION_MOVE与ACTION_UP的传递机制

    mFirstTouchTarget作用

    在ViewGroup源码中使用了一个全局变量mFirstTouchTarget来记录是否有View处理了Down事件。mFirstTouchTarget默认为null,如果发现了View可以处理,那么就会把mFirstTouchTarget的值设置为对应的View。那么随之而来的Down和Up都会交给该View处理。
    下面通过源码来说明:

    首先看一下mFirstTouchTarget的赋值:

    
        /**
         * Adds a touch target for specified child to the beginning of the list.
         * Assumes the target child is not already present.
         */
        private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
            final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
            target.next = mFirstTouchTarget;
            mFirstTouchTarget = target;
            return target;
        }
    
    

    上面的这段代码可以看出这是一个单链表的插入操作,将mFirstTouchTarget插入到链表的队头并且返回。再来看下这个方法的调用:

        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
    
                // 代码省略,主要是判断当前事件是否被cancel和intercepted
                if (!canceled && !intercepted) {
    
                    // 处理Down事件
                    if (actionMasked == MotionEvent.ACTION_DOWN
                            || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                            || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                        final int actionIndex = ev.getActionIndex(); // always 0 for down
    
                        final int childrenCount = mChildrenCount;
                        if (newTouchTarget == null && childrenCount != 0) {
                            final float x = ev.getX(actionIndex);
                            final float y = ev.getY(actionIndex);
                            // Find a child that can receive the event.
                            // Scan children from front to back.
                            final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                            final boolean customOrder = preorderedList == null
                                    && isChildrenDrawingOrderEnabled();
                            final View[] children = mChildren;
                            // 遍历每一个子view
                            for (int i = childrenCount - 1; i >= 0; i--) {
                                final int childIndex = getAndVerifyPreorderedIndex(
                                        childrenCount, i, customOrder);
                                final View child = getAndVerifyPreorderedView(
                                        preorderedList, children, childIndex);
    
                                //... 省略部分代码
    
                                // 找到可以处理该事件的view
                                if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                    // 省略部分代码
                                    newTouchTarget = addTouchTarget(child, idBitsToAssign);    // 重点在这里!!!!将可以处理该事件的view设置为mFirstTouchTarget
                                    break;
                                }
    
                                // The accessibility focus didn't handle the event, so clear
                                // the flag and do a normal dispatch to all children.
                                ev.setTargetAccessibilityFocus(false);
                            }
                            if (preorderedList != null) preorderedList.clear();
                        }
                        // 略部分代码
                        }
                    }
                }
                // 省略部分代码。。。
    
            return handled;
        }
    
    

    通过上面的代码可以看到,如果当前是Down事件,而且没有被拦截或者取消的话,就会遍历这个ViewGroup的children,找到可以处理事件的view,并且添加到mFirstTouchTarget单链表中。也就是说,mFirstTouchTarget单链表中存储的view是可以处理该Down事件的子view。

    那么当Move事件及Up事件来的时候,又是如何根据mFirstTouchTarget的值来进行分发的呢?
    继续看这部分源码:

    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
    
            // 省略部分代码
            boolean handled = false;
            if (onFilterTouchEventForSecurity(ev)) {
                final int action = ev.getAction();
                final int actionMasked = action & MotionEvent.ACTION_MASK;
                // 省略部分代码
                // Check for interception.
                final boolean intercepted;
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || mFirstTouchTarget != null) {
                    
                    // 重点1   mFirstTouchTarget != null时 会走这个if
                    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                    if (!disallowIntercept) {
                        intercepted = onInterceptTouchEvent(ev);
                        ev.setAction(action); // restore action in case it was changed
                    } else {
                        intercepted = false;
                    }
                } else {
                    // There are no touch targets and this action is not an initial down
                    // so this view group continues to intercept touches.
                    intercepted = true;
                }
    
                // If intercepted, start normal event dispatch. Also if there is already
                // a view that is handling the gesture, do normal event dispatch.
                if (intercepted || mFirstTouchTarget != null) {
                    ev.setTargetAccessibilityFocus(false);
                }
    
                // Check for cancelation.
                final boolean canceled = resetCancelNextUpFlag(this)
                        || actionMasked == MotionEvent.ACTION_CANCEL;
    
                // Update list of touch targets for pointer down, if needed.
                final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
                TouchTarget newTouchTarget = null;
                boolean alreadyDispatchedToNewTouchTarget = false;
                // Dispatch to touch targets.
                if (mFirstTouchTarget == null) {
                    // No touch targets so treat this as an ordinary view.
                    handled = dispatchTransformedTouchEvent(ev, canceled, null,
                            TouchTarget.ALL_POINTER_IDS);
                } else {
                    // 重点2 mFirstTouchTarget != null会走到这里
                    // Dispatch to touch targets, excluding the new touch target if we already
                    // dispatched to it.  Cancel touch targets if necessary.
                    TouchTarget predecessor = null;
                    TouchTarget target = mFirstTouchTarget;
                    while (target != null) {
                        final TouchTarget next = target.next;
                        if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                            handled = true;
                        } else {
                            final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                    || intercepted;
                            if (dispatchTransformedTouchEvent(ev, cancelChild,
                                    target.child, target.pointerIdBits)) {
                                handled = true;
                            }
                            if (cancelChild) {
                                if (predecessor == null) {
                                    mFirstTouchTarget = next;
                                } else {
                                    predecessor.next = next;
                                }
                                target.recycle();
                                target = next;
                                continue;
                            }
                        }
                        predecessor = target;
                        target = next;
                    }
                }
                
            }
            
            return handled;
        }
    
    
    

    上面的代码部分一共标注了两处重点。在重点一的地方,我们看到ViewGroup仍然可能会通过onInterceptTouchEvent方法对事件进行拦截。假设ViewGroup没有进行拦截。那么在重点二的地方,就会遍历mFirstTouchTarget链表中的节点,并且将事件分发给对应的view,但是注意的是此时分发的不一定的Move或者Up事件,有可能是Cancel事件,详细可以查看Android事件分发之ACTION_CANCEL机制及作用

    mFirstTouchTarget为什么是链表结构

    在上一小节可以看到,mFirstTouchTarget指向了可以处理事件的子view,但是直观上来说能够处理的子view只有一个,为什么会是一个链式结构呢?我们再通过源码看下mFirstTouchTarget的插入时机。

     if (actionMasked == MotionEvent.ACTION_DOWN
            || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
            || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
            // 。。。
            newTouchTarget = addTouchTarget(child, idBitsToAssign);
        }
    
    

    我们可以看到,在三种情况下,可能会走到addTouchTarget方法中。分别看下这三种情况:

    1.actionMasked == MotionEvent.ACTION_DOWN,这个就是我们所说的Down事件。
    2.actionMasked == MotionEvent.ACTION_HOVER_MOVE,这个是用于监听鼠标移动的事件。暂时忽略。
    3.split && actionMasked == MotionEvent.ACTION_POINTER_DOWN,重点来看下这个。MotionEvent.ACTION_POINTER_DOWN出现在多指触控时。第一根按下的手指触发ACTION_DOWN事件,之后按下的手指触发ACTION_POINTER_DOWN事件。
    所以当有多指进行触控的时候,addTouchTarget方法可能会被调用多次,mFirstTouchTarget以链式结构存储对应的view。
    下面我们用代码来验证一下我们的结论:

    
    import android.content.Context
    import android.support.constraint.ConstraintLayout
    import android.util.AttributeSet
    import android.util.Log
    import android.view.MotionEvent
    import java.lang.reflect.Field
    
    class CustomViewGroup @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
    ) : ConstraintLayout(context, attrs, defStyleAttr) {
        override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
            // Log.d("TAG", "${ev?.action}:CustomViewGroup onInterceptTouchEvent")
            return false
        }
    
        override fun onTouchEvent(event: MotionEvent?): Boolean {
            // Log.d("TAG", "${event?.action}:CustomViewGroup onTouchEvent")
            return true
        }
    
        override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
            super.dispatchTouchEvent(ev)
    
            ev?.let {
                if (it.action == MotionEvent.ACTION_DOWN) {
                    val count = getField(this, this.javaClass, "mFirstTouchTarget")
                    Log.d("TAG", "MotionEvent.ACTION_DOWN-->mFirstTouchTarget count:$count")
                }
                if ((it.action and MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
                    val count = getField(this, this.javaClass, "mFirstTouchTarget")
                    Log.d("TAG", "MotionEvent.ACTION_POINTER_DOWN-->mFirstTouchTarget count:$count")
                }
            }
    
            return true
        }
    
    
    }
    
    fun getField(
        obj: Any,
        clazz: Class<*>, fieldName: String
    ): Int? {
        if (clazz.superclass == null) {
            return null
        }
        var field: Field? = null
        try {
            field = clazz.getDeclaredField(fieldName)
        } catch (e: NoSuchFieldException) {
            return getField(obj, clazz.superclass, fieldName)
        }
    
        var nodeCount = 0
        val declaredClasses = clazz.declaredClasses
        declaredClasses.iterator().forEach {
            if (it.simpleName == "TouchTarget") {
                field.isAccessible = true
                var mTouchTarget = field.get(obj)
    
                var next = it.getDeclaredField("next")
                while (mTouchTarget != null) {
                    nodeCount++
                    mTouchTarget = next.get(mTouchTarget)
                }
            }
        }
    
        return nodeCount
    }
    
    

    上面是一段kotlin代码。实现了一个自定义ViewGroup,继承于ConstraintLayout,在覆盖的dispatchTouchEvent方法中,判断当前的事件是MotionEvent.ACTION_DOWN或者MotionEvent.ACTION_POINTER_DOWN时,通过反射拿到ViewGroup中"mFirstTouchTarget"属性对应的链表中节点的数量。

    我们看下布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <com.lee.myapplication.CustomViewGroup
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/parent"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">
    
        <Button
                android:layout_width="160dp"
                android:layout_height="126dp"
                android:text="button1"
                android:gravity="center"
                android:textColor="#FFF"
                android:background="#8cFF0000"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                android:id="@+id/button1"
                android:layout_marginBottom="8dp" app:layout_constraintBottom_toTopOf="@+id/button2"/>
    
    
        <Button
                android:layout_width="159dp"
                android:layout_height="142dp"
                android:text="button2"
                android:gravity="center"
                android:textColor="#FFF"
                android:background="#8c00FF00"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                android:id="@+id/button2"
        />
        <Button
                android:layout_width="159dp"
                android:layout_height="142dp"
                android:text="button3"
                android:gravity="center"
                android:textColor="#FFF"
                android:background="#8c0000FF"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                android:id="@+id/button3"
                android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/button2"/>
    
    </com.lee.myapplication.CustomViewGroup>
    
    

    其实就是我们自定义的ViewGroup中有三个Button,效果图如下:

    ViewGroup中有三个Button
    假设我们现在点击button1,查看Log:

    04-23 13:51:30.017 13325-13325/com.lee.myapplication D/TAG: MotionEvent.ACTION_DOWN–>mFirstTouchTarget count:1

    假设我们先点击了button1,然后手指不松开,又点击了button2:

    04-23 13:52:55.599 13325-13325/com.lee.myapplication D/TAG: MotionEvent.ACTION_DOWN–>mFirstTouchTarget count:1
    04-23 13:52:56.217 13325-13325/com.lee.myapplication D/TAG: MotionEvent.ACTION_POINTER_DOWN–>mFirstTouchTarget count:2

    假设我们先点击了button1,手指不松开又点击了button2,手指不松开最后点击了button3:

    04-23 13:53:56.316 13325-13325/com.lee.myapplication D/TAG: MotionEvent.ACTION_DOWN–>mFirstTouchTarget count:1
    04-23 13:53:56.763 13325-13325/com.lee.myapplication D/TAG: MotionEvent.ACTION_POINTER_DOWN–>mFirstTouchTarget count:2
    04-23 13:53:57.292 13325-13325/com.lee.myapplication D/TAG: MotionEvent.ACTION_POINTER_DOWN–>mFirstTouchTarget count:3

    通过上面的结论,验证了"mFirstTouchTarget"的链式结构。

    展开全文
  • @Action指定一个类为action,相应配置文件里的<action>....</action>标签,当中能够配置例如以下属性 results:配置返回的结果集属性,相当于struts2中的列表,能够在{}中配置属性,详细例如以下 value:配置...
  • ROS入门之——action

    万次阅读 多人点赞 2018-08-13 15:12:10
    action实现了一种类似于service的请求/响应通讯机制,区别在于action带有反馈机制,用来不断向客户端反馈任务的进度,并且还支持在任务中途中止运行。 操作起来就像这样子,客户端给服务端抛出一个目标,然后...
  • Oozie中workflow的构成及mapreduce、hive、sqoop、shell action的配置运行。
  • Struts2系列(二)Action

    千次阅读 2018-07-16 16:58:57
    一.Action是Struts2的核心,所有用户请求都需要使用Action处理并返回数据。 二.Action测试代码 直接上代码,具体解释见代码注释 代码演示内容:创建ActionAction默认值,Action获取参数 HelloAction.java ...
  • SOAP Action介绍

    千次阅读 2019-11-26 16:02:00
    SOAP Action  据说SOAP Action header在SOAP 1.1规范中着实让不少开发者喝了一壶。真的有那么难理解么?我想这应该是因为SOAP有两种非常不同的使用方式,而SOAP Action主要是被用来支持其中应用较少的一种方式的...
  • iText API + iText in Action 2nd Edition + 中文帮助

    千次下载 热门讨论 2011-09-09 09:11:49
    iText API - 官方帮助文档 iText in Action 2nd Edition - 官方指定教材 iText中文帮助 - 网友提供简单的iText介绍
  • Odoo中的五种Action详解

    千次阅读 2019-06-24 18:17:26
    Odoo中的五种Action详解 Odoo中的五种action都是继承自ir.actions.actions模型实现的子类,共有五种。分别对应五种类型、五种用途。 odoo中还有其他含有action命名的模型,诸如:action.todo等,都不是actions的...
  • C++/CLI in Action

    热门讨论 2008-10-10 17:08:56
    C++/CLI in Action shows you how to bridge the gap between your existing C++ code and the .NET platform. C++/CLI in Action will help you if: You’re hesitant to migrate to .NET because it means ...
  • Android事件分发之ACTION_CANCEL机制及作用

    千次阅读 多人点赞 2019-04-23 14:11:04
    如果要查看ACTION_MOVE与ACTION_UP的事件传递机制,查看Android事件分发之ACTION_MOVE与ACTION_UP的传递机制 ACTION_CANCEL产生场景
  • Intent的中文意思是“意图,目的”的意思,可以理解为不同组件之间通信... Intent在由以下几个部分组成:动作(action),数据(data),分类(Category),类型(Type),组件(Component),和扩展信息(Extr...
  • action . ConfigurationAction - debug attribute not set 09 : 00 : 17 , 153 | - INFO in ch . qos . logback . core . joran . util . ConfigurationWatchListUtil @78e16155 - Adding [ jar : file : /...
  • GitHub Action开启自动化

    千次阅读 2019-11-16 14:23:18
    这周突然发现公司项目的GitHub代码库多了一个Tab页,叫做GitHub Action。于是了解了一下,发现这就是大家期待已久的自动化功能。自动化不只是我们常说的CICD(持续集成和持续部署),通过定义一个yml文件我们可以...
  • Spring.Boot.in.Action.2015.12.pdf

    千次下载 热门讨论 2016-01-05 21:34:59
    Spring.Boot.in.Action.2015.12.pdf
  • android.intent.action大全和用法收集

    千次阅读 2017-04-13 18:26:42
    收集学习一下android.intent.action的使用: 1.从google搜索内容  Intent intent = new Intent();  intent.setAction(Intent.ACTION_WEB_SEARCH);  intent.putExtra(SearchManager.QUERY,"searchString") ...
  • 如果你需要启动一个你自己写的另一个app的activity,你可以在那个的menifest.xml里自定义activity的action: < activity  android:name =" .MainActivity "  android:label =" @string/app_name " ...
  • 随着深度学习技术的发展,以及计算能力的进步(GPU等),现在基于视频的...这个方向的主要目标是判断一段视频中人的行为的类别,所以也可以叫做Human Action Recognition。虽然这个问题是针对视频中人的动作,但基...
  • C# Action和Func的用法详解

    千次阅读 多人点赞 2019-01-25 10:24:52
    以前我都是通过定义一个delegate来写委托的,但是最近看一些外国人写的源码都是用action和func方式来写,当时感觉对这很陌生所以看起源码也觉得陌生,所以我就花费时间来学习下这两种方式,然后发现确实代码简洁了...
  • 在博客园看到一篇写的比较详细文章,转来分享下,也便于自己用到时查询 1 Intent.ACTION_MAIN String: android.intent.action.MAIN 标识Activity为一个程序的开始。比较常用。 Input:nothing Output:nothing Xml...
  • Struts2中的Action详解

    万次阅读 2016-07-12 22:36:38
    1.Action的由来  struts2框架中有一种java类叫做Action,这种说法就像J2EE项目中有一种java类加Servlet.(人们的生活中有一种人的职业叫警察)struts2框架底层还是封装了Servlet的相关内容而实现出来的.只不过struts2...
  • vuex 中在一个action方法中调用另一个action方法 场景:常用于登录以后拉取用户信息 1.页面加载完成判断是否保存token,有则直接拉取用户信息 2.当用户第一次登录时,登录调取vuex中action中登录方法,成功登录以后调取...
  • 本篇文章主要的介绍了关于HTML中form标签action属性的用法介绍和实例,还有关于form标签的action属性的定义和语法介绍,最后徐还有关于form标签的action属性的作用解释。现在让我们一起来看吧。 打造全网web前端...
  • Docker.in.Action.2016.3.pdf

    千次下载 热门讨论 2016-03-18 12:21:30
    Docker.in.Action.2016.3.pdf英文版
  • 从上一篇《AndroidStudio插件开发(Hello World篇)》中我们已经大致了解了Action,这篇文章继续深入探究IntelliJ IDEA插件开发中的Action机制。一个Action本质上来说就是一个Java类,并且这个类需要继承AnAction。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,822,677
精华内容 729,070
关键字:

action