www.91084.com

GVKun编程网logo

自定义插件 wordpress 更改数据库(wordpress自定义数据表)

4

如果您想了解自定义插件wordpress更改数据库的相关知识,那么本文是一篇不可错过的文章,我们将对wordpress自定义数据表进行全面详尽的解释,并且为您提供关于Airflow自定义插件,使用da

如果您想了解自定义插件 wordpress 更改数据库的相关知识,那么本文是一篇不可错过的文章,我们将对wordpress自定义数据表进行全面详尽的解释,并且为您提供关于Airflow 自定义插件,使用 datax 抽数、Android gradle 自定义插件、ckeditor 自定义插件,添加的按钮不显示、ckeditor4 自定义插件 多图上传 图片上传的有价值的信息。

本文目录一览:

自定义插件 wordpress 更改数据库(wordpress自定义数据表)

自定义插件 wordpress 更改数据库(wordpress自定义数据表)

如何解决自定义插件 wordpress 更改数据库

我创建了一个插件,我需要创建一个新表并更新一个现有的表

我有这个代码,错误数据库没有更新,我可以看到它随着插件版本的更新进入了方法

我已经尝试过这个选项,但它仍然不起作用; Wordpress dbDelta not functioning

如果我在 PHPmyadmin 中运行查询,它会起作用

  1. add_action( ''plugins_loaded'',''plugin_update'' );
  2. function plugin_update() {
  3. $version = get_option( ''my_plugin_version'',''1.0'' );
  4. if ( version_compare( $version,''1.1'' ) < 0 ) {
  5. plugin_updates();
  6. }
  7. }
  8. function plugin_updates() {
  9. global $wpdb;
  10. $table_name = $wpdb->prefix . ''member'';
  11. require_once( ABSPATH . ''wp-admin/includes/upgrade.PHP'' );
  12. $sql = "ALTER TABLE $table_name
  13. MODIFY COLUMN nameMember varchar(255)";
  14. dbDelta( $sql );
  15. update_option( ''my_plugin_version'',''1.1'' );
  16. }

Airflow 自定义插件,使用 datax 抽数

Airflow 自定义插件,使用 datax 抽数

Airflow 自定义插件

Airflow 之所以受欢迎的一个重要因素就是它的插件机制。Python 成熟类库可以很方便的引入各种插件。在我们实际工作中,必然会遇到官方的一些插件不足够满足需求的时候。这时候,我们可以编写自己的插件。不需要你了解内部原理,甚至不需要很熟悉 Python, 反正我连蒙带猜写的。

插件分类

Airflow 的插件分为 Operator 和 Sensor 两种。Operator 是具体要执行的任务插件, Sensor 则是条件传感器,当我需要设定某些依赖的时候可以通过不同的 sensor 来感知条件是否满足。

Airflow 对插件提供的支持

插件肯定是 Python 文件了,系统必然需要加载才能执行。Airflow 提供了一个简单插件管理器,会扫描 $AIRFLOW_HOME/plugins 加载我们的插件。

所以,我们只需要将写好的插件放入这个目录下就可以了。

插件语法

Operator 和 Sensor 都声明了需要的参数,Operator 通过调用 execute 来执行, sensor 通过 poke 来确认。以 Operator 为例子。

插件的使用过程为:

dag  -> operator -> hook

Hook 就是任务执行的具体操作了。

Operator 通过继承 BaseOperator 实现对 dag 相关属性的绑定, Hook 通过继承 BaseHook 实现对系统配置和资源获取的一些封装。

自定义一个通知插件 NotifyOperator

前文 https://www.cnblogs.com/woshimrf/p/airflow-dag.html 提到我们通过自定义通知实现多功能任务告警,以下就是一个 demo。

文件结构如下:

plugins
│   ├── hooks
│   └── operators

NotifyOperator

首先,在 operators 目录下创建一个 Operator.

# -*- coding: utf-8 -*-
#

from hooks.notify_hook import NotifyHook
from airflow.operators.bash_operator import BaseOperator

class NotifyOperator(BaseOperator):
    """
    使用通知服务发送通知

    :param message: 内容
    :type message: str or dict
    :param receivers: 英文逗号分割的罗盘账号
    :type receivers: str
    :param subject: 邮件主题
    :type subject: str
    """
    template_fields = (''message'', ''subject'')

    @apply_defaults
    def __init__(self,
                 subject=None,
                 message=None,
                 receivers=None,
                 *args,
                 **kwargs):
        super().__init__(*args, **kwargs)
        self.message = message
        self.receivers = receivers
        self.subject = subject

    def execute(self, context):
        self.log.info(''Sending notify message. receivers:{}  message:{}''.format(self.receivers, self.message))
        hook = NotifyHook(
            subject=self.subject,
            message=self.message,
            receivers=self.receivers
        )
        hook.send()
  • 继承 BaseOperator
  • 引入 NotifyHook, 这个还没创建,等下创建
  • template_fields, 想要使用模板变量替换,比如 {{ds}}, 字段必须声明到 template_fields
  • Operator 执行的时候会调用 execute 方法, 这个就是执行的内容

