俺言語。

自分にしか理解できない言語で書かれた備忘録

【Android】Activity間でデータの共有 ->Applicationクラスの使い方

各アクティビティ間でデータを共有したい時は通常

-> 確かプリミティブな型しか渡せなかったような気がする。

-> オリジナルな型も渡せるけど実装がめんどくさかった気がする。

の方法がある。

だけど、双方とも実装がわかりづらいのと、
参照渡しが確か出来ないので受け渡し元で値が更新されるようなデータの共有には向いていない気がする。

そこでApplicationクラスを継承したデータ共有用のクラスを作成。
グローバル変数のように使えるので全体の見通しも良く使いやすくてGood.

使い方

Applicationクラスを実装したクラス

1. Applicationクラスを継承する

2. メンバーに共有する変数を宣言

3. onTerminateをオーバーライドする。

4. メンバーにアクセスするような関数を定義する。

参考例はこちら

public class MyApplication extends Application {

    private final String TAG = this.getClass().getName();
    public HashMap<String, Double> sensor_data = new HashMap<String, Double>();

    @Override
    public void onCreate() {
        super.onCreate();

        sensor_data.put("SAMPLINGTIME", 0.0);
        sensor_data.put("ACC_X", 0.0);
        sensor_data.put("ACC_Y", 0.0);
        sensor_data.put("ACC_Z", 0.0);

    }

    @Override
    public void onTerminate() {
        super.onTerminate();
    }
    
    /*
    * メンバーにデータをセットするためのメソッド
    */
    public void setObj(String key, Double value){
        sensor_data.put(key, value);
    }

    /*
    * メンバーからデータを取得するためのメソッド
    */
    public double getObj(String key){
        return sensor_data.get(key);
    }
}
Applicationクラスにアクセスするクラス

1. this.getApplication() を実行してAppクラスが使用できるようにする。

2. Appクラスで定義したデータアクセス用のメソッド等を用いてデータにアクセスする。

参考例はこちら

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // このActivityを画面に表示
        setContentView(R.layout.activity_sdc_can_disp);

        // 受信データを格納しているAppクラスを使用できるようにする
        myapp = (MyApplication)this.getApplication();
        latestdata = new HashMap<String, Double>();

        // データを取得
        data = myapp.getObj("SDC_FACTOR_FL");

【Android】BindServiceの使い方

ServiceにはIntent ServiceとBindServiceがあり、それぞれの特徴は下記、

IntentService

  • UIスレッドとは別のスレッドで作動する
  • 実装が比較的楽そう
  • 呼び出し元のActivityが破棄されても実行し続ける

BindService

  • UIスレッドで動く..(2)
  • 実装が少しめんどくさい
  • 呼び出し元のActivityが破棄されると一緒に終了する...(1)


ここで呼び出し元のActivityが破棄されると一緒に終了するという特徴(1)が
アプリが終了しても変にサービスが残らなくていいかと思い、BindServiceを採用。

しかしのちに(2)の結局はUIスレッドで動作するという結果を知る.
よってBindServiceではUIの描画を妨げる程の思い動作には向いていないと思われるので注意.

使い方

呼び出し元

1. ServiceConnectionクラスのインターフェイスを実装したサブクラスを作成.

その中に onServiceConnected と onServiceDisconnected をOverrideする.
onServiceConnected内に呼び出し先で定義したBinderクラスを継承した独自クラスのインスタンスを受け取る


2. bindService()でServiceを起動する. bindServiceメソッドはContextクラスのため何もせずそのまま呼び出せる.


呼び出し先
1. Serviceを継承する.

2. onBindメソッドを継承する。これは最初にbindServiceされたときに呼ばれる。
ここでBinderクラスを継承した独自クラスのオブジェクトを返す.

3. Binderクラスを継承した独自クラスを定義する
ここにデータや処理を記述することで呼び出し元がbinderオブジェクトを通して操作できる

4. onCreate, onRunError, onUnbind, OnDestroyメソッドをOverrideする。

参考例はこちら
MainActivity.java (呼び出し元)

public class MainActivity extends AppCompatActivity {

    // ServiceConnection object class
    private ServiceConnection conn = new ServiceConnection() {
        // callback for connection successful
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            System.out.println("Service connected");
            // onBindされたときにbinderが返される。このbinderにサービス側で処理やデータを格納しておいて使う側が呼び出す
            binder = (BindServiceSensorHandling.MyBinder) iBinder;
        }
        // callback for connection unsuccessful
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            System.out.println("Service disconnected");
        }
    };

        findViewById(R.id.button4).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //サービスがすでに動いているかをチェック
                ActivityManager manage = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
                for(ActivityManager.RunningServiceInfo serviceInfo : manage.getRunningServices(Integer.MAX_VALUE))
                    if(BindServiceSensorHandling.class.getName().equals(serviceInfo.service.getClassName())){
                        // 動いている場合は一回unBindする
                        unbindService(conn);
                    }
                // Bind BindService
                // Intent for MainActivity with Bind service
                final Intent intent = new Intent(MainActivity.this, BindServiceSensorHandling.class);
                bindService(intent, conn, Service.BIND_AUTO_CREATE);
            }
        }
}

