안녕하세요. 안피곤입니다.
이전 글 "STEEM/SCT 보팅파워(VP) 안드로이드 위젯 #1"에서 안드로이드용 STEEM&SCOT 보팅파워 위젯앱을 소개를 하였습니다. 반나절만에 급하게 개발한거라 앱디자인 퀄리티는 그렇다 치더라도 위젯 새로고침 버그가 있었습니다. ㅠ
나는 왜 리액트 네이티브로 위젯을 개발할 수 있다고 생각했을까?
위젯앱은 리액트 네이티브에서 제공하는 Headless JS를 사용하여 개발했습니다. Headless JS는 백그라운드에서 JavaScript를 실행할 수 있게 해줍니다. 리액트 네이티브는 역시 짱입니다.ㅋ
위젯 작동 방식은 대략 이렇습니다. 위젯의 새로고침 버튼을 터치하면, 위젯 프로바이더(AppWidgetProvider
)가 헤드리스JS서비스(HeadlessJsTaskService
)를 호출합니다. 그리고 HeadlessJsTaskService
에서는 React Native에 있는 JavaScript를 실행합니다. 그리고 그 JavaScript는 비동기 통신 후 다시 ReactContextBaseJavaModule
모듈을 호출하여 Java 코드에 값을 전달합니다. 이렇게 설명하고 보니 굉장히 비효율적인 구조네요.
그리고 Android O 버전(API 26)에는 한가지 문제점이 있습니다. AppWidgetProvider
에서 HeadlessJsTaskService
서비스 호출이 안 됩니다. Context.startService(serviceIntent)
로 서비스를 호출하면 오류가 발생합니다. 해결 방법은 targetSdkVersion
의 버전을 낮추면 된다고 하네요. 하지만 개발자의 알량한 자존심이 허락하지 않습니다.
안드로이드 O에서는 앱이 백그라운드에 진입하게 되면 몇분 뒤 동작 중인 백그라운드 서비스는 자동으로 중지되며 onDestroy()가 호출됩니다. 더하여 백그라운드 상태에서 서비스를 구동하기 위한 startService()의 호출은 IllegalStateException이 발생하며 허용되지 않습니다.
- 출처: https://medium.com/til-kotlin-ko/android-o%EC%97%90%EC%84%9C%EC%9D%98-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%B2%98%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-jobintentservice-250af2f7783c
그래서 Context.startForegroundService(serviceIntent)
를 사용해서 서비스를 호출했습니다. 하지만 startForegroundService를 사용하여 서비스를 호출하면, Android O에서는 상태바에 Notification 알람이 발생합니다. 위젯을 새로 고칠 때마다 알람이 발생하니 보기에 좋지 않네요. 게다가 백그라운드에서 실행되는 Javascript는 가끔 네트워크 통신 응답이 없을 때가 있습니다. Javascript에서는 connection timeout 컨트롤하기가 매우 까다롭습니다.
결국 안드로이드 Java 코드로 위젯을 다시 구현하였습니다. 앱 실행 화면은 리액트 네이티브로 그대로 사용하고, 위젯만 안드로이드 Java로 구현하였습니다. 위젯 화면 갱신을 위해 안드로이드 버전 26.1.0에 추가된 JobIntentService를 사용하여 구현하였습니다.
개발 과정
JobIntentService를 사용하여 백그라운드 작업을 처리하는 방법은 다음과 같습니다.
AndroidManifest.xml
에 서비스를 추가합니다.
<service android:name=".UpdateService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="true"
android:exported="true" />
UpdateService
은 다음과 같이 구현합니다.
public class UpdateService extends JobIntentService {
static final int JOB_ID = 1000;
static void enqueueWork(Context context, Intent work) {
enqueueWork(context, UpdateService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(Intent intent) {
String username = intent.getStringExtra("username");
int[] appWidgetIds = intent.getIntArrayExtra("appWidgetIds");
new SteemAsyncTask(this, appWidgetIds).execute(username);
}
}
WidgetProvider
에서는 새로고침 브로드캐스트가 발생하면 UpdateService
를 실행합니다.
public class WidgetProvider extends AppWidgetProvider {
// (...)
@Override
public void onReceive(final Context context, final Intent intent) {
// (...)
if (Constant.ACTION_REFRESH.equals(intent.getAction())) {
// AsyncLocalStorage 에서 username 가져오기
ReactDatabaseSupplier rdbs = ReactDatabaseSupplier.getInstance(context);
SQLiteDatabase db = rdbs.get();
String username = AsyncLocalStorageUtil.getItemImpl(db, "username");
rdbs.closeDatabase();
if(username!=null && !username.isEmpty()) {
Intent intent = new Intent();
intent.putExtra("username", username);
intent.putExtra("appWidgetIds", appWidgetIds);
UpdateService.enqueueWork(context, intent); // 서비스 실행
}
}
}
참고로 리액트 네이티브의 AsyncLocalStorage에 저장한 데이터를 자바 코드에서 사용하려면, 다음과 같이 가져올 수 있습니다.
ReactDatabaseSupplier rdbs = ReactDatabaseSupplier.getInstance(context);
SQLiteDatabase db = rdbs.get();
String username = AsyncLocalStorageUtil.getItemImpl(db, "username");
rdbs.closeDatabase();
앱을 수정하여 다시 배포하였습니다. 변경 사항은 아래와 같습니다.
현재까지 10분이 앱을 설치해주셨습니다. 앱을 설치하신 모든 분들에게 항상 행운이 깃들길 빕니다. 부적이라고 생각하고 앱을 사용했으면 좋겠습니다. 감사합니다.
앱 관련 피드백은 댓글로 부탁드립니다.
해피 코딩하세요~!
always1success님이 anpigon님을 멘션하셨습니당. 아래 링크를 누르시면 연결되용~ ^^
always1success님의 Active Bloggers #30
헉... 하루를 마칠 시간에 피곤을 던져주시네요! ㅋㅋㅋ 앗피곤~~ ㅋㅋ
오늘 하루도 수고하셨습니다^^
오늘도 안피곤~! 고생하셨습니다. ㅋ
안피곤님과는 매번 이런 대화밖엔 없는것 같아요! ㅋㅋㅋㅋ
안피곤~ 난 피곤... 즐거운 저녁되세요!
오 테스트 끝. 정말 정말 잘 됩니다. 감사합니다.
테스트 해주셔서 감사합니다.
저도 깔았습니다
잘되는군요 감사합니다 ^^
개인적인 바람으로는 멀티계정도 지원했으면 좋겠네요
위젯을 끌어다 놓고 생성할때 아이디를 넣게 한다던가해서..
카카오버스위젯 같은건 위젯 만들때 버스번호를 검색해서 여러개 할수 있거든요
좋은 아이디어 입니다. 위젯 생성시 아이디를 지정하게 하면 더 편하겠어요. 그리고 멀티 계정 기능도 추가해야겠습니다. 감사합니다.
아낌없이주는 나무에 대한 후원으로 왔어요. 미약하나마 보팅 하고 가요.
Posted using Partiko Android
wonsama님이 anpigon님을 멘션하셨습니당. 아래 링크를 누르시면 연결되용~ ^^
wonsama님의 [SCT] 2019.05.28 글 분석
ㅎㅎㅎ 중요하죠. 저도 보팅파워뷰어 왼발로 써서 업로드했다 너무 창피해서 오른발로 업데이트했습니다. 시간날때 왼손으로 다시ㅎㅎ
ㅎㅎ 넘 재밌네요. 근데 이거 구글 통계에 어떤 정보들이 나오나요? 단순 숫자만 아니면 설마 아이디라던가 ip 나오고 이렇진 않죠?
아이디와 ip는 안나오네요. ㅋ 라이브러리를 사용하지 않아서 지금은 숫자만 나와용 ㅋ
Hi @anpigon!
Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your UA account score is currently 2.369 which ranks you at #19017 across all Steem accounts.
Your rank has improved 33 places in the last three days (old rank 19050).
In our last Algorithmic Curation Round, consisting of 255 contributions, your post is ranked at #201.
Evaluation of your UA score:
Feel free to join our @steem-ua Discord server
blockchainstudio님이 anpigon님을 멘션하셨습니당. 아래 링크를 누르시면 연결되용~ ^^
happyberrysboy님의 Steem Tools 설치 및 기본 기능들 소개
happyberrysboy님이 anpigon님을 멘션하셨습니당. 아래 링크를 누르시면 연결되용~ ^^
happyberrysboy님의 [Steem Tools] 스팀잇! 스팀코인판! 사이트에서 바로 보팅파워확인