ぽんたのプログラミング勉強部屋(仮)

プログラミングについて色々勉強したことのメモ集です。どこにでも載っているような情報ばかりですw

VS2017で開発したAndroidアプリのapkファイルを作って実機にインストールする。

手順1:Releaseビルド

手始めにReleaseビルドを完了させておきます。
特筆すべき点は特にないので以上。

手順2:apkファイルを作成する

メニューから[ビルド] → [アーカイブ]を選択します。
勝手にアーカイブ化が始まるので、完了まで待ちます。
完了後は以下にapkファイルが作成されています。

obj/Release/android/bin

実機にインストール…してみたのだが

なぜかエラーになって正常にインストールできない…。
色々調べてみたところReleaseビルドして作成されたapkファイルではなく、
アーカイブマネージャから作成しないといけないことが発覚。

手順3:apkファイルを作成する(アーカイブマネージャ版)

メニューから[ビルド] → [アーカイブ]を選択します。
表示された画面の下部に「配布...」と書かれたボタンがあるので、それを押下します。

f:id:ponta666:20170604002747p:plain:w498:h309

アドホック」か「Google Play」か配布するチャンネルを選択するダイアログが表示されますが、
ここでは「アドホック」を選択します。

f:id:ponta666:20170604003244p:plain:w425:h242

次の画面では、「+」をクリックしてAndroidのキーストアを作成します。
勉強用に作っただけなので、とりあえずエラーにならない程度に適当に項目を埋めました。

f:id:ponta666:20170604003533p:plain:w225:h274

「作成」ボタンを押下すると処理完了後にダイアログが閉じられ署名IDの画面に戻ります。
リストには先ほど作成した名前が表示されているので、選択して「名前を付けて保存」ボタンを押下します。
保存先を聞かれるので適当な場所を選択します。
実行中にパスワードを聞かれるので、キーストア作成時に設定したパスワードを入力すれば、
処理完了後に署名付きのapkファイルが作成されています。

手順4:実機にインストー

手順3で作成した署名付きのapkファイルで再度実機にインストールしてみました。
因みにインストールはAndroid Studioを使っていた時と同じ方法で、adb.exeを使ってインストールしました。
インストール方法の詳細は以下の記事を参照。

Android Studioでapk(署名なし)ファイルを作成して実機にインストールする - ぽんたのプログラミング勉強部屋(仮)

今度はインストールが成功し、アプリリストに追加されているのが確認できました。

Visual Studio 2017で開発したAndroidアプリの実機デバッグ

メモる程の内容でもないと思うけど、久々の投稿なのでリハビリがてらに…。

環境

今回の環境は以下の通り。

Windows7
Microsoft Visual Studio Community 2017
SONY XPERIA Z4

事前準備

デバッグ前に以下のことをやっておく。

①実機側で「USBデバッグ」をONにしておく。
②ドライバのインストー

①はここで説明するまでもないので省略。
Android Studioを使っていたせいか、②をしなくても動いたのでこれも省略。
環境が変わってハマったらその時にでも詳細を調べようかな。

プロジェクトの作成