BindServiceSensorHandling.java (呼び出し先)

public class BindServiceSensorHandling extends Service{

    // onBind()で返されるBinder
    private MyBinder binder;

    /*
    最初のbindService呼び出しのみ、システムにIBinderインターフェースを渡すために呼ばれる。
     */
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("Service is binded");
        binder = new MyBinder();
        return binder;
    }

     /*
     Serviceのインスタンスがない状態で、クライアントがstartServiceまたはbindServiceを呼んだ時に
     *Serviceのインスタンス生成で呼ばれる。すでにインスタンスが存在している場合は呼ばれない。
     */
    @Override
    public void onCreate() {}

    /*
     onBind()で返されるbindeオブジェクト. ここに処理やデータを記述する。
     */
    public class MyBinder extends Binder {

        boolean sendRequest(int req){
            String buf = String.valueOf(req);
            try {
                usbSerialPort.write(buf.getBytes(), WRITE_WAIT_MILLIS);
                return true;
            }catch (IOException e){
                Log.e(TAG, "Mode request error");
                return false;
            }
        }
    }
    @Override
    public void onRunError(Exception e) {
        Log.d(TAG, "Runner stopped");
    }

    /*バインドしているクライアントが「全て」いなくなったときに呼ばれる。そのためunbindServiceが呼ばれても、
     *ほかにバインドしているクライアントが存在した場合、onUnbindは呼ばれない。
     */
    @Override
    public boolean onUnbind(Intent intent) {
        disconnect();
        System.out.println("System is unbined");
        return true;
    }

    //バインドされたクライアントがなくなって、onUnbindが呼ばれたあとに呼ばれる
    @Override
    public void onDestroy() {
        System.out.println("Service is destroyed");
        this.quit = true;
    }
}

【Android】usb-serial-for-androidの使い方

使うとき

使用する際の流れはたぶん...

1. USBデバイスを取得
-> UsbManagerクラスの getSystemService

2. USBシリアルとして使用できるデバイスドライバのリストを列挙
-> UsbSerialProberクラスの getDefaultProber().findAllDrivers(usbManager)

3. 使用するUSBドライバでデバイスを開く
-> UsbManagerクラスの openDevice()

4. 使用するUSBドライバでUSBポートを開く
-> UsbSerialPortクラスの open()

5. シリアル通信パラメータ(BaudRateなど)を設定する
-> UsbSerialPortクラスのsetParameters()

6. データ送信要求をセットする *Arduino系と通信する際はこれが必要。
-> UsbSerialPortクラスのsetDTR()

7. データを送受信する
-> UsbSerialPortクラスのread/write 
またはSerialInputOutputManager.Listenerクラスを実装してonNewDataメソッドをOverrideする

USBデバイスとそのバイスのポートを開く必要がある。
また、6.のsetDTRをしないと通信がスタートしない物もあるので注意。

別途USBのパーミッション処理が必要。

使用例はこちら

public class BindServiceSensorHandling extends Service implements SerialInputOutputManager.Listener{

    public SerialInputOutputManager usbIoManager;
    private UsbDeviceConnection usbConnection;
    private UsbSerialDriver usbdriver;
    public UsbSerialPort usbSerialPort;

    // 1 
    UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);

    // 2
    List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager);

    // 3
    usbdriver = availableDrivers.get(0);
    usbConnection = usbManager.openDevice(usbdriver.getDevice());

    // 4. Open USB port
    usbSerialPort = usbdriver.getPorts().get(0); // Most devices have just one port (port 0)
    usbSerialPort.open(usbConnection);

    // 5
    usbSerialPort.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);

    // 6
    usbSerialPort.setDTR(true);

    // 7. Listenerクラスをセット.非同期処理を開始
    usbIoManager = new SerialInputOutputManager(usbSerialPort, this);
    Executors.newSingleThreadExecutor().submit(usbIoManager);

}

終わるとき

1. USBポートを閉じる
-> UsbSerialPortクラスのclose()

2. リスナーを止める
-> UsbInputOutputManagerクラスのstop()

使用例はこちら

    private void disconnect(){
        if (usbSerialPort != null) {
            try {
               // 1
                usbSerialPort.close();
                usbSerialPort = null;
            } catch (IOException e) {
            }
        }
        if(usbIoManager != null)
            // 2     
            usbIoManager.stop();
        usbIoManager = null;
     }

