NetInstallのソースにできるOS Xインストーラのバージョン
OS X Serverの動作しているMacと同じバージョンのインストールイメージしか、認識しないようです。
イメージの作成には、「システムイメージユーティリティ」を利用します。
Yosemite上のシステムイメージユーティリティでEl Capitanのインストールイメージをベースにしようとしたら、ダメでした。
「ソースが見つかりません」となっていて、選択できない状態。
El Capitan上では、YosemiteのもOKなのだろうか?
2.3.2016追記
El Capitan上で確認したところ、Yosemiteのインストーラは認識できませんでした。作成済みのNetRestoreイメージ(Yosemite版)であれば、El Capitan上のOS X Serverで配信は可能です。
Core Dataで作成されたSQLiteファイルの場所を確認する
Core Dataで管理されている、SQLiteファイルの場所の特定方法です。
開発時、アプリをシミュレーターへ転送して実行する度にパスが変わるのでちょっと確認しづらいですよね。
方法
AppDelegate.swift内でパスをログ出力
プロジェクト作成時にCore Dataを利用するように設定すると、AppDelegate.swift内にCore Dataの基本的なコードが生成されます。
そのコード中、persistentStoreCoodinator属性に代入されるクロージャ内で変数urlにSQLiteのパスが設定されています。
まず、それをprintln()関数でログ出力します。
persistentStoreCoodinator属性を評価
しかし、persistentStoreCoodinator属性はlazy、すなわち遅延評価されます。
開発が進んだ後であれば、この属性が評価されるはずなので問題ないです。が、プロジェクトを作成してすぐの段階では、まだこの属性を評価するコードを書いていないので、クロージャが実行されません。
そのため、この属性を評価しておきます。
コード例
AppDelegate.swiftより抜粋
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = { var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("CoreDataSample.sqlite") // ===== SQLiteファイルのパスを表示する ===== println(url) // ====================================== 〜 中略 〜 return coordinator }()
ViewController.swiftより抜粋
override func viewDidLoad() { super.viewDidLoad() // ===== AppDelegateのpersistentStoreCoodinator属性を評価する ===== let coodinator = (UIApplication.sharedApplication().delegate as AppDelegate).persistentStoreCoordinator // ============================================================= }
出力例
file:///Users/User/Library/Developer/CoreSimulator/Devices/5D679BDA-E43B-406A-BB91-7B9A41002F38/data/Containers/Data/Application/1E2246CC-C713-4E69-ACBD-F043DC7C3A68/Documents/CoreDataSample.sqlite
アクティビティが起動中のときだけ NFC に反応させる(Foreground Dispatch)
最近、 NFC タグで遊んでます。
でもなかなか、アプリのネタが思いつかないものです。
さて、今回は Android アプリで NFC アプリを開発する際のポイントのひとつについて書きます。
今だけ反応して欲しいんだけど…
たとえば、 NFC タグにデータ書き込みを行うアクティビティを作成していたとします。
NFC タグに反応するように、通常は以下のようにインテントフィルターをマニフェストファイルに書くと思います。
<activity android:name="NFC にデータを書き込むアクティビティ" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <action android:name="android.nfc.action.TAG_DISCOVERED" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
しかし、上記の形式では
- アプリを起動していなくても、 NFC タグをかざすと常に書き込もうとする
ことになります。
でも、 NFC を使う場合って、書き込むよりは読み込む場面の方が多いですよね。
なので、
ということが求められます。
Foreground Dispatch
この要求を満たすために用意されている仕組みが、 Foreground Dispatch です。
マニフェストファイルにインテントフィルターを指定してしまうと、上記のように常に NFC に対してアクティビティが反応してしまいます。
そのため、コード内で動的にインテントフィルターを指定する必要があります。この場合、マニフェストファイルには NFC に関するインテントフィルターは記述しません。
アクティビティ内のコード例(抜粋)は以下となります。
private NfcAdapter mNfcAdapter; private PendingIntent mPendingIntent; private IntentFilter[] mIntentFilters; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); 〜省略(レイアウトの指定など)〜 /* * Foreground Dispatch の設定 * このアプリが優先して Intent を受け取れるようにする * 実際の設定・解除は onResume と onPause で実行 */ mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext()); Intent intent = new Intent(getApplicationContext(), getClass()); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); mPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0); IntentFilter intentFilter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); mIntentFilters = new IntentFilter[]{intentFilter}; } @Override protected void onResume() { super.onResume(); // Resumed に移る前に、 Foreground Dispatch を有効にしておく mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, mIntentFilters, null); } @Override protected void onPause() { super.onPause(); // Paused に移る前に、 Foreground Dispatch を解除しておく mNfcAdapter.disableForegroundDispatch(this); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); 〜 省略(NFC タグ発見時の読み込み or 書き込み処理) 〜 }
このように Foreground Dispatch を設定しておくことで、アクティビティが表示されている場合だけ NFC タグに対する処理を行うことができます。
それ以外の場合に NFC タグを近づけても、別の NFC 対応アプリが反応するようになります。
Notification が通知されない
知り合いがハマってたので書いておきます。
問題
Notification が表示されない。文法的には正しいし、エラーを吐いているようでもないんだけど…。
解決策
Notification.Builder#setSmallIcon() を呼びましょう。
Android で Notification を利用する際は、 SmallIcon を設定しないと通知がされないです。「通知は文字列だけでいいんだけど」という場面もありそうな気はしますけどね。
API ドキュメントには特に記載がありませんが、 Building a Notification | Android Developers の方に書いてあります。
このドキュメントを読むと、以下の 3 つが必須だということがわかりますね。
- setSmallIcon()
- setContentTitle()
- setContentText()
サンプル
package foo.bar; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends Activity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button buttonNotify = (Button) findViewById(R.id.button_notify); buttonNotify.setOnClickListener(this); } @Override public void onClick(View view) { NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); Intent intent = new Intent(this, NextActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); Notification notification = new Notification.Builder(this) .setContentIntent(pendingIntent) .setAutoCancel(true) .setTicker("ティッカーだよ。") .setContentText("コンテントテキストだよ。") .setContentInfo("コンテントインフォだよ。") .setContentTitle("コンテントタイトルだよ。") .setWhen(System.currentTimeMillis()) // .setSmallIcon(R.drawable.my_icon) // SmallIcon を設定しないと通知されない(例外も発生しない) .build(); notificationManager.notify(0, notification); } }
Dropbox API 利用時の ProGuard 設定
android:launchMode="singleTask"
タスクをいろいろといじっていて、頭の中が混乱してきたので整理。
launchMode
デフォルトでは、起動したアクティビティはみんな同じタスクに入る。かつ、呼び出されるごとに同じアクティビティがいくつでもインスタンス化されて積まれる。
でも、その挙動を変えたいよねー、というときには AndroidManifest.xml に launchMode を設定する。
launchMode の種類
- standard(デフォルト値)
- singleTop
- singleTask
- singleInstance
singleTask
The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.
<activity> | Android Developersより引用
私の認識(公式ドキュメントと、自分で作成したサンプルによる考察)
- この設定がされたアクティビティは新しいタスクのルートとして配置される
- 同じ taskAffinity が設定されているアクティビティ同士は同じタスクに配置されるので、必ずしもルートとして配置されるとは限らない(明示的な記述は見つからないが、実際の動きがそうなっている)
- でも同じアクティビティが既にあったら、新しく作らずに onNewIntent() を呼ぶ
- その際、対象アクティビティが処理を受け付ける必要があるため、上に積まれていたアクティビティは破棄(明示的な記述は見つからないが、実際の動きがそうなっている)
TODO: あとで図を使って整理