編輯:關於android開發
首先說明一下為什麼會有這篇文章。前段時間,插件化以及熱修復的技術很熱,Nuwa熱修復的工具NuwaGradle,攜程動態加載技術DynamicAPK,還有希望做最輕巧的插件化框架的Small。這三個App有一個共同的地方就是大量的使用了Gradle這個強大的構建工具,除了攜程的框架外,另外兩個都發布了獨立的Gradle插件提供自動化構建插件,或者生成熱修復的補丁。所以學習一下Gradle插件的編寫還是一件十分有意義的事。
Gradle的插件一般有這麼幾種:
一種是直接在項目中的gradle文件裡編寫,這種方式的缺點是無法復用插件代碼,在其他項目中還得復制一遍代碼(或者說說復制一遍文件) 另一種是在獨立的項目裡編寫插件,然後發布到中央倉庫,之後直接引用就可以了,優點就是可復用。就和上面的Nuwa和Small一樣。本篇文章不會詳細說明Gradle相關的語法,如果要學習gradle相關的東西,請查看Gradle for Android
Gradle插件是使用Groovy進行開發的,而Groovy其實是可以兼容Java的。Android Studio其實除了開發Android App外,完全可以勝任開發Gradle插件這一工作,下面來講講具體如何開發。
首先,新建一個Android項目。 之後,新建一個Android Module項目,類型選擇Android Library。 將新建的Module中除了build.gradle文件外的其余文件全都刪除,然後刪除build.gradle文件中的所有內容。 在新建的module中新建文件夾src,接著在src文件目錄下新建main文件夾,在main目錄下新建groovy目錄,這時候groovy文件夾會被Android識別為groovy源碼目錄。除了在main目錄下新建groovy目錄外,你還要在main目錄下新建resources目錄,同理resources目錄會被自動識別為資源文件夾。在groovy目錄下新建項目包名,就像Java包名那樣。resources目錄下新建文件夾META-INF,META-INF文件夾下新建gradle-plugins文件夾。這樣,就完成了gradle 插件的項目的整體搭建,之後就是小細節了。目前,項目的結構是這樣的。打開Module下的build.gradle文件,輸入
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies {
compile gradleApi()
compile localGroovy()
}
repositories {
mavenCentral()
}
下面我們在包名下新建一個文件,命名為PluginImpl.groovy,注意有groovy後綴,然後在裡面輸入,注意包名替換為你自己的包名。
package cn.edu.zafu.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
public class PluginImpl implements Plugin {
void apply(Project project) {
project.task('testTask') << {
println "Hello gradle plugin"
}
}
}
然後在resources/META-INF/gradle-plugins目錄下新建一個properties文件,注意該文件的命名就是你只有使用插件的名字,這裡命名為plugin.test.properties,在裡面輸入
implementation-class=cn.edu.zafu.gradle.PluginImpl
注意包名需要替換為你自己的包名。
這樣就完成了最簡單的一個gradle插件,裡面有一個叫testTask的Task,執行該task後會輸出一段文字,就像當初我們輸出HelloWorld一樣。
接著,我們需要將插件發布到maven中央倉庫,我們將插件發布到本地倉庫就好了,在module項目下的buidl.gradle文件中加入發布的代碼。
repositories {
mavenCentral()
}
group='cn.edu.zafu.gradle.plugin'
version='1.0.0'
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('../repo'))
}
}
}
上面的group和version的定義會被使用,作為maven庫的坐標的一部分,group會被作為坐標的groupId,version會被作為坐標的version,而坐標的artifactId組成即module名,我們讓其取一個別名moduleName。然後maven本地倉庫的目錄就是當前項目目錄下的repo目錄。
這時候,右側的gradle Toolbar就會在module下多出一個task
點擊uploadArchives這個Task,就會在項目下多出一個repo目錄,裡面存著這個gradle插件。
目錄就像上圖這樣,具體目錄結構和你的包名等一系列有關,time是我的module名。
發布到本地maven倉庫後,我們就使用它,在叫app的android項目下的gradle.build的文件中加入
buildscript {
repositories {
maven {
url uri('../repo')
}
}
dependencies {
classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
}
}
apply plugin: 'plugin.test'
apply plugin後面引號內的名字就是前文plugin.test.properties文件的文件名。而class path後面引號裡的內容,就是上面grade中定義的group,version以及moduleName所共同決定的,和maven是一樣的。
同步一下gradle,右側app下other分類下就會多出一個testTask,雙擊執行這個Task,控制台就會輸出剛才我們輸入的字符串
接下來我們將其發布到jcenter中央倉庫。參考之前寫的一篇文章 使用Android Studio將開源庫發布到Jcenter中央庫
在項目根目錄下的build.gradle文件中加入。
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0-beta6'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
classpath 'com.github.dcendents:android-maven-plugin:1.2'
}
在項目根路徑下新建bintray.gradle文件,輸入
apply plugin: 'com.jfrog.bintray'
apply plugin: 'maven-publish'
def projectName = "timePlugin"
def mavenDesc = 'your desc'
def baseUrl = 'https://github.com/yourBaseUrl'
def siteUrl = baseUrl
def gitUrl = "${baseUrl}/yourGitUrl"
def issueUrl = "${baseUrl}/yourGitIssueUrl"
def licenseIds = ['Apache-2.0']
def licenseNames = ['The Apache Software License, Version 2.0']
def licenseUrls = ['http://www.apache.org/licenses/LICENSE-2.0.txt']
def inception = '2016'
def username = 'lizhangqu'
install {
repositories {
mavenInstaller {
pom.project {
// Description
name projectName
description mavenDesc
url siteUrl
// Archive
groupId project.group
artifactId archivesBaseName
version project.version
// License
inceptionYear inception
licenses {
licenseNames.eachWithIndex { ln, li ->
license {
name ln
url licenseUrls[li]
}
}
}
developers {
developer {
name username
}
}
scm {
connection gitUrl
developerConnection gitUrl
url siteUrl
}
}
}
}
}
task sourcesJar(type: Jar) {
from sourceSets.main.allGroovy
classifier = 'sources'
}
task javadocJar(type: Jar, dependsOn: groovydoc) {
from groovydoc.destinationDir
classifier = 'javadoc'
}
artifacts {
archives javadocJar
archives sourcesJar
}
bintray {
user = BINTRAY_USER
key = BINTRAY_KEY
configurations = ['archives']
pkg {
repo = 'maven'
name = projectName
desc = mavenDesc
websiteUrl = siteUrl
issueTrackerUrl = issueUrl
vcsUrl = gitUrl
labels = ['gradle', 'plugin', 'time']
licenses = licenseIds
publish = true
publicDownloadNumbers = true
}
}
將對應的描述性文字修改為你自己的信息,尤其是最前面的一系列的def定義,然後在gradle.properties文件中加入BINTRAY_USER和BINTRAY_KEY。
在你的module中apply該grade文件
apply from: '../bintray.gradle'
右側的gradle的toolbar就會多出幾個task
之後我們先運行other下的install這個task,再執行bintrayUpload這個task,如果不出意外,就上傳了,之後不要忘記到後台add to jcenter。成功add到jcenter之後就會有link to jcenter的字樣
耐心等待add to center成功的消息,之後就可以直接引用了,將module下的gradle文件maven部分的定義
maven {
url uri('../repo')
}
前面加入
jcenter()
最終的內容如下
buildscript {
repositories {
jcenter()
maven {
url uri('../repo')
}
}
dependencies {
classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
}
}
apply plugin: 'plugin.test'
就是這麼簡單,再次運行一下測試下是否成功。
最佳實踐的來源是源自multidex,為什麼呢,因為最近當方法數超了之後,如果選擇multidex,編譯的過程就會慢很多很多,為了檢測到底是哪一步的耗時,需要編寫一個插件來統計各個task執行的時間,因此就有了這麼一個最佳實踐。
在PluginImpl同級目錄下新建TimeListener.groovy文件。輸入
package cn.edu.zafu.gradle
import org.gradle.BuildListener
import org.gradle.BuildResult
import org.gradle.api.Task
import org.gradle.api.execution.TaskExecutionListener
import org.gradle.api.initialization.Settings
import org.gradle.api.invocation.Gradle
import org.gradle.api.tasks.TaskState
import org.gradle.util.Clock
class TimeListener implements TaskExecutionListener, BuildListener {
private Clock clock
private times = []
@Override
void beforeExecute(Task task) {
clock = new org.gradle.util.Clock()
}
@Override
void afterExecute(Task task, TaskState taskState) {
def ms = clock.timeInMs
times.add([ms, task.path])
task.project.logger.warn "${task.path} spend ${ms}ms"
}
@Override
void buildFinished(BuildResult result) {
println "Task spend time:"
for (time in times) {
if (time[0] >= 50) {
printf "%7sms %s\n", time
}
}
}
@Override
void buildStarted(Gradle gradle) {}
@Override
void projectsEvaluated(Gradle gradle) {}
@Override
void projectsLoaded(Gradle gradle) {}
@Override
void settingsEvaluated(Settings settings) {}
}
然後將PluginImpl文件中的apply方法修改為
void apply(Project project) {
project.gradle.addListener(new TimeListener())
}
完成後打包發布到jcenter()。之後你只要引用了該插件,就會統計各個task執行的時間,比如運行app,就會輸出像下面的信息。
最佳實踐的末尾,推廣一下這個插件,這個插件我已經將其發布到jcenter倉庫,如果要使用的話加入下面的代碼即可
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
}
}
apply plugin: 'plugin.time'
上面的是小試牛刀了下,接下來我們需要獲得自定義的參數。
首先按照上面的步驟新建一個module。新建PluginExtension.groovy,輸入
public class PluginExtension {
def param1 = "param1 defaut"
def param2 = "param2 defaut"
def param3 = "param3 defaut"
}
然後我們希望能傳入嵌套的參數,再新建一個PluginNestExtension.groovy,輸入
public class PluginNestExtension {
def nestParam1 = "nestParam1 defaut"
def nestParam2 = "nestParam2 defaut"
def nestParam3 = "nestParam3 defaut"
}
然後新建一個CustomTask.groovy,繼承DefaultTask類,使用 @TaskAction注解標注實現的方法
public class CustomTask extends DefaultTask {
@TaskAction
void output() {
println "param1 is ${project.pluginExt.param1}"
println "param2 is ${project.pluginExt.param2}"
println "param3 is ${project.pluginExt.param3}"
println "nestparam1 is ${project.pluginExt.nestExt.nestParam1}"
println "nestparam2 is ${project.pluginExt.nestExt.nestParam2}"
println "nestparam3 is ${project.pluginExt.nestExt.nestParam3}"
}
}
只是做了拿到了參數,然後做最簡單的輸出操作,使用 ${project.pluginExt.param1}和 ${project.pluginExt.nestExt.nestParam1}等拿到外部的參數。
別忘了在META-INF/gradle-plugins目錄下新建properties文件指定插件的接口實現類。
復制之前新建的PluginImpl.groovy到包下,修改apply方法
public class PluginImpl implements Plugin {
void apply(Project project) {
project.extensions.create('pluginExt', PluginExtension)
project.pluginExt.extensions.create('nestExt', PluginNestExtension)
project.task('customTask', type: CustomTask)
}
}
將插件發布到本地maven後,進行引用。
buildscript {
repositories {
maven {
url uri('../repo')
}
}
dependencies {
classpath 'cn.edu.zafu.gradle.plugin:test:1.0.0'
}
}
apply plugin: 'plugin.test'
定義外部參數,這裡我們定義了param1,param2,nestParam1,nestParam2,此外param3和nestParam3保持默認。
pluginExt {
param1 = 'app param1'
param2 = 'app param2'
nestExt{
nestParam1='app nestParam1'
nestParam2='app nestParam2'
}
}
同步一下gradle,執行customTask。
上面的代碼很簡單,不用解釋也能看到,所以不再解釋了。
在android上要實現類似Launch的抽屜效果,大家一定首先會想起SlidingDrawer。
導入arr包,導入arr提起項目的aar包 導入目標項目中 添加依賴
可展開的列表組件——ExpandableListView深入解析,expandablelist展開可展開的列表組件——ExpandableListView深入解析 一、知
第3章 用C#編寫百度地圖Android手機應用程序(第5講),分類:C#、Android; 日期:2016-02-04 3.5 示例5--多地圖展示 一、簡介 地圖控件