하드디스크 무료 소프트웨어 복구 방법


2012에 구매한 하디드스크를 쓰고 있다가 최근에 큰 봉변을 당했다.
DropBox GoogleDrive auto-sync를 켜둔 덕분에 간당 간당 HDD가 결국 인식불능에 도달 했다.

HDD는 2년이상이되면 급속도로 배드섹터가 많이 생기고 신뢰성이 떨어지는 장비인데 이것을 6년 가까이 사용했으니 당연한 일이다.

석박사기간 통틀어 6년간의 연구데이터가 몽땅 날라가 버리니 정말 후회가 몰려왔다. 가장 최근에 백업한 데이터가 2017.2월이니 난감한 상황이다.

어떻게든 공짜로 해결하려고 사투한 2주간의 기록을 남긴다.

  • 하드 인식이 안되면 외장 도커를 이용해서 연결
  • 절대로 포맷하지 않음
  • 문제가 생기면 Write는 무조건 중지

주의: 정말 소중한 데이터라면 전문가에게 돈을 주고 의뢰 하는게 맞습니다. 이 방법대로 했다가 데이터 날라가는것은 저는 책임지지 않습니다.

배드섹터 복구

아래 프로그램을 사용해서 복구 했다.
DRevitalize+2.42 (한글 버전)

DRevitalize+2.42.egg

연구실 후배가 설치해서 돌려준 프로그램이다.
실제 공식 홈페이지에가면 영문으로 더 상위 버전이 있긴하다.

아래와 같이 2TB 가량의 하드에 있는 배트섹터를 복구 했다.

실제론 약 3만개가 있었고 2주간 걸렸다.
배트섹터 없는 구간은 빠르지만 BAD Sector 구간에 오면 10KB이하로 속도가 급격히 떨어져서 시간이 엄청나게 오래 걸린다.

하지만 확실히 모두 고치고나면 인식이 된다.

파티션 복구

파티션까지 없어진 상황이면 이것도 복구해야 한다. 필자는 그랬다.

검색하면 도구가 많이 나온다. 하지만 대부분 Scan완료후 복구를 시도하면 10GB이상은 Pro구매 하라는 팝업창만 나온다.

정말 사람 답답한데 이런거 가지고 과금청구하는 악덕 프로그래머들에게 화가날 뿐이다.

검색 끝에 TestDisk라는 GPL라이센스를 따르는 도구를 찾았다.
역사가 오래된만큼 Windows, , uBuntu등의 다양한 OS를 지원 했다.

실제로도 상용툴에서 보여준 성능을 그대로 보여주었다.

TestDisk Dwonload
TestDisk 사용 공식 설명서

필자는 7.1 버전을 윈도우즈10 에서 사용하요 성공적으로 파티션을 복구 했다.

성공 로그
재부팅하면 파티션이 정상 복구되어 있다.

  • Partition Find and Mount
    • 무료지만 Full scan으로 검색하면 partition이 나오긴 하지만 Fragement되어서 나와서 신뢰가 안간다.


멀티 부팅 윈도우즈 설치 후 우분투 설치 (Install uBuntu alongside Windows10)


Legacy 설치 기준

  • 윈도우즈10과 Ubuntu LTS 16.04 기준
  • UEFI가 둘다 아니다.
  • legacy로 둘다 설치
  • 윈도우즈 10부터 먼저 설치한다.

BIOS 설정

  • Fast Boot를 disable
  • Secure Boot를 disable

윈도우즈 10을 Legacy BIOS에서 설치

쉽게 설치 가능

기존 파티선 축소 (윈도우즈10)

디스크 관리자에서 기존 운영체제가 설치된 파티션을 축소한다.
그래서 새 볼륨을 생성한다.

파티션 설정

우분투 설치 창에서 기타를 선택한다.
자동 옵션들은 잘 동작하지 않는다.

SWAP 파티션 생성

메모리 넉넉하면 생성안해도 된다고 한다.
그래도 일단 생성 했다.

아까 쭐인 남은공간을 선택해서 아래와 같이 설정해서 SWAP영역을 생성한다.
메모리 8GB여서 4GB 정도만 주었다.

루트 파티션 생성

이제 남은 모든 공간을 아래와 같이 루트 파티션으로 생성해 준다.

부트로더 설치경로 설정

아래와 같이 물리적 저장 장치들이 해당 이름으로 연결 되어 있다고 하면,

  • sda가 HDD
  • sdb가 SDD

기본적으로 부트로더를 설치할 장치가 보통 /dev/sda로 잡힌다.
sdb에 즉 SSD에 운영체제가 있다면 반드시 변경해서 /dev/sdb에 설치해야한다.

그렇지 않으면 부팅시에 GRUB 부트로더가 나오지 않는다.

정상적으로 설치되면 아래와 같이 GRUB으로 선택 화면이 나오게 된다.

요점은 windows boot loader가 설치된 곳에 ubuntu의 GRUB을 설치해야 멀티 부팅 화면이 제대로 나오게 된다. 추후 UEFI 모드가 활성화된 방법에서는 EFI 파티션이 보이므로 좀 더 쉽게 이 부분을 찾을 수 있다.

참고자료

UEFI 설치 기준 (Widnows10, ubuntu 둘다)

legacy 방법과 기본적으로 파티션 설정은 같다.
우분투설치시 기타선택 후에 아래의 스크린샷과 같이 GRUB이 설치될 위치를 윈도우즈 부트 매니저가 있는 EFI 파티션으로 선택해 준다.

partition

위 스샷에서 부트로더 설치할 장치는 EFI가 있는 nvme0n1p1이 된다. 이 부분을 잘 못 선택하면 EFI 경고가 뜨고 부팅이 제대로 이뤄지지 않을 수 있음을 알려 준다.

계속을 누르면 아래와 같이 설치가 진행 되고
설치

완료되어 재부팅하면 아래와 같이 나온다.
Ubuntu를 선택하면 우분투로 들어가며, Windows Boot Manager를 선택하면 윈도우즈로 접속 된다.
Grub

참고자료


몬슨 파워모니터 (Monsoon Power Monitor)를 이용한 일체형 배터리 타입의 스마트폰 전력 소모 측정


이전 포스트에서 Monsoon Power Monitor를 이용해서 전력 소모를 측정하는 법을 다뤘다.

요즘 Mobile Device는 대부분 non-removable 배터리를 채택 하고 있기 때문에 이러한 장치들에 대해서 어떻게 측정하는지를 이번 포스트에서 다룬다.

보통 논문을 보면 사진을 작게 집어 넣어서 사실 잘 안보인다.

