Android: Bitmapを画面サイズにリサイズする

なんか同じ事を何度もやってるような気がしてきたので、クラス作っときました。
Bitmapを端末の画面サイズに合わせてリサイズします。縦横比は固定してるので、変な画像になることはないかと。画像扱う人はよくやる処理だと思うので、よかったら使ってやって下さい。パッケージ名は勝手に付けて下さい。

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.util.DisplayMetrics;

public class ImageUtils {
	
	private static final String TAG = "ImageUtils";
	
	public static Bitmap resizeBitmapToDisplaySize(Activity activity, Bitmap src){
		int srcWidth = src.getWidth(); // 元画像のwidth
		int srcHeight = src.getHeight(); // 元画像のheight
		Log.d(TAG, "srcWidth = " + String.valueOf(srcWidth)
				+ " px, srcHeight = " + String.valueOf(srcHeight) + " px");

		// 画面サイズを取得する
		Matrix matrix = new Matrix();
		DisplayMetrics metrics = new DisplayMetrics();
		activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
		float screenWidth = (float) metrics.widthPixels;
		float screenHeight = (float) metrics.heightPixels;
		Log.d(TAG, "screenWidth = " + String.valueOf(screenWidth)
				+ " px, screenHeight = " + String.valueOf(screenHeight) + " px");

		float widthScale = screenWidth / srcWidth;
		float heightScale = screenHeight / srcHeight;
		Log.d(TAG, "widthScale = " + String.valueOf(widthScale)
				+ ", heightScale = " + String.valueOf(heightScale));
		if (widthScale > heightScale) {
			matrix.postScale(heightScale, heightScale);
		} else {
			matrix.postScale(widthScale, widthScale);
		}
		// リサイズ
		Bitmap dst = Bitmap.createBitmap(src, 0, 0, srcWidth, srcHeight, matrix, true);
		int dstWidth = dst.getWidth(); // 変更後画像のwidth
		int dstHeight = dst.getHeight(); // 変更後画像のheight
		Log.d(TAG, "dstWidth = " + String.valueOf(dstWidth)
				+ " px, dstHeight = " + String.valueOf(dstHeight) + " px");
		src = null;
		return dst;
	}
}

使い方は、Activity内でしか使えませんが、

Bitmap src = BitmapFactory.decodeResource(getResources(), R.drawable.src_image);
Bitmap dst = ImageUtils.resizeBitmapToDisplaySize(this, src);

みたいな感じですかね。


Android: 画面を回転させないようにする

Androidで画面を縦方向・横方向などで固定するのはXMLを使えば簡単にできますが、開いた後の画面で固定させたい(つまり縦で開いたから縦固定、横で開いたから横固定)というふうにするにはどうしたらいいのかなと思って調べてみました。

// 現在の画面の向きを取得するために設定を取得
Configuration config = getResources().getConfiguration();
if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
	// 縦固定
	setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else if(config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
	// 横固定
	setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}

ActivityのonCreateで実行しておけば、固定化されます。
シェイクするアプリとかだと、シェイク中に画面の縦横が切り替わってメチャクチャになってしまうことがあったので、これで固定化しておけばとりあえず問題ないんじゃないかなと思います。


Android: プロセス毎のメモリ取得について

Androidアプリのプロセス毎のメモリ使用量を知りたくて調べていたのだけれど、よくわからないまま。まず方法がわかっていなかったので、検索して一通り調べた後、twitterで#androidevを付けて質問。すると、psコマンドで取得した戻り値をうまいこと処理したらどうか?と言われて、なるほどと。そう思ってpsコマンドを打ってみたところ、なんかすごい量のメモリを使ってるアプリばかりだと。自分が使っているProcessManagerというアプリで示されているメモリ使用量のおよそ3倍くらいをほとんどのアプリが使っていることになっていた。そんなにメモリないだろうに、どういうことだろう?と思って呟いていたら、瀬戸デ部な方々が反応してくれた。

あれこれやってみた後に、topコマンドから取得したRSSじゃないか?という話になったんだけれど、この値も結局3倍くらいあるようで怪しいと…。このまま結局わからずじまいでとりあえず他のことをやろうと思って別件に取り組んでしまった(実は探偵ナイトスクープ見たあとにやろうと思っていたのにすっかり忘れていた)。

そして、土曜日のAndroid勉強会@愛媛 3回目にて、再度チャレンジ。そのときにstachibanaさんに聞いたら、以下のページはどうですか?と教えてくれた。
http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-android

なるほど、まさにそのような内容である。
が、全編英語なので、とりあえず、はしょりながら読む。

AcitivityManager.getProcessMemoryInfoというメソッドがあるらしい!
そして構造は以下の通り。

/** The proportional set size for dalvik. */
public int dalvikPss;
/** The private dirty pages used by dalvik. */
public int dalvikPrivateDirty;
/** The shared dirty pages used by dalvik. */
public int dalvikSharedDirty;

/** The proportional set size for the native heap. */
public int nativePss;
/** The private dirty pages used by the native heap. */
public int nativePrivateDirty;
/** The shared dirty pages used by the native heap. */
public int nativeSharedDirty;

/** The proportional set size for everything else. */
public int otherPss;
/** The private dirty pages used by everything else. */
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;

ただ、これらがどういう値を示しているのかがいまいち良く分からない。
英語のほうでは、「共有メモリの量も入ってるから、合計するとおそらく物理メモリ量よりも多くなるだろう」と書かれているっぽい。

PrivateDIrtyというのが、他のプロセスとシェアしていない、1プロセスのみでの物理メモリ使用量なのではないか?と思う。(プロセスがなくなったらシステムがそのメモリを利用可能になるとかかれていたので)

ほぼリンク先に書かれているソースになってしまうけれど書かせてもらう。

ActivityManager am = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
PackageManager pm = getPackageManager();
// 現在稼働中のプロセスをLISTで取得
List<RunningAppProcessInfo> rapis= am.getRunningAppProcesses();

// プロセスIDとプロセス名のマップを作成
Map<Integer, String> pidMap = new TreeMap<Integer, String>();
for(RunningAppProcessInfo rapi : rapis) {
	pidMap.put(rapi.pid, rapi.processName);
}
			
Collection<Integer> keys = pidMap.keySet();
for (int key : keys) {
	int pids[] = new int[1];
	pids[0] = key;
	// プロセスのメモリ情報を取得
	android.os.Debug.MemoryInfo[] memoryInfos = am.getProcessMemoryInfo(pids);
	for (android.os.Debug.MemoryInfo memoryInfo : memoryInfos) {
		ApplicationInfo ai;
		ai = pm.getApplicationInfo(pidMap.get(pids[0]), 0);
		// メモリ情報を取得するためのサンプル
		// ただ、どの値が何を示しているのかが、理解できていない…。
		//
		Log.d(TAG, String.format("** MEMINFO in pid %d [%s] **\n", pids[0], pidMap.get(pids[0])));
		Log.d(TAG, " AppName: " + pm.getApplicationLabel(ai));
		Log.d(TAG, " memoryInfo.getTotalPrivateDirty(): " + memoryInfo.getTotalPrivateDirty());
		Log.d(TAG, " memoryInfo.getTotalPss(): " + memoryInfo.getTotalPss());
		Log.d(TAG, " memoryInfo.getTotalSharedDirty(): " + memoryInfo.getTotalSharedDirty());
	}
}

これでプロセス単位でメモリの情報は取れる!のだけれど、取得した本人が、よくわかっていないので、どれを使えばいいのやら…。英語に堪能な方に教えてもらいたい。また、英語の重要性を知る事になる。いつもいつも思うのだけれどね…。


Android: 時刻設定の自動同期の有効かどうかチェックする

またまたAndroidの調査で。メモメモ。

設定の日時設定の自動の項目がONかOFFになっているかどうかを確認する方法です。Androidのソースcom.android.settings.DatetimeSettings.javaに書いてありました。アドバイスをくださったdumapickさん、ありがとうございました!

private boolean getAutoState() {
    try {
        return Settings.System.getInt(getContentResolver(), 
            Settings.System.AUTO_TIME) > 0;            
    } catch (SettingNotFoundException snfe) {
        return true;
    }
}

Android: 3Gが有効になっているかを取得

これもまた調べ物の一環で。

