スマートフォンアプリ作成

Facebookは2012年8月に、それまでのハイブリッドアプリをネイティブアプリに作り替えました。

利用者からの「動作が遅くて使う気にならない」という意見を聞き入れたのです。

ハイブリッドアプリに投資した金額は全く無駄になりました。
ハイブリッドアプリをネイティブアプリに作り替えるには一からすべて作り直さなければならないためです。


「HTML5はプラットホームに依存せず、HTML5ならアプリも作れる。」という幻想が終焉した瞬間だった。

このエントリーをはてなブックマークに追加 mixiチェック

iOSとandroidアプリ作成時の対照表

動機

わたしはiOSからはじめたので、AndroidSDKやJavaの使い方に慣れていないため、iOSとandroid双方でアプリ作成した際の対照表を作ってみました。

2016.06.08更新
2016.03.31更新
2014.05.26作成

iOSとandroidアプリ作成時の対照表

  iOS
(Cocoa Touch framework on Xcode)
android
(android SDK, NDK on Android Studio)
開発者 Apple inc. Alphabet inc.
開発言語 Modern Objective-C, C++, C, Swift*

*SwiftはJavaライクな言語なので、androidアプリしか開発できない人やCが苦手な人には入りやすいと思います。
実行速度や生産性に変わりはありませんのでObjective-Cの資産を捨てて乗り換える必要性はまったくありません。
Java, C, C++(JNI)

*C++のほうがJNIで作りやすい
標準開発環境
(IDE)
Xcode (Apple) Android Studio (Google)
コンパイラ/ビルドシステム Apple LLVM Gradle(makeみたいなもの)
コンパイラは Javac と Eclipse Compiler for Java on JavaDevelopmentTools から選べる
プロジェクトのフォルダ構成 フォルダはどんな名称でどこに作ってもOK Androidではプロジェクトのフォルダ構成が細かく決まっている

proj
  build.gradle
  local.properties
  - module
    build.gradle
    - libs (jar library)
    - src - main
      AndroidManifest.xml
      - java - package(java souce files)
      - jni(JNI souce files)
      - jniLibs(JNI library)
      - res(resource files)
        - drawable(png, shape, selector)
        - layout(レイアウト, LinearLayout)
        - values(color, dimen, string, style)
        - menu(メニュー, menu)
        - anim(アニメーション, set)
        - raw(サウンドファイル, mp3)
      - assets(SQLite, HTML, Text file, vertics data files)


resファイルに属性を指定する方法
* 多言語化 : (フォルダ名)-(国コード)
例:values-ja
* 解像度 : (フォルダ名)-(解像度レベル)
例:mipmap-xxhdpi
* androidバージョン : (フォルダ名)-(バージョン)
例:values-v21
* 画面横サイズ : (フォルダ名)-(サイズ)
例:values-w820dp

アプリ内課金 inAppPurchase inAppBuilling
アプリストア App Store Google Play
デベロッパー登録(アプリストアに掲載するために必要) 年間登録料 99米国ドル(7,800円 税込み12,744円、学生無料)
Apple Developer Program - Membership for one year
電話またはメールでのサポート2回付き
登録料 25米国ドル(約2,500円+税)
販売手数料 アプリ価格、アプリ内課金価格の30% アプリ価格、アプリ内課金価格の30%
サブスクリプション(購読単位)アプリ内課金 1週間、1、2、3か月、6か月、1年 1か月、1年
アプリ販売申請からストア公開まで

7日ほどの審査期間、問題なければ公開される

2日ほどの審査期間、問題なければ公開される(2016年後半から審査が早くなった)

1回だけ Expedited review(特急審査)をリクエストすることができる。

審査なし、1~2時間後に公開される
ストア掲載申請時に必要な情報 App StoreへiOSアプリ申請時に必要な掲載情報一覧 GooglePlayへandroidアプリ申請時に必要な掲載情報一覧
アプリ設定 Info.plist(Build設定に定義されたファイル名)
最初に起動する nibファイル(StoryBoard)を定義する
AndroidManifest.xml
最初に起動するActivityを定義する
IntentするActivityはここですべて定義しなければならない
画面定義 nib (.xib)
storybord (.storyboard)
layout/.xml
国際化 Localizable.strings 文字列
localize.xib 画面
localize.storyboard 画面群
localize.png 画像



*Xcodeの右ペインで国際化指定すると自動的にフォルダ分けされる

res/values-ja/strings.xml 文字列
res/values-ja/arrays.xml 配列
res/layout-ja/layout.xml 画面
res/drawable-ja/loalize.png 画像


*国際化はリソースサブフォルダ名に国コードを付加したフォルダを作成する

プロジェクトで使用する画像 xxx.png

*どのフォルダに置いても良い
drawable/xxx.png
アプリ配布形式

.ipa(appStore, adHoc, enterprise)

.xcarchive(Xcodeでインストール)

 

.apk
アプリサイズ上限 3G4G接続でダウンロード上限:100MB
WiFi接続でダウンロード上限:4GB

android4.0以上
3G4G接続でダウンロード上限:200MB(内apk本体100MB)
WiFi接続推奨のダウンロード上限:4GB+100MB(拡張APKアセット)

android4.0未満

3G4G接続でダウンロード上限:150MB(内apk本体50MB)
WiFi接続推奨のダウンロード上限:4GB+50MB(拡張APKアセット)

ファイルタイプ .m
.mm
.cpp
.c
.java
.cpp (JNI)
.c (JNI)
デバイス画面サイズ(px)

画面フラグメント:3

*AutoLayout(SizeClass)という開発し易い仕組みを導入し、画面dpを統一せずフラグメントさせることを選んだApple。

*フルHDで再生表示できるのはiPhone6(S)PlusとiPadのみ

画面フラグメント:1

* 画面dpを統一し開発者がアプリ開発するのを容易にすることを選んだGoogle。

* 格安スマホ以外はすべてフルHDに対応した。

  iPhone
*320x480(dp:320x480)3.5インチ (iPhone, iPhone3GS)
*以上は現在では使用されていない

640x960 (dp:320x480) 3.5インチretina (iPhone4, iPhone4S)