그래서 직접 크게 사진을 찍어서 아래와 같이 첨부한다.

전극 기판 추출

일단 스마트폰을 teardown 해야 한다.
아래 사진은 nexus 5를 한것이다.
Google에 teardown 치면 왠만한 기기들은 다 있다. 보면서 공구 사서 따라하면 된다.

그 다음 battery를 추출한 다음 아래와 같이 기판을 분리한다.

다시 충전 하지 않으면 보통 배터리가 터지진 않으니 너무 겁먹지 말고 기판을 칼로 잘라내면 된다.

자르기 쉽게 고분자로 구성된 이음 부분이 있다. 그 부만 제거하면 쉽게 분리된다.

몬슨과 연결

이제 해당 기판을 다시 스마트폰에 연결하고
양극 음극에 맞춰서 몬슨 VCC Ground를 해준다.

그럼 정상적으로 스마트폰이 켜지고 전력 소모가 측정된다.

어떤 스마트폰이나 스마트시계와 같은 웨러블 장치는 기판에 보호 회로가 있어서 무한대 저항이 걸려서 cutoff 될 수도 있다. 그럴 때는 납땜을 해서 연결해야 한다.

사실 이러한 저전력 연구는 제조사에서 하면 별것도 아닌데 이런 off the shelf 제품을 가지고 직접 하려고하면 완제품이다보니 이래저래 잔손이 많이 간다.


Docker를 ubuntu 16.04 LTS에 설치하기


https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/#install-using-the-convenience-script

가장 쉽게 설치 할수 있는 방법은 convenience script를 이용하는 것이다.
get.docker.com test.docker.com에서 각각 stable 버전과 testing 버전을 Docker Comunity Edition으로 받을 수 있다.
이러한 자동 스크립트 방식은 하나씩 설치하는 것이 비해서 간편하지만 아래와 같은 불이익이 있음을 공식 홈페이지에서 언급한다.

  • The scripts require root or sudo privileges in order to run. Therefore, you should carefully examine and audit the scripts before running them.
  • The scripts attempt to detect your Linux distribution and version and configure your package management system for you. In addition, the scripts do not allow you to customize any installation parameters. This may lead to an unsupported configuration, either from Docker’s point of view or from your own organization’s guidelines and standards.
  • The scripts install all dependencies and recommendations of the package manager without asking for confirmation. This may install a large number of packages, depending on the current configuration of your host machine.
  • Do not use the convenience script if Docker has already been installed on the host machine using another mechanism.

스크립트 실행

jaynux@jaynux-desktop:~$ curl -fsSL get.docker.com -o get-docker.sh
jaynux@jaynux-desktop:~$ sudo sh get-docker.sh

설치됨

# Executing docker install script, commit: 11aa13e
+ sh -c apt-get update -qq >/dev/null
+ sh -c apt-get install -y -qq apt-transport-https ca-certificates curl software-properties-common >/dev/null
+ sh -c curl -fsSL "https://download.docker.com/linux/ubuntu/gpg" | apt-key add -qq - >/dev/null
+ sh -c echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial edge" > /etc/apt/sources.list.d/docker.list
+ [ ubuntu = debian ]
+ sh -c apt-get update -qq >/dev/null
+ sh -c apt-get install -y -qq --no-install-recommends docker-ce >/dev/null
+ sh -c docker version
Client:
 Version:      17.11.0-ce
 API version:  1.34
 Go version:   go1.8.3
 Git commit:   1caf76c
 Built:        Mon Nov 20 18:37:39 2017
 OS/Arch:      linux/amd64

Server:
 Version:      17.11.0-ce
 API version:  1.34 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   1caf76c
 Built:        Mon Nov 20 18:36:09 2017
 OS/Arch:      linux/amd64
 Experimental: false
If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:

  sudo usermod -aG docker your-user

Remember that you will have to log out and back in for this to take effect!

WARNING: Adding a user to the "docker" group will grant the ability to run
         containers which can be used to obtain root privileges on the
         docker host.
         Refer to https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
         for more information.

참고문헌

초보를 위한 도커 안내서 -설치하고 컨테이너 실행하기


NotificationListenerService 이상 종료 문제 및 디버깅 방법


Debugging 방법

notification access를 끈다음 debug rerun을 하고 다시 enable하면 잘된다.

원글: https://stackoverflow.com/questions/37774499/cant-use-debugger-with-notificationlistenerservice

NotificationListenerService가 호출되지 않는 문제

Android caching 문제라고 한다.
즉, 장치에 app을 업로드 할 때 OS는 해당 서비스를 notification manager에 connection하고 있으므로 다시 앱을 재실행 한다고해서 그 서비시를 재 연결하지 않는다. 따라서 그 후로 이상하게 onPosted나 onRemoved 같은 callback 함수가 호출이 안되는 것이다.

해결 방법: 서비스 이름을 변경 한다. 앱을 pushing하기 전에

onListenerConnected 해당 API를 이용하면 해당 Listener가 연결 되었는지를 알 수 있다.

원글: https://stackoverflow.com/questions/33530807/why-is-this-notificationlistenerservice-not-working

Cannot get the NotificationListenerService class to work

If your APK/binary changes and your NotificationListenerService stops:

  • Rebooting fixes it.
  • Going back in to Notification access and disabling and re-enabling your app it fixes it.

원글: https://stackoverflow.com/questions/17911883/cannot-get-the-notificationlistenerservice-class-to-work

Ask the user to enable the setting (Notification Access) if needed

if (!Utilities.hasNotificationAccess(this)) 
{
     Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
     startActivity(intent);
     Log.i(TAG,"hasNotificationAccess NO");
}
else
{
    Log.i(TAG,"hasNotificationAccess YES");

    // without this startService, it never works with Android 4.4...
    // but this is not needed in Android 6... 
    Intent mServiceIntent = new Intent(this, NLService.class);
    startService(mServiceIntent);
}


Firebase 설정 방법


홈페이지에 워낙 잘 나와있지만 그냥 정리 차원에서 다뤄 본다.

앱등록

debug key 값 알아내기

  • Gradle-> Tasks-> android -> signingReport -> SHA1 key 값

그 다음 아래와 같이 등록한다.

구성 파일 다운로드

  • google-services.json 다운로드 한다음 프로젝트에다가 넣어준다.

주의할 것은 app폴더안에다가 넣어야 한다는 것이다. build가 아니다. 스크린샷을 보고 잘 해야 한다.

Firebase SDK 추가

Gradle Scripts의 첫 번째 build.gradle dependencies라는 항목안에다가 classpath 추가한다.

  • 'com.google.gms:google-services:3.1.0'의 내용을 추가 한다.

