일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- package-lock.json
- 개인정보보호위원회
- 웹 프레임워크
- 마감임박
- 확장 유클리드 알고리즘
- arrow function
- node.js
- 백엔드
- 곱셈 암호
- 한국정보보호산업협회
- 한국산업인력공단
- 덧셈 암호
- 모듈러 연산
- 포너블
- 개인정보보호
- 개인정보안전성
- 국가인적자원개발컨소시엄
- 가명정보처리
- 디오판투스 알고리즘
- 동적타이핑
- 유클리드_알고리즘
- 백엔드입문
- package.json
- Writeup
- 개인정보보호교육
- 무료교육
- pwnable.tw
- function scope
- 호이스팅
- 한국정보보호산업협회기자단
- Today
- Total
짱짱해커가 되고 싶은 나
13-2. 스레드 본문
* 프로그레스바 & 시크바
프로그레스바 : 작업의 진행 상태 확인용으로 자주 사용
시크바 : 음악/동영상 재생의 위치 지정용으로 자주 사용
package com.example.project13_1;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ProgressBar pgBar = (ProgressBar)findViewById(R.id.progressBar);
Button btn1 = (Button)findViewById(R.id.btn1);
Button btn2 = (Button)findViewById(R.id.btn2);
final TextView textView = (TextView)findViewById(R.id.tvSeek);
SeekBar seekBar = (SeekBar)findViewById(R.id.seekBar);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pgBar.incrementProgressBy(10);
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pgBar.incrementProgressBy(-10);
}
});
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
textView.setText("진행률 : " + progress + "%");
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
}
지금은 버튼을 클릭해서 프로그레스 바를 강제로 증감시켰는데 일반적으로는 다른 작업(ex.음악 재싱)을 하면서 프로그레스바가 자연스럽게 진행되도록 만든다. 이처럼 2개의 작업을 동시에 진행하기 위해서는 thread 가 필요하다.
* 스레드
여러 작업을 동시에 수행하기 위해 사용 (멀티 스레드/경량 스레드)
스레드는 함수와 달리 하나의 작업이 끝나기 전에 다른 작업을 동시에 진행시킬 수 있다.
ex) ProgressBar 2개가 있고 모두 max=100이라고 가정하자.
ProgressBar 1 은 10에서 2씩 증가하고, ProgressBar 2는 30에서 1씩 증가한다면 동시에 진행시킨다면 Bar 1이 먼저 100에 도착한다.
package com.example.project13_1;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
ProgressBar pgBar1, pgBar2;
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pgBar1 = (ProgressBar)findViewById(R.id.pgBar1);
pgBar2 = (ProgressBar)findViewById(R.id.pgBar2);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
public void run(){
for(int i=pgBar1.getProgress(); i<100; i+=2){
pgBar1.setProgress(pgBar1.getProgress()+2);
SystemClock.sleep(100);
}
}
}.start();
new Thread(){
public void run(){
for(int i=pgBar2.getProgress(); i<100; i++){
pgBar2.setProgress(pgBar2.getProgress()+1);
SystemClock.sleep(100);
}
}
}.start();
}
});
}
}
* UI 스레드
UI 스레드는 화면의 위젯을 변경할 때 사용한다.
일반적인 스레드는 스레드 안에서 필요한 내용을 계산하는 것만 가능하며 화면의 다른 위젯을 변경할 수 없다.
ex) 프로게스바가 진행될 때마다 텍스트뷰에 진행률 표시
<일반 스레드 - 동작x>
package com.example.project13_1;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
ProgressBar pgBar1, pgBar2;
TextView textView1, textView2;
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pgBar1 = (ProgressBar)findViewById(R.id.pgBar1);
pgBar2 = (ProgressBar)findViewById(R.id.pgBar2);
btn = (Button)findViewById(R.id.btn);
textView1 = (TextView)findViewById(R.id.textView1) ;
textView2 = (TextView)findViewById(R.id.textView2);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
public void run(){
for(int i=pgBar1.getProgress(); i<100; i+=2){
pgBar1.setProgress(pgBar1.getProgress()+2);
textView1.setText("1번 진행률 : " + pgBar1.getProgress() + "%");
SystemClock.sleep(100);
}
}
}.start();
new Thread(){
public void run(){
for(int i=pgBar2.getProgress(); i<100; i++){
pgBar2.setProgress(pgBar2.getProgress()+1);
textView2.setText("2번 진행률 : " + pgBar2.getProgress() + "%");
SystemClock.sleep(100);
}
}
}.start();
}
});
}
}
앞에서 했던 그냥 스레드에서 TextView 를 변경하려고 했기 때문에 에러가 발생한다.
(예외적으로 프로그레스바와 그 자식 위젯은 스레드 안에서 변경이 가능하다.)
<UI 스레드 - 동작 o>
package com.example.project13_1;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
ProgressBar pgBar1, pgBar2;
TextView textView1, textView2;
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pgBar1 = (ProgressBar)findViewById(R.id.pgBar1);
pgBar2 = (ProgressBar)findViewById(R.id.pgBar2);
btn = (Button)findViewById(R.id.btn);
textView1 = (TextView)findViewById(R.id.textView1) ;
textView2 = (TextView)findViewById(R.id.textView2);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
public void run(){
for(int i=pgBar1.getProgress(); i<100; i+=2){
runOnUiThread(new Runnable() {
@Override
public void run() {
pgBar1.setProgress(pgBar1.getProgress()+2);
textView1.setText("1번 진행률 : " + pgBar1.getProgress() + "%");
}
});
SystemClock.sleep(100);
}
}
}.start();
new Thread(){
public void run(){
for(int i=pgBar2.getProgress(); i<100; i++){
runOnUiThread(new Runnable() {
@Override
public void run() {
pgBar2.setProgress(pgBar2.getProgress()+1);
textView2.setText("2번 진행률 : " + pgBar2.getProgress() + "%");
}
});
SystemClock.sleep(100);
}
}
}.start();
}
});
}
}
runOnUiThread(new Runnable) 안에 위젯을 변경하는 코드를 넣으면 된다.
프로젝트1: MP3 플레이어 스레드 추가
TextView 1개 추가 - 현재 재생 중인 노래 시간
package com.example.project13_1;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
ListView listView;
Button btn1, btn2;
TextView textView, textView2;
ProgressBar pgMP3;
ArrayList<String> mp3List;
String selectedMP3;
String path = "/sdcard/Music/";
MediaPlayer mPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("간단 MP3 플레이어");
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
MODE_PRIVATE);
mp3List = new ArrayList<String>();
File[] listFiles = new File("/sdcard/Music/").listFiles();
String fileName, extName;
for(int i=0; i<listFiles.length; i++){
fileName = listFiles[i].getName();
extName = fileName.substring(fileName.length() - 3);
if(extName.equals((String)"mp3")){
mp3List.add(fileName);
}
}
listView = (ListView)findViewById(R.id.listView);
btn1 = (Button)findViewById(R.id.btn1);
btn2 = (Button)findViewById(R.id.btn2);
textView = (TextView)findViewById(R.id.textView);
textView2 = (TextView)findViewById(R.id.textView2);
pgMP3 = (ProgressBar)findViewById(R.id.pbMP3);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_single_choice, mp3List);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setAdapter(adapter);
listView.setItemChecked(0, true);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectedMP3 = mp3List.get(position);
}
});
selectedMP3 = mp3List.get(0);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try{
mPlayer = new MediaPlayer();
mPlayer.setDataSource(path + selectedMP3);
mPlayer.prepare();
mPlayer.start();
btn1.setClickable(false);
btn2.setClickable(true);
textView.setText("실행중인 음악 : " + selectedMP3);
new Thread() {
SimpleDateFormat timeFormat = new SimpleDateFormat(" mm:ss ");
public void run() {
if (mPlayer == null) return;
pgMP3.setMax(mPlayer.getDuration());
while (mPlayer.isPlaying()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
pgMP3.setProgress(mPlayer.getCurrentPosition());
textView2.setText("진행 시간 : " + timeFormat.format(mPlayer.getCurrentPosition()));
}
});
SystemClock.sleep(200);
}
}
}.start();
} catch(IOException e){
}
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPlayer.stop();
mPlayer.reset();
btn1.setClickable(true);
btn2.setClickable(false);
textView.setText("실행중인 음악 : ");
textView2.setText("진행 시간 : ");
pgMP3.setProgress(0);
}
});
btn2.setClickable(false);
}
}
프로젝트2: MP3 플레이어 시크바 추가
시크바로 이동한 지점의 음악 자동 재생
pgMP3.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(fromUser){
mPlayer.seekTo(progress);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
프로젝트3: MP3 플레이어 Handler 객체 이용
UI 작업을 비동기로 처리하다 보면 동기화 문제가 발생할 수 있다. 따라서 안드로이드는 이런 문제를 해결하고자 병렬로 동작하는 메인 스레드와 워커 스레드 사이에 핸들러를 두고 UI 작업은 모두 메인 스레드로 전달되도록 한다.
package com.example.project13_1;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
ListView listView;
Button btn1, btn2, btn3;
TextView textView, textView2;
SeekBar pgMP3;
ArrayList<String> mp3List;
String selectedMP3;
int position;
String path = "/sdcard/Music/";
MediaPlayer mPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("간단 MP3 플레이어");
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
MODE_PRIVATE);
mp3List = new ArrayList<String>();
File[] listFiles = new File("/sdcard/Music/").listFiles();
String fileName, extName;
for(int i=0; i<listFiles.length; i++){
fileName = listFiles[i].getName();
extName = fileName.substring(fileName.length() - 3);
if(extName.equals((String)"mp3")){
mp3List.add(fileName);
}
}
listView = (ListView)findViewById(R.id.listView);
btn1 = (Button)findViewById(R.id.btn1);
btn2 = (Button)findViewById(R.id.btn2);
btn3 = (Button)findViewById(R.id.btn3);
textView = (TextView)findViewById(R.id.textView);
textView2 = (TextView)findViewById(R.id.textView2);
pgMP3 = (SeekBar)findViewById(R.id.pbMP3);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_single_choice, mp3List);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setAdapter(adapter);
listView.setItemChecked(0, true);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectedMP3 = mp3List.get(position);
}
});
selectedMP3 = mp3List.get(0);
Handler mp3Handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
SimpleDateFormat timeFormat = new SimpleDateFormat(" mm:ss ");
pgMP3.setProgress(mPlayer.getCurrentPosition());
textView2.setText("진행 시간 : " + timeFormat.format(mPlayer.getCurrentPosition()));
}
};
pgMP3.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(fromUser){
mPlayer.seekTo(progress);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
mPlayer = new MediaPlayer();
mPlayer.setDataSource(path + selectedMP3);
mPlayer.prepare();
mPlayer.seekTo(position);
mPlayer.start();
btn1.setClickable(false);
btn2.setClickable(true);
btn3.setClickable(true);
textView.setText("실행중인 음악 : " + selectedMP3);
new Thread() {
public void run() {
if (mPlayer == null) return;
pgMP3.setMax(mPlayer.getDuration());
while (mPlayer.isPlaying()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mp3Handler.sendEmptyMessage(0);
}
});
SystemClock.sleep(200);
}
}
}.start();
} catch (IOException e) {
}
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPlayer.pause();
position = mPlayer.getCurrentPosition();
btn1.setClickable(true);
btn2.setClickable(false);
btn3.setClickable(true);
}
});
btn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPlayer.stop();
mPlayer.reset();
btn1.setClickable(true);
btn2.setClickable(false);
btn3.setClickable(false);
textView.setText("실행중인 음악 : ");
textView2.setText("진행 시간 : ");
position = 0;
pgMP3.setProgress(0);
}
});
btn2.setClickable(false);
btn3.setClickable(false);
}
}
'모바일' 카테고리의 다른 글
14-1. 서비스 (0) | 2021.02.27 |
---|---|
13-3. 구글 지도 (0) | 2021.02.27 |
13-1. 오디오 (0) | 2021.02.27 |
12-2. SQLite 연습 (0) | 2021.02.26 |
12-1. SQLite (0) | 2021.02.26 |