*以降は16:9の画面
640x1136 (dp:320x568) 4インチretina (iPhone5, iPhone5S)
750x1334 (dp:375x667) 4.7インチretina (iPhone6, iPhone6S)
1242x2208 (dp:414x736) 5.5インチretina@3x (iPhone6Plus, iPhone6SPlus)

Phone
*mdpi 320x480
*hdpi 480x854 (dp:320x569)  4.3インチ
*以上は現在では使用されていない

*以降は16:9の画面
hdpi 540x960 (dp:360x640) QHD 4.3インチ *Quarter 1/4HD
xhdpi 720x1280 (dp:360x640) HD 4.3インチ~4.6インチ
xxhdpi 1080x1920 (dp:360x640) FullHD 5インチ~5.4インチ
xxxhdpi 1440x2560 (dp:360x640) WQHD 5.5インチ *QuadのほうのQHD

 

iPad
768x1024 (dp:768x1024) 10インチ (iPad, iPad2), 8インチ(iPadmini)

1536x2048 (dp:768x1024) 10インチretina (iPad(air)), 8インチretina(iPadmini)
*以上は3:4の画面比率(iPad)

2048x2732 (dp:1024x1366) 12.9インチ(iPad Pro)

Tablet
hdpi 800x1280 (dp:600x960) HD+ 7インチ~8インチ
xhdpi 1200x1920 (dp:600x960) FullHD+ 7インチ~10インチ
xhdpi 1600x2560 (dp:800x1280) FullHD+ 10インチ


デバイス機能

iPhone (2007)
登場時は期待していなかったが、私の人生を変えたデバイスとなった。

iPhone3G (2008)
日本の電波に対応

iPhone4 (2010)
Retinaディスプレイ(人の目で判別できる限界解像度)実装
フロントカメラ実装

iPhone 5 (2012)
ライトニングケーブル実装

iPhone 5S (2013)
モーション・コプロセッサ(Health Care連携)実装
指紋センサ実装

iPhone 6, 6plus (2014)
マルチサイズディスプレイ採用


iPhone 6S (2015)
感圧タッチセンサ実装

iPad Pro (2015)
iPadのマルチサイズディスプレイ採用

機種によりまちまちで統一されていない
*プロセッサも統一されていないので、ネイティブプログラムを動作させるには4通りのネイティブコード(バイナリ)をアプリに実装しなければならない。

HTC Dream (2008)
Android 1.0搭載
キーボードはソフトではなくハードで、画面タッチもできなかった。
*これがandroidの最初のデバイスだったということを記憶しておく必要がある。

Nexus One (2010)
Android 2.1搭載

前面ハードウェアボタンはほとんどの機種で実装していない (2015)

 

android5からほぼiPhoneと使いやすさが並んだと言ってよい。

市場シェアデータ
世界シェア
(途上国の影響が大きいため、実際アプリを積極的に使う人がどれだけいるか疑問)
40%

60%

*途上国では安いandroidが使われているため。

日本国内シェア
*ここ数年はずっとこんな感じ

60%

*日本は世界シェアとは逆にiPhoneシェアが6割を超えている。
*メーカシェアでは各国でAppleが1位だが、OSシェアでiPhoneが1番使われている国は日本1カ国だけである。

40%

国内メーカ別シェア(スマホ、2015年) Apple iPhoneシリーズ 50%
Sony Xperiaシリーズ 15%
Sharp AquosPhoneシリーズ 10%

国内出荷台数
(スマホ、2015年)
3,650万台

出典:MM総研調査
対応すべきバージョン
(2016年5月現在)

X iOS3 (2009) 0%台
X iOS4 (2010) 0%台
X iOS5 (2011) 0%台 *iCloud導入、互換性ない
X iOS6 (2012) 1%未満 *画面デザイン変わった
X iOS7 (2013) 4%未満 *AutoLayout SizeClass BackGroundFetch導入
○iOS8 (2014) モーション・コプロセッサ連携Health Care導入(iPhone5S以降)
◎iOS9 (2015)

出典 developer.apple.com/support/appstore/

*iOSは新OS配布開始から3ヶ月で導入率7割になるが、ここ最近は配布初期のOS不具合が頻繁に発生することから、徐々にフラグメントが広がりつつある。

X android2 (2010) 2%
X android3 (2011) 0%
◎android4 (2011-2013) 49%
○android5 (2014-2015) 35%
○android6 (2015) 13%

 

*日本国内に限定するとandroid4と5が50%づつのシェア



<詳細>
X 2.2 (API 8 2010) x1%未満
X 2.3 (API 10,GingerBread ジンジャーブレッド 2010) *2% supportLibraryで対応
X 3.x (API 11-13,HaneyComb ハニカム 2011 Tablet用) 1%未満
X 4.0-.2 (API 14 2011) 1%未満
X 4.0.3-4 (API 15 2012) 2%
○4.1 (API 16,JellyBean 2012)
○4.2 (API 17,JellyBean 2012)
○4.3 (API 18,JellyBean 2013) *4.1-4.3 計18%
◎4.4 (API 19,KitKat 2013) *30%
X 4.4W (API 20, 2014 Glass,Wear用)
○ 5.0 (API 21,Lolipop 2014)
△ 5.1 (API 22,Lolipop 2015) *5.x 計35%
△ 6.0 (API 23,Marshmallow 2015) 13%

出典 developer.android.com/about/dashboards/

*androidはお菓子の名前の愛称が読みにくいし、APIとバージョンが1対1でないので分かりにくい

     
スプラッシュスクリーン

Storyboardで定義する場合
LaunchScreen.storyboard でiPhone、iPadのイメージを指定する


画像ファイルで定義する場合
(*昔は画面サイズごとに画像を作る必要があった)

iPhone
Default.png 320x480
Default@2x.png(retina) 640x960
Default-568h@2x.png(4inch) 640x1136
Default-667h@2x.png(4.7inch) 750x1334
Default-736h@3x.png(5.5inch) 1242x2208

iPad
Default-Landscape.png(iPad)
Default-Portrait.png(iPad) 768x1024
Default-Landscape@2x.png(iPad retina)
Default-Portrait@2x.png(iPad retina) 1536x2048

