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

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

 
1
2
3
6
7
8
9
10
11
13
14
15
16
17
18
20
21
22
23
24
25
27
28
29
30
31
05

スポンサーサイト

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

アクティビティのライフサイクル

アクティビティのライフサイクルとは、アクティビティが生成されてから破棄されるまでの流れのことを指します。
このライフサイクルは Android アプリ開発において基本となることですのでしっかり理解してから開発するようにしましょう。
※ Fragment もライフサイクルを持っていますが今記事では触れません。

ソフトウェア技術ドキュメントを勝手に翻訳 - 1. アクティビティ


Activity のライフサイクル図
(Tech Booster - 図解Androidのライフサイクルとプラットフォーム より)
android_lifecycle.png


【主要メソッド】
ライフサイクルの説明で欠かせないメソッドです。
この中でも主に使うメソッドは色つきにしました。

onCreate()
  アクティビティがはじめて生成された時に呼ばれる。
onStart()
  アクティビティが開始された時に呼ばれる。
onRestart()
  アクティビティが再開された時に呼ばれる。
onResume()
  アクティビティが表示された時に呼ばれる。
onPause()
  他のアクティビティが表示された時に呼ばれる。
onStop()
  アクティビティが非表示になった時に呼ばれる。
onDestroy()
  アクティビティが破棄される時に呼ばれる。


ライフサイクル図を見てもらうとわかるように、開始時には onStart()、停止時には onStop() と onDestroy() が呼ばれないパターンがあります。
そのため、開始時は onResume()、停止時は onPause() にまとめて記述することが多いです。

私の場合は、以下のように使い分けています。
onCreate() :レイアウト定義、イベント設定、受取データの設定(getIntent)
onResume() :データ読込、スレッド開始、レイアウトへのデータ反映
onPause() :データ保存、スレッド停止


【その他メソッド】
ライフサイクルの説明で除外されることがありますが、他にも以下のようなメソッドも使われます。

onSaveInstanceState()
  アクティビティの状態を保存する時に呼ばれます。
onRestoreInstanceState()
  アクティビティの状態を復元する時に呼ばれます。
onActivityResult()
  他のアクティビティをリクエスト要求で起動して戻ってきた時に呼ばれます。
onNewIntent()
  自身のアクティビティを要求したときに呼ばれます。
  ※ android:launchMode が singleTop/singleTask/singleInstance のアクティビティに限る


後述の実行パターンになりますが onRestoreInstanceState() は呼ばれるタイミングが少なく、呼ばれるときには必ず onCreate() も呼ばれます。
そのため、onCreate() の引数は onRestoreInstanceState() と同じため、onCreate() にまとめられることが多いです。


【実行パターン】
ライフサイクルの流れは以下のパターンに分けられます。

アクティビティの開始
  onCreate() → onStart() → onResume()

アクティビティの終了 (戻るキー)
  onPause() → onStop() → onDestroy()

スリープモード、ホームボタン
  onPause() → onSaveInstanceState() → onStop()

スリープモード、ホームからの復帰
  onRestart() → onStart() → onResume()

他のアクティビティを起動 (※ [ ] は他のアクティビティの処理)
  onPause() → [ onCreate() → onStart() → onResume() ] → onSaveInstanceState() → onStop()

他のアクティビティから戻る (※ [ ] は startActivityForResult 起動時のみ)
  [ onActivityResult() → ] onRestart() → onStart() → onResume()

デバイス回転
  onPause() → onSaveInstanceState() → onStop() → onDestroy() → onCreate() → onStart() → onRestoreInstanceState() → onResume()

メモリ解放後の起動 (長時間放置からの起動など)
  onCreate() → onStart() → onRestoreInstanceState() → onResume()


【実行パターン - singleXXX】
アクティビティの launchMode が singleTop/singleTask/singleInstance の場合には、以下のように動作が異なります。
[ ] は自身のアクティビティを起動した場合に実行されるメソッド。

アクティビティを起動
  onPause() → [ onNewIntent() → ] onResume()

アクティビティをリクエスト要求して起動
  onPause() → onActivityResult() → onResume() → onPause() → [ onNewIntent() → ] onResume()

