Androidアプリリリースしました。

リリースしたはいいけど、DL数がまるで伸びないのでアピールしておく。

Instaplus

http://jp.androlib.com/android.application.com-limelabo-instaplus-pDpjD.aspx

主な機能
  • 共有からinstapaperにurlを追加できます。
  • 追加したときにトーストとステータスバーで通知します。
  • クリップボードにURLぽいのがあるとinstapaperに追加します。



今後の展開
  • いくつか実装したい機能があるので、しばらく細かいアップデートがあると思います。

思っていること

Androidアプリをとりあえず作ろうと思ってリリースしたはいいけどあまりのDL数の少なさにへこんでいる。
後発だから厳しいのか、アプリがしょぼいのか、instapaperが思いのほか認知されていないのか?

Hello QuickAssist

ちょっと、はまったのでメモ。

クイックアシストとは?

補完とは違って、変数選択時にCtrl+1とか押すと出てくるやつですね。
ローカル変数/メソッドを抽出したり、あわよくばエラーも直してくれるやつです。

Hello QuickAssistの流れ

  1. plugin.xmlにextentionを追加
  2. IQuickAssistProcessorを実装したクラスを作成する

基本的には上記だけでOK。
注意点としてはIQuickAssistProcessorの実装をしたときにデフォルトコンストラクタを作っておくこと(英語で書いてあったために見落として、結構はまった・・・)

依存するプラグイン

plugin.xml

そのままですね。

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.jdt.ui.quickAssistProcessors">
      <quickAssistProcessor
            class="com.limelabo.ui.MyQuickAssist"
            id="com.limelabo.ui.MyQuickAssist"
            name="com.limelabo.ui.MyQuickAssist">
      </quickAssistProcessor>
   </extension>

</plugin>

ソース

実際に表示するには、IQuickAssistProcessor#getAssists()でIJavaCompletionProposalの配列を返さないといけないので、そのあたりも適当に実装する。
IJavaCompletionProposal#apply()はアシスタントが実行されたときの処理で、まともに実装すると結構大変そうなので、あとで書く(かも)。

package com.limelabo.ui;

import org.eclipse.jdt.ui.text.java.IInvocationContext;
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
import org.eclipse.jdt.ui.text.java.IProblemLocation;
import org.eclipse.jdt.ui.text.java.IQuickAssistProcessor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;

/**
 * 
 */
public class MyQuickAssist implements IQuickAssistProcessor {

	/**
	 * コンストラクタ
	 * (引数なしコンストラクタが無いと生成してくれない)
	 */
	public MyQuickAssist() {
		super();
	}

	public boolean hasAssists(IInvocationContext context) {
		System.out.println("hasAssists");
		return true;
	}

	public IJavaCompletionProposal[] getAssists(IInvocationContext context,
			IProblemLocation[] locations) {
		System.out.println("getAssists");
		IJavaCompletionProposal[] assists = { new HelloCompletionProposal() };
		return assists;
	}

	class HelloCompletionProposal implements IJavaCompletionProposal {

		@Override
		public int getRelevance() {
			// 表示順番0-100(100が一番上に表示される)
			return 100;
		}

		@Override
		public void apply(IDocument document) {
			// 実行時の処理
			System.out.println("apply");
		}

		@Override
		public String getAdditionalProposalInfo() {
			return null;
		}

		@Override
		public IContextInformation getContextInformation() {
			return null;
		}

		@Override
		public String getDisplayString() {
			// 表示ラベル
			return "Hello QuickAssist";
		}

		@Override
		public Image getImage() {
			// アイコン
			return null;
		}

		@Override
		public Point getSelection(IDocument document) {
			return null;
		}
	}
}

実行結果

Inkscapeでエンベロープを実行するとエラーが出る

ロゴでも作るようにInkscapeを入れてみたはいいけど、エンベロープを実行すると↓のエラーが・・・

The fantastic lxml wrapper for libxml2 is required by inkex.py and therefore this extension. Please download and install the latest version from http://cheeseshop.python.org/pypi/lxml/, or install it through your package manager by a command like: sudo apt-get install python-lxml

要するに、pythonのlxmlライブラリ(?)がないといっているみたい。

lxmlのインストール(Windowsの場合)

以下のサイトからpythonをダウンロードして、インストールする。

http://www.python.jp/Zope/download/pythoncore

以下のサイトからez_setup.pyを適当なのフォルダにダウンロード

http://peak.telecommunity.com/dist/ez_setup.py

以下のコマンドを実行する

easy_install lxml

終わり

最後に

結局、エンベロープは機能的に使えるようになったけど、私にはどうも使いこなせないかも・・・

クラスロードについてもう少し調べてみた