上面可以看出,operator 就是接口声明。

NotifyHook

在 hooks 目录下创建 NotifyHook

# -*- coding: utf-8 -*-
#

import json
import requests
from airflow import AirflowException
from airflow.hooks.http_hook import HttpHook

class NotifyHook(HttpHook):
    """
    使用通知服务发送通知

    :param send_type: 通知类型选填 MAIL,DINGDING,SMS,选填多个时中间用英文逗号隔开
    :type send_type: str
    :param message: 内容
    :type message: str or dict
    :param receivers: 英文逗号分割的账号
    :type receivers: str
    :param subject: 邮件主题
    :type subject: str
    """

    def __init__(self,
                 notify_conn_id=''notify_default'',
                 send_type=''MAIL'',
                 subject=None,
                 message=None,
                 receivers=None,
                 *args,
                 **kwargs
                 ):
        super().__init__(http_conn_id=notify_conn_id, *args, **kwargs)
        self.send_type = send_type
        self.message = message
        self.subject = subject
        self.receivers = receivers


    def _build_message(self):
        """
        构建data
        """
        data = {
                "content": self.message,
                "contentType": "HTML",
                "receivers": self.receivers,
                "sendType": self.send_type,
                "sender": ''【Airflow】'',
                "subject": ''【Airflow】'' + self.subject
            }
        return json.dumps(data)

    def get_conn(self, headers=None):
        """
        Overwrite HttpHook get_conn because just need base_url and headers and
        not don''t need generic params

        :param headers: additional headers to be passed through as a dictionary
        :type headers: dict
        """
        self.base_url = ''http://notify.ryan-miao.com''
        session = requests.Session()
        if headers:
            session.headers.update(headers)
        return session

    def send(self):
        """
        Send Notify message
        """
        data = self._build_message()
        self.log.info(''Sending message: %s'',  data)
        resp = self.run(endpoint=''/api/v2/notify/send'',
                        data=data,
                        headers={''Content-Type'': ''application/json'',
                                 ''app-id'': ''ryan'',
                                 ''app-key'': ''123456''})
        if int(resp.json().get(''retCode'')) != 0:
            raise AirflowException(''Send notify message failed, receive error ''
                                   ''message %s'', resp.text)
        self.log.info(''Success Send notify message'')
  • 这里使用的我自己的通知服务 api 调用。因为是 http 请求,所以直接继承 HttpHook 来发送请求就可以了。
  • http_conn_id 是用来读取数据库中 connection 里配置的 host 的,这里直接覆盖,固定我们通知服务的地址。
  • 通过抛出异常的方式来终止服务

如何使用

将上面两个文件放到 airflow 对应的 plugins 目录下, airflow 就自动加载了。然后,当做任务类型使用

from operators.notify_operator import NotifyOperator

notification = NotifyOperator(
                    task_id="we_are_done",
                    subject=''发送邮件'',
                    message=''content'',
                    receivers=''ryanmiao''
                )

也可以直接执行。比如,我们前面提到任务失败告警可以自定义通知。

from operators.notify_operator import NotifyOperator

def mail_failure_callback(receivers):
    """
    失败后邮件通知
    :receivers  接收人,多个接收人用英文逗号分开
    """

    def mail_back(context):
        subject="【执行失败】DAG {} TASK {} ds {}".format(
                                                context[''task_instance''].dag_id,
                                                context[''task_instance''].task_id,
                                                context[''ds''])
        message="【执行失败】DAG:  {};<br\> TASK:  {} <br\>; ds {} <br\>;   原因: {} .<br\>" \
                "查看地址: http://airflow.ryan-miao.com/admin/airflow/tree?dag_id={}" \
                .format(
                context[''task_instance''].dag_id,
                context[''task_instance''].task_id,
                context[''ds''],
                context[''exception''],
                context[''task_instance''].dag_id)
            
        return NotifyOperator(
                    task_id="mail_failed_notify_callback",
                    subject=subject,
                    message=message,
                    receivers=receivers
                ).execute(context)

    return mail_back

default_args = {
    ''owner'': ''ryanmiao'',
    ''depends_on_past'': False,
    ''start_date'': datetime(2019, 5, 1, 9),
    ''on_failure_callback'': mail_failure_callback(receivers=''ryanmiao''),
    ''retries'': 0
}

dag = DAG(
    ''example'', default_args=default_args, schedule_interval=None)

自定义一个 RDBMS2Hive 插件

我们任务调度有个常见的服务是数据抽取到 Hive,现在来制作这个插件,可以从关系数据库中读取数据,然后存储到 hive。这样,用户只要在 airflow 配置一下要抽数的 database, table 和目标 hive table 就可以实现每天数据入库了。