イメージ画像をプロジェクトフォルダに置くだけ

画像サイズ
上記の右側の数値

res/values/style.xml (AppTheme)
res/drawable/splash.xml
res/drawable/splash_image.png
を作って
MainActivityのonCreateメソッドでsuper呼んだ直後に
setTheme(R.style.AppTheme);する

画像サイズ
hdpi 540x960
xhdpi 720x1280
xxhdpi 1080x1920
xxxhdpi 1440x2560
アプリケーションデリゲート(メインアクティビティ)

AppDelegate
*main()関数中のUIApplicationMain()で定義したAppDelegateクラス

MainActivity(最初に起動されるActivity)
*Androidmanifest.xmlで
intent-filter android.intent.action.MAIN
intent-filter android.intent.category.LAUNCHER
で指定されたactivityクラス
最初に実行されるviewController (Activity) plistで指定したMainStoryboardで Is Initial ViewController をチェックしたViewController

Storyboardを使わないとき
applicationDelegateのdidFinishLaunchingWithOptions:メソッドで[window setRootViewController:viewController];したViewController
Androidmanifestで
intent-filter android.intent.action.MAIN
intent-filter android.intent.category.LAUNCHER
で指定されたactivityクラス
provider   Androidmanifestに宣言したproviderクラスは最初に起動されるActivityよりも前にonCreateされる。
ビューコントローラー(アクティビティ、フラグメント) ViewController Activity
Fragments
*ActivityGroupがdeprecateされ、Activity + Fragmentsで複数のアクティビティを実現する。
ビュー UIView
(EagleViewなどに継承されて使用)
View
(GLSurfaceViewなどに継承されて使用)

widget
(Spinner, TextView, ImageView, EditText)
実際はViewを継承してるクラスだけど、通称でwidgetと呼ばれてる
viewController (Activity)に表示するview指定方法 nibでviewを定義する
or
viewControllerのviewDidLoadメソッドでviewをalloc initしてaddSubviewする
setContentView(R.layout.xmlFileName);
インスタンス生成
(ビュー、ウィジェット、コントローラ、クラスなど)
ViewClassName *obj = [[ViewClassName alloc] init];

UIButton view = [[UIButton alloc] initWithFrame:CGRectMake()];

nibファイルから生成
ViewController *vc = [[ViewController alloc] initWithNibName:@"nibName" bundle:nil]:

ViewClassName obj = new ViewClassName();

別の方法 Activity#findViewById, View#findViewById
Button view = (Button)findViewById(R.id.btn);

次の2通りのView生成方法は全く同じこと
LinearLayout view = (LinearLayout)View.inflate(activity, R.layout.viewName, null);

LinearLayout view = (LinearLayout)getLayoutInflator().inflate(R.layout.viewName, null);
C++ // スタック上に生成(メソッド内でのみ有効)
ViewClassName obj;
// ヒープ上に生成
ViewClassName *obj = new ViewClassName();
Javaではヒープ上にインスタンスが生成される
(Sub)View表示 [view addSubView:subView]; ViewGroup#addView(subView);
*ViewGroup(LayoutView)だけaddViewできる

Activity#addContentView()
*R.layoutをinflateしてaddContentViewする
(Sub)Viewを非表示にする [subView removeFromSuperview];

*Viewのvisible属性をfalseしてもOK
ViewGroup viewParent = (ViewGroup)this.subView.getParent();
viewParent.removeView(this.subView);

*Viewのvisible属性をfalseしてもOK
  view.setVisibility(View.GONE);
  クラス名は大文字始まり クラス名は大文字始まり
  変数名は小文字始まり 変数名は小文字始まり
フレームワークで定義されているクラス名 cocoaフレームワークで定義されているクラス名はプレフィックスがついていて何に使うクラスなのかが一目瞭然(ex. UIView, NSData) Android SDKで定義されているクラス名にプレフィックスが全くないので調べないと何に使うクラスなのかわからない(ex. View, Data)
リソースファイル名 パソコンで使えるファイル名ならOK 小文字と数字だけ。大文字が使えない
  [UIColor whiteColor] Color.WHITE
又は
R.color.white
文字定数の国際化 NSLocalizedString(@"name", @"comment")

定数はLocalizable.strings(Localization)で定義する
getResources().getString(R.string.name)

定数は
res/values/strings.xml
res/values-ja/strings.xml
で定義する
  @“文字定数” “文字定数”
  nibでもコードでも画面作れる xmlでもコードでも画面作れる
  application:didFinishLaunchingWithOptions: onCreate()
メソッドスコープ .hの@interface class に定義 public type methodName()
  .mの@interface class (private)に定義 private type methodName()
変数スコープ .hの@interface className にプロパティ定義
@property (assign, readwrite) type varName;
これによりセッター、ゲッター、インスタンス変数(_varName)を生成する
public type varName;
  .mの@implementation className {}内にインスタンス変数定義
type _varName;
private type varName;
C++ public:
private:
 
クラスメソッド + (type)methodName public static type methodName() 
インスタンスメソッド - (type)methodName private type methodName() 
C++ クラスメソッド static type methodName()
インスタンスメソッド type methodName()
 
  [instanceVariable methodName] instanceVariable.methodName()
  self.instanceVariable this.instanceVariable
*this.はつけてもつけないくても同じ意味
  [array objectAtIndex:0] array.get(0)
  NSInteger, int int
  IBOutlet *view or [topView viewWithTag:] findViewById()
定数

const type constName = value;

#define constName value