두 번째 build.gradle에서는 맨 하단에다가 apply plugin: 'com.google.gms.google-services'을 추가한다.

사용 가능한 라이브러리 추가

원하는 Firebase 기능 구현에 따라 아래의 라이브러리를 gradle에 추가한다.

  • com.google.firebase:firebase-core:11.0.4 애널리틱스
  • com.google.firebase:firebase-database:11.0.4 실시간 데이터베이스
  • com.google.firebase:firebase-storage:11.0.4 저장소
  • com.google.firebase:firebase-crash:11.0.4 오류 보고
  • com.google.firebase:firebase-auth:11.0.4 인증
  • com.google.firebase:firebase-messaging:11.0.4 클라우드 메시징
  • com.google.firebase:firebase-config:11.0.4 원격 구성
  • com.google.firebase:firebase-invites:11.0.4 초대 및 동적 링크
  • com.google.firebase:firebase-ads:11.0.4 AdMob
  • com.google.firebase:firebase-appindexing:11.0.4 앱 색인 생성
  • com.google.firebase:firebase-perf:11.0.4 성능 모니터링


The device 'SAMSUNG_Android' was unable to connect to its ideal host controller.

An attempt will be made to connect this device to the available host controller. This might result in undefined behavior for this device.


그냥 USB 2.0에해서 해결 했다.


https://communities.vmware.com/thread/446744

APK 바이너리 수정후 리패키징(repack)


APK 추출 방법

사용자 앱의 apk 저장 위치는 /data/packageName이다.
system 앱의 경우 apk 저장위치는 /system이다.
각종 앱 데이터 파일들은 (Database, so 파일)
/data/data/<appname>에 존재 한다.

이런 정보들은 기본적으로 adb shell pm 명령어를 통해서도 알 수 있다.

root@jemin-virtual-machine:~/AndroidStudioProjects# adb -d shell pm list packages -f -e com.mobisystems.android.notifications
package:/data/app/com.mobisystems.android.notifications-1/base.apk=com.mobisystems.android.notifications
# -f // see their associated file.
# -e // filter to only show enabled packages.

Resource XML 파일 분석 및 smali/baksmali 수준 분석

java 코드 생성과는 상관 없다.

APKTool

단순히 unzip을 할경우 .xml 파일과 같은 것들이 정상적으로 그 값을 표시하지 못하고 있다.
따라서 apktool를 이용해서 압축을 해제해야 한다.
apk를 디컴파일해서 구조를 살펴 볼 수 있도록 로우 레벨에서 도움을 준다. pirate 목적으로는 쓰지말라고 한다.

공식사이트: http://ibotpeaches.github.io/Apktool/

설치방법
1.5.2 이상의 apktool를 사용하기 위해서는 Java 1.7 이상이 필요하다.
java -version 을 실행해서 자신의 PC에 자바가 설치되어 있는지 확인 하자.

상세한 설치방법은 공식 사이트 참조
현재 사용한 버전은 Apktool v2.0.2이다.
2015.10.12일에 release된 것이다.
jar 파일 다운받고 -> script 다운받고 -> apk d test.apk하면 depack 된다.

Linux:

  • Download Linux wrapper script (Right click, Save Link As apktool)
  • Download apktool-2 (find newest here)
  • Make sure you have the 32bit libraries (ia32-libs) downloaded and installed by your linux package manager, if you are on a 64bit unix system.
  • (This helps provide support for the 32bit native binary aapt, which is required by apktool)
  • Rename downloaded jar to apktool.jar
  • Move both files (apktool.jar & apktool) to /usr/local/bin (root needed)
  • Make sure both files are executable (chmod +x)
  • Try running apktool via click

사용방법

$ apktool d testapp.apk
I: Using Apktool 2.0.0 on testapp.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: 1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
$

APK Studio

Apkstudio: http://www.vaibhavpandey.com/apkstudio/
GUI로 구성된 것이다.
smali code 할 때 하이라이팅이 되어서 편하다.
depackaging / re-packaging 모두 버튼으로 가능 하다.

APK Manager

ApkManager : http://forum.xda-developers.com/showthread.php?t=1310151
여러 기능이 있다.

Dalvik Executable (.dex) 파일을 jar 파일로 변경

관련 사이트
github 공식사이트
bitbucket:
사용법
wiki

UserGuide

  1. Install JDK7
// For Ubuntu 
sudo apt-get install openjdk-7-jre
  1. Download dex2jar
    2015.03.16일자로 dex2jar-2.0.zip이 최신 버전이다.

  2. 압축을 풀어서 실행 스크립트 파일을 생성한다.

# For Linux
unzip -x dex2jar-version.zip -d /home/panxiaobo
  1. dex2jar를 이용해서 xx.apk파일을 xx-dex2jar.jar로 변환 한다.
  • bat은 윈도우
  • sh는 리눅스
# For Linux, Mac OSX, Cygwin
# 경로 /root/repackToolBox/reversingTools/dex2jar-2.0
d2j-dex2jar.sh xx.apk
  1. use a decompiler to view the source.
    아래 세개중 하나의 decopiler를 이용해서 source code를 볼 수 있다. 필자는 2012년 부터 개발된 jd-gui를 사용한다.
    jd-gui: http://jd.benow.ca/
    JAD: http://varaneckas.com/jad/
    Procyon: https://bitbucket.org/mstrobel/procyon

wiki 정리 (FAQ)
이것은 decomplier 인가?
-> 이것은 단순히 .dex format을 또다른 format인 .class format으로 변경 해주는 것이다. 이 또한 하나의 binary format 이다. 이것은 source code가 아니다.

JD-GUI 사용해서 소스코드 보기

jar파일을 decompile해서 java source code 레벨로 보여준다.
smali code를 바로 수정하기 어렵기 때문에 이것을 이용해서 해당 코드를 찾는다.

java용 GUI 도구를 설치하자.
eclipse와 InteliJ용도 있다.

File>Save All Sources를 선택하면 소스코드로 저장 할 수 있다.
이것과 이전에 Apktool로 unpack한것을 같이 묶어서 Eclipse로 분석하면, 좀 더 편하게 작업할 수 있다.

Building (APK Repack)

수정한 APK를 다시 repack 하는 방법이다.

# The build option can be invoked either from b or build like shown below
# builds a directory into new.apk
$ apktool b directory_name -o new.apk

모든 build 관련 명령어

$ apktool b foo.jar.out
// builds foo.jar.out folder into foo.jar.out/dist/foo.jar file

