Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
Bundle b = new Bundle();
b.putInt("key", 1); //Your id
intent.putExtras(b); //Put your id to your next Intent
startActivity(intent);
finish();
Bundle b = getIntent().getExtras();
int value = b.getInt("key");
한 Activity 내에서 다시 UI영역을 분할해서 관리할 수 있게 해준다. Fragment도 Activiy와 비슷한 생명주기를 갖는다.
Fragment에 파라매터 전달
/*** From Activity you send data with intent as: ***/
Bundle bundle = new Bundle();
bundle.putString("edttext", "From Activity");
// set Fragmentclass Arguments
Fragmentclass fragobj = new Fragmentclass();
fragobj.setArguments(bundle);
/*** in Fragment onCreateView method: ***/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String strtext = getArguments().getString("edttext");
return inflater.inflate(R.layout.fragment, container, false);
}
String 변수에 담겨있는 HTML데이터를 WebView를 통해서 보여주고자 할 때, 문자열 인코딩 문제가 발생할 수 있다. 이 경우, WebView의 loadData() 말고 loadDataWithBaseURL() 매서드를 사용하자. loadData()에서는 인코딩 설정이 제대로 동작하지 않는다. (버그인듯)
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
goBack();
return true;
}
return super.onKeyUp(keyCode, event);
}
}
Javascript에서 Android 코드 호출
public class WebAppInterface {
Context mContext;
/** Instantiate the interface and set the context */
WebAppInterface(Context c) {
mContext = c;
}
/** Show a toast from the web page */
@JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}
public class ProgressDialog01 extends Activity {
private ProgressDialog dlg = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
public class ProgressBarSampleActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
public class LocalDbHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "test.db";
private static final int DATABASE_VERSION = 1;
public LocalDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// create DB tables.
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
dbHelper = new LocalDbHelper(context);
// open
SQLiteDatabase db = dbHelper.getWritableDatabase();
// close
if (db != null) db.close();
if (dbHelper != null) dbHelper.close();
Data Types
NULL NULL値
INTEGER 符号付整数。1, 2, 3, 4, 6, or 8 バイトで格納。날짜 등의 정보도 숫자로 변환해서 처리해야 한다.
REAL 浮動小数点数。8バイトで格納
TEXT テキスト。UTF-8, UTF-16BE or UTF-16-LEのいずれかで格納
BLOB Binary Large OBject。入力データをそのまま格納
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 */) {
sndPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, 0);
}
else {
sndPool = new SoundPool.Builder()
.setMaxStreams(10)
.build();
}
Load
sndPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int soundId, int status) {
// TODO
}
});
int soundID = sndPool.load(audioPath, 1 /* priority */);
//--- or ---//
int res_id = 00000; // Resource ID
int soundID = sndPool.load(res_id, 1 /* priority */);
Calendar calendar = Calendar.getInstance();
calendar.clear(); // 중요: set 하기 전에 반드시 clear를 해줄 것!
calendar.set(year, month, day, hour, min, sec);
long datetile = calendar.getTimeInMillis();
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
new DownloadFilesTask().execute(url1, url2, url3);
AsyncTask's generic types
The three types used by an asynchronous task are the following:
1.Params, the type of the parameters sent to the task upon execution.
2.Progress, the type of the progress units published during the background computation.
3.Result, the type of the result of the background computation.
Not all types are always used by an asynchronous task. To mark a type as unused, simply use the type Void:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
Events
When an asynchronous task is executed, the task goes through 4 steps:
1.onPreExecute()
2.doInBackground(Params...)
3.onProgressUpdate(Progress...)
4.onPostExecute(Result)
주의할 점
The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
The task instance must be created on the UI thread.
execute(Params...) must be invoked on the UI thread.
Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.
The task can be executed only once (an exception will be thrown if a second execution is attempted.)
====================================================================================
Google Cloud Messaging (GCM)
API Key 발급받기
당연히 구글 개발자 등록이 먼저 되어 있어야 한다.
1. https://code.google.com/apis/console 접속
2. API Access > Create new Server Key 메뉴로 들어감.
3. 허가할 IP address를 입력하라고 나오는데, 아무것도 쓰지 않으면 모두 허용.
4. Create 버튼을 누르면 키 생성.
여기서 생성된 키로 서버 어플케이션에서 푸시 메세지를 보낼 수 있다.
Sender ID (Project ID)
클라이언트에서 GCM에 등록을 할 때 필요한 Sender ID로서, Project ID가 있어야 한다.
API Console사이트의 URL에서 project: 이후에 나오는 숫자가 Project ID이다.
필요한 라이브러리(Android)
1. SDK Manager > Extras/Google Play services 라이브러리 설치.
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
void showNoti(String message) {
long id = Calendar.getInstance().getTimeInMillis();
showNoti((int)id, getApplicationContext(), "GCM Sample", message, message);
}
void showNoti(int notiID, Context context, String title, String message, String ticker) {
// 받은 메세지를 Notification에 표시하는 예제.
// 이 부분은 상황에 맞게....
Intent intent = new Intent(context, LoginActivity.class);
// Check device for Play Services APK. If check succeeds, proceed with GCM registration.
if (checkPlayServices()) {
gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (regid.isEmpty()) {
registerInBackground();
}
}
else {
Log.i(Config.getAppTag(), "No valid Google Play Services APK found.");
}
// . . .
}
/////// GCM ///////////////////
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
/**
* Substitute you own sender ID here. This is the project number you got
* from the API Console, as described in "Getting Started."
*/
String SENDER_ID = "your Project ID (API Console)";
GoogleCloudMessaging gcm;
Context context;
String regid;
/**
* Check the device to make sure it has the Google Play Services APK. If
* it doesn't, display a dialog that allows users to download the APK from
* the Google Play Store or enable it in the device's system settings.
*/
private boolean checkPlayServices() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.i(Config.getAppTag(), "This device is not supported.");
finish();
}
return false;
}
return true;
}
/**
* Stores the registration ID and the app versionCode in the application's
* {@code SharedPreferences}.
*
* @param context application's context.
* @param regId registration ID
*/
private void storeRegistrationId(Context context, String regId) {
// GCM 등록이 성공할 때 받은 registration ID를 어딘가에 보관해 둔다.
Config.gcm_reg_id = regId;
Config.savePreference();
}
/**
* Gets the current registration ID for application on GCM service, if there is one.
* <p>
* If result is empty, the app needs to register.
*
* @return registration ID, or empty string if there is no existing
* registration ID.
*/
private String getRegistrationId(Context context) {
Config.loadPreference(context);
String registrationId = Config.gcm_reg_id;
if (registrationId.isEmpty()) {
Log.i(Config.getAppTag(), "Registration not found.");
return "";
}
// Check if app was updated; if so, it must clear the registration ID
// since the existing regID is not guaranteed to work with the new
// app version.
int registeredVersion = Config.app_ver;
int currentVersion = getAppVersion(context);
if (registeredVersion != currentVersion) {
Log.i(Config.getAppTag(), "App version changed.");
/**
* Registers the application with GCM servers asynchronously.
* <p>
* Stores the registration ID and the app versionCode in the application's
* shared preferences.
*/
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
// You should send the registration ID to your server over HTTP, so it
// can use GCM/HTTP or CCS to send messages to your app.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device will send
// upstream messages to a server that echo back the message using the
// 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return msg;
}
@Override
protected void onPostExecute(String msg) {
// TODO ...
/**
* @return Application's version code from the {@code PackageManager}.
*/
private static int getAppVersion(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
// should never happen
throw new RuntimeException("Could not get package name: " + e);
}
}
/**
* Sends the registration ID to your server over HTTP, so it can use GCM/HTTP or CCS to send
* messages to your app. Not needed for this demo since the device sends upstream messages
* to a server that echoes back the message using the 'from' address in the message.
*/
private void sendRegistrationIdToBackend() {
// 서버에 registration ID를 전송할 필요가 있을 때, 이곳에서 처리.
// 굳이 여기서 처리하지 않더라도, 보관해둔 registration ID를 로그인 과정 등에서 전달해도 된다.
}
Server
https://android.googleapis.com/gcm/send 이 URL로 다음과 같이 푸시 메세지를 날린다.
Content-Type:application/json
Authorization:key=서버API_KEY
{
"registration_ids" : ["메세지를 전송할 Client의 Registration ID", ...],
"data" : {
// key: value 형식으로 자유롭게 구성할 수 있다.
...
},
}
/* MainActivity class */
public void setAlram(String title, String message, int year, int month, int day, int hour, int min, int sec) {
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
alarmIntent.putExtra("title", title);
alarmIntent.putExtra("message", message);
if (Config.DEBUG_MODE) {
Log.i("PROJECT_D", "alarm: " + title + " | "+ message);
}
String.format() 등에서 사용하는 형식 문자열(formatted string)을 그대로 xml 리소스로 빼내면 다음과 같은 에러가 발생.
Multiple substitutions specified in non-positional format; did you mean to add the formatted="false" attribute?
이유는 %문자가 xml에서 사용되는 특수 기호이기 때문. 다음과 같이 처리하자.
<!-- 이것은 에러 -->
<string name="msg_format_date">%4d年%02d月%02d日</string>
<!-- 이렇게 한다 -->
<string name="msg_format_date">%1$4d年%2$02d月%3$02d日 [新着号]</string>
1$, 2$ 등의 의미는 1번째 인자, 2번째 인자, ... 를 의미한다.
ConnectivityManager.getNetworkInfo() 문제
ICS 버전에서 ConnectivityManager의 getNetworkInfo() 호출이 null이 되는 경우가 있다.
ConnectivityManager cm =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
3G 기능이 없는 기기일 때, 위 코드에서 ni는 null 값이 되버린다.
그러니 반드시 getNetworkInfo()의 반환값이 null인지 먼저 확인하고 진행할 것.
MapView 추가할 때
MapActivity에서 supoer.onCreate()가 먼저 호출된 다음에 MapView가 추가되어야 오류가 발생하지 않는다.
layout.xml 등으로 MapView를 추가할 때도 setContentView() 호출이 supoer.onCreate() 다음에 되도록 주의.
Debug Certificate expired
어느날 프로젝트를 빌드하는데 Debug Certificate expired 오류가 뜬다. 이건 뭥미?
해결: clean project 후 rebuild는 이미 시도해 보았겠지? 그래도 저 오류가 뜬다면 debug.keystore 인증기간이 지난 것이다. (인증기간이 1년짜리라고 한다. 쫌시럽게..) Windows '사용자(Users)' 폴더 밑에 .android 라는 폴더에서 debug.keystore 파일을 지워보고 다시 빌드하면 된다.
네트워크 스레드 문제
Android 3.0 이상에서 android.os.NetworkOnMainThreadException 오류를 만났다면, 이것은 메인 스레드에서 네트워크 작업을 수행했기 때문이다.