static final (定数、継承しないメソッド、クラス)
  @interface subClass : superClass { class subClass extends superClass {
  #import importFileName import packageName.className
(importするのはフレームワークのみ/自動)
  クラス名はプロジェクトで一意 .javaでpackage名を定義すると同じクラス名でもOK、別スコープとなる
     
ライフサイクル AppDelegateとviewControllerライフサイクル ActivityとFragmentライフサイクル
  didFinishLaunchingWithOptions(appDelegate) onCreate
  applicationDidBecomeActive(appDelegate) onStart
  applicationWillEnterForground(appDelegate) onResume
  applicationDidEnterBackground(appDelegate) onPause
  applicationWillTerminate(appDelegate) onStop
  applicationDidBecomeActive(appDelegate) onRestart
  dealloc onDestroy
バンドル情報(パッケージ情報)取得 productName-Info.plist で定義した値

[[NSBundle mainBundle] infoDictionary]
 setObject, objectForKey etc.
AndroidManifest.xml で定義した値

PackageInfo
 #packageName etc.
keyValue persistentストア(アプリ内共通preference)に保存・読み出し preferenceファイルは1つ

書込み
[[NSUserDefaults standardUserDefaults] setObject:data forKey:keyName];

読み出し
NSInteger value = [[NSUserDefaults standardUserDefaults] objectForKey:keyName];

*設定アプリのアプリ設定情報読み出しも同じ方法(NSUserDefaults)
書込み
SharedPreferences settings = getSharedPreferences("prefName", 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("key", value);
editor.commit();

読み出し
Activity#getSharedPreferences("prefName", 0).getInt("key", 0);

*アプリのSetting(設定情報)は PreferenceManager で読み出す
スマートフォン設定情報取得(ネットワーク接続) [[UIDevice currentDevice] networkAvailable];
ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

ConnectivityManager#getActiveNetworkInfo();
NetworkInfo#getType(); // ConnectivityManager.TYPE_WIFI
画面の向きを固定する plistのUISupportedInterfaceOrientations AndroidManifestのscreenOrientation
アプリ設定情報(Setting)の取得
*iOSは設定アプリ、androidはアプリ内のActionBarButtonから設定した値
設定アプリに設定項目を設定する
Settings.bundle (Root.plist, Root.strings) に設定項目(key)を定義する

NSInteger value = [[NSUserDefaults standardUserDefaults] objectForKey:keyName];
ActionBarButton(アプリ内のOptionMenu)表示用Activityを設定する
startActivity(new Intent(getBaseContext(), PreferenceActivity.class));


PreferenceManager.getDefaultSharedPreferences(this).getString("key", "defaultValue");
データベース SQLiteが使える

CoreDataを使う
*結果をUITableViewと連動する場合はCoreDataを使ったほうがよいです。

FMDBなどのthird party libraryもある
SQLiteが使える

ContentResolver cr = Activity#getContentResolver();
Cursor result = cr.query(ContentProviderUrl,null,null,null,null);
if(result.getCount() > 0) {
}

*Androidmanifest.xmlにContentProviderを定義しておく
*JavaのBeans,DAO,DTOとデータベース関連クラスを複雑にカプセル化するやり方はしない。
フェッチ機構 NSFetchedResultController LoaderManager
CursorAdapter (SimpleCursorAdapter)
DBインタフェース NSManagedObjectContext ContentResolver
データ管理 NSManagedObject Cursor
テーブルビューコントローラー(ListActivity)とデータベースの連携 <UITableViewDelegate, NSFetchedResultControllerDelegate>
を宣言して
UITableViewController

NSFetchedResultController
で取得した値をテーブルセルに-tableView:cellForRowAtIndexPath:で表示する

NSManagedObjectContext

NSEntityDescription

CoreData(Data Modelでスキーマ定義 = SQLite)

implements LoaderManager.LoaderCallbacks<Cursor>
を宣言して
ListActivity(or ListFragment)

CursorAdapter(Loaderを使うときCursorは定義しない=null)
*リスト行表示レイアウト、取得するDBカラム、表示するレイアウトフィールドを指定する
を連携させてリストに表示する
*非同期でデータ取得する場合はCursorLoaderと連携する
*配列のデータをリスト表示するときはArrayAdapter<T>を使う

CursorLoader(Cursorを定義する)
で別スレッドでコンテントプロバイダをとおしてデータベースアクセスして、アダプターに完了通知する
*コンテントプロバイダの指定、取得するDBカラム、抽出条件(sql文のwhere句)を指定する

Content Provider(コンテントプロバイダ)
Androidmanifest.xmlにprovider定義しておく

SQLiteDatabase
画面遷移(画面表示) pushViewController, presentModalViewController startActivity()
viewController間の値の受け渡し allocしたインスタンスのインスタンス変数に値をセットしてから表示(pushViewController etc.)する。

protocol定義のdelegateメソッド引数にセットして呼び出す。
Intentのdata,extrasにセットしてstartActivity()する。
画面閉じる(非表示) dismissModalViewController
dismissPopOver とか
Activity#finish()
  Controller - NSManagedObjectContext - CoreDataModel Controller -> Bean -> DTO <- DAO (sql)-> DB(Entity定義)
*Bean:画面UIのためのデータを扱う 
 DTO : テーブル操作のためのデータを扱う
クラス定義(継承) .h
@interface className : superClass <protocolName>
@property (retain, nonatomic) type publicVarName;
- (type)publicMethodName:(type)paramA rabel:(type)paramB;
@end
.java
public class className extends superClass implements interfaceName {
  accessLevel type vName;
  accessLevel type methodName() {
  ...
  }
}
クラス実装

.m
@implementation className {
   type privateInstanceVarName;
}
- (type)methodName:(type)paramA rabel:(type)paramB {
...
}
@end

定義と実装は同じファイル(.java)で行う
クラス規約定義
(クラスが持つべき型みたいなもの)
@protocol protocolName <NSObject> interface interfaceName
C++ .h クラス定義
class className : accessLevel super {
 public:
  type vName;
 private:
  type vName;
  type methodName(...);
}

.mm or .cpp メソッド実装
type className::methodName(...) {
...
}
accessLevelはsuperクラスのpublicメンバの扱いを定義する
コールバック Delegate (デリゲート) Interface (インタフェース)
Listener (リスナー)
デリゲート(リスナー)機能実装 プロトコルを宣言して
@interface className : superClass <UISearchBarDelegate>

nibでデリゲートクラスを定義して

クラスにメソッドを実装する
- (void)finishSearchWithString:
インターフェースを宣言して
public class className extends superClass implements SearchView.onQueryTextListener

コールバッククラスを定義して
SearchView.setOnQueryTextListener(this);

クラスにメソッドを実装する
public boolean onQueryTextChange() {}

インナークラスで実装することもできる
*インターフェース宣言(implements)はしなくてよい
*1箇所への記述だけでよいため手軽だが分かりにくくなる恐れあり

SearchView.setOnQueryTextListener(new SearchView.onQueryTextListener() {
   public boolean onQueryTextChange() {};
});
クラスのインスタンスが持つ変数 インスタンス変数 プロパティ、フィールド、メンバ変数
などいろんな呼称がある
インスタンス変数の初期化 initとかのコンストラクタで行う クラス定義で初期値を設定できる
インスタンス変数使用方法 _iVarName = someOne; // setterは走らない
             // retainカウント増えない
self.iVarName = someOne; // setterが実行される
             // retainカウント増える
iVarName = someOne;
this.iVarName = someOne;
// 全く同じ意味
ObjectiveCの@property指定方法 何も指定しないと、
クラス変数デフォルト(atomic, strong)
プリミティブ変数(intなど)のデフォルト(atomic, assign)
strong // このクラスで管理するインスタンス変数
weak // 他クラスで管理しているインスタンスを参照するためのインスタンス変数
nonatomic // 非スレッドセーフ(通常この指定)
 
     
デバッグ時だけログ出力

#ifdef DEBUG
NSLog("%f", floatVal);
#endif

*通常appName_Prefix.pchでプリプロセッサ定義して利用する


#ifdef DEBUG
#define FBLog(fmt,...) NSLog((@"%s l.%d " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#define FBLogErr(fmt,...) NSLog((@"[ERROR]%s l.%d " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define FBLog(fmt,...) ;
#define FBLogErr(fmt,...) NSLog((@"[ERROR]%s l.%d " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif

public class AALog {
  public static void v(String message) {
    if(BuildConfig.DEBUG) {
      if (message.isEmpty()) {
        message = "...";
      }
      StackTraceElement ste = Thread.currentThread().getStackTrace()[4];
      Log.v(ste.getClassName() + "#" + ste.getMethodName() + ":" + ste.getLineNumber, String.format("%f", floatVal));
} } }


クラス変数 クラス変数なし
*クラスメソッドを経由してクラス変数のように扱いたい変数を操作する
static type variableName
コンストラクタ - (id)init
- (id)initWithNibName:
クラス名と同じ名前のメソッド
public ClassName()
デストラクタ - (void)dealloc Javaの言語としてのデストラクタはない

androidライフサイクルとしてのデストラクタ
Activity#onDestroy()
C++ className::className() { コンストラクタ  
  className::~className() { デストラクタ  
定数(literal) 0.1f 0.1f
  [@"あ" stringByAppendingString:@"い"] "あ" + "い"
変数(valiable) aValiable aVariable
クラスのインスタンス変数 ClassName *instance ClassName instance
アクセスレベル(スコープ)デフォルト 継承クラス内(protected) パッケージ内
C++ クラス内(private)  
アプリ間連携(カスタムURLスキーマ、カスタムアクション) カスタムURLスキーマ
<渡すほうのアプリ>
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"myscheme://anystrings?param=1"]]; 
*URLのスキーマでどのアプリが受取るか判断する
*パラメータも渡せる
UIWebViewのアンカータグでも可能
<受取るほうのアプリ>
info.plistのCFBundleURLNameにCFBundleIdentifier+scheme(com.company.app.scheme)を定義し、CFBundleURLTypesに扱うカスタムスキーマ(scheme)を定義する

ドキュメントを受け渡し
<ドキュメントを渡すほうのアプリ>
UIDocumentInteractionController
<ドキュメントを受取って開くほうのアプリ>
info.plistのCFBundleDocumentTypes に扱えるドキュメントタイプを定義する
applicationDelegateクラスの application: openURL: sourceApplication: annotation:メソッドでファイルを開く手続きをする

Pasteboard
*通常カスタムURLスキーマによる通知と組み合わせて使う
<データを渡すほうのアプリ>
*pb = [UIPasteboard pastebordWithName:@"name" create:];
[pb setValue:@"val" forPasteboardType:@""];
<データを受取るほうのアプリ>
*pb = [UIPasteboard pastebordWithName:@"name" create:];
NSString *s = pb.string;
カスタムアクションで暗黙的Intent(外部インテント) を発行
<渡すほうのアプリ>
Intent mailIntent=new Intent(Intent.ACTION_SEND);
mailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"adress@dom.com"});
mailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(filePath));
startActivity(mailIntent);
<受取るほうのアプリ>
manifestでactivityのintent-filterでカスタムアクションを定義する
メール連携 アプリ内からメールの編集送信ができる