onActivityResult() は同一のタスク上からしか結果を取得できません。
そのため singleTask の場合には別タスク上で動くため結果が取得できず、直後にキャンセルを返すようになっています。

Android Developers - Activity#startActivityForResult() より抜粋
Note that this method should only be used with Intent protocols that are defined to return a result. In other protocols (such as ACTION_MAIN or ACTION_VIEW), you may not get the result when you expect. For example, if the activity you are launching uses the singleTask launch mode, it will not run in your task and thus you will immediately receive a cancel result.


遷移の種類や、状態に応じて呼ばれるメソッドが変わるため覚えるのはかなり大変です。
どのときにどのようなメソッドが呼ばれるかを理解したうえで使うようにしましょう。
スポンサーサイト

Android 4.2 開発者向けオプションを表示する

Android 4.2 (Jelly Bean :API 17) より、初期状態では設定に開発者向けオプションが表示されなくなりました。


■ 開発者向けオプションを表示するには
設定 > タブレット情報 にある [ビルド番号] を7回タップする。
これで「これでデベロッパーになりました!」とメッセージが表示されて、設定のシステムに[開発者向けオプション]が表示されます。
なお、サブアカウントでログインしている場合は表示されません。

Android Developers - JellyBean Android 4.2 より抜粋
New built-in developer options
On devices running Android 4.2, developer options are hidden by default, helping to create a better experience for users. You can reveal the developer options at any time by tapping 7 times on Settings > About phone > Build number on any compatible Android device.

■ 一度表示した開発者向けオプションを非表示にするには?
調べましたが方法は不明です…
表示させる方法と同じことをすれば良いのかな、と試しましたが「必要ありません。既にデベロッパーです」と表示されるだけでした。


Nexus 4, Nexus 7 で確認しており、今後発売されるメーカーの機種によっては表示されるかもしれません。

Nexus 7 の場合、Android 4.1 では表示されていましたが、システムバージョンアップで Android 4.2 にアップグレードすると消えてしまうので戸惑った人も多かったのではないでしょうか(自分がそうでした)

Android 4.2 では開発者向けオプションで変更できる設定項目もかなり充実したものになっています。
開発者ではない一般ユーザーが間違えて触ってしまわないための対処だと思われますが、システムバージョンアップで通知無く消えるとどうすればいいかわからなくて困りますね…。

ウェブブラウザ (ウェブアプリ) : WebView

Android アプリ内でウェブページを表示するには、ブラウザ機能を有する WebView を使います。

API Reference: WebView
ソフトウェア技術ドキュメントを勝手に翻訳 - 3. ウェブアプリの WebView への組み込み

インターネット上にあるウェブページを表示するには AndroidManifest.xml にインターネット接続のパーミッションを追加する必要があります。
※ アプリ内の html ファイルを参照するだけなら不要です
<uses-permission android:name="android.permission.INTERNET" />

WebView に表示するウェブページを設定するには WebView#loadUrl(String url) に対象の URL を渡します。
WebView#setWebViewClient(WebViewClient client)WebView にクライアント設定しないと他のブラウザアプリを起動してしまいます。基本的には loadUrl とセットで設定してください。
WebView webView = new WebView(this);
webView.setWebViewClient(new WebViewClient() {});
webView.loadUrl("http://www.google.com");

アプリ内のファイル (assets) を表示する場合は WebView#loadUrl(String url) にファイルのパスを指定します。
assets 内のファイルパスは file:///android_asset/ から始まります。
パスの最後に s をつけないように気をつけてください。
こちらはクライアント設定をする必要はありません。
WebView webView = new WebView(this);
webView.loadUrl("file:///android_asset/test.html");

WebView では JavaScript は標準で無効となっているため、有効にするには以下の1行を追加します。
webView.getSettings().setJavaScriptEnabled(true);
JavaScript を有効にすることで XSS (Cross Site Scripting) などのセキュリティの脆弱性を有する可能性があるとの警告文が表示されます。
Using setJavaScriptEnabled can introduce XSS vulnerabilities into you application, review carefully.
この警告文はクラス、または、メソッドに @SuppressLint("SetJavaScriptEnabled") を追加することで消すことができます。


