class

AndroidManifest package名で改正します

手動で改正して名を包む時,AndroidManfest.xml名を包んで自動的に改正することはでき(ありえ)ない. 名の変更を包む時、行って手動で改正しなければなりません。

Androidメッセージ処理

ア.術語を説明する

Looper

MessageQueueの管理者,1つのスレッドの中で、もしLooper対象が存在するならば、きっとMessageQueue対象が存在する、しかもただ1つのLooperの対象と1つのMessageQueueの対象だけが存在する。

Handler

メッセージの処理者。HandlerでMessageの対象を実装する,sendMessage(msg)でMessage対象をMessageQueueの中に添加する;
MessageQueueがこのMessageまで循環する時、このMessage対象の対応するhandler対象のhandleMessage()方法を実行してそれに対して処理を行います。

Message

メッセージ対象、メッセージ情報を記録するのクラス。
Messageを使う時、new Message()でMessageの実例を創建することができて、
しかしAndroidがMessage.obtain()あるいはHandler.obtainMessage()を推薦して、Message対象を取得する。

MessageQueue(MQ)

メッセージ隊列、用いてMessage対象のデータの構造を保管して、“FILO”の原則によってメッセージを保管する。 ①Looper(普通なスレッドをLooperスレッドにならせます)
1)Looper.prepare()

上記の図より、Looper Threadの中に1つのLooperの対象があって、その内部は1つのMQを守りました。

  • ps:1つのThreadはただ1つのLooperの対象です。

2)Looper.loop()

loop方法を実行した後に、Looperスレッドは本当に働いたことを始めて、それは絶え間なくて自分のMQの中からMQの先頭メッセージを取り出し、実行する。 その他方法 Looper.myLooper()  現在のスレッドlooper対象を取得する getThread()  looper対象の所属のスレッドを取得する quit()  looper循環を終える

②Handler(1つの任務(sendMessage)を実行することを知らせて、そしてloopが自分に着く時この任務(handleMessage)を実行して、全体の過程は非同期のです。)

上記の図より、1つのスレッドは多数Handlerがあることができて、しかし1つのLooperがあることしかできません

1)メッセージを発送する方法  post(Runnable)  postAtTime(Runnable,long)  postDelayed(Runnable long)  sendEmptyMessage(int)  sendMessage(Message)  sendMessageAtTime(Message,long)  sendMessageDelayed(Message,long)

2)メッセージを処理する方法  dispatchMessage(Message msg)  handleMessage(Message msg)

3)handlerの作用  a.handlerは任意のスレッドでメッセージを発送することができて、これらのメッセージ関連MQを添加される

b.関連looperスレッドの中でメッセージを処理する

これでその他は非主なスレッドのです中でUIを更新することを実現する.
過程は下記通りです

③Message フィールド a.intタイプを保存する    public int what    public int arg1    public int arg2 例えば、

b.Bundleオブジェクトを保存する public Object obj 例えば、

イ.非同期処理方法

使用場合:多くの時間を消耗するの計算作業と定時操作 例えば、IO操作(ネットワーク操作、ファイル操作、データベース操作….)、複雑の運算….

  • 方法一、handler

処理方法はア.②と③を参考する

  • 方法二、AsyncTask

一.パラメーター

  • Params  任務をスタートするのパラメーター
  • Progress  バックグランドの任務を実行するのパーセンテージ。
  • Result  戻る結果

二.実現方法

  • 1 onPreExecute() この方法は実際的なバックグランドの操作を実行する前にをUI threadに調達し使用されます。 この方法の中で準備作業をすることができて、例えば、画面の上でようです1つの進度の条を明らかに示します。
  • 2 doInBackground(Params...) onPreExecute方法が実行した後にすぐ実行、この方法の運行はバックグランドのスレッドの中で。 多くの時間を消耗するの計算作業を実行する。 publishProgress方法はリアルタイムの任務の進度を更新しにきます。 この方法は抽象的な方法で、子のクラスは必ず実現しなければなりません。
  • 3 onProgressUpdate(Progress...) publishProgress方法が実行された後で、UI threadは画面の上でこの方法展示の任務の進展を調達し使用してそれによって、例えば1つの進度の条を通じて展示を行います。
  • 4 onPostExecute(Result) doInBackground 方法を実行した後に、onPostExecute 方法はUI threadに実行されて、 バックグランドの計算結果はこの方法を通じてUI threadへ順次伝える

プログラムを終了する前にポップアップウィンドウを閉じる必要があります

問題: 画面にポップアップウィンドウが表示さらた場合(例えば:listbar)、プログラム終了を行ったらDDMSにエラーが発生する。

