티스토리 뷰
LocalBroadcastReceiver 를 이해하자
브로드캐스트는 다른 앱에 송신하는 것이 가능하지만 경우에 따라서는 다른 앱에 알릴 필요 없이 앱 내에서 완결시키고 싶을 때도 있습니다. 그럴 때는 로컬 브로드캐스트로 다른 앱에 알리지 않고 끝낼 수 있습니다.
로컬 브로드캐스트를 수신하려면 LocalBroadcastReceiver 를 이용합니다.
로컬 브로드캐스트는 다음에 설명할 Service와 조합해 이용하는 경우가 많습니다.
Service 쪽에서 처리를 하고, 처리가 끝나면 액티비티나 프래그먼트에 로컬 브로드캐스트에 처리 완료를 알리는 식으로 사용합니다.
브로드캐스트를 수신해 처리할 때 주의할 점
안드로이드는 전력 소비를 줄이고자 사용자가 화면을 끄면 슬립 상태로 들어갑니다. 브로드캐스트를 수신해서 뭔가 시간이 오래 걸리는 처리를 한창 하는중에 슬립되는 경우가 있습니다.
슬립상태란 일반적으로 화면이 꺼지고 CPU가 동작하지 않는 상태를 슬립 상태라고 합니다. 이런 슬립 상태에서도 브로드캐스트를 받을 수 있지만 받은 우에 곧 바로 슬립하므로 시간이 걸리는 처리는 취소되어 계속할 수 없습니다
그러므로 처리를 계속하려먼 CPU를 깨울 필요가 있습니다 이런 동작을 안드로이드 시스템에서는 WakeLock을 얻는다고 합니다. 잠들 수 없는 방에 CPU를 넣고 열쇠로 잠궈두고 반대로 WakeLock을 해제 합니다. 단말기 상태와 WeakLock 종류는 정리 해보겠습니다.
상태 |
대응하는 WakeLock |
화면 ON(밝다), CPU ON |
FULL_WAKE_LOCK |
화면 ON(약간 어둡다), CPU ON |
SCREEN_DIM_WAKE_LOCK |
화면 OFF, CPU ON |
PARTIAL_WAKE_LOCK |
화면 OFF, CPU OFF |
없음 |
아울러 FULL_WAKE_LOCK, SCREEN_DIM_WAKE_LOCK 은 현재는 폐기 예정 상태라서
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 을 대신 이용하길 권장합니다.
한가지 주의할 점은 WakeLock 해제를 잊으면 CPU가 슬립할 수 없이 계속 동작하고 전력을 낭비하게 됩니다. 이런 실수는 앱의 신뢰성과 연관되는 충분히 주의 합시다.
다행이 지원 라이브러리에 WakeFulBroadcastReceiver라는 클래스가 있어서 이 클래스를 이용하면 처리중에만 WakeLock을 얻고 처리가 끝나면 해제하는 일련에 흐름을 실행해 줍니다.
덧붙여, WakefulBroadcastReceiver로 얻은 WakeLock는 60초로 타임아웃이 설정돼 있습니다. 따라서 처리 시간이 60초 이상 걸리면 슬립 상태로 전환됩니다. 이점에도 주의가 필요 합니다.
예제 : 퍼미션 WAKE_LOCK을 이용한 선언
<uses-permission android:name="android.permission.WAKE_LOCK" />
WakefulBroadcastReceiver.onReceive() 에서 startWakefulService() 를 호출해
WakeLock 을 얻은 상태에서 IntentService를 시작하고 그 안에서 처리합니다.
public class MyReceiver detends WakeFulBroadcastReceiver {
public MyReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
Intent serviceIntent = new Intent(context, MyIntentService.class);
startWakefulService(context, serviceIntent);
}
}
IntentService 쪽에서 처리가 끝나면 WakeFulBroadcastReceiver.completeWake fulIntent() 를 호출해 WakeLock을 해제합니다. 덧붙여, IntentService는 처리를 백그라운드 스레드에서 실행해주는 Service로서 처리가 끝나면 자신을 완료하는 편리한 클래스 입니다.
@Override
protected void onHandleIntent(Intent intent) {
try {
Log.d("MyIntentService", "onHandleIntent START");
// 30초 슬립
Thread.currentThread().sleep(30 * 1000);
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("test", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("KEY", "SAVED");
editor.apply();
Log.d("MyIntentService", "onHandleIntent SAVED");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
Log.d("MyIntentService", "onHandleIntent Finally");
WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
}
전체 예제
Androidmanifast.xml
: 리시버를 등록 한다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.advanced_android.wakefulbroadcastreceiversample">
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="com.advanced_android.wakefulbroadcastreceiversample.TEST_ACTION"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<service android:name=".MyIntentService"/>
</application>
</manifest>
activity.xml
: 리스버를 받은 이후 확인을 위해 text 값을 셋팅 하자
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.advanced_android.wakefulbroadcastreceiversample.MainActivity">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>
: 앱이 첫 실행되면
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 이전 설정 클리어
SharedPreferences sharedPreferences = getSharedPreferences("test", Context.MODE_PRIVATE);
sharedPreferences.edit().clear().apply();
setContentView(R.layout.activity_main);
(new Thread(new Runnable() {
@Override
public void run() {
try {
Log.d("TEST", "1 : 30초간 sleep 시작 ~ ");
Thread.currentThread().sleep(30 * 1000);
sendBroadcast(new Intent("com.advanced_android.wakefulbroadcastreceiversample.TEST_ACTION"));
Log.d("TEST", "3 : 30초 이후 sendBroadcast 시작 Intent 생성 ");
} catch (InterruptedException e) {
}
}
})).start();
}
@Override
protected void onResume() {
super.onResume();
SharedPreferences sharedPreferences = getSharedPreferences("test", Context.MODE_PRIVATE);
String result = sharedPreferences.getString("KEY", null);
TextView textView = (TextView) findViewById(R.id.text);
if (result == null) {
Log.d("TEST", "2 : 첫 onResume시 key value 값 null");
textView.setText("잠시 방치해서 슬립상태로 해주세요(30초미만으로 슬립하도록 설정해 두세요).");
} else {
Log.d("TEST", "7: 다시 onResume 되었을때 실행됨 ~ ");
textView.setText("MyIntentService로 처리가 완료");
}
}
}
MyReceiver.java
: Androidmanifast.xml 에서 등록된 리시버가 있으므로 실행..
public class MyReceiver extends WakefulBroadcastReceiver {
public MyReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
// onReceive 처리
Log.d("TEST", "4 : 리스브를 받고 인텐트 생성 후 startWakefulService 실행");
Intent serviceIntent = new Intent(context, MyIntentService.class);
startWakefulService(context, serviceIntent);
}
}
MyIntentService.java
/**
* * IntentService 는 Service 의 subclass 로 일반 Service 와 달리 worker thread 를 이용하여
* reqeust 를 handle 한다. 이것은 동시에 여러 요청을 처리할 필요가 없을 때 best option이 된다.
* IntentService는 각 요청마다 onHandleIntent() 가 들어온다.
*/
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
try {
Log.d("TEST", "5 : SharedPreferences 셋팅 후 completeWakefulIntent 실행 ");
// 30초 슬립
Thread.currentThread().sleep(30 * 1000);
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("test", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("KEY", "SAVED");
editor.apply();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
Log.d("TEST", "6 : 또 30초 이후 finally 실행 .... ");
WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
}
}
'android' 카테고리의 다른 글
9. 상주 서비스를 만들자 (0) | 2018.05.23 |
---|---|
8. Service의 종류와 수명주기를 이해하자 (0) | 2018.05.20 |
6. BroadcastReceiver로 브로드캐스트 이벤트를 수신하자 (0) | 2018.05.20 |
5. UI를 갖지 않는 프래그먼트를 이용하자 (0) | 2018.05.20 |
4.중첩 프래그먼트를 이용하자 (0) | 2018.05.09 |