MFMailComposeViewController
アプリ内でのメール送信はできないのでメールソフトを起動する

暗黙的Intentを発行
Intent mailIntent=new Intent(Intent.ACTION_SEND);
mailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"adress@dom.com"});
mailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(filePath));
startActivity(mailIntent);
SNS連携(Twitter, Facebook) twitter
iOS6以降
Social Framework
iOS5
Twitter Framework

facebook
facebook sdk

*twitter,facebookとも設定アプリからログインできる
twitter
Google+のためか、twitterからもGoogleからもsdkは出ていない
サードパティのTwitter4Jライブラリを使ってtwitterWebAPIを利用する

facebook
facebook sdk
サーバからアプリへのプッシュ通知(リモート通知) RemoteNotification

証明書と鍵をサーバーに置く→アプリがデバイストークンを取得→アプリからデバイストークンをサーバに送信→サーバは(APNS)Apple Push Notification Serviceに通知をプッシュ→APNSからアプリ(デバイス)に通知送信

文字列で通知する

通知形式は設定アプリでユーザが決定する

GCM(Google Cloud Messaging for Android)

ServerAPIKeyとProjectIDをサーバーに置く→アプリがRegistrationIDを取得→アプリからRegistrationIDをサーバに送信→サーバはGoogleAPIサーバに通知をプッシュ→GoogleAPIサーバからアプリ(デバイス)に通知送信→BroadcastReceiverクラスで受信してNotificationManagerで通知表示(ステータスバー表示のとき)

json形式で通知する

通知形式はアプリが決定する

ローカル通知(アプリがローカルデバイスに対して通知する) UILocalNotification

