layout analyzerでのgroovyの利用について
TRANSCRIPT
LayoutAnalyzerでのGroovyの利用について
2011/04/09
@kimukou_26
自己紹介Kimukou_26 といいます
関東でフリーのSEしています
主に参加している勉強会JGGUG ・・日本Grails/Groovy ユーザーグループ
#jggug
ATEC ・・Android テスト部#android_tec
あまり貢献できてません。すみません><
今回は #yapf には勉強しにきたつもりが。。何故か発表する事にw
今回話す事になったきっかけ
CategoryAA() / B()
では解析してみましょう使っているツールは?Layoutopt
日本語解説サイト http://goo.gl/QCMij
本家解説サイト http://goo.gl/7cdt7
どんなツール?レイアウト.xmlの最適度チェックツール
あくまでナビゲーションだけで変更はしないよ
どこでGroovyを使っているの? XMLの解析Rule(GroovyRule)
Use Category(クラスに機能を後付けする機能)
対象クラス、Clousre
useスコープ内だと対象クラスでCategoryAの
ルール(処理)が使えるイメージ
use(CategoryA){
}
Groovyで実際使うとこんなイメージ
class ExcelCategory {
/** ファイルを読み込む */
static void readXls(File self, Closure yield){
//読込処理
}
/** ファイルを編集する */
static void editXls(File self, Closure yield){
//編集処理
}
}
use (ExcelCategory) {
new File('hogehoge.xls').readXls{ excelApp ->
// hogehoge.xlsに対する何かしらの処理 }
}
}
FileクラスにreadXls()
関数が追加されたイメージ
File self
の参照
どんな構成になっているの?
ANDROID_HOME/tools/
layoutopt.bat
lib/layoutpot.jar
lib/uix.jar(LayoutAnalyzer , LayoutAnalysis )
lib/groovy-all-1.7.0.jar
com.android.layoutopt.cli.Main
LayoutAnalyzer analyzer = new LayoutAnalyzer()
LayoutAnalyzer
loadRules
Rule(Interface)
GroovyRule(implements Rule)
実際の構造はどんな感じ?
処理の流れの抜粋
void main(String[] args)
static void analyzeFiles(File[] files)
LayoutAnalyzer analyzer = new LayoutAnalyzer();
static void analyze(LayoutAnalyzer analyzer, File file)
LayoutAnalysis analysis = analyzer.analyze(file);
com.android.layoutopt.cli.Main
Layoutopt.jar
uix.jar
com.android.layoutopt.uix.LayoutAnalyzer
LayoutAnalyzer()loadRules()
loadRule
com.android.layoutopt.uix.rules.GroovyRule
import com.android.layoutopt.uix.LayoutAnalysis;import com.android.layoutopt.uix.groovy.LayoutAnalysisCategory;
import groovy.lang.*;import groovy.xml.dom.DOMCategory;import java.util.*;import org.codehaus.groovy.runtime.GroovyCategorySupport;import org.w3c.dom.Node;
void loadRules()ClassLoader parent = getClass().getClassLoader();GroovyClassLoader loader = new GroovyClassLoader(parent);GroovyShell shell = new GroovyShell(loader);
void loadRule(GroovyShell shell, String name, InputStream stream) {Script script = shell.parse(stream);this.mRules.add(new GroovyRule(name, script));
}
GroovyShellを
初期化
public void addRule(Rule rule) があるので外からRule追加は可能
かと
public class GroovyRule implements Rule {private final String mName;private final Script mScript;private final Binding mBinding;private final Closure mClosure;private final List<Class> mCategories;
public GroovyRule(String name, Script script) {this.mName = name;this.mScript = script;this.mBinding = new Binding();this.mScript.setBinding(this.mBinding);
this.mClosure = new Closure(this) {public Object call() {
return GroovyRule.this.mScript.run();}
};
this.mCategories = new ArrayList();Collections.addAll(this.mCategories,
new Class[]{
DOMCategory.class,LayoutAnalysisCategory.class });
}
適応するCategoryクラス
を配列定義
実際の処理を行うClosure
Uix.jarRules/XX.rule
を読み込んだスクリプトファイルデータ
Closure、Scriptのクラスのソース抜粋<参考package groovy.lang;
public abstract class Closure extends GroovyObjectSupport implements Cloneable,Runnable, Serializable {
public Closure(Object owner, Object thisObject) {
this.owner = owner;
this.delegate = owner;
this.thisObject = thisObject;
CachedClosureClass cachedClass =CachedClosureClass)ReflectionCache.getCachedClass(getClass());
this.parameterTypes = cachedClass.getParameterTypes();
this.maximumNumberOfParameters =cachedClass.getMaximumNumberOfParameters();
}
package groovy.lang;public abstract class Script extends GroovyObjectSupport{
private Binding binding;public abstract Object run();
}
com.android.layoutopt.uix.rules.GroovyRule(続)
public class GroovyRule implements Rule {
~略~
public void run(LayoutAnalysis analysis, Node node) {this.mBinding.setVariable("analysis", analysis);this.mBinding.setVariable("node", node);GroovyCategorySupport.use(
this.mCategories, this.mClosure);}
}
•GroovyCategorySupport.use ってなに?•実行処理クラスを別のクラスに後付けする(use)方法です•http://d.hatena.ne.jp/fumokmm/20090117/1232209001
•下記の関数を後付けします
行番号を表示しているのはcom.android.layoutopt.uix.xml.XmlDocumentBuilder
で行っています
uix.jar内のruleファイルにuse適応
している認識が正のようです
rules/XX.rule<= 実は
groovyファイル
// Rule: UselessLayout
//
// Description: Checks whether current node can be removed.
//
// Conditions:
// - The node has children
// - The node does not have siblings
// - The node's parent is not a scroll view (horizontal or vertical)
// - The node does not have a background or its parent does not have a
// background or neither the node and its parent have a background
// - The parent is not a <merge/>
if (!node.isRoot() && !(node['..'].name() in ["ScrollView", "HorizontalScrollView"])&&
node['..']['*'].size() == 1 && node['*'].size() > 0 &&((node.'@android:background' ||
node['..'].'@android:background') || (!node.'@android:background' &&
!node['..'].'@android:background'))) {
analysis << "This ${node.name()} layout or its ${node['..'].name()} parent is " +
"${node['..'].'@android:id' ? "possibly useless" : "useless"}"
}
uix.jarの Rules/ UselessLayout.rule ファイル
com.android.layoutopt.uix.groovy.LayoutAnalysisCategory
public static LayoutAnalysis leftShift(
public static int getStartLine(Node node)public static int getEndLine(Node node)
com.android.layoutopt.uix.groovy.LayoutAnalysisCategory
com.android.layoutopt.uix.LayoutAnalysispublic int getStartLine() {
return LayoutAnalysisCategory.getStartLine(this.mNode);}
public int getEndLine() {return LayoutAnalysisCategory.getEndLine(this.mNode);
}
com.android.layoutopt.uix.groovy.LayoutAnalysisCategorypublic static class Issue
内部Innerクラスで使っている
からあえて宣言書いていると思われ
他のG系の技術を使ったコード
import groovy.lang.GString;public static LayoutAnalysis leftShift(
LayoutAnalysis analysis, GString description){analysis.addIssue(description.toString());return analysis;
}
•GString ・・String型のGroovy拡張クラスです•@fumokmm さん解説サイトhttp://d.hatena.ne.jp/fumokmm/20110323/
com.android.layoutopt.uix.groovy.LayoutAnalysisCategory
•Groovyコード上だと• int num=2• String aaa=“ほげほげ$num ${num/20}” と書けます• String bbb=‘まいう’
• シングルで囲むと普通のString型
計算式OK
import groovy.xml.dom.DOMCategory;
public static List<Node> all(Element element) {NodeList list = DOMCategory.depthFirst(element);int count = list.getLength();List nodes = new ArrayList(count - 1);for (int i = 1; i < count; i++) {
nodes.add(list.item(i));}return nodes;
}
•DOMCategory ・・ DOM操作を簡単にするCategory定義クラス• XML読み(英語) http://goo.gl/SPG93• XML書き(英語) http://goo.gl/nPxqO•@nobusue さんサンプル
•https://gist.github.com/619066
com.android.layoutopt.uix.groovy.LayoutAnalysisCategory
Groovyは何が美味しいの?
Javaは実行コンパイル面倒
スクリプトで実行したい!
Java実行スクリプトJRubyで書けるけど、Ruby文法で書き直すの嫌だよね
XXX.java=>XXX.groovyと拡張子変更だけで実行できると楽だよね GroovyConsoleだと拡張子Javaでも実行可能
groovyConsoleで多少の修正で動作可能配列記述 「}」 =>「]」 に修正
もっとシンプルに書こうよーJSみたいクロージャ等使って
Groovyを学習するには@fumokmm さんGroovy基礎文法最速マスター
http://d.hatena.ne.jp/fumokmm/20100605/1275736594
Java使いをGroovyに引き込むサンプル集http://d.hatena.ne.jp/fumokmm/20110213/1297616436
@uehaj さんDevLOVEぐるぐるGroovyで「気楽に使おう
Groovy」http://d.hatena.ne.jp/uehaj/20110126/1296003293
とここら辺の情報を見てください
Groovyってちょっと試してみたいけど、インストール面倒くさそうだよね~
自分はGroovyConsoleのWebStart版使ってます>http://d.hatena.ne.jp/bluepapa32/20101006/1286337869
ここをクリック
インストールは手軽~
1. ウインドウ閉じた時に記述保持機能あるよ
2. Grape やAntBuilder(AntのDSL)も問題なしに使えます
(Grape機能を使うには、groovy-allの他にivy.jarが必要ですが、これには標準で入っています)
えーっ。でも普通、他ライブラリ使ってるんだけど?
mavenにアクセス@GrabResolver でmavenURL指定標準のMavenCentralならいらない
@Grabで落としてくるライブラリ、バージョンを指定 Mavenにないライブラリは?%USERPROFILE%/.groovy/lib にライブラリを入れれば自動的に通せます
GroovyConsoleのメニューから通す事も可能 java –cp “./;lib/*” groovy.ui.GroovyMain
XXX.groovyの形でBAT起動
set JAVA_HOME=C:/opt/jdk::set JAVA_OPTS=-Dgroovy.source.encoding=UTF-8 -Dfile.encoding=UTF-8
%JAVA_HOME%/bin/java %JAVA_OPTS% -cp ".;./lib/*;./libprocess/*"groovy.ui.GroovyMain processing_test.groovy
::use groovyConsole::%JAVA_HOME%/bin/java %JAVA_OPTS% -cp ".;./lib/*;./libprocess/*"groovy.ui.Console processing_test.groovy
pause
BATイメージ <デモ有
具体例(https://gist.github.com/896140 )
<デモは時間があれば
import it.sauronsoftware.cron4j.*
pattern="* * * * *" //equals "*/1 * * * *" is every 1 minutes
Scheduler s = new Scheduler()
s.schedule(pattern, new Runnable() {
public void run() {
println "Another minute ticked away...${new Date()}"
}})
s.start()
try {
Thread.sleep(1000L * 60L * 2L) //60秒2回待ち
} catch (InterruptedException e) {}
s.stop()
@GrabResolver (name='gridgainsystems',root='http://www.gridgainsystems.com/maven2')
@Grab(group = 'net.sf.cron4j', module='cron4j', version='*')
そんなに便利なら何でAndroidに使われてないのw
一応こんなのあります<要Linux環境
@uehaj さん解説 Groovy on android by discobot http://d.hatena.ne.jp/uehaj/20100515/1273934021
CentOS5.5で試して動かなかったのでパッチココに置きました https://github.com/kimukou/gradlestudy/tree/master/discobot
package org.jggug.sampleimport android.app.Activity;import android.os.Bundleimport android.widget.TextView
public class Main extends Activity{/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)TextView tv = new TextView(this)def list = ["Welcome","to", "discobot." , "¥nAnd", "good",
"luck", "with", "Main!",日本語もOK!"]def str = ""list.each {
str += it + " "}tv.setText(str)this.setContentView(tv)
}}
使用コードイメージ(UTF8保存)
でもWinでJavaだけで
書きたい人には厳しいw Discobot環境cygwinなら動くのかな?<Win
Groovyをシェル扱いに出来ないからmkProject.sh動かない
dx.batのdex変換処理でgroovy-all.jarのクロージャクラス変換がStackOverFlowが発生してOutOfMemory出るみたい
でもWinでJavaだけで書きたい人は垣根高いよね ASEに期待したいよw(現在未サポート
だからGradle等で補助(コンパイル)ツールとして使うのが吉?
Gradleってなに? Groovyで書かれたビルドツールです XMLでごちゃごちゃ書いていた記述をDSL記述で
<=pom.xml や build.xml のXML地獄からの脱出Maven、AntBuilder(AntのDSL)が使えます
時間があれば ATECのTestterを使ったデモ
使った設定はここにあります Testter
https://sites.google.com/site/androidtestclub/testter Gradle設定ファイル(use Android Gradle Plugin)
https://github.com/kimukou/gradlestudy/tree/master/testter
Gradleファイル記述例buildscript {
repositories {
mavenRepo(urls: 'http://jvoegele.com/maven2/')
}
dependencies {
classpath 'com.jvoegele.gradle.plugins:android-plugin:0.9.9'
}
}
apply plugin: 'android'
repositories {
flatDir(dirs: file("$rootDir/libs"))
//mavenCentral()
}
dependencies {
compile group: 'org.twitter4j', name: 'twitter4j-core', version: '2.1.8-SNAPSHOT'
}
使うpluginをmaven
から取得する設定
使うjarとバージョンを記載
Gradleファイル記述例(続き1)// プロジェクトのバージョンを設定
version = "x.y.z"
//ソース位置
[compileJava, compileTestJava]*.options.collect {options -> options.encoding= 'UTF-8'}
sourceSets.main.java.srcDirs += ['src']
ant.echo sourceSets.main.java.srcDirs
// 署名設定
androidPackage {
//keyStore = System.getProperty('user.home')+"/.android/debug.keystore"
keyStore = "${projectDir}/debug.keystore"
keyAlias = 'androiddebugkey'
// パスワードが設定されてなければ、コマンド・プロンプトで入力を求められる
keyStorePassword = "android"
keyAliasPassword = "android"
}
Gradleファイル記述例(続き2)// プロジェクトのプロパティをリソースのフィルタリング(プロパティ
の解決)に使うヨ
processResources {
expand (project.properties)
}
jar {
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
// デバッグ・ビルド設定
task configureDebug << {
jar.classifier = "debug"
}
// リリース・ビルド設定
task configureRelease << {
proguard.enabled = true
}
Apkに同梱するjarを指定します
(この記述の場合は関連全部)
御静聴ありがとうございました 解析に使ったソフト
ソース位置が解りませんでした><。従ってjarを直参照
JD
谷本さん解説ページ http://goo.gl/DRWiR
本家サイト http://goo.gl/ux1I
ANDROID_HOME/Tools/libディレクトリ上で検索すると関連jarが見れます