$ apktool build foo.jar.out
// builds foo.jar.out folder into foo.jar.out/dist/foo.jar file

$ apktool b bar
// builds bar folder into bar/dist/bar.apk file

$ apktool b .
// builds current directory into ./dist

$ apktool b bar -o new_bar.apk
// builds bar folder into new_bar.apk

$ apktool b bar.apk
// WRONG: brut.androlib.AndrolibException: brut.directory.PathNotExist: apktool.yml
// Must use folder, not apk/jar file

Signing Your App Manullay without Android Studio

SDK와 JDK에 있는 standard tool을 이용해서 apk를 signing 할 수도 있다.

1. Generate a private key using keytool

$ keytool -genkey -v -keystore my-release-key.keystore
-alias alias_name -keyalg RSA -keysize 2048 -validity 10000

2. Compile your app in release mode to obtain an unsigned APK

3. Sign your app with your private key using jarsigner
아래의 명령어를 이용할경우 하나의 APK에 대해서 서로다른 sign으로 여러번 서명을 할 수 있게 된다.

$ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1
-keystore my-release-key.keystore my_application.apk alias_name

4. Verify that your APK is signed
아래의 명령어를 통해서 해당 App이 정상적으로 해당 Key로 sign 되었는지를 알 수 있다.

$ jarsigner -verify -verbose -certs my_application.apk

저장 위치: \jdk\bin

5. Align the final APK package using zipalign
zipalign을 이용해서 모든 비압축 데이터는 특정한 bye alignment로 시작하게 된다. 이를 통해서 해당 앱에 의해서 사용되는 RAM의 양을 줄일 수 있다.

$ zipalign -v 4 your_project_name-unaligned.apk your_project_name.apk

추후 확인 사이트

http://www.cs.bham.ac.uk/~axm514/NotifyMe/#

악성코드 정적분석 네이버블로그: http://suspected.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%85%EC%84%B1%EC%BD%94%EB%93%9C%EC%A0%95%EC%A0%81-%EB%B6%84%EC%84%9D-%EB%B0%A9%EB%B2%95

APK Studio 관련: http://secuinfo.tistory.com/entry/Android-Smali

Smali: https://github.com/JesusFreke/smali
http://0x616b616d61.tistory.com/21
http://strawberryit.tistory.com/142
http://secuinfo.tistory.com/entry/Android-Smali


'Computer Science > Reverse Engineering' 카테고리의 다른 글

smali/baksmali 수준의 코드 수정  (3) 2015.12.16

Ch 09 Recursion and Dynamic Programming


재귀적 방법

  • 상향식과 하향식으로 나눠 진다.

동적 프로그래밍의 예: 피보나치 수
중간과정을 저장하면 그게 동적 프로그래밍이다.

$n^2$ 복잡도의 일반 코드

int fibonacci(int i){
	if(i==0) return 0;
	if(i==1) return 1;
	return fibonacci(i-1) + fibonacci(i-2);
}

Time complexity를 $O(n)$으로 줄이는 코드

int[] fib = new int[max];
int fibonacci(int i){
	if (i==0) return 0;
	if (i==1) return 1;
	if (fib[i] != 0) return fib[i]; // 캐시된 결과 반환
	fib[i] = fibonacci(i-1) + fibonacci(i-2); // 계산 결과 캐시
	return fib[i];
}

동적 프로그래밍을 구현 할 때의 전략은 다음과 같다.

  • 재귀함수를 먼저 구현
  • 캐쉬 부분을 구현


'Computer Science > Coding Interview' 카테고리의 다른 글

Ch02 LinkedList  (0) 2017.08.11
Ch01 Array and Strings  (0) 2017.08.10
Sorting  (0) 2017.08.06
Graph Search  (0) 2017.08.06
String  (0) 2017.07.31

Handler를 이용한 시간제한 기능 구현


BLE Scanning 주기 조절

private static final long SCAN_PERIOD = 1000