ズームイン/ズームアウト機能を追加するには以下の1行を追加します。
webView.getSettings().setBuiltInZoomControls(true);

これで簡単なブラウザ機能を有したアプリを作成することができます。

以下、今回の記事で作成したサンプルソース。

■ 画面イメージ
android_webview.png

■ activity_web.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

■ WebViewActivity.java
package jp.inujirushi.android.sample;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class WebViewActivity extends Activity {

@SuppressLint("SetJavaScriptEnabled")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
WebView webView = (WebView) findViewById(R.id.webView);

// WebView 自身をクライアントにしてページを表示させる
webView.setWebViewClient(new WebViewClient() {});

// JavaScript を有効にする
webView.getSettings().setJavaScriptEnabled(true);

// ズームイン/ズームアウトを許可する
webView.getSettings().setBuiltInZoomControls(true);

// Google のサイトを表示する
webView.loadUrl("http://www.google.com");
// webView.loadUrl("file:///android_asset/test.html");
}
}

ANR と traces.txt

Android ではアプリが一定時間応答が無いとき ANR (Application Not Responding) ダイアログが表示されます。

android_anr.png

ソフトウェア技術ドキュメントを勝手に翻訳 - 7. レスポンスのための設計

一定時間とは以下の時間をさします。
・メインスレッドから 5 秒以上応答が無い
・BroadcastReceiver が 10 秒以内に完了しない
ただし、この時間はメーカーがいじっている場合があるため必ずしも上記の時間で発生するとは限りません。

時間のかかる処理は避けるべきですが、ファイル作成など時間のかかる処理を行う場合には子のスレッド(Thread や AsyncTask)や Service など介して行うことで対処できます。


ANR が発生したときには Logcat にログは出力されないため、どこの処理で時間がかかっているか分かりません。
しかし、traces.txt というファイルに最後に発生した ANR の情報が出力されているのでこのファイルを元に原因を特定することが可能です。

出力パス: /data/anr/traces.txt
出力例: traces.txt

一般の root 権限の無いデバイスではこのファイルが出力されている /data フォルダの中を見ることはできませんが、ファイル自体は参照可能になっています。

・adb シェルを介して traces.txt を操作する
Windows の場合、コマンドプロンプトを起動して以下のコマンドを入力して操作します。
※ [ANDROID_SDK]\platform-tools のパスが通っている必要があります

① 直接ファイルを参照する
adb cat /data/anr/traces.txt

② ファイルをローカルにコピーする
adb pull /data/anr/traces.txt [出力先]


・Android アプリで traces.txt を取得する
ファイル自体は参照可能なため、通常のファイルコピー等によりファイルを取得できます。
package jp.inujirushi.android.sample;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

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