【Android】usb-serial-for-android セットアップ、jar作成、インポート

セットアップ

[20/06/10追記]

下記の方法でうまく行かないことがあったので本家のサイトに書かれているように
jitpackというサービスを使ってライブラリをインポートする方法に変更。

1. Projectのbuild.gradleに下記を追加
f:id:hukkuramamemoti:20200510140352p:plain

2. Module.appの方のbuild.gradleに下記を追加
f:id:hukkuramamemoti:20200510140520p:plain


これでライブラリが無事動くようになった。あら不思議。

[追記おわり]

                                                                                                                                                                                        • -

GitHubでプロジェクトをCloneする。
Cloneの方法は
f:id:hukkuramamemoti:20200412011443p:plain

f:id:hukkuramamemoti:20200412012032p:plain
ここに下記のパスを入れてCloneしてくる。
https://github.com/mik3y/usb-serial-for-android

Jar作成

ライブラリとして他のアプリケーションで使えるようJar化する。


1. build.gradleに処理を追加

下記のbuild.gradleに
f:id:hukkuramamemoti:20200412012415p:plain
f:id:hukkuramamemoti:20200412012524p:plain
を追加する。
いくつかのサイトには
from('build/intermediates/bundles/release/')と書かれていたがこのフォルダは自分がクローンしてたusb-serial-for-androidにはなぜかなく、
ビルドも失敗してしまったので、classes.jarが格納されていた'build/intermediates/aar_main_jar/release/'を使用したところbuild出来た。


2. モジュールのビルド

f:id:hukkuramamemoti:20200412012847p:plain
これを実行するとGradleのメソッド?オプション?からMakeJarが実行できるようになる。


3. MakeJar

f:id:hukkuramamemoti:20200412013046p:plain
f:id:hukkuramamemoti:20200412013122p:plain
これをダブルクリックで実行。
\usb-serial-for-android\usbSerialForAndroid\release\内にusbSerialForAndroidLibrary.jarができる。

アプリケーションへの組み込み

f:id:hukkuramamemoti:20200412013748p:plain
ここへ追加。ファイルを右クリックしてAdd as Libraryを選んでライブラリとしてこのファイルを認識させる。

f:id:hukkuramamemoti:20200412014109p:plain
f:id:hukkuramamemoti:20200412014231p:plain
appのbuild.gradleに依存を追加

f:id:hukkuramamemoti:20200412014333p:plain
usbSerialExamples/src/main/res/xml/device_filter.xmlを上記パスにコピー

f:id:hukkuramamemoti:20200412014505p:plain
f:id:hukkuramamemoti:20200412014612p:plain
ここのマニフェストにUSBのインテントフィルタを追加

lipoyang.hatenablog.com

【Android】よく使う&よく忘れる単語

  • アクティビティ

スマホ等に表示される画面.
画面が切り替わる(違う操作画面になる)毎にそれぞれアクティビティが存在する.

アクティビティを呼び出すための概念、オブジェクト.
ボタンが押されたときに違う画面を表示するにはインテントを使ってアクティビティを呼ぶみたいな感じ.

暗黙的インテントが飛んできたときにアクティビティやサービスが受け取って処理する場合に設定するフィルタ

【Android】参考サイト

参考にさせて頂きます。

AndroidをUSBシリアルのホストに - 滴了庵日録


Android USBシリアル通信方法 - Qiita

mbedのUSB CDCクラスとAndroidのUSBシリアルドライバとの通信について - Question | Mbed

AndroidのUSBホスト(OTG)機能を使って、接続されたArduinoとシリアル通信してみる話 - Androidのメモとか

【Python】Pyinstaller specファイル

Pyinstaller Specファイルのメモ

# -*- mode: python -*-

block_cipher = None
a = Analysis(
             [],  # 実行する.pyファイル
             pathex=[''],  # 上記.pyファイルが格納されているフォルダのパス
             binaries=[],  # 依存するdllファイルがあれば
             datas=[],  # 使用する画像ファイルやPDF、iniファイルを記述。タプルの2つ目はフォルダ指示。 e.g.) datas=[("memo.ini", ".")]
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)

pyz = PYZ(
           a.pure,
           a.zipped_data,
           cipher=block_cipher)

exe = EXE(
          pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=False   # 実行時のコンソール表示/非表示(debug時に使用すると便利)
 )

coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='')

Pyinstallerは実行時に
(例:%USERPROFILE%/AppData/Local/Temp/_MEIxxxxxx)
に展開されて実行される。
そのため、埋め込んだ画像ファイル等のリソースがこれを参照して実行するように
変更する必要あり。

Specファイルを作成後、
Terminalでカレントのディレクトリをspceファイルの場所まで移動して
>pyinstaller ***.spec
を実行する。