private void scanLeDevice(final boolean enable){
	if(enable){
		mHandler.postDelyaed(new Runnable(){
			@Override
			public void run(){
				mScanning = false;
				mBluetoothAdapter.stopLeScan(mLeScanCallback);				
			}
		}, SCAN_PERIOD);
	mScanning = true;
}

백키 두번 이상 연속해서 누를 때만 액티비티 종료

private boolean isBackPressedOnce = false;
private static final long BACKKEY_DELAY = 5000;

@Override
public void onBackPressed(){
	if (isBackPressedOnce){
		super.onBackPressed(); // 종료한다.
	} else {
		Toast.makeText(this, R.string.backpressed_message, Toast.LENGTH_SHORT).show();
		isBackPressedOnce = true; // 한 번 백키 누른것을 저장한다.
		mHandler.postDelayed(timerTask, BACKKEY_DELAY); // 5초후 작업을 지정한다.
	}
}

// 5초가 지나면 한 번 누른 백키를 초기화 한다.
private final Runnable timerTask = new Runnable(){
	@Override
	public void run(){
		isBackPressedOnce = false;
	}

}


'Computer Science > Android Application' 카테고리의 다른 글

NotificationListenerService 이상 종료 문제 및 디버깅 방법  (0) 2017.10.31
Firebase 설정 방법  (0) 2017.10.31
AIDL과 Remote Service  (1) 2017.08.22
NDK 사용  (0) 2017.08.20
Preference  (0) 2017.08.20

Java-Builder Pattern


  • Telescoping Pattern은 생성자 overloading을 말하며 인수가 여러개면 가독서이 떨어진다.
  • JavaBeans Pattern은 getter, sgetter 메소드를 이용하는 방법을 말한다. 하지만 중간에 객체 상태가 노출되고 초기화 과정에서 객체를 참조하면 문제가 된다. 또한 언제든지 객체가 변경 될 수 있기 때문에 immutable하지 않다.

이러한 객체 초기화 문제점들을 Builder Pattern으로 해결한다.

public class BuilderPattern { 
    private int a; 
    private int b; 
    private int c; 
    private int d; 
     
    public static class Builder{ 
        private int a; 
        private int b; 
        private int c; 
        private int d; 
         
        public Builder a(int a){ 
            this.a = a; 
            return this; 
        } 
        public Builder b(int b){ 
            this.b = b; 
            return this; 
        } 
        public Builder c(int c){ 
            this.c = c; 
            return this; 
        } 
        public Builder d(int d){ 
            this.d = d; 
            return this; 
        } 
        public BuilderPattern build(){ 
            return new BuilderPattern(this); 
        } 
    } 
    private BuilderPattern(Builder builder){ 
        a = builder.a; 
        b = builder.b; 
        c = builder.c; 
        d = builder.d; 
    } 
}

BuilderPattern builderPattern = new BuilderPattern.Builder().a(1).b(2).c(3).d(4).build();


출처

http://cleancodes.tistory.com/15


'Computer Science > Design Pattern' 카테고리의 다른 글

Java-Singleton Pattern  (0) 2017.08.22

Java-Singleton Pattern


  1. 생성자를 private으로 만든다. new를 막기 위함이다.
  2. private static final 변수를 내부에서 초기화
public class Singleton{
	private static final Singleton instance = new Singleton();
	private Singleton(){ }
	public static Singleton getInstance(){ return instance; }
}

Lazy Initialization: 필요할 때 객체를 생성해서 자원 낭비를 막는다.

public class Singleton{
	private static Singleton instance;
	private Singleton{}{ }
	
	public synchronized static Singleton getInstance(){
		if(instance == null){
			instance = new Singleton();
		}
		return instance;
	}
}

이 경우 getInstance 메소드 호출할 때 마다 동기화를 수행하기 때문에 성능 저하가 심하다.

DCL (Double-Checking Locking)방법

  • 인스턴스가 null인지 확인 한 후, null이면 동기화를 얻고 객체를 생성한다. 그리고 그 다음부터는 동기화를 하지 않아도 된다.
public class Singleton{
	private volatitle static Singleton instance;
	
	private Singleton(){}

	public static Singleton getInstance(){
		if(instance == null){
			synchronized(Singleton.class){
				if(instance == null){
					instance = new Singleton();
				}
			}
		}
	}
}

참고자료

http://cleancodes.tistory.com/14


'Computer Science > Design Pattern' 카테고리의 다른 글

Java-Builder Pattern  (0) 2017.08.22

AIDL과 Remote Service


AIDL(Android Interface Definition Language)은 전에 다뤄본 다른 IDL과 유사합니다. 클라이언트와 서비스가 프로세스간 통신(IPC)을 사용하여 서로 소통하는 데 동의한 프로그래밍 인터페이스를 정의할 수 있습니다. Android에서는 한 프로세스가 다른 프로세스의 메모리에 정상적으로 액세스할 수 없습니다. 따라서 객체들을 운영 체제가 이해할 수 있는 원시 유형으로 해체하고 해당 경계에 걸쳐 마샬링해야 합니다. 이 마샬링을 위해 코드를 작성하는 일은 상당히 지루한 작업인데, Android는 AIDL을 이용해 그 일을 대신 해줍니다.

마샬링: 한 객체의 메모리에서의 표현 방식을 저장 또는 전송에 적합한 다른 데이터 형식으로 변환하는 과정이다. 마샬링은 직렬화와 유사하며 한 오브젝트 여기서는 직렬화 된 오브젝트로 멀리 떨어진 오브젝트와 통신하기 위해 사용된다.

AIDL 인터페이스 정의

AIDL을 사용하여 바인드된 서비스를 생성하려면 다음 단계를 따라야 한다.

  1. aidl 파일 생성

    • 이 파일은 메서드 서명으로 프로그래밍 인터페이스를 정의한다.
  2. 인터페이스 구현

    • Android SDK 도구는 .aidl파일을 기반으로 Java 프로그래밍 언어로 인터페이스를 생성한다. 이 인터페이스는Binder를 확장하고 AIDL 인터페이스로부터 메서드를 구현하는 Stub라는 내부 추상 클래스를 가지고 있다. Stub클래스를 확장하고 메서드를 구현해야 한다.
  3. 클라이언트에게 인터페이스 노출

    • Service를 구현하고 onBind()를 재정의하여 Stub 클래스의 구현을 반환한다.

리모트 바인딩

  • 리모트 바인딩 서비스는 다른 프로세스에서 접근하는 것을 전제로 만들어진다. 따라서 로컬에서만 사용하는 서비스라면 리모트 바인딩 서비스를 굳이 만들 필요가 없다.

생성방법

  • 바인딩한 클라이언트에 제공하는 메서드를 aidl 인터페이스로 작성한 다음에 서비스에서 stub클래스의 추상 메서드를 구현해 주면된다.

aidl 인터페이스와 생성 클래스

package com.cnu.eslab.suite;

interface ITrainingService{
	boolean setServiceMode(String filename);
}

이렇게하면 Android Studio에서는 자동으로build/generated/source/aidl디렉터리에IRemoteService.java가 생성된다.

Service에 Stub 구현

  • Service에서는 추상 클래스인 Stub 구현체를 만든다.
public class RemoteService extends Service{
    @Override
    public IBinder onBind(Intent intent) {
            return mBinder;
    }
	
	ITrainingService.Stub mBinder = new ITrainingService.Stub() {
		
		@Override
		public boolean setServiceMode(String filename) throws RemoteException {
			// TODO Auto-generated method stub
			ResultFileName = filename;
			return true;
		}
	};

위와 같이 Stub의 내부 기능을 구현하면 된다.

클라이언트에서 서비스 바인딩

Activity에서 바인딩해서 사용해야 한다.
bindService()는 바인딩 결과를 비동기로 받기 때문에, 콜백으로 사용할 Service Connection인스턴스를 bindService()메서드에 파라미터로 전달한다.

리모트 서비스 바인딩

ITrainingService counterService;

@Override
pulbic void onCreate(Bundle savedInstanceState){
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	
	conn = new TrainingConnection();
}

private class TrainingConnection implements ServiceConnection {

	public void onServiceConnected(ComponentName className, 
                                   IBinder boundService) {
      counterService = ITrainingService.Stub.asInterface((IBinder)boundService);
    }
	
    //bind service가 연결 해지 됬을 때 실행 된다.
    public void onServiceDisconnected(ComponentName className) {
      counterService = null;
    }
  } 
    @Override
	protected void onResume() {
		// TODO Auto-generated method stub
    	//restorePrefs();
	   	super.onResume();
	   	lock.disableKeyguard();
	   	bindService(serviceIntent, conn, 0);
	}
  • connectionCall: ServiceConnection을 생성한다.
  • Stub.asInterface() 메서드를 통해서 로컬인 경우는 Stub 인스턴스, 리모트인 경우 Proxy 인스턴스가 mIRemoteService에 대입된다.
  • 연결이 끊길 때는 mIRemoteService를 null로 만든다.
  • bindService()에 ServiceConnection을 전달한다.
  • mIRemoteService의 메서드를 호출할 때는 먼저 null인지 체크한다.


'Computer Science > Android Application' 카테고리의 다른 글

Firebase 설정 방법  (0) 2017.10.31
Handler를 이용한 시간제한 기능 구현  (0) 2017.08.22
NDK 사용  (0) 2017.08.20
Preference  (0) 2017.08.20
Broadcast Receiver  (0) 2017.08.16

NDK 사용


NDK를 이용해서 간단한 예제를 개발해 본다.

  • NDK (Native Developement Kit): C/C++로 안드로이드 앱을 개발할 수 있도록 도와주는 도구이다.

  • JNI (Java Native Interface): Java와 Native 언어들 간에 서로 호출하고 호출될 수 있도록 인터페이스를 제공한다.

1. NDK 설치

  • SDK Manager를 통해서 3가지를 설치한다.

    • Android NDK (Native Development Kit): Android에서 C 및 C++ 코드를 사용할 수 있도록 지원하고 네이티브 액티비티를 관리하고 물리적 구성 요소 (예: 센서 및 터치 입력)에 엑세스할 수 있는 플랫폼 라이브러리를 제공하는 툴바이다.
    • CMake: Gradle과 함께 작동하여 네이티브 라이브러리를 빌드하는 외부 빌드 도구이다.ndk-build만 사용하려는 경우에는 이 구성 요소가 필요하지는 않다.
    • LLDB: Android Studio가 네이티브 코드를 디버그하는데 사용하는 디버거이다.
  • File -> Project Structure -> SDK Location -> Android NDK location에 설정 되었는지 확인 한다.

2. Project 생성

평상시와 같이 생성하고 단지 옵션에서 include C/C++항목을 체크해 준다.

  • 그냥 자동으로 샘플 코드를 생성해 준다.

샘플 예제를 실행하면 아래와 같다.

빌드과정

  • cpp: 해당 파일은 stringFromJNI()함수를 제공해서 문자열을 반환하게 된다.
  • External Build Files: 해당 그룹에서 CMake또는 ndk-build용 빌드 스크립트를 확인 할 수 있다. build.gradle 파일이 Gradle에 앱을 빌드하는 방법을 알리는 방법과 유사하게 CMake ndk-build에서 네이티브 라이브러리를 빌드하는 방법을 파악하려면 빌드 스크립트가 필요하게 된다. 새로운 프로젝트의 경우 Android Studio CMake 빌드 스크립트 CMakeLists.txt를 생성하여 모듈의 루트 디렉토리에 배치한다.

샘플 앱 빌드 및 실행

  • Gradle이 외부 빌드 스크립트 CMakeLists.txt를 호출
  • CMake가 이 빌드 스크립트에 포함된 명령을 따라 C++소스 파일 native-lib.cpp를 공유 객체 라이브러리로 컴파일하고 이를 libnative-lib.so로 명명한다. 그러면 Gradle이 이를 APK로 패키징한다.
  • 런타임에 앱의 MainActivity System.loadLibrary()를 사용하여 네이티브 라이브러리를 로드한다. 이제 앱에서 라이브러리의 네이티브 함수 stringFromJNI()를 사용할 수 있다.
  • MainActivity.onCreate()가 Hello from C++를 반환하는 stringFromJNI()를 호출하고 이를 사용하여 TextView를 업데이트 한다.

기존 프로젝트에 C/C++ 코드 추가 (Eclipse Migration 포함)

네이티브 코드를 기존 프로젝트에 추가할 수 있다.

  • 새로운 네이티브 소스 파일 생성

이미 있으면 생략

  • CMake 빌드 스크립트 생성

이미 있으면 생략, ndk-build를 사용하기 위해서 Android.mk가 있는 경우에도 생략

  • CMake 또는 ndk-build 스크립트 파일의 경로를 제공하여 Gradle에 네이티브 라이브러리를 링크 시킨다. 방법은 아래와 같다.
    • Android Studio UI를 이용할 경우 ANdroid View에서 앱 모듈에 마우스 오른쪽 버튼을 클릭한 후 메뉴에서 Link C++ Project with Gradle을 선택한다. CMake또는ndk-build를 선택한다. CMake의 경우 CMakeLists.txt 스크립트 파일을 경로에 설정하고 ndk-build의 경우 Android.mk파일을 지정한다. 설정창은 아래와 같다.

참고문헌

http://yucaroll.tistory.com/1
공식사이트


'Computer Science > Android Application' 카테고리의 다른 글

Handler를 이용한 시간제한 기능 구현  (0) 2017.08.22
AIDL과 Remote Service  (1) 2017.08.22
Preference  (0) 2017.08.20
Broadcast Receiver  (0) 2017.08.16
Service  (0) 2017.08.16

Preference


프리퍼런스를 관리하는 클래스는 SharedPreferences이다.

  • SharedPreferences getSharedPreferences (String name, int mode)

    • 첫 번째 인수는 프레퍼런스를 저장할 XML 파일의 이름
    • mode 인수는 이 파일의 공유 모드로 0이면 읽기 쓰기가 가능 하다.
  • SharedPreferences getPreferences (int mode)

    • 파일 인수가 생략되어 있는데 이 경우 액티비티의 이름과 같은 xml 파일이 생성된다.
  • int getInt (String key, int defValue)

  • String getString (String key, String defValue)

  • boolean getBoolean (String key, boolean defValue)

값을 기록하는 메서드는 내부 클래스인 SharedPreferences.Editor가 제공된다.

  • SharedPreferences.Editor putInt(String key, int value)
  • SharedPreferences.Editor putBoolean(String key, boolean value)
  • SharedPreferences.Editor putstring(String key, String value)
  • SharedPreferences.Editor remove(String key)
  • boolean commit()
  • SharedPreferences.Editor clear()

문자열과 정수를 저장하는 간단한 예제

public class PrefTest extends Activity {
	TextView textName;
	TextView textStNum;
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.preftest);

		textName = (TextView)findViewById(R.id.name);
		textStNum = (TextView)findViewById(R.id.stnum);

		SharedPreferences pref = getSharedPreferences("PrefTest",0);
		String Name = pref.getString("Name", "이름없음");
		textName.setText(Name);

		int StNum = pref.getInt("StNum",20101234);
		textStNum.setText("" + StNum);
	}

	public void onPause() {
		super.onPause();

		SharedPreferences pref = getSharedPreferences("PrefTest",0);
		SharedPreferences.Editor edit = pref.edit();

		String Name = textName.getText().toString();
		int StNum = 0;
		try {
			StNum = Integer.parseInt(textStNum.getText().toString());
		}
		catch (Exception e) {}

		edit.putString("Name", Name);
		edit.putInt("StNum", StNum);

		edit.commit();
	}
}
  • getSharedPreferences메서드로 프레퍼런스 객체를 얻는다.
  • onPause에서 edit메서드로 기록할 데이터를 프레퍼런스에 기록 한다.
  • onCreate에서 객체를 읽어온다.