异构数据传输转换工具很多, 最简单的就是使用原生的 dump 工具,将数据 dump 下来,然后 import 到另一个数据库里。

比如 postgres dump

${sql} 查询的列导出到文件 ${export_data_file}

psql -h$SRC_HOST_IP -U$SRC_USER_NAME -d$SRC_DB -p$SRC_HOST_PORT -c  "\copy (${sql}) to ''${export_data_file}'' WITH NULL AS ''''"

然后导入 hive

LOAD DATA LOCAL INPATH ''${export_data_file}'' INTO TABLE $TAR_TABLENAME PARTITION (BIZDATE=''$BIZ_DATE'')

对 postgres 来说,copy 是最快的方案了, 但可能会遇到 \t,\n 等各种转义符号,导出的 txt 文件或者 cvs 文件格式就会混乱,需要做对应符号转义处理。

同样, mysql 可以直接把数据查询出来

cat search.sql | mysql -h"$SRC_HOST_IP" -u"$SRC_USER_NAME" -p"$SRC_USER_PWD" -P"$SRC_HOST_PORT" -D"$SRC_DB" --default-character-set=${mysql_charset} -N -s | sed "s/NULL/\\\\N/ig;s/\\\\\\\\n//ig" > result.txt

上述这些命令行的好处就是快,不好的地方在于 shell 命令的脆弱性和错误处理。最终,选择了集成化的数据转换工具 datax. datax 是阿里巴巴开源的一款异构数据源同步工具, 虽然看起来不怎么更新了,但简单使用还是可以的。https://github.com/alibaba/DataX

datax 的用法相对简单,按照文档配置一下读取数据源和目标数据源,然后执行调用就可以了。可以当做命令行工具来使用。

结合 airflow,可以自己实现 datax 插件。通过读取 connections 拿到数据源链接配置,然后生成 datax 的配置文件 json,最后调用 datax 执行。下面是一个从 pg 或者 mysql 读取数据,导入 hive 的插件实现。

主要思路是:

  1. hdfs 创建一个目录
  2. 生成 datax 配置文件
  3. datax 执行配置文件,将数据抽取到 hdfs
  4. hive 命令行 load hdfs

RDBMS2HiveOperator

# -*- coding: utf-8 -*-
#
#

"""
postgres或者mysql 入库到hdfs
"""
import os
import signal

from hooks.rdbms_to_hive_hook import RDBMS2HiveHook
from airflow.exceptions import AirflowException
from airflow.models import BaseOperator


class RDBMS2HiveOperator(BaseOperator):
    """
    传输pg到hive
    https://github.com/alibaba/DataX

    :param conn_id: pg连接id
    :param query_sql : pg查询语句
    :param split_pk  : pg分割主键, NONE表示不分割,指定后可以多线程分割,加快传输
    :param hive_db   : hive的db
    :param hive_table: hive的table
    :param hive_table_column  column数组, column={name:a, type: int} 或者逗号分割的字符串, column=a,b,c
    :param hive_table_partition 分区bizdate值
    """
    template_fields = (''query_sql'',  ''hive_db'', ''hive_table'',''hive_table_partition'')
    ui_color = ''#edd5f1''

    @apply_defaults
    def __init__(self,
                 conn_id,
                 query_sql,
                 hive_db,
                 hive_table,
                 hive_table_column,
                 hive_table_partition,
                 split_pk=None,
                 *args,
                 **kwargs):
        super().__init__(*args, **kwargs)
        self.conn_id = conn_id
        self.query_sql = query_sql
        self.split_pk = split_pk
        self.hive_db = hive_db
        self.hive_table = hive_table
        self.hive_table_column = hive_table_column
        self.hive_table_partition = hive_table_partition
        

    def execute(self, context):
        """
        Execute 
        """
        task_id = context[''task_instance''].dag_id + "#" + context[''task_instance''].task_id

        self.hook = RDBMS2HiveHook(
                        task_id = task_id,
                        conn_id = self.conn_id,
                        query_sql = self.query_sql, 
                        split_pk=self.split_pk, 
                        hive_db=self.hive_db, 
                        hive_table=self.hive_table,
                        hive_table_column=self.hive_table_column,
                        hive_table_partition=self.hive_table_partition
                        )
        self.hook.execute(context=context)

        
    def on_kill(self):
        self.log.info(''Sending SIGTERM signal to bash process group'')
        os.killpg(os.getpgid(self.hook.sp.pid), signal.SIGTERM)

RDBMS2HiveHook

# -*- coding: utf-8 -*-
#

"""
datax入库hive
"""
import subprocess
import uuid
import json
import os

from airflow.exceptions import AirflowException
from airflow.hooks.base_hook import BaseHook