前回の記事と同様に、クラスロードに関してはVM次第な部分があるので、参考程度でお願いします。

前回からさらに

init/createdのログをみてもらうとわかるのだけれど、この時点でClassLoaderSampleがすでにクラスロードされている。
ClassLoaderSampleには、Buzz4を返すメソッドがあるにもかかわらず、Buzz4はロードされておらず、クラスロードされるのは、実際にBuzz4をnewしたときとなっている。
逆にBuzz/Buzz3はClassLoaderSampleがロードされた段階で、ClassLoaderに登録されてしまっている

ようするに

クラスがロードされるときは、自クラスを構成するクラスを全部読み込んでるわけではなく、キャストの必要なものだけがロードされているようだ。
返り値の型とreturn時の型が同じであれば、呼ぶまでは何もロードしていない。

ちなみに

フィールドでも同じ現象が起きるみたい。
また別の機会にでも検証してみようかな

環境

JDK1.6

出力結果

### init     ####
class limelabo.ClassLoaderSample
class limelabo.ClassLoaderSample$Buzz
class limelabo.ClassLoaderSample$Buzz3
#### created  ####
class limelabo.ClassLoaderSample
class limelabo.ClassLoaderSample$Buzz
class limelabo.ClassLoaderSample$Buzz3
#### Buzz3  ####
class limelabo.ClassLoaderSample
class limelabo.ClassLoaderSample$Buzz
class limelabo.ClassLoaderSample$Buzz3
#### Buzz4  ####
class limelabo.ClassLoaderSample
class limelabo.ClassLoaderSample$Buzz
class limelabo.ClassLoaderSample$Buzz3
class limelabo.ClassLoaderSample$Buzz4

ソースコード

package limelabo;

import java.lang.reflect.Field;
import java.util.Vector;

public class ClassLoaderSample {

    public static void main(String[] args) {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        printClassLoader("### init     ####", cl);
        ClassLoaderSample hoge = new ClassLoaderSample();
        printClassLoader("#### created  ####", cl);
        
        hoge.createBuzz3();
        printClassLoader("#### Buzz3  ####", cl);
        hoge.createBuzz4();
        printClassLoader("#### Buzz4  ####", cl);
        
    }

    private Buzz createBuzz3() {
        return new Buzz3();
    }

    private Buzz4 createBuzz4() {
        return new Buzz4();
    }

    static class Buzz {
    }

    static class Buzz3 extends Buzz {
    }

    static class Buzz4 extends Buzz {
    }

    /**
     * @param message
     * @param cl
     */
    private static void printClassLoader(String message, ClassLoader cl) {
        System.out.println(message);
        Class<?> c = cl.getClass();
        // ClassLoaderまでさかのぼる
        for (; !ClassLoader.class.getName().equals(c.getName()); c = c.getSuperclass()) {
        }
        try {
            Field classesField = c.getDeclaredField("classes");
            classesField.setAccessible(true);
            Vector<Class> classes = (Vector<Class>) classesField.get(cl);
            for (Class loadedClass : classes) {
                System.out.println(loadedClass);
            }
        } catch (Exception e) {
            // とりあえず無視
            e.printStackTrace();
        }
    }
}

JavaでのClassLoaderの動き(まとめ)

※ソースと出力結果が間違っていたので一部修正(2010/5/11)

プロジェクトでクラスがロードされるのが遅い*1とかで調べることがあったので、実際どんな条件でロードしているのかをまとめてみた。そもそも、クラスを小さく(適切な大きさに)していればClassLoaderの動きなんて調べなくてもいいはずなんだけど、そこは大人の事情ってやつで。

ちなみに、ホントに細かい動作とかは使ってるVMとかに影響されるので100%これが正しいってわけではないと思います。
まぁ、参考までに結構動きが変わるってことがわかればいいかなと

調査方法

基本的には、事あるごとにClassLoaderからロード済みのクラスをSystem.outして、どんな感じでロードしているかを調べてみた。

流れとしては、

  1. Hogeインスタンスを生成
  2. Hoge#getBuzz(int)を1-8まで実行

で、それぞれgetBuzz(int)から先の動きは以下のとおり

int 返り値 クラス名 インスタンス生成方法
0 (Buzz0) Buzz0 コンストラクタを直接
1 Buzz Buzz1 Buzz1.getInstance()内でBuzz1のコンストラクタ生成
2 Buzz2 Buzz2 Buzz2.getInstance()内でBuzz2のコンストラクタ生成
3 Buzz Buzz3 Hoge#createBuzz3()内でBuzz3のコンストラクタ生成
4 Buzz4 Buzz4 Hoge#createBuzz4()内でBuzz4のコンストラクタ生成
5 Buzz Buzz5 Factory.createBuzz5()内でBuzz5のコンストラクタ生成
6 Buzz6 Buzz6 Factory.createBuzz6()内でBuzz6のコンストラクタ生成
7 Buzz Buzz7 リフレクションによる生成
8 Buzz8 Buzz8 リフレクションによる生成