PreferenceActivity

사용자가 설정값을 입력하고 불러 올 수 있는 UI를 미리 만들어서 제공하는 자동화된 방법이다.
xml만 잘 작성하고 Activity에서 PreferenceActivity만 잘 상속해서 사용하면 된다.

<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
	android:key="age"
	android:title="나이"
	android:summary="너 도대체 몇 살이니?"
	android:defaultValue="19" 
/>
<CheckBoxPreference
	android:key="male"
	android:title="성별"
	android:summary="남자면 체크"
	android:defaultValue="true" 
/>
</PreferenceScreen>
public class PrefActivity extends PreferenceActivity {
	@SuppressWarnings("deprecation")
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		addPreferencesFromResource(R.layout.prefactivity);
	}
}

TextPref

xml을 이용한 방법은 parser를 이용해야 하기 때문에 속도가 느리다.
빈번하게 onPause에서 현재 상태를 저장해야 한다면 문제가 발생 한다.

그래서 그냥 단순히 text에 기록하고 불러오는 방법을 사용한다.

테스트 코드

public class TextLogTest extends Activity {
	LinearLayout mLinear;

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.textlogtest);

		// onCreate에서 로그 유틸리티 초기화
		TextLog.init(this);
		TextLog.mAppendTime = true;
		TextLog.mReverseReport = true;

		mLinear = (LinearLayout)findViewById(R.id.linear);
		mLinear.setOnTouchListener(new View.OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					// 필요할 때 로그 기록
					lg.o("down. x = " + (int)event.getX() + 
							", y = " + (int)event.getY());
					return true;
				case MotionEvent.ACTION_MOVE:
					lg.o("move. x = " + (int)event.getX() + 
							", y = " + (int)event.getY());
					return true;
				}
				return false;
			}
		});
	}

	// 다음 두 메서드를 디버깅 프로젝트의 엑티비티에 추가한다.
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		TextLog.addMenu(menu);
		return true;
	}

	public boolean onOptionsItemSelected(MenuItem item) {
		if (TextLog.execMenu(item) == true) {
			return true;
		}
		return false;
	}
}
//텍스트 파일에 설정 정보를 저장하는 클래스. 안드로이드의 프레프런스가 너무 느려 새로 만듬
//Ready()를 호출하여 입출력 준비하고 기록할 때는 CommitWrite, 읽기만 했을 때는 EndReady를 호출한다.
class TextPref {
	String mPath;
	StringBuilder mBuf;
	static final String HEADER = "__Text Preference File__\n"; 