class RDBMS2HiveHook(BaseHook):
    """
    Datax执行器 
    """

    def __init__(self, 
                 task_id,
                 conn_id,
                 query_sql,
                 hive_db,
                 hive_table,
                 hive_table_column,
                 hive_table_partition,
                 split_pk=None):
        self.task_id = task_id
        self.conn = self.get_connection(conn_id)
        self.query_sql = query_sql
        self.split_pk = split_pk
        self.hive_db = hive_db
        self.hive_table = hive_table
        self.hive_table_partition = hive_table_partition
        self.log.info("Using connection to: {}:{}/{}".format(self.conn.host, self.conn.port, self.conn.schema))
         
        self.hive_table_column = hive_table_column 
        if isinstance(hive_table_column, str):
            self.hive_table_column = []
            cl = hive_table_column.split('','') 
            for item in cl:
                hive_table_column_item = {
                    "name": item,
                    "type": "string"
                }
                self.hive_table_column.append(hive_table_column_item)

    
    def Popen(self, cmd, **kwargs):
        """
        Remote Popen

        :param cmd: command to remotely execute
        :param kwargs: extra arguments to Popen (see subprocess.Popen)
        :return: handle to subprocess
        """
        self.sp = subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            **kwargs)

        for line in iter(self.sp.stdout):
            self.log.info(line.strip().decode(''utf-8''))

        self.sp.wait()

        self.log.info("Command exited with return code %s", self.sp.returncode)

        if self.sp.returncode:
            raise AirflowException("Execute command failed")



    def generate_setting(self):
        """
         datax速度等设置
        """
        self.setting= {
            "speed": {
                 "byte": 104857600
            },
            "errorLimit": {
                "record": 0,
                "percentage": 0.02
            }
        }
        return self.setting
    
    def generate_reader(self):
        """
        datax reader
        """
        conn_type = ''mysql''
        reader_name = ''mysqlreader''
        if(self.conn.conn_type == ''postgres''):
            conn_type = ''postgresql''
            reader_name = ''postgresqlreader''
        
        self.jdbcUrl =  "jdbc:"+conn_type+"://"+self.conn.host.strip()+":"+str(self.conn.port)+"/"+ self.conn.schema.strip()
        self.reader =  {
            "name": reader_name,
            "parameter": {
                "username": self.conn.login.strip(),
                "password": self.conn.password.strip(),
                "connection": [
                    {
                        "querySql": [
                            self.query_sql
                        ],
                        "jdbcUrl": [
                            self.jdbcUrl
                        ]
                    }
                ]
            }
        }
        
        return self.reader

    def generate_writer(self):
        """
        datax hdafs writer
        """
        self.file_type = "text"
        self.hdfs_path = "/datax/"+self.hive_db+"/"+self.hive_table+"/"+self.hive_table_partition
        self.log.info("临时存储目录:{}".format(self.hdfs_path))
        self.writer = {
                    "name": "hdfswriter",
                    "parameter": {
                        "defaultFS": "hdfs://nameservice1",
                        "hadoopConfig": {
                            "dfs.nameservices": "nameservice1",
                            "dfs.ha.automatic-failover.enabled.nameservice1": True,
                            "ha.zookeeper.quorum": "bigdata2-prod-nn01.ryan-miao.com:2181,bigdata2-prod-nn02.ryan-miao.com:2181,bigdata2-prod-nn03.ryan-miao.com:2181",
                            "dfs.ha.namenodes.nameservice1": "namenode117,namenode124",
                            "dfs.namenode.rpc-address.nameservice1.namenode117": "bigdata2-prod-nn01.ryan-miao.com:8020",
                            "dfs.namenode.servicerpc-address.nameservice1.namenode117": "bigdata2-prod-nn01.ryan-miao.com:8022",
                            "dfs.namenode.http-address.nameservice1.namenode117": "bigdata2-prod-nn01.ryan-miao.com:50070",
                            "dfs.namenode.https-address.nameservice1.namenode117": "bigdata2-prod-nn01.ryan-miao.com:50470",
                            "dfs.namenode.rpc-address.nameservice1.namenode124": "bigdata2-prod-nn02.ryan-miao.com:8020",
                            "dfs.namenode.servicerpc-address.nameservice1.namenode124": "bigdata2-prod-nn02.ryan-miao.com:8022",
                            "dfs.namenode.http-address.nameservice1.namenode124": "bigdata2-prod-nn02.ryan-miao.com:50070",
                            "dfs.namenode.https-address.nameservice1.namenode124": "bigdata2-prod-nn02.ryan-miao.com:50470",
                            "dfs.replication": 3,
                            "dfs.client.failover.proxy.provider.nameservice1": "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider"
                        },
                        "fileType": self.file_type,
                        "path": self.hdfs_path,
                        "fileName": self.task_id,
                        "column": self.hive_table_column,
                        "writeMode": "nonConflict",
                        "fieldDelimiter": "\t"
                    }
            }
        return self.writer

    def generate_config(self):
        content = [{
            "reader": self.generate_reader(),
            "writer": self.generate_writer()
        }]

        job = {
            "setting": self.generate_setting(),
            "content": content
        }

        config = {
            "job": job
        }

        self.target_json = json.dumps(config)

        # write json to file
        self.json_file= ''/tmp/datax_json_''+self.task_id+ uuid.uuid1().hex
        # 打开一个文件
        fo = open(self.json_file, "w")
        fo.write(self.target_json)
        fo.close()
        self.log.info("write config json {}".format(self.json_file))
        return self.json_file

    

    def execute(self, context):
        self.generate_config()
        # check hdfs_path
        hdfs_path = self.hdfs_path
        if(not hdfs_path.startswith(''/datax/'')):
            raise AirflowException("hdfs路径填写错误,不在/datax目录下")

        # 创建目录
        cmd = [''hadoop'', ''fs'', ''-mkdir'', ''-p'', hdfs_path]
        self.Popen(cmd)

        # 删除文件
        if(not hdfs_path.startswith(''/datax/'')):
            raise AirflowException("hdfs路径填写错误,不在/datax目录下")
        
        files_path = hdfs_path+"/*";
        try:
            cmd = [''hadoop'', ''fs'', ''-rm'', files_path]
            self.Popen(cmd)
        except Exception:
            self.log.info(''ignore err, just make sure the dir is clean'')
            pass
        
        
        # 上传文件
        datax_home = ''/data/opt/datax/bin''
        cmd = [ ''python'', datax_home + ''/datax.py'', self.json_file]
        self.Popen(cmd)
        # 删除配置文件
        os.remove(self.json_file)

        # hive加载
        #hive load data from hdfs
        hql = "LOAD DATA INPATH ''"+ hdfs_path + "'' OVERWRITE  INTO TABLE " \
              + self.hive_db+"."+self.hive_table + " PARTITION (bizdate="+ self.hive_table_partition  +")"
        cmd = [''hive'', ''-e'', "\"" + hql + "\""]
        self.Popen(cmd)

