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

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

 
1
2
3
4
5
6
7
8
9
10
11
13
14
15
16
17
18
19
20
21
22
24
27
28
30
31
08

スポンサーサイト

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

SQLiteで他のデータベースに接続する:attach database

AndroidのデータベースはSQLiteになっています。
そのSQLiteで他のデータベースに接続してテーブルを参照などする方法。

SQLiteコマンドの attach database を使います。

以下、サンプルソース。
データベースもテーブルもすでに作成されているものとしてみてください。
public static boolean copyUserTable(Context context) {

SQLiteDatabase db = null;
try {
// メインデータベースを開く
db = context.openOrCreateDatabase("main.db", MODE_PRIVATE, null);

// サブデータベースのパスを取得する
String subPath = context.getDatabasePath("sub.db").getPath();

// サブデータベースに接続する(※Honeycombを対象にするならステートメントは使わない)
db.execSQL("attach database '" + subPath + "' as sub_db");

// トランザクションを開始する
db.beginTransaction();

// SQLを実行する
db.execSQL("INSERT OR REPLACE INTO T_USER SELECT * FROM sub_db.T_USER");

// コミットする
db.setTransactionSuccessful();

return true;

} catch (Exception e) {
return false;

} finally {
if (db != null) {
// トランザクションを終了する
db.endTransaction();

// データベースを閉じる
db.close();
}
}
}
データベースの接続には SQLiteOpenHelper を使う方がいいのですが、ざっくり説明するために Context から接続。
attach database 以外の所は気にしないでいただけると助かります…。

さて、このサンプルソースコードで注意するところは attach database を行っているところです。
// サブデータベースに接続する(※Honeycombを対象にするならステートメントは使わない)
db.execSQL("attach database '" + subPath + "' as sub_db");
subPath は接続するデータベースファイルのフルパスになります。

このパスを指定するときにステートメントを使っていないところに注目してください。
どうやら HoneyComb (Android 3.*) ではステートメントの処理に不具合があるようで、ステートメントを使用するとアタッチしてくれません

× db.execSQL("attach database ? as sub_db", new String[] { subPath });

参考(英語):stack overflow - Attached databases in Honeycomb

実際にAndroid 3.2 で試したところ、アタッチされませんでした。
(Android 2.3、Android 4.0 ではアタッチされます)

なので、仕方なく文字列結合を使用しています。

HoneyComb はソースコードが公開されていないので中で何をしているかはわかりません…。
今後アップデートされる可能性も低い(無い?)ですし、あったとしても今障害が出ている、また、ユーザーがアップデートするとは限らないのでステートメントを使用している方は文字列結合にすることをお勧めします。
スポンサーサイト

特定のアプリがインストール済みか確認する

デバイスに特定のアプリがインストールされているかは、インストール済みアプリの取得と同様に PackageManager クラスを使います。

API Reference: PackageManager, ResolveInfo

■ 画面イメージ -----------------------------------------------
android_install_app_search.png

■ ソースコード -----------------------------------------
package jp.inujirushi.sample.activity;

import java.util.ArrayList;
import java.util.List;

import jp.inujirushi.sample.R;
import jp.inujirushi.sample.adapter.TwoLineImageAdapter;
import jp.inujirushi.sample.adapter.TwoLineImageItem;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ListView;

public class SampleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_listview);

// アプリ情報を設定するリストを生成する(独自のリストビューを使用しています)
List<TwoLineImageItem> appList = new ArrayList<TwoLineImageItem>();

// 取得したいアプリを指定する(明示的・暗黙的どちらもOK)
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER); // ← 起動アプリのみ取得するために指定
intent.addCategory(Intent.CATEGORY_BROWSABLE);

// アプリ情報を取得する
PackageManager manager = getPackageManager();
List<ResolveInfo> resolveInfos = getPackageManager()
.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);

// アプリ情報をリストに追加する
if (!resolveInfos.isEmpty()) {
for (ResolveInfo info : resolveInfos) {
// アイコンを取得する
Drawable icon = info.loadIcon(manager);

// アプリ名を取得する
CharSequence appName = info.loadLabel(manager);

// パッケージ名を取得する
String packageName = info.activityInfo.packageName;

// リストに追加する
appList.add(new TwoLineImageItem(icon, appName, packageName));
}
}

// リストビューにアダプタを設定する
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(new TwoLineImageAdapter(this, appList));
}
}
※ 表示につかっている独自のリストビューの作成方法は以下の記事を参考にしてください。
(記事は1行表示用なのでこれを2行表示に修正すればOK)
【関連記事】ListView をカスタマイズする

インストール済みアプリ一覧取得 : PackageManager

デバイスにインストールされているアプリケーションに関連する情報は PackageManager クラスから取得することができます。

API Reference: PackageManager, ApplicationInfo

特定のアプリがインストール済みか確認するには次の記事を参照してください。
【関連記事】特定のアプリがインストール済みか確認する

■ 画面イメージ -----------------------------------------------
android_install_app.png

■ ソースコード -----------------------------------------
package jp.inujirushi.sample.activity;

import java.util.ArrayList;
import java.util.List;

import jp.inujirushi.sample.R;
import jp.inujirushi.sample.adapter.TwoLineImageAdapter;
import jp.inujirushi.sample.adapter.TwoLineImageItem;
import android.app.Activity;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ListView;

public class SampleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_listview);

// アプリ情報を設定するリストを生成する(独自のリストビューを使用しています)
List<TwoLineImageItem> appList = new ArrayList<TwoLineImageItem>();

// インストールされているアプリ情報を取得する
PackageManager manager = getPackageManager();
List<ApplicationInfo> infos = manager
.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo info : infos) {

// プリインストールされているアプリを除外する
// if ((info.flags & ApplicationInfo.FLAG_SYSTEM) ==
// ApplicationInfo.FLAG_SYSTEM)
// continue;

// アイコンを取得する
Drawable icon = info.loadIcon(manager);

// アプリ名を取得する
CharSequence appName = info.loadLabel(manager);

// パッケージ名を取得する
String packageName = info.packageName;

// リストに追加する
appList.add(new TwoLineImageItem(icon, appName, packageName));
}

// リストビューにアダプタを設定する
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(new TwoLineImageAdapter(this, appList));
}
}

インストールされているアプリにはプリインストールされているものも含まれているため、大量のアプリ一覧が表示されます。
これらは37~40行目のコメントアウトを解除することで表示から除外できます。
// プリインストールされているアプリを除外する
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM)
continue;


表示につかっている独自のリストビューの作成方法は以下の記事を参考にしてください。
(記事は1行表示用なのでこれを2行表示に修正すればOK)
【関連記事】ListView をカスタマイズする

外部Jarが読み込まれなくなった:NoClassDefFoundError

久しぶりに色々いじろうと少し昔に作ったアプリを動かしてみたところ、操作中に突然 java.lang.NoClassDefFoundError が発生して終了してしました。
発生したのは外部jarのメソッドを呼び出しているところ。

昔は動いていたのになぜだろう?と調べたら ADT 17以降の外部Jarの扱いが変わったのが原因でした。

【参考サイト】
Yet Another Diary - Androidで外部JarがAPKファイルに取り込まれなくなった時の対処法

【原因】
Android SDK Tools r17以降、外部Jar は libs フォルダで管理されるようになりました

いままでは lib フォルダなどに入れてビルド・パスからJar追加をしていましたが、これからは libs フォルダに .jar ファイルが入っていれるだけで勝手に組み込んでくれるようになりました。
あら便利!!

が、こちらが追加したビルド・パスは無視しやがります。
ビルド・パスが通っているとコーディングは行えるが実行すると呼び出す際に java.lang.NoClassDefFoundError が発生します。

【対策】
①ビルド・パスから追加したJarを削除する
②外部Jar を libs フォルダに移動する


これで正常に動くようになります。

バグは仕方ないにしても開発環境だけは安定させてほしい…

外部ストレージのパス取得の記事を修正しました。

タイトルのとおり、遅くなりましたが『SDカードのパス取得:Environment と System.getenv』の記事を修正しました。

HTC J を買ってはや3ヶ月。
HTCの外部ストレージパス(mnt/sdcard/ext_sd)を知ったのに修正するのを忘れていました。

FC2のアクセス解析を見て、外部ストレージのパス関係で来られている方が多数いるのに気づいてから動くとかダメですね!しかも3ヶ月もたってから!!

最近、月1,2個の記事しか上げていないのでもう少しがんばらないと。
ネタは色々あるんだけどなー。フラグメントもやらないと。あー。

しかし、外部ストレージの記事書いてから1年以上たってるのか。
時間が経つのは早い…
プロフィール

とむ・やむくん

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

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

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

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