結果

(0,2,4,6,8)はgetBuzz(int)の返り値Buzzと、生成時の返り値がBuzz以外のものは、Hogeがロードされた段階で(今回はHoge内にmainがあるのでまとめて呼ばれてしまっている)getBuzz(int)内で返される可能性のあるものすべてがクラスロードされている。
(3)も基本的にはHogeのクラスロード時に読み込まれるけど、メソッドが別なのでワンテンポ遅れてるっぽい
(1,5,7)は実際にメソッドがキックされたときにクラスがロードされている
(7)は生成はしているはずなのにClassLoader内に含まれていない(謎)

まとめ

リフレクションは別格だとしても、返り値は具象クラスを指定するより、抽象クラスやインターフェースを指定したほうが、最小限のクラスロードですみそう。
そのために、ファクトリーメソッドを用意してメソッド内のクラスの粒度(?)をそろえるのが良さそう。*2

以下、参考資料

環境

JDK1.6

出力結果

### init     ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
#### created  ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
#### getBuzz0(limelabo.Hoge$Buzz0) ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
#### getBuzz1(limelabo.Hoge$Buzz1) ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
class limelabo.Hoge$Buzz1
#### getBuzz2(limelabo.Hoge$Buzz2) ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
class limelabo.Hoge$Buzz1
#### getBuzz3(limelabo.Hoge$Buzz3) ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
class limelabo.Hoge$Buzz1
#### getBuzz4(limelabo.Hoge$Buzz4) ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
class limelabo.Hoge$Buzz1
#### getBuzz5(limelabo.Hoge$Buzz5) ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
class limelabo.Hoge$Buzz1
class limelabo.Hoge$Facotory
class limelabo.Hoge$Buzz5
#### getBuzz6(limelabo.Hoge$Buzz6) ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
class limelabo.Hoge$Buzz1
class limelabo.Hoge$Facotory
class limelabo.Hoge$Buzz5
#### getBuzz7(limelabo.Hoge$Buzz7) ####
class limelabo.Hoge
class limelabo.Hoge$Buzz
class limelabo.Hoge$Buzz0
class limelabo.Hoge$Buzz2
class limelabo.Hoge$Buzz4
class limelabo.Hoge$Buzz6
class limelabo.Hoge$Buzz8
class limelabo.Hoge$Buzz3
class limelabo.Hoge$Buzz1
class limelabo.Hoge$Facotory
class limelabo.Hoge$Buzz5
class limelabo.Hoge$Buzz7

ソースコード

package limelabo;

import java.lang.reflect.Field;
import java.util.Vector;

public class Hoge {

    public static void main(String[] args) {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        printClassLoader("#### init     ####", cl);
        Hoge hoge = new Hoge();
        printClassLoader("#### created  ####", cl);

        for (int i = 0; i < 8; i++) {
            Buzz buzz = hoge.getBuzz(i);
            printClassLoader("#### getBuzz" + i + "("
                    + buzz.getClass().getName() + ") ####", cl);
        }
 
    }

    private Buzz getBuzz(int i) {
        switch (i) {
        case 0:
            return new Buzz0();
        case 1:
            // 返り値はBuzz
            return Buzz1.getInstance();
        case 2:
            // 返り値はBuzz2
            return Buzz2.getInstance();
        case 3:
            // 返り値はBuzz
            return createBuzz3();
        case 4:
            // 返り値はBuzz4
            return createBuzz4();
        case 5:
            // 返り値はBuzz
            return Facotory.createBuzz5();
        case 6:
            // 返り値はBuzz6
            return Facotory.createBuzz6();
        case 7:
            // 返り値はBuzz
            return createBuzz7ByReflection();
        case 8:
            // 返り値はBuzz8
            return createBuzz8ByReflection();
        default:
            throw new IllegalArgumentException("arg is " + i);
        }
    }

    private Buzz createBuzz3() {
        return new Buzz3();
    }

    private Buzz4 createBuzz4() {
        return new Buzz4();
    }

    static abstract class Buzz {
    }

    static class Buzz0 extends Buzz {
    }

    static class Buzz1 extends Buzz {
        static Buzz getInstance() {
            return new Buzz1();
        }
    }

    static class Buzz2 extends Buzz {
        static Buzz2 getInstance() {
            return new Buzz2();
        }
    }

    static class Buzz3 extends Buzz {
    }

    static class Buzz4 extends Buzz {
    }

    static class Buzz5 extends Buzz {
    }

    static class Buzz6 extends Buzz {
    }
    