如何使用

  1. admin 登录 airflow
  2. 配置 connection, 配置 pg 或者 mysql 的数据库
  3. 修改 hdfs 集群配置信息
  4. 创建一个 DAG
from airflow import DAG

from operators.rdbms_to_hive_operator import RDBMS2HiveOperator
from datetime import datetime, timedelta
from dag_utils import compass_utils


default_args = {
    ''owner'': ''ryanmiao'',
    ''depends_on_past'': False,
    ''start_date'': datetime(2019, 5, 1, 9),
    ''on_failure_callback'': compass_utils.failure_callback(dingding_conn_id=''dingding_bigdata'', receivers=''ryanmiao''),
    # ''on_success_callback'': compass_utils.success_callback(dingding_conn_id=''dingding_bigdata'', receivers=''ryanmiao''),
    ''retries'': 0
}

dag = DAG(
    ''example_pg2hive'', default_args=default_args, schedule_interval=None)

# CREATE TABLE test.pg2hive_test(
#      ftime int,
#      raw_cp_count int,
#      raw_to_delete_cp_count bigint,
#      create_time timestamp
#      )
#  COMMENT ''这个是测试datax表''
#  PARTITIONED BY (bizdate int)
# ROW FORMAT DELIMITED
# FIELDS TERMINATED BY ''\t''
# LINES TERMINATED BY ''\n''
#  STORED AS TEXTFILE;
 
hive_table_column = "ftime,raw_cp_count,raw_to_delete_cp_count,create_time"

t1 = RDBMS2HiveOperator(
    task_id=''pg2hive'',
    conn_id=''pg_rdb_poi'',
    query_sql=''select ftime, raw_cp_count, raw_to_delete_cp_count, create_time from tbl_poi_report limit 1000'',
    hive_db=''test'',
    hive_table=''pg2hive_test'',
    hive_table_column=hive_table_column,
    hive_table_partition="{{ ds_nodash }}",
    dag=dag
)

原文出处:https://www.cnblogs.com/woshimrf/p/airflow-plugin.html

Android gradle 自定义插件

Android gradle 自定义插件

Gradle 的插件有三种打包方式:

  • 构建脚本:插件逻辑写在 build.gradle 中,适用于逻辑简单的任务,但是该方式实现的插件在该构建脚本之外是不可见的,只能用于当前脚本。
  • buildSrc项目:根据语言插件代码放在 rootProjectDir/buildSrc/src/main/groovy 目录中或者 rootProjectDir/buildSrc/src/main/java 或者rootProjectDir/buildSrc/src/main/kotlin,该插件对于该项目中构建使用的每个构建脚本都是可见的,适用于逻辑复杂但又不需要对外可见的插件

  • 独立项目:一个独立的 Groovy/Java/Kotlin 项目,该项目生成并发布一个 jar 然后可以在多个版本中使用它并与他人共享,并且此 jar 包可能包含一些插件,或者将多个相关的任务类捆绑到单个库中