	// 생성자로 프레퍼런스의 완전 경로를 전달한다. 
	public TextPref(String Path) throws Exception {
		mPath = Path;
		File file = new File(mPath);
		if (file.exists() == false) {
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(HEADER.getBytes());
			fos.close();
		}
	}

	// 설정 파일을 삭제한다.
	public void Reset() {
		File file = new File(mPath);
		file.delete();
	}

	// 버퍼를 준비하여 읽기 및 쓰기 준비를 한다.
	public boolean Ready() {
		try {
			FileInputStream fis = new FileInputStream(mPath);
			int avail = fis.available();
			byte[] data = new byte[avail];
			while (fis.read(data) != -1) {;}
			fis.close();
			mBuf = new StringBuilder(avail);
			mBuf.append(new String(data));
		}
		catch (Exception e) {
			return false;
		}
		return true;
	}

	// 버퍼의 내용을 파일로 기록한다.
	public boolean CommitWrite() {
		File file = new File(mPath);
		try {
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(mBuf.toString().getBytes());
			fos.close();
		} 
		catch (Exception e) {
			return false;
		}
		mBuf = null;
		return true;
	}

	// 버퍼를 해제하고 읽기를 종료한다. 변경한 내용은 모두 취소된다.
	public void EndReady() {
		mBuf = null;
	}
	
	// name키의 위치를 검색하여 = 다음 위치를 리턴한다. 없으면 -1을 리턴한다.
	// 우연한 중복 방지를 위해 키 이름앞에 __를 붙인다.
	int FindIdx(String name) {
		String key = "__" + name + "=";
		int idx = mBuf.indexOf(key);
		if (idx == -1) {
			return -1;
		} else {
			return idx + key.length();
		}
	}

	// 문자열 키를 기록한다. 이미 있으면 대체한다.
	public void WriteString(String name, String value) {
		int idx = FindIdx(name);
		if (idx == -1) {
			mBuf.append("__");
			mBuf.append(name);
			mBuf.append("=");
			mBuf.append(value);
			mBuf.append("\n");
		} else {
			int end = mBuf.indexOf("\n", idx);
			mBuf.delete(idx, end);
			mBuf.insert(idx, value);
		}
	}

	// 문자열 키를 읽는다. 없으면 디폴트를 리턴한다.
	public String ReadString(String name, String def) {
		int idx = FindIdx(name);
		if (idx == -1) {
			return def;
		} else {
			int end = mBuf.indexOf("\n", idx);
			return mBuf.substring(idx, end);
		}
	}

	// 정수를 읽는다. 일단 문자열 형태로 읽은 후 변환한다.
	public void WriteInt(String name, int value) {
		WriteString(name, Integer.toString(value));
	}

	// 정수를 기록한다. 문자열 형태로 변환하여 기록한다.
	public int ReadInt(String name, int def) {
		String s = ReadString(name, "__none");
		if (s.equals("__none")) {
			return def;
		}
		try {
			return Integer.parseInt(s);
		}
		catch (Exception e) {
			return def;
		}
	}
	
	public void WriteLong(String name, long value) {
		WriteString(name, Long.toString(value));
	}
	
	public long ReadLong(String name, long def) {
		String s = ReadString(name, "__none");
		if (s.equals("__none")) {
			return def;
		}
		try {
			return Long.parseLong(s);
		}
		catch (Exception e) {
			return def;
		}
	}
	
	// 진위값은 true, false가 아닌 1, 0으로 기록한다.
	public void WriteBoolean(String name, boolean value) {
		WriteString(name, value ? "1":"0");
	}

	public boolean ReadBoolean(String name, boolean def) {
		String s = ReadString(name, "__none");
		if (s.equals("__none")) {
			return def;
		}
		try {
			return s.equals("1") ? true:false;
		}
		catch (Exception e) {
			return def;
		}
	}
	