通知はOSが受け取り表示する

文字列で通知する
通知形式は設定アプリでユーザが決定する
AlarmManager 指定時間後にブロードキャストインテント発行
PendingIntent
BroadcastReceiver ブロードキャストインテント受信
NotificationManager ステイタスバーに通知を表示

PendingIntent(文字列)で通知する
通知形式はアプリが決定する
  plist - CFBundleURLTypes, CFBundleDocumentTypes AndroidManifest - intent-filter
  UIGestureRegognizer GestureDitector
  @interface myView : UIView <UIGestureRecognizerDelegate> { public class myView extends Activity implements OnGestureListener {
  CoreGraphics Canvas
CoreGraphics

座標 (Coordinate)

左下が原点(変更可能)
ただし、UIView#drawRect: 内では左上が原点

 

座標系変更方法

// 原点のy座標を移動
CGContextTranslateCTM(context, 0.0, 470.0);
// y座標描画方向を反転
CGContextScaleCTM(context, 1.0, -1.0);

    ○:原点

    CoreGraphics (CG**)

    ^

    |

    |

    |

    ○-------->

    

    UIKit (UI**)、UIView#drawRect:内

    ○-------->

    |

    |

    |

    V


* UIView#drawRect: 内では左上が原点になるようにあらかじめframeworkが座標系を変更している


回転方向

右回り

    * CGContextScaleCTM(context, 1.0, -1.0); // Y座標ベクトルを反転 で変更すると、左回りになる

 

画像(png, jpeg)のレンダリング

CoreGraphics (CG**) 標準は左下が描画の原点なのでCGContextDrawImage() すると画像が逆立ちに描画される

y座標描画方向を反転(CGContextScaleCTM(context, 1.0, -1.0))してから CGContextDrawImage() すると画像の右下から左上に向かって逆立ちに描画される


画像を回転するには
90度右に回転する場合
UIImage *image = [UIImage imageNamed:@"image.jpg"];

CGSize imageSize = { image.size.height, image.size.width };     // 縦と横サイズを反転

UIGraphicsBeginImageContext(imageSize);

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextScaleCTM(context, 1.0, -1.0); // y座標描画方向を反転

float radian = (180+90) * M_PI / 180; // 右に90度回転(実際には左に270度回転している)

CGContextRotateCTM(context, radian);

CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);

UIImage *rotatedImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

 

*Quartz 2Dと呼ぶ場合があるが、これは描画エンジンの名称でまぎらわしい

*UIView#drawRect: 内では左上が原点なので書籍によっては左上原点で説明されているものもある

 
     
OpenGL OpenGLES(C++) OpenGLES(Java or C++(JNI, NativeActivity))
  EAGLView(CAEAGLLayer) GLSurfaceView (Java, JNI)
  layoutSubview onSurfaceChanged
  シェーダとは(プログラム・シェーダの書き方)  
    NativeActivity (C++)
UI座標 左上が原点 左上が原点
  CoreGraphicsは左下が原点(変更可能)  
  OpenGLは左下or中央が原点 (変更可能)  
  OpenGL関数 GL10 gl.xxx
  GL関数を呼び出す GLSurfaceViewにわたされるGL10(GL20)インスタンスのラッパーメソッド(gl.openGlfunctionName)を呼び出す
  glViewport() gl.glViewport() * in onDrawFrame()
  描画バッファにいつでも頂点を追加できる onDrawメソッドで描画しなければならない(surfaceView使用時)*毎回全ての頂点を描画
    AndroidのsurfaceViewでsetDebugFlagsでデバッグモードにすると例外が発生してしまう
配列 int var[3]; int[] array_var = new int[3];
int array_var[] = new int[3];
どっちでもよい。
ポインタ(参照、エイリアス、アドレスポインタ) 基本データ型
int *pointer_var = address;


オブジェクト
オブジェクトはすべてポインタ
基本データ型
int[] pointer_var = new int[1];
*メソッド引数へのポインタ渡し(参照渡し)に使う。

オブジェクト
オブジェクトはすべてポインタ
  C(Objective-C)とJavaのポインタのコンセプトの違い
ポインタとはコンピュータ(CPU)の仮想アドレス空間上のメモリアドレスをポイントする変数のことです。
Cのポインタ変数はメモリアドレスをメモリ上に保持することができるし、それを四則演算することもできる。
対して、Javaのポインタ(参照渡しされた変数)はメモリアドレスをメモリ上に保持していないし、それを四則演算することができない。
要するに、Javaは使う人にコンピュータハードウェアの知識を求めていないのです。コンピュータに詳しくない人でもプログラムが作れるようにと言うSun MicroSystemsの配慮です。
それ以外は、C言語のポインタとJavaの参照は全く同じ概念です。
ポインタ表現 ポインタ
*variable
配列にする
int[] pointer_var
クラス配列の使い方 [C++]
CGPoint **var = new CGPoint*[count];
CGPoint var[] = new CGPoint[count];
var[i] = new CGPoint();
リソースファイル読み出し glTexImage2D(),
CGContextDrawImage
R.idを指定するだけ
Assetファイルのファイル・コピー NSFileManager#copyItemAtURL を呼ぶだけ

InputStream#read して
OutputStream#write するのを ファイルサイズ/bufferサイズ回数ループしなければファイルコピーひとつできない。Javaのメモリ管理の脆弱さが理由だと思われるが低水準すぎる。これがフレームワークと言えるのか?
ANSI Cを思い出した。

 

*FileChannel#transferTo というメソッドもあるが、Assetファイル(AssetManager)などをパスに変換するメソッドが用意されていないため使えない。

UI操作はメインスレッドで行う
dispatch_async(dispatch_get_global_queue(), ^{
  // サブスレッド/バックグランドスレッド
  dispatch_async(dispatch_get_main_queue(), ^{
    // メインスレッド
  });
});


UIView#performSelectorOnMainThread
Activity.this.runOnUiThread(new Runnable() {
  public void run() {
    // メインスレッド
  }
});

or

// メインスレッドを指定してHandlerを生成
new Handler(); // 引数を省略するとメインスレッドに紐づく
new Handler(Handlerに紐づくThreadのLooperを指定);
*紐づくとはHandler#postする先のスレッドのQueueを規定するということ。

AsyncTask#doInBackground() { // バックグランド
 Handler#post(new Runnable() {
   @Override
   public void run() {
    // イベントキューに登録する
   }
 });
}
非同期実行 iOSはGCDのおかげでサブスレッド作成実行が簡単

