戌印-INUJIRUSHI- (Androidあれこれ)

Androidのプログラミングをメインにしてます。記事に貼られたソースコードはダブルクリックすることで行番号をはずしてコピーすることができます。

 
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
09

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Android Studio 1.0 セットアップ資料

有山圭二さん(@keiji_ariyama)著の「Android Studioではじめる 簡単Androidアプリ開発」の補足資料として以下にて
Android Studio 1,0 のセットアップ資料が公開されています。
※使用方法については「Android Studioではじめる 簡単Androidアプリ開発」をご購入ください。

■ HTML版
http://keiji.github.io/the-androidstudio-book/

■ PDF版
http://keiji.github.io/the-androidstudio-book/archives/book.pdf

リンク先の資料は、有山圭二さんの著作物です。
クリエイティブコモンズ2.1の表示—非営利—改変禁止ライセンスの元で提供されています。

スポンサーサイト

Android エミュレータ Intel Atom (x86)

Android のエミュレータには標準の ARM とは別に
Intel CPU を使った高速エミュレータ Intel Atom (x86) があります。

【使い方】
Android SDK から対象の API の Intel x86 Atom System Image をインストール。
AVD の CPU/ABI に Intel Atom (x86) を指定するだけ。

android_intelimage.png


これで高速エミュレータによるテストが可能となります。


■ 起動したエミュレータが真っ黒のまま表示されない
BIOS の設定で Virtualization Technology が無効になっている可能性があります。

【変更方法】
① PC起動時に F2 (※) を押して BIOS を起動。
② System Configuration の Virtualization Technology を Disabled から Enabled に変更。

これで Intel Atom (x86) が実行できるようになります。

※PCメーカーによっては ESC や Delete の場合があります。HPの場合は ESC でした。


■高速といわれてるのにまったく早くない
Intel HAXM (Hardware Accelerated Execution Manager) がインストールされていない可能性があります。

【インストール方法】
① Android SDK の一番下にある Extras - Intel x86 Emulator Accelerator (HAXM installer) を『ダウンロード』。
android_intelhaxm.png

② ダウンロードした Intel x86 Emulator Accelerator はエクスプローラを開いてインストールします。
  インストールファイルは以下にダウンロードされています。
  %ANDROID_SDK_HOME%\extras\intel\Hardware_Accelerated_Execution_Manager\intelhaxm.exe

エミュレータ起動時にログに以下のメッセージが表示されれば HAXM が有効となっています。
  HAX is working and emulator runs in fast virt mode



今更なネタだなー。

複数の拡張ストレージのアプリ固有ディレクトリ取得 : getExternalCacheDirs()

Android 4.4 (API19 / KitKat) より Context#getExternalFilesDirs() が追加されました。
このメソッドは、拡張ストレージ(内部ストレージ&外部ストレージ)のアプリ固有のディレクトリを File[] で返します。

Ex. Android 4.4.2 Emulator
/storage/sdcard/Android/data/[package name]/files

Android 2.2 (API8 / Froyo) で追加された Context#getExternalFilesDir(String type) (最後にsがない方)では拡張ストレージが内部と外部に分かれていた場合、内部ストレージのディレクトリしか返しませんでした。
外部ストレージのディレクトリを取得するAPIがなかったため、待ちに待ったAPIと言えます。

SupportLibrary v4 でも r19 より ContextCompat#getExternalFilesDirs(Context context, String type) が追加されています。
Android 4.4 と同様の機能を有しますが、残念なことに複数拡張ストレージに対応しているのは Android 4.4 以上です。
Android 4.4 未満の端末では内部ストレージしか返ってきません。

実際に HTC J ISW13HT (Android 4.0.4) で試してみました。
StringBuffer sb = new StringBuffer();
File[] files = ContextCompat.getExternalFilesDirs(this, null);
for (File file : files) {
sb.append(file.getAbsolutePath());
sb.append("\n");
}
Log.d("Storage", sb.toString());
結果:
/mnt/sdcard/Android/data/jp.inujirushi.android.sample/files