    static class Buzz7 extends Buzz {
    }
    
    static class Buzz8 extends Buzz {
    }

    static class Facotory {
        static Buzz createBuzz5() {
            return new Buzz5();
        }

        static Buzz6 createBuzz6() {
            return new Buzz6();
        }
    }

    private Buzz createBuzz7ByReflection() {
        try {
            return (Buzz) Class.forName("limelabo.Hoge$Buzz7").newInstance();
        } catch (Exception e) {
            // ignore
        }
        return null;
    }

    private Buzz8 createBuzz8ByReflection() {
        try {
            return (Buzz8) Class.forName("limelabo.Hoge$Buzz8").newInstance();
        } catch (Exception e) {
            // ignore
        }
        return null;
    }

    private static void printClassLoader(String message, ClassLoader cl) {
        System.out.println(message);
        Class<?> c = cl.getClass();
        // ClassLoaderまでさかのぼる
        for (; !ClassLoader.class.getName().equals(c.getName()); c = c.getSuperclass()) {
        }
        try {
            Field classesField = c.getDeclaredField("classes");
            classesField.setAccessible(true);
            Vector<Class> classes = (Vector<Class>) classesField.get(cl);
            for (Class loadedClass : classes) {
                System.out.println(loadedClass);
            }
        } catch (Exception e) {
            // ignore
        }
    }
}

*1:正確にはクラスが大きすぎて大量にクラスロードされているのが原因ぽい

*2:とはいっても、全部のクラスでgetInstance()みたいなのを作るのって気持ち悪い気がするんですが

Trac Lightningでハイライトを有効にする

Googleで検索してもなかなか見つからなかったので、一応メモ
wikiは最初からハイライトされるみたいだけど、リポジトリブラウザはされなかったので。

Trac Lightningでハイライトを有効にする(1)

Tracの設定をします。

  1. adminとしてログイン
  2. トップフレーム>管理
  3. サイドメニュー>trac.ini>mimeviwer
  4. pygments_modes>(javaの場合は)text/x-java
  5. [Apply Changes]で反映

Trac Lightningでjavaのハイライトを有効にする(2)

ソースコードのプロパティでsvn:mime-typeを変更します
Eclipsesvnが使えるの前提です
javaの場合

  1. javaファイルを選択(フォルダごと選択でもおk)
  2. 右クリック>チーム>プロパティの設定
  3. Name から「svn:mime-type」を選択
  4. valueに text/x-javaを入力
  5. フォルダを選択している場合は「Apply prperty recursively to」をチェック
  6. OKボタンを押す
  7. 対象のファイルをコミットする

ファイルタイプごとのmime-type

MIMEタイプ WikiProcessors
application/javascript js
application/pdf pdf
application/postscript ps
application/rss+xml rss
application/rtf rtf
application/x-csh csh
application/x-genshi genshi
application/x-genshi-text genshitext
application/x-sh sh
application/x-troff nroff roff troff
application/x-yaml yaml yml
application/xsl+xml xsl
application/xslt+xml xslt
image/svg+xml svg
image/x-icon ico
model/vrml vrml wrl
text/css css
text/html htm html
text/plain COPYING ChangeLog INSTALLAUTHORS README RELEASE TXT text txt
text/x-ada ada
text/x-asm asm
text/x-asp asp
text/x-awk awk
text/x-c++hdr H HH c++hdr hh hpp
text/x-c++src C C++ CC c++ c++src cc cpp
text/x-chdr chdr h
text/x-csharp C# c# cs csharp
text/x-csrc c csrc xs
text/x-diff diff patch
text/x-eiffel e eiffel
text/x-elisp el elisp
text/x-fortran f fortran
text/x-haskell haskell hs
text/x-idl idl
text/x-inf inf
text/x-ini cfg ini
text/x-java java
text/x-ksh ksh
text/x-lua lua
text/x-m4 m4
text/x-mail mail
text/x-makefile GNUMakefile Makefile make makefile mk
text/x-objc m mm objc
text/x-ocaml ml mli ocaml
text/x-pascal pas pascal
text/x-perl PL perl pl pm
text/x-php php php3 php4
text/x-psp psp
text/x-pyrex pyrex pyx
text/x-python py python
text/x-python-doctest pycon
text/x-rfc rfc
text/x-rst rst
text/x-ruby rb ruby
text/x-scheme scheme scm
text/x-sql sql
text/x-tcl tcl
text/x-tex tex
text/x-textile textile txtl
text/x-vba bas vb vba
text/x-verilog v verilog
text/x-vhdl vhd vhdl
text/x-zsh zsh
text/xml xml

バージョン

Trac Lightningversion: 2.4.2
Trac: 0.11.5.ja1