public class TestActivity extends Activity {

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

// traces.txt をコピーする
String srcPath = "/data/anr/traces.txt";
String destPath = Environment.getExternalStorageDirectory() + "/traces.txt";
try {
copyTo(srcPath, destPath);
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* ファイルを指定されたパスにコピーします。
*
* @param srcPath
* コピー元ファイルパス
* @param destPath
* コピー先のファイルパス
* @throws IOException
*/
public static void copyTo(String srcPath, String destPath)
throws IOException {
FileChannel srcChannel = null;
FileChannel destChannel = null;
try {
srcChannel = new FileInputStream(srcPath).getChannel();
destChannel = new FileOutputStream(destPath).getChannel();
srcChannel.transferTo(0, srcChannel.size(), destChannel);
} finally {
srcChannel.close();
destChannel.close();
}
}

}

ログの入出力 : Logcat

Android ではログ管理として Logcat が用意されています。

【ソフトウェア技術ドキュメントを勝手に翻訳】
6.4 ログの読み書き
‎8. ツール‎ - logcat

【参考サイト】
Android Wiki* - Androidアプリケーションからlogcatコマンドを実行する
BRILLIANT SERVICE - LogcatをAndroid端末上で見るツール LogcatViewer


■ ログの入力
Logcat にログを入力するには Log クラスを使用します。

Log#v(String tag, String msg) ( 冗長 )
Log#d(String tag, String msg) ( デバッグ )
Log#i(String tag, String msg) ( 情報 )
Log#w(String tag, String msg) ( 警告 )
Log#e(String tag, String msg) ( エラー )

ログを入力する際に注意しなければいけないことは、アプリのリリース/デバッグ関係なく全てのログが入力されてしまうことです。
そのため、デバッグログなどデバッグ時のみに出力したい場合には BuildConfig.DEBUG (※) 等をつけて制御を行う必要があります。
※ リリースビルド以外では必ず true になる。

実行例
if (BuildConfig.DEBUG) {
Log.d("TAG", "MESSAGE");
}
android_log.png


■ ログの出力
ログの出力には2つの方法があります。

① DDMS から Logcat を出力する
eclipse を使用している場合、メニューの [ウィンドウ(W)] - [ビューの表示(V)] - [その他()] から Android - Logcat を選択することで表示されます。
android_logcat_ddms.png


② adb シェルを介して logcat を実行する
logcat を実行する際のオプションやフォーマットはこちらのサイトを参照してください。
Android Wiki* - Androidアプリケーションからlogcatコマンドを実行する

・デバイスが接続されたPCにリモートで Logcat を出力
Windows の場合、コマンドプロンプトを起動して adb logcat と入力すれば表示されます 。
※ [ANDROID_SDK]\platform-tools のパスが通っている必要があります

・Android アプリで Logcat を取得する方法
Android アプリから Logcat を参照する場合、AndroidManifest.xmlandroid.permission.READ_LOGS パーミッションを追加する必要があります。
<uses-permission android:name="android.permission.READ_LOGS" />

なお、Logcat を読み込むための API は用意されていません。
そのため Runtime.getRuntime().exec(String prog) を使い、アプリ上で Logcat を実行します。
Process process = Runtime.getRuntime().exec("logcat");
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()), 1024);
String line;
while ((line = reader.readLine()) != null) { // -d オプションなければ読み続ける
// ログ処理
}

最後に Logcat をリアルタイム監視するサンプル
android_logcat.png
package jp.inujirushi.android.sample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

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

public class TestActivity extends Activity {

private static final int LOG_COUNT = 100; // ログ行数
private static final int RELOAD_TIME = 500; // 再取得時間
private Thread mThread;
private Timer mTimer;
private TextView mTextView;
private List<String> mLogList = new ArrayList<String>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
mTextView = (TextView) findViewById(R.id.textView);
}

@Override
protected void onResume() {
super.onResume();

// Logcat 取得スレッド開始
if (mThread == null) {
mThread = new Thread(mRunnable);
}
mThread.start();

// Logcat 表示タイマー開始
if (mTimer == null) {
mTimer = new Timer();
}
mTimer.schedule(mTimerTask, 0, RELOAD_TIME);
};

@Override
protected void onPause() {
super.onPause();

if (mThread != null) {
mThread.interrupt();
}
if (mTimer != null) {
mTimer.cancel();
}
};

// Logcat の内容をリストに出力する
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
Process process = null;
BufferedReader reader = null;
try {
// Logcat 出力コマンド
// String command = "logcat";
String[] command = { "logcat", "-v", "time", "*:V" };

// Logcat を出力する
process = Runtime.getRuntime().exec(command);
reader = new BufferedReader(new InputStreamReader(
process.getInputStream()), 1024);
String line;
while ((line = reader.readLine()) != null) {
if (mLogList.size() > LOG_COUNT) {
mLogList.remove(0);
}
mLogList.add(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};

// 出力した Logcat を一定時間毎に画面に表示する
private TimerTask mTimerTask = new TimerTask() {
@Override
public void run() {
// Logcat を取得する
final StringBuilder sb = new StringBuilder();
List<String> list = new ArrayList<String>(mLogList);
for (String log : list) {
sb.insert(0, log + "\n");
}

// Logcat を表示する
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(sb);
}
});
}
};
}

サービスを介した本格的な Logcat 監視アプリが以下のサイトでソースコード付きで公開されています。
BRILLIANT SERVICE - LogcatをAndroid端末上で見るツール LogcatViewer
実際にアプリとして作るならこちらを参考にすることをオススメします。
プロフィール

とむ・やむくん

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

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

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

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