結果に書いたように内部ストレージのみでHTC端末の外部ストレージ /mnt/sdcard/ext_sd は返ってきませんでした。結局、外部ストレージのディレクトリを取得するには独自実装しかないようです。

----------------------------------------
外部ストレージのディレクトリ取得方法例
1.【過去記事】外部ストレージのパスを取得する(Android 2.2~?)
2.我が征くは肥沃な荒野 - ストレージの扱いが混沌の極みの件


■ Android 4.4 (API 19 / KitKat) で追加された拡張ストレージに関する API
Context#getExternalFilesDirs(String type)
 拡張ストレージ内のアプリ固有のディレクトリを返します。

Context#getExternalCacheDirs()
 拡張ストレージ内のアプリ固有のキャッシュディレクトリを返します。

Context#getObbDirs()
 拡張ストレージ内のOBBファイルディレクトリを返します。

Environment#getStorageState(File path)
 指定したパスのストレージの状態を返します。
 拡張ストレージ以外のパスが指定された場合 MEDIA_UNKNOWN を返します。


■ 参考
TechBooster C85 Android MagicBook P.37~39

指定時間に処理を実行する : AlarmManager

Android では指定された時間、または、一定時間毎に処理を実行するために AlarmManager が用意されています。

例えば、10秒毎にサービスを実行するには以下のように記述します。
// 実行するサービスを指定する
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
new Intent(context, SampleService.class),
PendingIntent.FLAG_UPDATE_CURRENT);

// 10秒毎にサービスの処理を実行する
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), 10 * 1000, pendingIntent);


■ AlarmManager の注意点
①AlarmManager の実行周期が長いと時間が遅れる(数時間で2~3分?)
②AlermManager は cancel しなくても以下の時に終了する
 ・デバイスの電源をオフにする(再起動する)
 ・アプリをアップデートする
 ・アプリをアンインストールする

この問題は BroadcastReceiver クラスを使い、再実行することで回避することが出来ます。

①の時間が遅れる問題は時間が自動設定されているときに発生するので、システム時間が変更された時に発生する broadcast を受け取るようにします。
<receiver
android:name=".SampleBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.DATE_CHANGED" />
</intent-filter>
</receiver>

②はデバイス起動時とアプリ更新時に発生する broadcast を受け取ってください。アンインストール時はアプリ自体がなくなるので指定する必要はありません。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<receiver
android:name=".SampleBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data
android:path="jp.inujirushi.android.sample"
android:scheme="package" />
</intent-filter>
</receiver>
起動時に broadcast を受け取るには android.permission.RECEIVE_BOOT_COMPLETED 権限が必要です。
そして、アプリ更新時には <data android:scheme="package" /> が必要です。<data> タグに android:path を指定することで自身のアプリのみを対象にできる…らしいのですが android:path を指定しても他のアプリ更新時に broadcast が投げられてしまいました。BroadCast クラス側で path のチェックをする必要があるようです。
(HTC J ISW13HT で確認)



以上を踏まえ、10秒毎にログを出力する簡単なアプリを作ってみました。
AlermManager のON/OFFの管理は行っていません。アプリが1度でも起動したら常にONになります。

なお、Android 3.1 (API12 / Haneycomb) 以降、アプリが1度も実行されていない場合は STOP 状態となり broadcast も受け取りません。そのためインストール時に自動で開始させることはできません。なので「1度でも起動したら」という表記を用いています。

SampleService.java
package jp.inujirushi.android.sample;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import android.util.Log;

public class SampleService extends IntentService {

private static final String TAG = "SampleService";

public SampleService() {
super(TAG);
}

@Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "time:" + SystemClock.elapsedRealtime());
}

/**
* サービスを処理する AlarmManager を開始する。
*
* @param context
*/
public static void startAlarm(Context context) {
// 実行するサービスを指定する
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
new Intent(context, SampleService.class),
PendingIntent.FLAG_UPDATE_CURRENT);

// 10秒毎にサービスの処理を実行する
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), 10 * 1000, pendingIntent);
}
}