接下来我们先从基本的构建脚本中的插件开始 

1、构建脚本:

创建 Gradle 插件,需要创建一个实现 Plugin 接口的类,当插件应用于项目时,Gradle 会创建插件类的实例并调用实例的 apply() 方法,如果需要的话,插件还可以用于配置项目

把插件变得可配置,添加一些简单扩展属性到 project 上,比如可以添加一个 greeting 扩展对象到 project 上,这就允许我们去配置 greeting 对象。

文件是 build.gradle:

class GreetingPluginExtension {
    String message = ''Hello from GreetingPlugin''
}

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        // Add the ''greeting'' extension object
        def extension = project.extensions.create(''greeting'',GreetingPluginExtension)
         Add a task that uses configuration from the extension object
        project.task(''hello'') {
            doLast {
                println extension.message
            }
        }
    }
}

apply plugin: GreetingPlugin

 Configure the extension
greeting.message = ''Hi from Gradle''

2、独立项目 

创建Gradle Module

AndroidStudio中是没有新建类似Gradle Plugin这样的选项的,那我们如何在AndroidStudio中编写Gradle插件,并打包出来呢?

  1. 首先,你得新建一个Android Project

  2. 然后再新建一个 Module,这个 Module用于开发 Gradle 插件,同样,Module 里面没有 gradle plugin 给你选,但是我们只是需要一个“容器”来容纳我们写的插件,因此,你可以随便选择一个 Module 类型(如 Phone & Tablet Module 或 Android Librarty ),因为接下来一步我们是将里面的大部分内容删除,所以选择哪个类型的 Module 不重要。

  3. 将 Module 里面的内容删除,只保留 build.gradle 文件和 src/main 目录。

  4. 由于 gradle 是基于 groovy,因此,我们开发的 gradle 插件相当于一个 groovy 项目。所以需要在 main 目录下新建 groovy 目录

  5. groovy 又是基于Java,因此,接下来创建groovy的过程跟创建java很类似。在 groovy 新建包名,如:com.sjq.greetplugin,然后在该包下新建groovy文件,通过  new->file→GreetingPlugin.groovy 来新建名为 GreetingPlugin 的 groovy 文件。一定是要有.groovy 后缀文件。

  6. 为了让我们的 groovy 类申明为 gradle 的插件,新建的 groovy 需要实现 org.gradle.api.Plugin 接口。

如下所示:

package com.sjq.greetplugin   包名一定要有,不然打包不会打进对应包里去,很可能就会出现类找不到的情况。

import org.gradle.api.Plugin
 org.gradle.api.Project

 {
    @Override
     apply(Project project) {
        project.task("publishPluginTest"){  task 名字
            doLast {
                println("新内容是:null")
            }
        }
    }
}

定义好了自己的 gradle 插件类,接下来就是告诉 gradle,哪一个是我们自定义的插件类,因此,需要在 main 目录下新建 resources 目录,然后在resources 目录里面再新建 meta-inf 目录,再在 meta-inf 里面新建 gradle-plugins 目录。最后在 gradle-plugins 目录里面新建 properties 文件,注意这个文件的命名,你可以随意取名,但是后面使用这个插件的时候,会用到这个名字。比如,你取名为 com.sjq.gradle.properties,而在其他build.gradle 文件中使用自定义的插件时候则需写成: 

apply plugin: ''com.sjq.greetplugin''

然后在com.hc.gradle.properties文件里面指明你自定义的类

implementation-class=com.sjq.greetplugin.GreetingPlugin  注意后面跟的是插件名字,注意大小写

此处我们还是采用包名,方便记忆。

到这里后,整个目录结果如下图所示:

Android gradle 自定义插件 > image2020-4-7_20-42-55.png" src="http://wiki.baidu.com/download/attachments/1089400793/image2020-4-7_20-42-55.png?version=1&modificationDate=1586263375000&api=v2" alt="" height="400" data-image-src="/download/attachments/1089400793/image2020-4-7_20-42-55.png?version=1&modificationDate=1586263375000&api=v2" data-unresolved-comment-count="0" data-linked-resource-id="1097064836" data-linked-resource-version="1" data-linked-resource-type="attachment" data-linked-resource-default-alias="image2020-4-7_20-42-55.png" data-base-url="http://wiki.baidu.com" data-linked-resource-content-type="image/png" data-linked-resource-container-id="1089400793" data-linked-resource-container-version="6" data-location="沈嘉琪 > Android gradle 自定义插件 > image2020-4-7_20-42-55.png" data-image-height="843" data-image-width="727">

因为我们要用到 groovy 以及后面打包要用到 maven,所以在我们自定义的 Module 下的 build.gradle 需要添加如下代码:

apply plugin: ''groovy''
apply plugin: ''maven''

dependencies {
gradle sdk
compile gradleApi()
groovy sdk
compile localGroovy()
}

repositories {
mavenCentral()
}