3Gが有効かどうかを調べる方法をば。
if文のConnetivityManager.TYPE_MOBILEの場合が3Gです。
比較用にWiFiも条件に入れています。

あ、実行環境は私が所有するSoftbank X06HT(Desire) 2.2(Froyo)です。

// 3Gの状態を取得する
ConnectivityManager cm = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
// ネットワーク情報を全て取得
NetworkInfo[] networkInfos = cm.getAllNetworkInfo();
for(NetworkInfo ni: networkInfos) {
	if (ni.getType() == ConnectivityManager.TYPE_WIFI
		 || ni.getType() == ConnectivityManager.TYPE_MOBILE) {
		// 3GかWiFiの場合は情報を取得
		Log.d(TAG, "TypeName = " + ni.getTypeName());
		Log.d(TAG, "SubtypeName = " + ni.getSubtypeName());
		Log.d(TAG, "isAvailable = " + ni.isAvailable());
		Log.d(TAG, "connected = " + ni.isConnected());
	}
}
  1. モバイルネットワークOFF, WiFi OFFの場合:

    11-12 01:15:26.651: DEBUG/Top(8401): TypeName = mobile
    11-12 01:15:26.651: DEBUG/Top(8401): SubtypeName = UMTS
    11-12 01:15:26.651: DEBUG/Top(8401): isAvailable = true
    11-12 01:15:26.651: DEBUG/Top(8401): connected = false
    11-12 01:15:26.651: DEBUG/Top(8401): TypeName = WIFI
    11-12 01:15:26.651: DEBUG/Top(8401): SubtypeName =
    11-12 01:15:26.651: DEBUG/Top(8401): isAvailable = false
    11-12 01:15:26.651: DEBUG/Top(8401): connected = false

  2. モバイルネットワークOFF, WiFi ONの場合:

    11-12 01:19:07.901: DEBUG/Top(8401): TypeName = mobile
    11-12 01:19:07.901: DEBUG/Top(8401): SubtypeName = UMTS
    11-12 01:19:07.901: DEBUG/Top(8401): isAvailable = true
    11-12 01:19:07.901: DEBUG/Top(8401): connected = false
    11-12 01:19:07.901: DEBUG/Top(8401): TypeName = WIFI
    11-12 01:19:07.901: DEBUG/Top(8401): SubtypeName =
    11-12 01:19:07.901: DEBUG/Top(8401): isAvailable = true
    11-12 01:19:07.901: DEBUG/Top(8401): connected = true

  3. モバイルネットワークON, WiFi OFFの場合:

    11-12 01:16:53.891: DEBUG/Top(8401): TypeName = mobile
    11-12 01:16:53.891: DEBUG/Top(8401): SubtypeName = UMTS
    11-12 01:16:53.891: DEBUG/Top(8401): isAvailable = true
    11-12 01:16:53.891: DEBUG/Top(8401): connected = true
    11-12 01:16:53.891: DEBUG/Top(8401): TypeName = WIFI
    11-12 01:16:53.891: DEBUG/Top(8401): SubtypeName =
    11-12 01:16:53.891: DEBUG/Top(8401): isAvailable = false
    11-12 01:16:53.891: DEBUG/Top(8401): connected = false

  4. モバイルネットワークON, WiFi ONの場合:

    11-12 01:18:09.711: DEBUG/Top(8401): TypeName = mobile
    11-12 01:18:09.711: DEBUG/Top(8401): SubtypeName = UMTS
    11-12 01:18:09.711: DEBUG/Top(8401): isAvailable = true
    11-12 01:18:09.711: DEBUG/Top(8401): connected = false
    11-12 01:18:09.711: DEBUG/Top(8401): TypeName = WIFI
    11-12 01:18:09.711: DEBUG/Top(8401): SubtypeName =
    11-12 01:18:09.711: DEBUG/Top(8401): isAvailable = true
    11-12 01:18:09.711: DEBUG/Top(8401): connected = true

という結果でした。モバイルネットワークはOFFにしても、isAvailableはtrueのままだけど、WiFiはOFFにしたらisAvailableがfalseになるんすねー。接続だけはさせないよってことだろうか?

あとWiFiをONの状態でWiFiの電波のないところで試したいような気がしたけれど、まぁ3Gで接続するだけだろうからなぁ。