SampleActivity.java
package jp.inujirushi.android.sample;

import android.app.Activity;
import android.os.Bundle;

public class SampleActivity extends Activity {

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

// AlarmManager を開始する
SampleService.startAlarm(getApplicationContext());
}
}

SampleBroadcastReceiver.java
package jp.inujirushi.android.sample;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class SampleBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
// 他のアプリ更新時は対象外とする
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
if (!intent.getDataString().equals(
"package:" + context.getPackageName())) {
return;
}
}

// AlarmManager を開始する
SampleService.startAlarm(context);
}
}

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="jp.inujirushi.android.sample"
android:versionCode="1"
android:versionName="1.0" xmlns:android="http://schemas.android.com/apk/res/android">

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".SampleActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<receiver
android:name=".SampleBroadcastReceiver"
android:process=":remote" >
<intent-filter>
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.DATE_CHANGED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data
android:path="jp.inujirushi.android.sample"
android:scheme="package" />
</intent-filter>
</receiver>

<service android:name=".SampleService"></service>
</application>

</manifest>
<receiver> タグにある android:process=":remote" は別プロセスで起動する場合に指定します。必須ではありません。


■参考サイト
Y.A.M の 雑記帳 - AlarmManager
素人のアンドロイドアプリ開発日記 - AlarmManagerを使う場合の注意点
rokuta96のAndroidアプリ開発 - アラーム3
kino's blog - Android Service を自動的に再起動する方法

外部領域へのアクセス権限と Android 4.4 KitKat

Android では外部領域(内部ストレージ/外部ストレージ)へアクセスするためには、アクセスを行うアプリ毎に WRITE_EXTERNAL_STORAGE または READ_EXTERNAL_STORAGE をパーミッションに設定する必要があります。

パーミッションを設定せずに外部領域にアクセスすると SecurityException が発生します。

Android 4.4 (KitKat / API 19) からはこの外部領域における、自身のアプリケーション領域 Context#getExternalFilesDir(String type) へはパーミッションなしでもアクセスできるようになりました。
ただし、このアクセス許可は自身のアプリケーション領域のみを対象としており、全てのアプリケーションが共有する領域 Environment#getExternalStoragePublicDirectory(String type) は対象外となります。

【アプリケーションの外部領域】
 <外部領域>/Android/data/<package_name>/files/

例1)HTC J ISW13HT Android 4.0(非マルチユーザー)
  /mnt/sdcard/Android/data/jp.inujirushi.android.sample/files/
例2)Nexus7 (2012) Android 4.3(マルチユーザー)
  /storage/emulated/0/Android/data/jp.inujirushi.android.sample/files/

【外部領域へのアクセス権限(パーミッション)】
外部領域へのアクセス権限は、外部領域にある全てのファイルが対象となります。

WRITE_EXTERNAL_STORAGE
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
アプリケーションが外部領域へアクセスし、ファイルへの書き込み、および、読み込みを行うことを許可します。
読み込みも許可されるため READ_EXTERNAL_STORAGE の指定は不要です。

READ_EXTERNAL_STORAGE
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
アプリケーションが外部領域へアクセスし、ファイルの読み込みを行うことを許可します。
このパーミッションは Android 4.1 (JellyBean API 16) で「予約された権限」として追加されており、開発者向けオプションのSDカード保護にチェックを入れない限り有効になりませんでした。
Android 4.4 で正式に対応された形になります。


■ 参考サイト
Taosoftware - Android:KitkatのREAD_EXTERNAL_STORAGEと外部記憶領域
TechBooster - Android 4.4で追加されたパーミッションとDevice Features
Yukiの枝折 - Android:JBで追加されたREAD_EXTERNAL_STORAGE
プロフィール

とむ・やむくん

Author:とむ・やむくん
管理人について

Windows 7 / 64bit
Eclipse 4.2 Juno (日本語パッチ済)

スポンサーサイト
最新トラックバック
検索フォーム
ブロとも申請フォーム
QRコード
QR
Twitter
2013/01/04 19:00 カウント開始

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。