打包到本地Maven

前面我们已经自定义好了插件,接下来就是要打包到 Maven 库里面去了,你可以选择打包到本地,或者是远程服务器中。在我们自定义 Module 目录下的 build.gradle 添加如下代码:

group和version在后面使用自定义插件的时候会用到
group=''com.hc.plugin''
version=''1.0.0''

uploadArchives {
    repositories {
        mavendeployer {
            提交到远程服务器:
            repository(url: "http://www.xxx.com/repos") {
                authentication(userName: "admin",password: "admin")
            }
            本地的Maven地址设置为../repos
            repository(url: uri(''../repos''))
        }
    }
}

其中,group 和 version 后面会用到,我们后面再讲。虽然我们已经定义好了打包地址以及打包相关配置,但是还需要我们让这个打包 task 执行。点击 AndroidStudio 右侧的 gradle 工具,如下图所示:

Android gradle 自定义插件 > image2020-4-7_20-49-41.png" src="http://wiki.baidu.com/download/attachments/1089400793/image2020-4-7_20-49-41.png?version=1&modificationDate=1586263781000&api=v2" alt="" height="400" data-image-src="/download/attachments/1089400793/image2020-4-7_20-49-41.png?version=1&modificationDate=1586263781000&api=v2" data-unresolved-comment-count="0" data-linked-resource-id="1097064946" data-linked-resource-version="1" data-linked-resource-type="attachment" data-linked-resource-default-alias="image2020-4-7_20-49-41.png" data-base-url="http://wiki.baidu.com" data-linked-resource-content-type="image/png" data-linked-resource-container-id="1089400793" data-linked-resource-container-version="6" data-location="沈嘉琪 > Android gradle 自定义插件 > image2020-4-7_20-49-41.png" data-image-height="472" data-image-width="322">

 

可以看到有 uploadArchives 这个 Task,双击 uploadArchives 就会执行打包上传啦!执行完成后,去我们的 Maven 本地仓库查看一下:

Android gradle 自定义插件 > image2020-4-7_20-50-48.png" src="http://wiki.baidu.com/download/attachments/1089400793/image2020-4-7_20-50-48.png?version=1&modificationDate=1586263848000&api=v2" alt="" height="400" data-image-src="/download/attachments/1089400793/image2020-4-7_20-50-48.png?version=1&modificationDate=1586263848000&api=v2" data-unresolved-comment-count="0" data-linked-resource-id="1097064963" data-linked-resource-version="1" data-linked-resource-type="attachment" data-linked-resource-default-alias="image2020-4-7_20-50-48.png" data-base-url="http://wiki.baidu.com" data-linked-resource-content-type="image/png" data-linked-resource-container-id="1089400793" data-linked-resource-container-version="6" data-location="沈嘉琪 > Android gradle 自定义插件 > image2020-4-7_20-50-48.png" data-image-height="468" data-image-width="532">

 

其中,com/sjq/greetplugin 这几层目录是由我们的 group 指定,myplugin 是模块的名称,1.0.0 是版本号( version 指定)。 

使用自定义的插件

接下来就是使用自定义的插件了,一般就是在 app 这个模块中使用自定义插件,因此在 app 这个 Module 的 build.gradle 文件中,需要指定本地 Maven 地址、自定义插件的名称以及依赖包名。简而言之,就是在 app 这个 Module 的 build.gradle 文件中后面附加如下代码:

buildscript {
    repositories {
        maven {本地Maven仓库地址
            url uri(''../repos'')
        }
    }
    dependencies {
        格式为-->group:module:version
        classpath ''com.sjq.greetplugin:GreetingPlugin:1.0.0''
    }
}
com.sjq.greetplugin 为 resources/meta-inf/gradle-plugins 下的 properties 文件名称
apply plugin: ''com.sjq.greetplugin''

在当前目录下输入命令行:

./gradlew -q publishPluginTest 

运行结果:

Android gradle 自定义插件 > image2020-4-8_12-6-9.png" src="http://wiki.baidu.com/download/attachments/1089400793/image2020-4-8_12-6-9.png?version=1&modificationDate=1586318769000&api=v2" alt="" width="597" height="191" data-image-src="/download/attachments/1089400793/image2020-4-8_12-6-9.png?version=1&modificationDate=1586318769000&api=v2" data-unresolved-comment-count="0" data-linked-resource-id="1097070807" data-linked-resource-version="1" data-linked-resource-type="attachment" data-linked-resource-default-alias="image2020-4-8_12-6-9.png" data-base-url="http://wiki.baidu.com" data-linked-resource-content-type="image/png" data-linked-resource-container-id="1089400793" data-linked-resource-container-version="6" data-location="沈嘉琪 > Android gradle 自定义插件 > image2020-4-8_12-6-9.png" data-image-height="165" data-image-width="516">

3、开发只针对当前项目的Gradle插件

