Android 4.4からSDカードを読み取るにはパーミッションの追加が必要。
AndroidManifest.xml
1 |
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
Android 4.1~4.3端末で「開発者向けオプション→USBストレージの保護」にチェックを入れると4.4と同様にパーミッションが必要となる。
Android 4.4からSDカードを読み取るにはパーミッションの追加が必要。
AndroidManifest.xml
1 |
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
Android 4.1~4.3端末で「開発者向けオプション→USBストレージの保護」にチェックを入れると4.4と同様にパーミッションが必要となる。
Android 4.4(API 19)よりAlarmManagerの挙動が変わったらしい。4.4端末を購入して気付いた。
バッテリー消費を抑えるために登録されたアラームを個別に実行せずに、システムが判断してある程度まとめて実行するようになったようだ。例えば10分の間に複数のアラームがあったら、どこかのタイミングでまとめて実行するためにいくつかのアラームは指定した時刻には実行されないことになる。それがどれくらいずれるのかはシステムの状態によるのだろう。実機で確認したところ最大10分以上の遅延が発生した。
目覚ましアプリ等、正確な時刻に実行させる必要のあるアプリには致命的。
API 19以降で正確に実行させる場合は新しく追加されたsetExact()メソッドを使用する必要があるようだ。ただし、これは間隔指定ができないため、アラームが実行される毎に再度setExact()で次回アラームを登録するような処理が必要となる。
とりあえずの対応として「android:targetSdkVersion=18」にしておけば従来の挙動となるようだ。
GETの場合
1 |
webview.loadUrl("http://example.com/?param1=value1¶m2=value2"); |
POSTの場合
1 2 |
String postData = "param1=value1¶m2=value2"; webview.postUrl("http://example.com/", postData.getBytes()); |
ただし、サーバ側でPOST元の制限をされていたら表示できない気がする。試してないけど。
アプリがアップデートされたタイミングで処理を行う。
※新規インストール時も動く。
・AndroidManifest.xml
1 2 3 4 5 6 7 |
<receiver android:name=".MyAlarmBootCompletedReceiver" > <intent-filter> <action android:name="android.intent.action.PACKAGE_REPLACED" /> <data android:scheme="package" /> </intent-filter> </receiver> |
※注意
その他のBOOT_COMPLETED等も同時に設定する場合は以下のようにintent-filterを分けないとPACKAGE_REPLACEDしか受信されない。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<receiver android:name=".MyAlarmBootCompletedReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.DATE_CHANGED" /> <action android:name="android.intent.action.TIMEZONE_CHANGED" /> <action android:name="android.intent.action.TIME_SET" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.PACKAGE_REPLACED" /> <data android:scheme="package" /> </intent-filter> </receiver> |
・レシーバ
すべてのアプリのアップデートを受信するため、自アプリの判定が必要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class MyAlarmBootCompletedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String packagePath = intent.getDataString(); // TODO 自動生成されたメソッド・スタブ if(action.equals(Intent.ACTION_PACKAGE_REPLACED) && packagePath.equals("package:" + context.getPackageName())){ //自アプリのアップデート時の処理 } } } |
うるう年も考慮されている。
1 2 3 |
Calendar cal = Calendar.getInstance(); cal.set(2012, 2, 1); int lastDayOfmonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH); //29 |
注意点はcal.set(year, month, day);でday=1を与えること。
例えばdayに31日を与えると31日が存在しない月の場合に翌月の末日が取得される。
UILabelを角丸にする。
1 2 3 |
label.backgroundColor = [UIColor orangeColor]; label.layer.cornerRadius = 10; //←角丸 label.clipsToBounds = YES; //←iOSバージョンによって挙動が変わるのに対応 |
labelの背景をlabel.layer.backgroundColorで指定しているサイトがあったが、tableviewのセル上のlabelでlabel.layer.backgroundColorを指定するとセルをタップしたあと背景がクリアされる。
Background FetchではNSURLConnectionは使用できないのでNSURLSessionを使用する
・ダウンロード結果を受け取るクラスを作成
MyDownloadDelegate.h
1 2 3 4 5 6 7 8 |
#import <Foundation/Foundation.h> @interface MyDownloadDelegate : NSObject <NSURLSessionDataDelegate> - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes; - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite; - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location; @end |
MyDownloadDelegate.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#import "MyDownloadDelegate.h" @implementation MyDownloadDelegate - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSData *data = [NSData dataWithContentsOfURL:location]; NSString *identifier = session.configuration.identifier; NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"Downloaded!id=%@:%@",identifier, contents); } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { } -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { } |
AppDelegate.m
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 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //background fetchが呼ばれる最短の間隔 [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];//最短 return YES; } - (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { //処理 NSString *identifier; NSURLSessionConfiguration *conf; NSURLSession *session; NSURL *requestURL; NSMutableURLRequest *request; NSData *data; NSURLSessionDownloadTask *task; MyDownloadDelegate *downloadDelegate = [[MyDownloadDelegate alloc] init]; //GET identifier = @"BackgroundFetchSessionGet"; conf = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier]; session = [NSURLSession sessionWithConfiguration:conf delegate:downloadDelegate delegateQueue:nil]; requestURL = [NSURL URLWithString:@"URL"]; request = [NSMutableURLRequest requestWithURL:requestURL]; request.HTTPMethod = @"GET"; task = [session downloadTaskWithRequest:request]; [task resume];//処理開始 //POST identifier = @"BackgroundFetchSessionPost"; conf = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier]; session = [NSURLSession sessionWithConfiguration:conf delegate:downloadDelegate delegateQueue:nil]; requestURL = [NSURL URLWithString:@"URL"]; data = [@"a=111&b=222" dataUsingEncoding:NSUTF8StringEncoding]; request = [NSMutableURLRequest requestWithURL:requestURL]; request.HTTPMethod = @"POST"; request.HTTPBody = data; task = [session downloadTaskWithRequest:request]; [task resume];//処理開始 //処理後に呼ぶ //UIBackgroundFetchResultNewData 更新成功 //UIBackgroundFetchResultNoData 更新なし //UIBackgroundFetchResultFailed エラー completionHandler(UIBackgroundFetchResultNewData); } |
バックグランドで定期的に処理を行う。(例えば更新チェック等)
ただし、何分毎や何時何分にといった指定はできず実行間隔はOSによって決定される。アプリの起動頻度等によっても変動するらしい。
また、バックグランドではNSURLConnectionは動作しない。代わりにNSURLSessionを使用すること。
・プロジェクトの設定
プロジェクト選択→Capabilities→Background ModesをON→Background fetchにチェック
・コード(AppDelegate.m)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //background fetchが呼ばれる最短の間隔を設定 [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];//最短 //[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalNever];//呼ばれない return YES; } -(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { //処理 NSLog(@"receive fetch"); //処理後に必ず呼ぶ //UIBackgroundFetchResultNewData 更新成功 //UIBackgroundFetchResultNoData 更新なし //UIBackgroundFetchResultFailed エラー completionHandler(UIBackgroundFetchResultNewData); } |
・テスト
シミュレータで実行時はxcodeの
Debug→Simulate Background Fetch
で手動でBackground fetchを発生させることができる。