	public void WriteFloat(String name, float value) {
		WriteString(name, Float.toString(value));
	}

	public float ReadFloat(String name, float def) {
		String s = ReadString(name, "__none");
		if (s.equals("__none")) {
			return def;
		}
		try {
			return Float.parseFloat(s);
		}
		catch (Exception e) {
			return def;
		}
	}
	
	// 한꺼번에 값을 삽입하기 위해 준비한다. 헤더 작성하고 충분한 버퍼를 할당한다.
	void BulkWriteReady(int length) {
		mBuf = new StringBuilder(length);
		mBuf.append(HEADER);
		mBuf.append("\n");
	}

	// 문자열 형태로 받은 값을 무조건 뒤에 덧붙인다.
	void BulkWrite(String name, String value) {
		mBuf.append("__");
		mBuf.append(name);
		mBuf.append("=");
		mBuf.append(value);
		mBuf.append("\n");
	}

	// 키를 삭제한다. 
	void DeleteKey(String name) {
		int idx = FindIdx(name);
		if (idx != -1) {
			int end = mBuf.indexOf("\n", idx);
			mBuf.delete(idx - (name.length() + 3), end + 1);
		}
	}
}

출처

[1] 안드로이드 정복 4판, 김상형


'Computer Science > Android Application' 카테고리의 다른 글

AIDL과 Remote Service  (1) 2017.08.22
NDK 사용  (0) 2017.08.20
Broadcast Receiver  (0) 2017.08.16
Service  (0) 2017.08.16
Activity  (0) 2017.08.16

Broadcast Receiver


보내는 쪽은 하나이고 받는 쪽은 다수인 개념으로 Broadcast라는 용어를 통상 쓴다.

BroadcastReceiver클래스를 재정하여 사용 한다.

  • void onReceive (Context context, Intent intent)

메인 스레드에서 동작하기 때문에 간결하게 작성하고 신속하게 리턴해야한다.

시스템에서 응용프로그램으로 방송하는 것이 일반적이지만 응용 프로그램도 특별한 변화를 유발시켰다거나 특이한 변화를 발견했다면
다른 응용 프로그램에게 방송을 보낼 수 있다.
그래서 방송은 응용 프로그램끼리 통신하는 공식적인 수단으로 활용된다. 응용 프로그램이 방송할 때는 다음 메서드를 호출한다.

  • void sendBroadcast (Intent intent [, String receiverPermission])
  • void sendOrderdBroadcast (Intent intent, String receiverPermission)

일반 방송은 비동기적으로 동작하여 호출시 즉시 리턴한다. 수신자가 방송을 수신했는지의 여부는 관여하지 않으며 누가 먼저 방송을 받을지 알 수 없다. 비동기적이며 비순서적이므로 효율이 좋다.

BroadCast 발생과 수신

임의로 응용프로그램에서 BR을 발생시키는 코드이다.

public class DetectFree extends Activity {
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.detectfree);
	}

	public void mOnClick(View v) {
		Intent intent = new Intent();
		intent.setAction("andexam.ver6.FREEWIFI");
		sendBroadcast(intent);
	}
}

수신하는 코드는 다음과 같다.

public class FreeBR extends BroadcastReceiver {
	public void onReceive(Context context, Intent intent) {
		Intent intent2 = new Intent(context, 
				andexam.ver6.c28_network.AsyncDownHtml.class);
		intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		context.startActivity(intent2);
	}
}

매니패시트

<receiver android:name=".c29_br.FreeBR">
    <intent-filter>
        <action android:name="andexam.ver6.FREEWIFI" />
    </intent-filter>
</receiver>

BR이 수신되면 html을 다운받는 새로운 Activity를 실행하는 코드를 작성해 두었다.

동적 BR 등록

매니페스트에 등록하지 않고 코드상에서도 할 수 있다.
즉 필요할 때만 등록하고 해지 시킬 수 있다.

  • Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter)
  • void unregisterReceiver (BroadcastReceiver receiver)

등록 메서드로 BR 객체와 인텐트 필터를 직접 전달한다.

  • onResume() 등록
  • onPause() 해제

코드는 아래와 같다.

public class OnSaveZone extends Activity {
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.onsavezone);
	}
	
	public void onResume() {
		super.onResume();
		IntentFilter filter = new IntentFilter();
		filter.addAction("andexam.ver6.SAVEZONE");
		registerReceiver(mSaveZoneBR, filter);
	}
	
	public void onPause() {
		super.onPause();
		unregisterReceiver(mSaveZoneBR);
	}

	BroadcastReceiver mSaveZoneBR = new BroadcastReceiver() {
		public void onReceive(Context context, Intent intent) {
			Toast.makeText(context, "아싸! 공짜다.", 
					Toast.LENGTH_LONG).show();
		}
	};
}

배터리 감시용 코드

public class WatchBattery extends Activity {
	TextView mStatus;

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.watchbattery);

		mStatus = (TextView)findViewById(R.id.status);
	}
	
	public void onResume() {
		super.onResume();
		IntentFilter filter = new IntentFilter();
		filter.addAction(Intent.ACTION_BATTERY_CHANGED);
		filter.addAction(Intent.ACTION_BATTERY_LOW);
		filter.addAction(Intent.ACTION_BATTERY_OKAY);
		filter.addAction(Intent.ACTION_POWER_CONNECTED);
		filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
		registerReceiver(mBRBattery, filter);
	}	

	public void onPause() {
		super.onPause();        
		unregisterReceiver(mBRBattery);
	}

	BroadcastReceiver mBRBattery = new BroadcastReceiver() {
		int Count = 0;
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			Count++;
			if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
				onBatteryChanged(intent);
			}
			if (action.equals(Intent.ACTION_BATTERY_LOW)) {
				Toast.makeText(context, "배터리 위험 수준", Toast.LENGTH_LONG).show();
			}
			if (action.equals(Intent.ACTION_BATTERY_OKAY)) {
				Toast.makeText(context, "배터리 양호", Toast.LENGTH_LONG).show();
			}
			if (action.equals(Intent.ACTION_POWER_CONNECTED)) {
				Toast.makeText(context, "전원 연결됨", Toast.LENGTH_LONG).show();
			}
			if (action.equals(Intent.ACTION_POWER_DISCONNECTED)) {
				Toast.makeText(context, "전원 분리됨", Toast.LENGTH_LONG).show();
			}
		}

		public void onBatteryChanged(Intent intent) {
			int plug, status, scale, level, ratio;
			String sPlug = "";
			String sStatus = "";

			if (intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false) == false){
				mStatus.setText("배터리 없음