前面我们讲了如何自定义 gradle 插件并且打包出去,可能步骤比较多。有时候,你可能并不需要打包出去,只是在这一个项目中使用而已,那么你无需打包这个过程。

只是针对当前项目开发的 Gradle 插件相对较简单。步骤之前所提到的很类似,只是有几点需要注意:

新建的 Module 名称必须为 BuildSrc,无需 resources 目录

Android gradle 自定义插件 > image2020-4-15_19-59-52.png" src="http://wiki.baidu.com/download/attachments/1089400793/image2020-4-15_19-59-52.png?version=1&modificationDate=1586951992000&api=v2" alt="" height="250" data-image-src="/download/attachments/1089400793/image2020-4-15_19-59-52.png?version=1&modificationDate=1586951992000&api=v2" data-unresolved-comment-count="0" data-linked-resource-id="1106572607" data-linked-resource-version="1" data-linked-resource-type="attachment" data-linked-resource-default-alias="image2020-4-15_19-59-52.png" data-base-url="http://wiki.baidu.com" data-linked-resource-content-type="image/png" data-linked-resource-container-id="1089400793" data-linked-resource-container-version="6" data-location="沈嘉琪 > Android gradle 自定义插件 > image2020-4-15_19-59-52.png" data-image-height="326" data-image-width="423">

其中,build.gradle 内容为:

 依赖groovy插件
plugins {
    id ''groovy''
}

dependencies {
    implementation gradleApi()
    implementation localGroovy()
}

CustomPluginB 内容是

package com.sjq.happy.myplugin

 在单独的groovy文件中定义插件类
class CustomPluginB  {

    @Override
     apply(Project project) {
        project.task(''CustomPluginTaskB'') {
            doFirst {
                println ''This is custom plugin TaskB''
            }
        }
    }
}

app这个Module中如何使用呢?直接在app的build.gradle下加入

import com.sjq.happy.myplugin.CustomPluginB 

在输入命令行:

 ./gradlew -q CustomPluginTaskB

结果如下图所示:

Android gradle 自定义插件 > image2020-4-15_20-3-50.png" src="http://wiki.baidu.com/download/attachments/1089400793/image2020-4-15_20-3-50.png?version=1&modificationDate=1586952230000&api=v2" alt="" height="84" data-image-src="/download/attachments/1089400793/image2020-4-15_20-3-50.png?version=1&modificationDate=1586952230000&api=v2" data-unresolved-comment-count="0" data-linked-resource-id="1106572703" data-linked-resource-version="1" data-linked-resource-type="attachment" data-linked-resource-default-alias="image2020-4-15_20-3-50.png" data-base-url="http://wiki.baidu.com" data-linked-resource-content-type="image/png" data-linked-resource-container-id="1089400793" data-linked-resource-container-version="6" data-location="沈嘉琪 > Android gradle 自定义插件 > image2020-4-15_20-3-50.png" data-image-height="84" data-image-width="528">

 

总结

以上是小编为你收集整理的Android gradle 自定义插件全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

ckeditor 自定义插件,添加的按钮不显示

ckeditor 自定义插件,添加的按钮不显示

plugins目录下:

上面这个是我自己照着官网案例写的,

plugin.js内容:

 CKEDITOR.plugins.add(''timestamp'', {
        icons: ''vi'',
        hidpi:true,
        init: function (editor) {
            editor.addCommand(''insertTimestamp'', {
                exec: function (editor) {
                  
                    editor.insertHtml(''hello world'');
                }
            });
           
            editor.ui.addButton(''Timestamp'',{
                label:''Timestamp'',
                command:''insertTimestamp'',
                toolbar:''insert,21''
            })
        }
    });

这个是打开的效果,红框那里确实是有我添加的按钮存在,就是不显示图标:

然后我在控制台看到的下面图上第一个红框是我在官网上下载flash插件,这个是正常,可以看到style样式中有图标路径,第二个红框就是我自己添加的一个按钮,那里只有sytle中并没有图标的路径:

我对照flash插件,然后把falsh中的其他部分都注释掉,最后flash和我自己写的插件中的plugin.js中的一样了,但是还是和上面的情况一样,这是个什么情况。。。。。。。。求jiao

ckeditor4 自定义插件 多图上传 图片上传

ckeditor4 自定义插件 多图上传 图片上传




参考资料:整合ueditor多图 http://blog.csdn.net/shunlongjin/article/details/24266665

自定义插件:http://www.cnblogs.com/lts8989/archive/2011/08/04/2127326.html

基础配置:http://blog.csdn.net/itmyhome1990/article/details/17264627

关于自定义插件 wordpress 更改数据库wordpress自定义数据表的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于Airflow 自定义插件,使用 datax 抽数、Android gradle 自定义插件、ckeditor 自定义插件,添加的按钮不显示、ckeditor4 自定义插件 多图上传 图片上传等相关内容,可以在本站寻找。

本文标签: