땀이 삐질삐질 나는 개발 일기

안드로이드 Service Binding과 StepCount 만보기 본문

개발 Tip

안드로이드 Service Binding과 StepCount 만보기

삐질 2019. 7. 2. 22:32

안녕하세요. 삐질삐질 개발하는 개발자 삐질입니다.


오늘은 서비스와 만보계 센서를 이용해 바인딩하는 과정을 알아보도록 하겠습니다.


MainActivity.java


public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private StepService stepService; // 서비스 클래스 객체를 선언
boolean isService = false; // 서비스 중인 확인용

private TextView textCount, statusService;
private Button startBtn, endBtn;
private Intent intent; //서비스 객체를 가지고 있는 인텐트 객체

private StepCallback stepCallback = new StepCallback() { //서비스 내부로 Set되어 스텝카운트의 변화와 Unbind의 결과를 전달하는 콜백 객체의 구현체
@Override
public void onStepCallback(int step) {
textCount.setText("" + step);
}

@Override
public void onUnbindService() {
isService = false;
statusService.setText("해제됨");
Toast.makeText(MainActivity.this, "디스바인딩", Toast.LENGTH_SHORT).show();
}
};

private ServiceConnection serviceConnection = new ServiceConnection() { //서비스 바인드를 담당하는 객체의 구현체
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Toast.makeText(MainActivity.this, "예스바인딩", Toast.LENGTH_SHORT).show();
StepService.MyBinder mb = (StepService.MyBinder) service;
stepService = mb.getService(); //
stepService.setCallback(stepCallback);
isService = true;
statusService.setText("연결됨");
}

@Override
public void onServiceDisconnected(ComponentName name) { //요거는 사실상 서비스가 킬되거나 아예 죽임 당했을 때만 호출된다고 보시면 됨

// stopService 또는 unBindService때 호출되지 않음.
isService = false;
statusService.setText("해제됨");
Toast.makeText(MainActivity.this, "디스바인딩", Toast.LENGTH_SHORT).show();
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
stepService = new StepService();
startBtn = findViewById(R.id.startBtn);
endBtn = findViewById(R.id.endBtn);
textCount = findViewById(R.id.textCount);
statusService = findViewById(R.id.textStatusService);
setListener();

}
public void setListener() {
startBtn.setOnClickListener(this);
endBtn.setOnClickListener(this);
}

@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.startBtn:
intent = new Intent(this, StepService.class);
startService(intent);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
break;
case R.id.endBtn:
try {
stopService(intent);
unbindService(serviceConnection);
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
@Override
protected void onStop() {//액티비티를 벗어났을 땐 서비스와 액티비티의 바인드를 끊어줌 : 백그라운드에서 UI를 건들지 않게 하기 위함
super.onStop();
unbindService(serviceConnection);
}
}



StepCallback.Java

public interface StepCallback {
void onStepCallback(int step); //서비스에서 스텝 변화 시 액티비티로 전달하는 콜백 함수
void onUnbindService(); // 서비스 언바인드 시 액티비티로 전달하는 콜백 함수
}


StepService.Java

public class StepService extends Service implements SensorEventListener {

private MyBinder mMyBinder = new MyBinder();

class MyBinder extends Binder { //바인드 클래스를 생성
StepService getService() { // 서비스 객체를 리턴
return StepService.this;
}
}

private int mStepDetector;
private StepCallback callback;

public void setCallback(StepCallback callback) {
this.callback = callback;
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMyBinder;
}


private SensorManager sensorManager;
private Sensor stepDetectorSensor;
private Sensor stepCountSensor;

@Override
public void onCreate() {
super.onCreate();
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
stepDetectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
if (stepDetectorSensor == null) {
} else {
sensorManager.registerListener(this, stepCountSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
// COUNTER
stepCountSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
if (stepCountSensor == null) {
} else {
sensorManager.registerListener(this, stepDetectorSensor, SensorManager.SENSOR_DELAY_NORMAL);

}
}


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
stepDetectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
if (stepDetectorSensor == null) {
} else {
sensorManager.registerListener(this, stepCountSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
// COUNTER
stepCountSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
if (stepCountSensor == null) {
} else {
sensorManager.registerListener(this, stepDetectorSensor, SensorManager.SENSOR_DELAY_NORMAL);

}
return super.onStartCommand(intent, flags, startId);
}


@Override
public boolean onUnbind(Intent intent) {
unRegistManager();
if (callback != null)
callback.onUnbindService();
return super.onUnbind(intent);
}


public void unRegistManager() { //혹시 모를 에러상황에 트라이 캐치
try {
sensorManager.unregisterListener(this);
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_STEP_DETECTOR) {
if (event.values[0] == 1.0f) {
mStepDetector += event.values[0];
if (callback != null)
callback.onStepCallback(mStepDetector);
Log.e("스텝 디텍터", "" + event.values[0]);
}
} else if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) {
Log.e("스텝 카운트", "" + event.values[0]);
}
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/textStatusService"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="서비스상태" />
<Button
android:id="@+id/startBtn"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="시작" />

<Button
android:id="@+id/endBtn"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="스탑" />

<TextView
android:id="@+id/textCount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="스텝카운트" />
</LinearLayout>


설명이 필요하다면 단톡방으로 고고씽!


----2020.5/29 추가내용 -----------


-안드로이드 10 (Target SDK 29 )가 빌드업 되면서 센서를 사용하려면 권한을 인가 받아야 합니다. 

- 권한을 인가 받는 쉬운 방법으로는 : TedPermission  Or 직접 리퀘스트 코드 

- TedP는 검색해보시면 라이브러리 Github공홈이 사용법에 더 자세히 나와있으니 참고해주시고

- 직접 작성한다면 아래와 같은 양식으로 작성할 수 있습니다.

- 예시를 위해 권한을 요청하고 onRequestPermissionsResult  에서 권한 거절 / 승인 분기하지 않고 무조건 승인받은 것을 전제로 깔았으니, 실제 코딩할 땐 권한이 거절되었을때 분기 처리도 하셔야 합니다.

- 아래는 예시 입니다.


순서대로

1. Member Variables Field 

2. Activity or Fragment 일때는 onCreate() / Service일때는 onCreate / OnstartCommand() 양쪽  내부에 넣어주는 것이 좋습니다.

3. onRequestPermissionsResult 

4. Manifest.xml 

lateinit var  sensorManager :SensorManager
lateinit var sensor:Sensor
if(ContextCompat.checkSelfPermission(this,
Manifest.permission.ACTIVITY_RECOGNITION) == PackageManager.PERMISSION_DENIED){
//ask for permission
requestPermissions(arrayOf(Manifest.permission.ACTIVITY_RECOGNITION), 0);
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
sensorManager.registerListener(this,sensor,Sensor.TYPE_STEP_COUNTER)
}
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>





안드로이드 초보 개발자를 위해 아래와 같은 카카오 오픈톡을 운영 중입니다


Comments