対応方法: ActivityにonPauseメソッドをオーバーライドし、ポップアップウィンドウを閉じる。 例:

1
2
3
4
5
6
@Override
protected void onPause() {
    super.onPause();
//ポップアップウィンドウを閉じる
    closeListBar();
}

単例モードで問題を引き出す例

一、CustomerExport:

アプリを保存すると、データベースへ更新しました、しかし、アプリ最度開けて、旧いデータを表示しまう。原因 : 単例 前回 実例は解放しないため
下記のCustomerExport修正前にコーディング。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public static CustomerMasterDB getInstance(Context context){
   if (masterDB==null){
      masterDB=new CustomerMasterDB(context);
   }
   return masterDB;
}

public CustomerMasterDB (Context context){
  mContext =context;
  initMap();
//データベースから取得
  getCsvList();
}

修正方法は、getCsvList()メッソドは構造関数以外なところへ移動

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public CustomerMasterDB(Context context){
  mContext =context;
  initMap();
}
/**
 * 前回選択した顧客マスタでー
 */
public List<String> getSelectedList(){
  getCsvList();//修正前と違う、呼び出す場合は変わる。
  List<String> columnNameList=new ArrayList<String>();
  String value=contentValue.getAsString(CustomerSettingExData.CSV_LIST);
  if(TextUtils.isEmpty(value)==true){
     return null;
  }
  String[] fieldNames=value.split(COMMA);
  for(String string:fieldNames){
    columnNameList.add(fieldNameMap.get(string));
  }
  return columnNamesList;
}
private void getCsvList(){
  CustomerSettingExData settingEx=new CustomerSettingExData(mContext);
  String[] strSelectionArgs={CustomerSettingExData.CSV_LIST,CustomerSettingExData.MAIL_ADDRESS,
    CustomerSettingExData.ZIP_PASSWORD,CustomerSettingExData.MAIL_SUBJECT,CustomerSettingExData.MAIL_TEXT};
  contentValues=settingEx.queryValues(strSelectionArgs);
}

二、CRCustomizer: アプリを起動して、何もしないで閉じる、再度開けて、ボタンをクリックすると 異常は発生しまう。原因 : 単例 前回 実例は解放しないため

  • CrxXml.java

修正前:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//静態変数を定義する
private static CrxXml crxXml;
private CrxXml() {
}
private CrxXml(Context context) {
this.context = context;
}
public static CrxXml getInstance(Context context) {

 if (crxXml == null) {
   crxXml = new CrxXml(context);
   }
  return crxXml;
}

修正後

1
2
3
public CrxXml(Context context) {
      this.context = context;
}
  • ActivityKeyLayout.java 修正前:
1
2
3
4
5
6
7
8
 public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        setTitleTimerView(R.id.textViewTime);
        showProgress();
        initialize();
        isStarted = true;
    }

修正後:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 private CrxXml xml;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_keylayout);
        setTitleTimerView(R.id.textViewTime);
        xml = new CrxXml(this);
        showProgress();
        initialize();
        isStarted = true;
    }

EqualsメソッドとhashCodeメソッドを実装について

「equals()」と一緒にオーバーライドする必要なメソッド「hashCode()」の実装説明

  • 1.equalsメソッド

自身と引数に取ったオブジェクトが等価であるかどうかをboolean値で返すように定義します。   自作クラスの等価性の基準は自由に定めることができますが、同値関係を満たす必要があります。   オブジェクトa、 bについて、以下の3つの条件が成り立てば同値関係を満たしています。   ① a.equals(b)==true なら b.equals(a)==true である(対称律)   ② a.equals(a)==true である(反射律)   ③ a.equals(b)==true かつ b.equals©==true なら a.equals© である(推移律)

  • 2.hashCodeメソッド

hashCodeはint型のハッシュ値を返すメソッドです。ハッシュ値はハッシュテーブルにデータが収められるアドレスを指定する値です。   アドレスを指定するとはいっても、以下の条件を満たせばどのような整数値を与えても構いません。   ① equalsで等しいと判定されたオブジェクトは等しいハッシュ値を持つ   ② equalsで等価性の判定に用いられたフィールドに変更がない限り常に同じハッシュ値を持つハッシュ値の条件には、「equalsメソッドで等しくないと判定された場合には異なるハッシュ値を持たなければならない」という条件はないので、2つの等価でないオブジェクトのハッシュ値が等しくても構いません。極端な話hashCode()の中身が「return 0」だけであっても(つまり、どのオブジェクトもハッシュ値が0となります)、ハッシュテーブルに用いることはできます。ハッシュ値が等しい場合、アドレスの衝突が起こるのですが、その場合には格納されるアドレスを変更するなどの解決が内部で自動的に行われます。

ただしハッシュテーブルのアクセス速度などのパフォーマンスを向上させるために、等価でないオブジェクトはできる限り異なるハッシュ値を返すようにするのがよいでしょう。

  • 3.Javaの中の集合(Collection)

Javaの中の集合(Collection)は2種類あって、ListとSetなことがあります。   前者の元素は秩序があって、元素繰り返し。後者は秩序がないで、しかし元素繰り返し。

ハッシュアルゴリズムを使用するクラス(HashMap、HashSet等)のインスタンスに、hashCodeを正しく実装しないオブジェクト追加した場合、期待した動作を得られません。   オブジェクトの格納、検索を行う際に、あらかじめハッシュ値を計算し、得られたハッシュ値に 基づいてオブジェクトの格納、検索を行うことで、処理を効率化するアルゴリズムです。   ハッシュアルゴリズムを理解するには、ArrayListとHashSetの動作の違いを比較するとよいでしょう。   ArrayListとHashSetはともにCollectionインターフェースを実装するクラスですが、以下の ような動作の違いがあります。   ◆ArrayListの場合

ArrayListは追加された要素を1列のリストに格納して保持します。 格納されている要素を取り出すときは、リストの先頭から   順番にオブジェクトのequalsメソッド を呼び出して、equalsメソッド呼び出しの結果がtrueとなる要素を戻り値として返します。   ⇒ リストの要素の数が膨大で、かつ取り出したい要素がリストの後方に存在した場合、 検索効率が極端に悪化する可能性がある。

◆HashSetの場合   HashSetは追加された要素を以下の手順に従って格納、検索します。

① 追加対象のオブジェクトのhashCodeメソッドを呼び出します。
  ② hashCodeメソッドの戻り値ごとにオブジェクトを「部屋」(HashSetクラスの実装上の 用語はBucket)に分類し、hashCodeの部屋番号が付けられた「部屋」に対象オブジェクトを格納します。
  ③ 要素を取り出す場合は、hashCodeの値を元に、そのhashCodeの値を 部屋番号としてもつ「部屋」にまず対象となるオブジェクトを探しにいきます。
  ④ 手順3で探し当てた「部屋」に格納されているオブジェクトに対して順番にequalsメソッドを 呼び出し、equalsメソッド呼び出しの結果がtrueとなる要素を戻り値として返す。
  あらかじめ要素をhashCodeに基づく「部屋」に分類して保持しているため、 オブジェクト同士を比較する際、限られた数のオブジェクトを比較すればよく、検索効率が向上します。
  hashCodeが正しく実装されていない場合、同値のオブジェクトであるにもかかわらず 異なる部屋に対象となるオブジェクトを探しに行ってしまうため、対象となるオブジェクト が見つからないという事態が発生する可能性があります。
  hashCodeが0を返すように実装されている場合、実質部屋番号0の「部屋」に全ての オブジェクトが格納されることになり、アルゴリズムとしてはArrayListに要素を追加する 同じとなり、ハッシュアルゴリズムの利点が得られません。

  • 4.「equals()」と一緒にオーバーライドする必要なメソッド「hashCode()」の実装する

equalsメソッドとhashCodeメソッドはもともとはObjectクラスに実装されているメソッドです。
  自前のクラスにおいて、「このクラスにとっては、あるオブジェクトとほかのオブジェクトが等しいとは、こういう場合である」と定義したい場合、equals(Object)メソッドをオーバライドすると良いでしょう。
  ただし、同時にhashCodeメソッドをオーバライドして、「等価なオブジェクトは等価なハッシュコードを保持する必要がある」という hashCode()の
汎用規約に従うことが求められます。そのクラスが使い捨てではなく、ほかのクラスから用いられる場合は、必ず留意しましょう。
  一例として、2つの文字列を保持するMyTwinStringクラスを考えます。MyTwinStringクラスでは、あるオブジェクトとほかのオブジェクトで、保持する2つの文字列が両方ともに等しい場合に「等しい」とします。ハッシュコードを2つの文字列を用いて生成すると、以下のようになります

この例では、2つの文字列のハッシュコードの和をそのオブジェクトのハッシュコードとしています。これで「等価なオブジェクトは等価なハッシュコードを保持する必要がある」という hashCodeメソッドの汎用規約が実現します。   ただし、この方法では「MyTwinString("A", "B")」と「MyTwinString("B", "A")」のハッシュコードが等しくなりますので、このクラスの使い方によっては、hashCode()メソッドの内容を工夫すべきでしょう。

規約補足:複数の値を返すのに、配列やMapを使用しない

3.9.2 複数の値を返すのに、配列やMapを使用しない ★★★★

【説明・動機】    メソッドの戻り値で複数の値を返したい場合、安易に配列やMapなどを使用してはいけません。戻り値のどこ何が入っているのかがわからなくなり、可読性が落ち、バグが発生しやすくなるためです。特に計算結果やDBから取得した値ばどを返すときにこういった実装をしてしまうケースが多いため、注意してください。    複数の値を返したい場合は、戻り値となるクラスを作成して、そのクラスのインスタンスを返すようにしてください。    複数の値をまとめて返す場合には、クラスを使って適切なデータ構造を作成して、どのようなデータを持っているのか、どうやってデータを取り出すのかを、コードの利用者にも出来るだけわかりやすくしてください。|

配列やMapを使用するな場合、なんのものを代わるいいですか? これから、実際運用中使用する一つ例を説明します

資料来源:CameraScanning

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
修正前
public class Setting {
    private SharedPreferences sharedPreferences;
    private Map<String, String> settingMap;

    public Setting(Context context) {
        SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        readSettings();
    }

    public Map<String, String> getSettingMap() {
        // 規約を違反する
        return settingMap;
    }

    private void readSettings() {
        // ......
    }

    private HashMap<String, String> getCameraParameters() {
        Bundle bundleSettings = intent.getBundleExtra(SETTIN_ITEMS);
        Setting settingData = new Setting(this);
        HashMap<String, String> map = (HashMap<String, String>) settingData.getSettingMap();
        for (String settingItem : settingItemKey) {
            boolean isReceived = bundleSettings.containsKey(settingItem);
            if (isReceived == true) {
                map.remove(settingItem);
                map.put(settingItem, bundleSettings.getSting(settingItem));

            }
        }
        // 規約を違反する
        return map;
    }

}
修正後
public class Setting {
    private List<String> scanModeList;
    public Setting(){
        scanModeList=new ArrayList<String>();
    }

    public boolean isOutputCheckChar(){
        return false;
        //....;
    }
    public void setOutputCheckChar(){
        //....;
    }
    public void appendScanMode(boolean isMode,String mode){
        //...;    
    }
    public void clearScanMode(){
        //...;
    }
}


public class SettingManager {
    private Setting setting;

    public SettingManager(Context context) {
        setting = new Setting();
        readSettings(context, setting);
    }

    public Setting getSetting() {
        // 修正後
        return setting;
    }

    private void readSettings(Context context, Setting setting) {
        // ...
    }

    private void getCameraParameters() {
        Bundle bundleSettings = intent.getBundleExtra(SETTING_ITEMS);
        //修正後
        SettingManager settingManager = new SettingManager(this);
        Setting setting = settingManager.getSetting();

        ArrayList<String> modeArray = bundleSettings.getStringArrayList(SCAN_MODE);
        if (modeArray.isEmpty() == false) {
            setting.clearCcanMode();
            setting.setScanModeList(modeArray);
        }
    }
}

金額の格式化

◆金額項目の格式化

1
2
3
4
5
6
private String toFormatCurrency(int cur,String pattern) {    
   DecimalFormat df = new DecimalFormat();
   df.applyPattern(pattern);
   String str = df.format(cur);
   return str;
}

例:

1
2
String value=toFormatCurrency(1234567890,"#,###,###,###");
value ===>"1,234,567,890"

※共通ライブラリーを作成する必要がある

Timerの実現

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class CustomTimer {

    private int interval;
    private final Handler handler;
//タイマー内のスレッドである。
    private Runnable delegate;
    private boolean ticking;
//interval:間隔を指定する onTickHandler:外部イベント処理のインタフェースを実装して、渡す
    public CustomTimer(int interval,Runnable onTickHandler){
        this.interval=interval;
        setOnTickHandler(onTickHandler);
        // android.os.Handler クラスを使って 
        //・スレッドを遅延して、イベント処理を繰り返します
        //・スレッドを停止したい場合、繰り返しを中断します
        handler=new Handler();
    }
    public void setOnTickHandler(Runnable onTickHandler){
        if(onTickHandler==null){
            return;
        }
        final Runnable tickHandler=onTickHandler;
        delegate=new Runnable(){
            @Override
            public void run(){
                tickHandler.run();
                handler.postDelayed(delegate,interval);
            }
        };
    }
    //タイマーがスタート
    public void start(){
        if(ticking){
            return;
        }
        handler.postDelayed(delegate,interval);
        ticking=true;
    }
    //タイマーがストップ
    public void stop(){
        handler.removeCallbacks(delegate);
        ticking=false;
    }
}