16 Aug 2017
|
Android
onSaveInstanceState() 메소드를 이용하면 Activity가 종료될 때 데이터를 저장할 수 있습니다.
일반적으로 사용자가 정상적인 행동으로 Activity를 종료할 때는 해당 이벤트를 미리 감지하고
그에 맞는 대처를 해줄 수가 있지만, 실제로는 다양한 상황에서 Activity가 종료되는 현상이 발생할 수
있습니다.
Activity가 종료되는 경우
- 사용자가 ‘뒤로 가기(Back)’ 버튼을 눌러 Activity를 종료한 경우
- Activity가 백그라운드에 있을 때 시스템 메모리가 부족해진 경우(OS가 강제 종료시킴)
- 언어 설정을 변경할 때
- 화면을 가로/세로 회전할 때
- 폰트 크기나 폰트를 변경했을 때
onSaveInstanceState 예제
package snowdeer.customviewexample;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static final String KEY_DATA = "KEY_DATA";
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text_view);
if (savedInstanceState != null) {
String data = savedInstanceState.getString(KEY_DATA);
textView.setText(data);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String data = textView.getText().toString();
outState.putStringArrayList(KEY_DATA, data);
}
}
화면 회전시 라이프 사이클
화면을 회전할 때 발생하는 이벤트들은 다음과 같습니다. 화면을 회전할 때마다 Activity가 종료되고
새로 만들어지기 때문에 라이프 사이클을 잘 이해하고 대처하는 것이 좋을 것 같습니다.
onPause() → onSaveInstanceState() → onStop() → onDestory() → onCreate() → onStart() → onResume()
Fragment에서의 onSaveInstanceState
Activity에서와 마찬가지로 Fragment에서도 onSaveInstanceState 메소드를 이용해서 기존 데이터를
유지하도록 할 수 있습니다.
Fragment 종료시 onPause() → onSaveInstanceState() 순서로 호출됩니다.
Fragment 시작시 라이프 사이클
onAttach() → onCreate() → onCreateView() → onActivityCreated() → onStart() → onResume()
Fragment 전환시 라이프 사이클
onPause() → onSaveInstanceState() → onStop() → onDestoryView() → onDestory() → onDetach() → onAttach() → onCreate() → onCreateView() → onActivityCreated() → onStart() → onResume()
16 Aug 2017
|
Android
오래 걸리는 작업은 인텐트 서비스(Intent Service)를 이용해서 실행할 수 있습니다.
예를 들어 특정 Broadcast 메세지를 수신한 Broadcast Receiver에서 시간이 오래걸리면
‘ANR(Application Not Responding)’ 에러가 발생할 수도 있습니다.
이런 경우는 인텐트 서비스를 이용하면 수월하게 해결 할 수 있습니다.
예제는 다음과 같습니다.
SnowBroadcastReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class SnowBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent newIntent = new Intent(context, SnowIntentService.class);
context.startService(newIntent);
}
}
SnowIntentService.java
import android.app.IntentService;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.util.Log;
public class SnowIntentService extends IntentService {
public SnowIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
long sum = 0;
for (int i = 0; i < 100000; i++) {
sum += i;
}
try {
Thread.sleep(60 * 1000);
Log.i("", "Sum : " + sum);
} catch (Exception e) {
e.printStackTrace();
}
}
}
16 Aug 2017
|
Android
Stack
여기서 스택(Stack)은 각 Activity가 실행될 때 그 순서가 저장되는 공간입니다.
‘뒤로 가기(Back)’ 버튼을 누르면 최상단의 Activity가 종료되고 그 다음 Activity가
화면에 출력됩니다.
Task
Activity는 그룹으로 묶여서 관리됩니다. 여러 Activity를 하나의 그룹으로 묶은 것이
태스크(Task)입니다. 안드로이드 공식 사이트에서는 ‘Task는 사용자 관점에서 보는
App 단위’라고 말하고 있습니다.
Task 역시 스택에 저장됩니다.
복수의 Activity와 복수의 Task간 관계
예를 들어 다음과 같은 App이 있다고 가정합니다.
시나리오는 다음과 같습니다.
- 메일 작성 App을 실행한다.
- 첨부하기 버튼을 눌러 갤러리 App의 사진 선택 화면을 실행한다.
- 메일 작성칸에 사진이 첨부된다.
이 경우, 갤러리 App에 있는 사진 선택 화면 Activity는 메일 App의 Task에
포함됩니다. 갤러리 App의 Activity이지만, 사용자 관점에서는 메일 App에 포함된 것으로
보이기 때문입니다.
새로운 Task로 시작하기
위의 시나리오처럼 메일 App과 사진 선택 Activity가 밀접하게 연동되는 경우가 아니라
완전히 다른 App을 실행하는 경우는 서로 다른 Task로 시작되어야 합니다.
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Affinity
일반적으로 같은 Task에서 실행하는 Activity는 같은 Affinity를 갖고 있습니다.
Affinity는 AndroidManifest.xml에서 taskAffinity 속성에서 지정할 수 있습니다.
기본적으로 패키지 이름을 갖습니다.
보통은 크게 신경쓰지 않아도 되는 속성이지만, 만약 여러 개의 App을 하나의 패키지로 묶어서
배포하는 경우 각각의 App에 해당하는 Activity 들은 별도의 Affinity를 지정하는 것이
좋습니다. 그렇게 하지 앟은 경우, 예를 들어 하나의 패키지내에 2개의 Activity가 있으며
각 Activity는 별개의 App 처럼 동작할 때 Affinity가 같다면, 실행 아이콘을 클릭시 전혀
의도하지 않은 Activity가 실행되는 등의 예상하지 못한 문제가 발생할 수 있습니다.
Activity 이중 실행 방지
AndroidManifest.xml의 activity의 속성 중 launchMode라는 속성으로 설정할 수 있습니다.
| 옵션 |
설명 |
| standard |
기본값이며 여러 인스턴스 생성이 가능 |
| singleTop |
Task 상단에 해당 Activity 인스턴스가 있는 경우, 새로운 인스턴스 생성을 하지 않고 기존 인스턴스의 onNewIntent()를 호출 |
| singleTask |
Activity가 새로운 Task의 루트 인스턴스로 실행됨. 다른 Task에 속할 수 없음. Activity가 이미 존재하는 경우는 기존 Activity의 onNewIntent()를 호출 |
| singleInstance |
singleTask와 기본적으로 동일하지만, 시스템은 인스턴스를 보유하고 있는 것 외에 다른 Activity를 실행하지 않음. Task와 Activity는 항상 하나만 있음 |
일반적으로 standard와 singleTop를 많이 사용하며, singleTask와 singleInstance는 개발 사이트에서도 ‘일반적인 경우에는 권장하지 않는다’라고 되어 있습니다.
15 Aug 2017
|
Android
IDE
Tips
안드로이드 스튜디오를 보다 편리하게 사용할 수 있는 단축키들입니다.
| 기능 |
단축키 |
| Find Action |
Ctrl + Shift + A |
| 자동 완성(Basic Completion) |
Ctrl + Space |
| 오류 수정(Quick Fix) |
Alt + Enter |
| 선언부 이동(Navigate Declaration) |
Ctrl + B |
| 메소드 호출한 곳 찾기(Navigate Call Hierarchy) |
Ctrl + Alt + H |
| Formatter 적용(Reformatting) |
Ctrl + Alt + L |
| 파라메터 정보(Parameter Info) |
Ctrl + P |
| 최근 파일(Recent Files) |
Ctrl + E |
| 이전/다음 변경 내역 이동 |
Ctrl + Alt + ← 또는 Ctrl + Alt + → |
14 Aug 2017
|
리눅스 명령어
Grep
grep는 파일의 내용을 확인해서 찾는 문자열이 포함되었는지 아닌지 찾아주는 명령어입니다. ‘Global Regular Expression Print’의 약어입니다.
사용 방법
grep -r "검색하고 싶은 문자열" /home/pi
-r 옵션은 하위 폴더까지 검색하도록 하는 옵션입니다. /home/pi는 검색 시작 위치입니다.
만약 대소문자 구분을 하지 않으려면 -i 옵션을 사용하면 됩니다. -E 옵션은 파일 내용의 대소문자 차이를 무시하고 정규 표현식으로 검색하는 옵션입니다.
Regular Expression
grep에서 사용할 수 있는 간단한 정규식은 대표적으로 다음과 같습니다.
| 코드 |
설명 |
| ( ) |
그룹화 |
| | |
좌우 중 하나 |
| ? |
직전의 표현이 0회 또는 1회 등장 |
| * |
직전의 표현이 0회 이상 연속해서 등장 |
| + |
직전의 표현이 1회 이상 연속해서 등장 |
| . |
임의의 한 문자 |
| ^ |
줄 머리 |
| $ |
줄 끝 |