サンプルとして1つ適当なプロジェクトを作成します。
VSのメニューから
[ファイル]→[新規作成]→[プロジェクト]
と選択していき、[Visual C#]の項目から[空のアプリ(Androi)]を選択。
名前は適当に「XamarinSampleApp」とでもしておきました。

デバッグ

今回は出来上がったプロジェクトそのままビルドしてデバッグしてみました。

画面上部に実機を選択するドロップダウンリスト(下図の赤丸部分)があるので、
リストから使用する実機を選択。

f:id:ponta666:20170528185546p:plain:w401:h184

後はリスト横の実行ボタンをクリックすれば実機にプログラムが転送されてデバッグが可能になります。

実機側の画面
f:id:ponta666:20170528185537p:plain:w216:h384
さ、寂しすぎる…

DataTableからDictionaryを作成する。

仕事の中で、「DataTableをDictionaryに変換できたらいいのにな~」と思って調べたらあったので、
その方法のメモ。

Dictionary<string, string> dic;
DataTable dt;

// DataTableへのデータの設定等は割愛

dic = dt.AsEnumerable().ToDictionary(
              row => (string)row["ID"],
              row => (string)row["name"]);

上記の例だと"ID"がキー、"name"が値のDictionaryが作れます。

月末の日付を取得する方法

業務用アプリの開発をしているとよくあるのが、月末や月初の日付でどうのこうのって処理。
月初は必ず1日なのに対し、月末は月によって変わるから困りもの。
月末の取得方法でよくあるのが多分以下の方法。

DateTime dt = new DateTime(2016, 11, 1);
dt = dt.Add(-1)

10月の月末が欲しい場合、"2016/11/01"のDateTimeオブジェクトを作っておいて1日減算する方法。
俺も今まではそうして取得していたんだけど、最近こんな方法もあることを知った。

DateTime dt = new DateTime(2016, 10, DateTime.DaysInMonth(2016, 10));

ポイントはDaysInMonthメソッド。
こいつは指定した年月の末日を返してくれる。
こいつの戻り値をDateTimeオブジェクトを生成する際の引数に渡してやれば、
わざわざ11月1日から1日引いて~なんてやらなくても月末が取得できちゃうってすんぽー。

まぁ、手間はあまり変わらない気がするけどね。

ってか日付だけじゃなく、yyyy/mm/ddで返してくれたらいのにね…。

mutexを使ってアプリの二重起動を禁止する方法

Mutexクラスを使う事でアプリの二重起動を禁止することができます。

まずはApp.xamlにStartupとExitのイベントを登録します。

<Application x:Class="Sample.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Startup="Application_Startup"
             Exit="Application_Exit">
    <Application.Resources>
    </Application.Resources>
</Application>

App.xaml.csにStartupとExitのイベントハンドラを追加します。

using System.Threading;

// ①
private Mutex mutex = new Mutex(false, "ApplicationName");

// ②
private void Application_Startup(object sender, StartupEventArgs e)
{
    if (!mutex.WaitOne(0, false))
    {
        MessageBox.Show("アプリケーションは起動済みです。",
                        "アプリの二重起動",
                        MessageBoxButton.OK,
                        MessageBoxImage.Exclamation);
        mutex.Close();
        mutex = null;
        this.Shutdown();
    }

    // mutexを取得できたら何か処理
}

// ③
private void Application_Exit(object sender, ExitEventArgs e)
{
    if (mutex != null)
    {
        mutex.ReleaseMutex();
        mutex.Close();
        mutex = null;
    }
}

色々端折ってるけど、そこはどうにか脳内補完で。
①でmutexを生成しています。この時にアプリケーション固有の名前(サンプルでは"ApplicationName")を渡します。

②のStartupイベントハンドラ内では、mutexの取得を行い取得できなかった(=二重起動している)場合はメッセージを表示して、アプリを終了するようにしました。

③Exitイベントハンドラ内では、取得したmutexの解放を行います。これを忘れるとmutexを取得したままになって次回起動時に二重起動判定されちゃうので注意!
また、二重起動でShutdown()を読んで終了した時もExitイベントハンドラが呼ばれるので、二重で終了処理を行わないように、nullチェックを入れてます。

ざっくりとこんな感じ。
本当は例外発生したらどうすんだとか、もうちょっと色々考えないといけないんだろうけどね…。

Windowsの共有フォルダをAndroid端末から参照する方法

Android端末からWindowsの共有フォルダを参照する方法を調べたので、実装方法をメモ。

JCIFSのダウンロード

どうやらJCIFSというライブラリを使えば、AndroidからWindowsの共有フォルダを参照できる様子。
Windowsのファイル共有サービスで利用されているSMBを拡張し、Windows以外のOSやアプリで
利用できるようにしたものがCIFSだそうな。
で、Javaから利用できるようにしたものがJCIFSだとかなんとか…。

それはさておき、JCIFSのライブラリを以下からダウンロードします。
https://jcifs.samba.org/

jcifs-1.3.18.zipをダウンロードしたら展開し、jcifs-1.3.18.jarをAndroidのlibフォルダに配置します。

パーミッションの変更

AndroidManifest.xmlパーミッションの設定を追加します。
追加する内容は以下の通り。

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ADD_SYSTEM_SERVICE"/>

実装

以下、実装のサンプル。
ボタンを押したら共有フォルダにアクセスして、そこにあるファイルの数を取得するだけのプログラムです。

public class MainActivity extends AppCompatActivity {
    private AccessTask task;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView txtView = (TextView)findViewById(R.id.textView);

        task = new AccessTask(txtView);

        Button accessbtn = (Button)findViewById(R.id.button);
        accessbtn.setOnClickListener(AccessListener);
    }

    private View.OnClickListener AccessListener = new View.OnClickListener() {
        public void onClick(View v)
        {
            task.execute(1);
        }
    };
}

以下のプログラム内のhost、user、pswd、共有フォルダ名("/share/"の箇所)は適宜読み替えてください。

import jcifs.smb.SmbFile;

public class AccessTask extends AsyncTask<Integer, Integer, String> {
    private TextView textView;

    public AccessTask(TextView txtView)
    {
        super();
        textView = txtView;
    }

    @Override
    protected String doInBackground(Integer... value)
    {
        SmbFile dir;
        String host = "host-name";
        String user = "user";
        String pswd = "password";
        String str;

        try
        {
            String path = "smb://" + user + ":" + pswd + "@" + host + "/share/";
            dir = new SmbFile(path);

            SmbFile[] files = dir.listFiles();
            str = Integer.toString(files.length) + "個のファイルがあります。";
        }
        catch(Exception e)
        {
            Log.e("Err", e.toString());
        }

        return str;
    }

    @Override
    protected void onPostExecute(String param)
    {
        textView.setText(param);
    }
}

ここで重要なのが以下の2点。
①パスの最後はスラッシュ"/"を付けること
②共有フォルダにアクセスする処理はメインスレッドではなく、別スレッドで行うこと。

②についてですが、Android3.0以降でこのような動作になったらしく、
メインスレッドでアクセスしようものなら、もれなくNetworkOnMainThreadExceptionが付いてきます。

最初コレに気づかずに3時間くらい悩んだよ…。

Android Studioでapk(署名なし)ファイルを作成して実機にインストールする

毎回何か作る度にやり方を忘れているので、いい加減メモっておく。

apkファイルの作成

まずは署名なしapkファイルを作成する。
Android Studioでは以下の通り。

メニューバーから、[Build]⇒[Build APK]を選択。
「APK(s) generated successfully.」と表示されたら正常終了。

"プロジェクトフォルダ\app\build\outputs\apk\"配下に.apkファイルが作成されます。

実機にインストール

①事前準備
事前準備として、以下を実施。

  • 「提供身元不明のアプリ」を有効にする
  • USBデバッグを有効にして、PCにUSB接続する

②作成したapkファイルをAndroid SDKのインストールディレクトリにコピー
apkファイルをadb.exeがあるディレクトリにコピーします。
ちなみに、うちの環境では以下の場所。

C:\Users\ユーザ名\AppData\Local\Android\sdk\platform-tools

③インストール
下記のコマンドを実行すればインストール開始。

adb install -r apkファイル

コマンドプロンプトで直接打ち込んで実行してもいいし、
batを作成して実行してもOK。