dispatch_async(dispatch_get_global_queue(), ^{
  // サブスレッド
});

or

NSThread detachNewThreadSelector

or

UIView#performSelectorOnMainThread
UIView#performSelectorInBackground

androidのサブスレッド作成実行手続きはiOSに比べて複雑

HandlerThread subThread = new HandlerThread("threadName");
subThread.start();
Handler handlerWithSubThread = new Handler(subThread.getLooper());
handlerWithSubThread.post(new Rannable() {
  @Override public void run() {
    // サブスレッド
  }
});

or

AsyncTask を継承してインナークラスを作る
class AsyncTaskClass extends AsyncTask<...> {
}
そして、非同期実行する
new AsyncTaskClass().execute();

or

(new Thread(new Rannable() {
  @Override public void run() {
    // サブスレッド
  }
})).start();

or

class Rannable
・自分自身をスレッド実行開始するとき
public class RannableClassName implements Runnable {
  public RannableClassName() {
    Thread thread = new Thread(this);
    thread.start();
  }

  public void run() {
    // サブスレッド
  }
}


・別クラスのインスタンスをスレッド実行開始するとき
public class ClassName extends Activity {
  RannableClassName rannableClassInstance = new RannableClassName();
  Thread thread = new Thread(rannableClassInstance);
  thread.start();
}
非同期ネットワーク通信(XML取得など) [NSURLRequest requestWithURL:NSURL];

[NSURLConnection initWithRequest:NSURLRequest delegate:];
*NSURLConnectionはデフォルトで非同期です。同期指定も可能。


delegateのメソッドで非同期受信する
-connection:NSURLConnection didReceiveData:NSData
-connectionDidFinishLoading:NSURLConnection


AsyncTask<String, Void, String>#execute(url);

java.net.URL#openConnection();

HttpURLConnection#connect();
HttpURLConnection#getInputStream();
*HttpURLConnectionは同期のみ。

AsyncTaskをextendsしたクラスをインナークラスで定義して、
AsyncTaskのOverrideメソッドで非同期受信する
AsyncTask#doInBackground();
AsyncTask#onPostExecute();

リソースフォルダ 好きな名称にしてよい

projectRoot/app/src/main/res/
*R.id でアクセスすることができる。

projectRoot/app/src/main/assets/
*URLでアクセスすることができる。 例)file:///android_asset/xxx.html でアクセス可能

 

htmlファイルなどはres/rawでもassetsにも保存することができるが、アクセス方法が異なる。
raw
getResources().openRawResource(R.raw.xxx);
assets
getResources().getAssets().open("xxx.html");

画像フォーマット png png
サウンドフォーマット caf, mp3, wav ogg, mp3, wav
アプリアイコン(ランチャーアイコン)サイズ Asset Catalog で指定

icon.png 72x72
icon@2.png 144x144 (retina)

*iPhone, iPad共通
48dp

mdpi:48dp(x1.0) *dp
hdpi:72dpi(x1.5)
xhdpi:96dpi(x2.0)
xxhdpi:144dpi(x3.0)
xxxhdpi:192dpi(x4.0)
アプリ起動時に前回終了状態復元 NSUserDefault に保存して、アプリ起動時に読み出す

UIStateRestoration(iOS6以降)
SharedPreferences に保存して、アプリ起動時に読み出す
画面回転時に行うこと アプリのメモリ内の状態は保持されるので、何もしなくてよい。

UIView#layoutSubviews
が呼ばれる
アプリのメモリ内の状態は保持されないので、保存復元をアプリで行う必要がある。

Activity#onSaveInstanceState(Bundle) {
 Bundle#putInt();
}
Activity#onRestoreInstanceState(Bundle) {
 Bundle#getInt();
}
static変数(ヒープ領域にメモリ領域を確保する) メソッド中に定義できる クラスメンバ変数として定義する
  nill null
  UIEvent MotionEvent
  使用するクラスは #import または @class して使う パッケージ内のクラスは定義する必要なく使える
  bundleID packageName
  CGPoint ポイント座標の構造体がない
  touchBigan, Move, Endを別メソッドで受け取る onTouchEventメソッドですべて受け取る
  typedef 型定義の機能がない
  ポイント座標のY軸をgl->cocoa変換する 変換しなくてよい(android座標で取得される)
  bool boolean
配列初期化 type var[10];
*var = malloc(10*sizeof(type))
type[] var = new type[10];
  NSFileManager File (ContentWrapperを継承したクラスで使う)
エンディアン little-endian little-endian(nexsus7 native) x big-endian (JVMの使用で)
オリエンテーション取得 // デバイスの向き
UIDeviceOrientation = [[UIDevice currentDevice] orientation];

// 描画されている画面の向き
UIInterfaceOrientation = [UIAppliation sharedApplication].statusBarOrientation;
this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT
オリエンテーションイベント取得 UIViewController#viewWillTransitionToSize: withTransitionCoordinator:

iOS7まで
willRotateFromInterfaceOrientation:
didRotateFromInterfaceOrientation:
manifestに
Activity android:configChanges="orientation|keyboardHidden"すると
onConfigurationChanged()が呼ばれる
座標とピクセル値対応 scalefactor
1.0, 2.0(retina)
getResources().getDisplayMetrics().density;
mdpi(1.0),hdpi(1.5),xhdpi(2.0),xxhdpi(3.0)
     
テーブル(リスト)表示管理 UITableViewController ListActivity
ListFragment
*テーブルUI+表示データの管理
セクション(グループ)数 tableView:numberOfSectionInTableView: CursorAdapter#getViewTypeCount()
セクション・ヘッダー表示 tableView:titleForHeaderInSection: CursorAdapter#newView()
テーブル(リスト)表示内容設定 tableView:cellForRowAtIndexPath:

CursorAdapter#newView()
CursorAdapter#bindView()

     
ビューとウィジェット UITabView ActionBarActivity (旧TabHost, TabActivity)
  UIRadioButton RadioButton
  UITextView TextView
    CheckBox
コレクション(配列) NSArray *vName = @[]; // 空の配列
NSMutableArray *vName = [@[ @"val1", @"val2" ] mutablecopy]; // 配列の初期化
List<ObjectType> vName = new ArrayList<ObjectType>(); // 空の配列
List<ObjectType> vName = Arrays.asList("value1", "value2", "value3"); // 配列の初期化

コレクション(連想配列) NSDictionary *vName = @{};  // 空の連想配列

NSMutableDictionary *vName = [@{ @"key1" : @"val1", @"key1" : @"val1" } mutablecopy]; // 連想配列の初期化

Map<ObjectType, ObjectType> vName = new Hashtable<ObjectType, ObjectType>(); // 空の連想配列

Map<Integer, String> vName = new Hashtable<Integer, String>({ // 連想配列の初期化
  put(0, "value1");
  put(1, "value2");
put(2, "value3");
});
     
 
画面構成デザイン
  ステイタスバー ステイタスバー Status Bar
ホームボタン/ナビゲーションバー ハードウェア
シングル、ダブルタップ、長押しに対応
アプリ終了、タスク一覧、切り替え、音声入力
OSにより画面下部に自動表示される3つのアイコン
アプリ終了、タスク一覧、切り替え

*ソフトボタンならアプリで表示制御や無効にできる
*現行モデルではハードウェアボタン(4つも)はない

ナビゲーションバー Navigation Bar
ナビゲーションバー/アクションバー

(メニューボタン)
UToolBar, UINavigationBar

画面上部に表示する

ActionBar

画面上部または画面下部を選べる

オプションメニュー

SDK10以下:画面下のメニューボタン押下で画面下にタイル表示される
SDK11以上:タイトルバーに表示される

onCreateOptionsMenu()


アプリ画面で使用する標準アイコンサイズ 32dp
(normal:32dpi, retina:64dpi)

*iPhone, iPad共通
32dp(24dp)
ldpi:19dpi(x0.75)
mdpi:25dpi(x1.0) *dp
hdpi:38dpi(x1.5)
xhdpi:51dpi(x2.0)
xxhdpi:96dpi(x3.0) *本来だと75dpiだがxxxhdpiと共用化しているため?
xxxhdpi:96dpi(x4.0)

*frameworkで実際に使っているアイコンサイズ
*iOS用アイコンはサイズ変更せずにそのまま使える
  ナビゲーションバーボタンはステータスごとにアイコンを定義できる(タップされたときなど) アクションバーボタンをタップしたときにハイライトするなどはできない。背景色がかわるだけ。
タブバー 画面下に表示

UITabBarController
画面上に表示
*画面下にはナビゲーションバーがあるためと思われる

ActionBarActivity (旧TabHost, TabActivity)
 
メソッド・オーバーライド 普通にsuperClassやプロパティのメソッドを再定義すればOK @Override public void superClassMethod() {}
@Override public void otherClassMethod() {}
いろいろ使うAppDelegateやMainActivityの求め方 [[UIApplication sharedApplication] delegate]で求める MainActivityのonCreateでGlobalクラスのクラス変数に保存しておく
*所属するFragmentやView以外からは求めるしくみは用意されていない
FragmentからActivityの求め方   Fragment#getActivity();
ViewからActivityの求め方   View#getContext();
変数型(データ型) 変数型を見ただけで何に使われる変数かが分かる。
ex. UIDeviceOrientation orientation
Java標準のデータ型しかない。
ex. int orientation
*Javaにはプリプロセッサー(#define)やtypedefが無いため
タッチイベント viewのtouchesEnd: でイベントを受け取る viewのpublic boolean onTouchEvent(MotionEvent event) { で手続きすると複雑

GestureDetector.SimpleOnGestureListener
this.setOnTouchListener(new View.OnTouchListener() {で手続きしたほうがよい
サウンド再生 AVAudioPlayer
MediaPlayer 再生時に読み込む BGMなどに使用
SoundPool メモリ上に常駐  SEに使用
  YES, NO true, false
配列領域確保 float var[4]; float[] var = new float[4];
三角関数 cos(); (float)Math.cos();
クラウド iCloud GoogleDrive
国際化(文字定数、画面レイアウト)

文字定数を国際化するとき
Localizable.stringsに値を定義して
NSLocalizedString(@"key", @"comment")で呼び出す

画面ごとに国際化するとき
className.xib
xcodeの右ペインのLocalization

文字定数を国際化するとき
res/values-ja/strings.xmlに値を定義して
getResources().getString(R.string.name)で呼び出す


画面ごとに国際化するとき
res/layout-ja/activity_main.xml
機種判定 UIViewController#UITraitCollectionで判定

if (self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) {

        // iPad

} else {

        // iPhone

}


iOS7以下
UI_USER_INTERFACE_IDIOM()
res/layout/activity_main.xml
res/values/dimens.xml
↑と↓で画面サイズによって、レイアウトやパディング値を切り替える
res/layout-w820dp/activity_main.xml
res/values-w820dp/dimens.xml
OSバージョンチェック if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
  // iOS6以上
}
if (Build.VERSION.SDK_INIT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  // android 4以上
}
文字列数値変換 NSString *text = [[NSNumber numberWithInt:intVal] stringValue];
or
NSString *text = [NSString stringWithFormat:@"%d", intVal];

NSString *text = @"1";
int intVal = [text intValue];
String text = Integer.toString(intVal);





int intVal = Integer.parseInt(text);
写真などの画像操作 CGImage

CGDataProvider

CGContent (UIGraphics)
Bitmap

ByteBuffer (IntBuffer)

Canvas



iOSアプリをAdHocで公開する方法 Xcode6.1の場合

iOSアプリのビルドが通ってから新しいApple版テストフライトにアップするまで

SIMフリーのスマホの中にはMacとUSB接続できないものがある

iOSのSizeClassとConstraintsはフラグメント化したiOSには欠かせないものとなった



スマートフォンアプリ作成