반응형

안드로이드 코드를 작성하다보면

android.view.WindowManager$BadTokenException

위 같은 BadTokenException 유형의 에러가 확인될 때가 있다.

 

해당 문제에 대하여 원인파악 및 조치방안을 확인해보자

 

 

일단 위치를 확인하면 WindowManager 아래에 있는 함수다.

 

그럼 WindowManager 는 어떤건가?

[공식문서]

 

WindowManager  |  Android 개발자  |  Android Developers

WindowManager Jetpack WindowManager 라이브러리를 사용하면 애플리케이션 개발자가 새로운 기기 폼 팩터와 멀티 윈도우 환경을 지원할 수 있습니다. 라이브러리는 API 버전 14 이상에서 공통 API 노출 영역

developer.android.com

 

간단하게는 화면 표출에 관련된 함수다.. 그럼 왜 BadTokenException 이 발생하는걸까??

 

화면에 출력을 해줄려고 했는데 !! 

해당 Activity 가 동작하지 않는 경우에 발생한다!!!

반응형

 

그럼 심플하게 생각하면 

 

해당 Activity 가 동작중인지 체크만 추가해주면? ㅎㅎ 해결된다.

 

어떻게??

 

아래처럼 ㅎㅎ 

Activity.this.isFinishing() 조건식을 확인해보면 가능!! 

try {
    // Activity 동작 확인 조건식 추가
    if(!Activity_Connect.this.isFinishing()) {
        TextView tv = new TextView((Activity) CurrentOBJ);
        tv.setText("Dialog 타이틀");
        tv.setTextColor(0xFFFF8000);
        tv.setPadding(70, 50, 20, 30);
        tv.setTextSize(20F);
        AlertDialog.Builder builder = new AlertDialog.Builder((Activity) CurrentOBJ);
        builder.setCustomTitle(tv)
                .setMessage("본문 내용")
                .setPositiveButton("확인",
                        (dialog, which) -> {
                            //확인 클릭 시 동작 처리
                        })
                .setNegativeButton("취소",
                        (dialog, which) -> {
                            //취소 클릭 시 동작 처리
                        })
                .setCancelable(false)
                .show();
    }
}catch (WindowManager.BadTokenException e){
    e.printStackTrace();
}
SMALL

조건식으로 확인 후 동작하게되면 에러는 사라지게 됨 ㅎㅎ 

 

간단한 예외처리 완료 ㅎㅎ 

반응형
반응형

오늘은 간단하게 사용 가능한 파일이동 함수를 알아보겠음... 

 

A 위치에서 B 위치고 파일을 이동시키고 싶을 때 미디어파일이나 기타등등 파일이동을 시키고 싶은 때 간단하게 사용가능한 함수를 공유합니다

반응형

Function : MOVE_FILE

    /**
     * @param context : Context
     * @param inputPath : 원본 파일 Path 
     * @param inputFile : 원본 파일 이름
     * @param outputPath : 옮길 폴더 경로
     */
    public static void MOVE_FILE(Context context, String inputPath, String inputFile, String outputPath){
        InputStream in = null;
        OutputStream out = null;
        try{
            File dir = new File(outputPath);
            Log.e("dir", dir.getPath());
            if(!dir.exists()){
                dir.mkdirs();
            }

            Log.e("MOVE_FILE", outputPath + "/" + inputFile + "______" + dir.getPath());

            in = new FileInputStream(inputPath + inputFile);
            out = new FileOutputStream(outputPath + "/" + inputFile);

            byte[] buffer = new byte[1024];
            int read;
            while((read = in.read(buffer)) != -1){
                out.write(buffer, 0, read);
            }
            in.close();
            in = null;

            out.flush();
            out.close();
            out = null;

            // 기존 원본파일 삭제
            new File(inputPath + inputFile).delete();
            
            // 파일 미디어 동기화 , 사진 혹은 동영상 파일 갤러리 동기화
            File tmp_file = new File(outputPath + "/" + inputFile);
            context.sendBroadcast(new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(tmp_file)) );
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
SMALL

아시는 분은 아시겠지만 사용법은 간단합니다.

 

파라미터 설명
inputPath  원본파일 Path
inputFile  원본파일 이름
outputPath  이동할 폴더 경로

 

해당 파라미터에 대한 값을 입력 후 동작시키면 원본파일은 이동할 폴더에 원본 파일명으로 생성된 뒤 원본파일은 삭제처리 됨... ㅎㅎ 

 

 

반응형
반응형

오늘은 삽질을 하다가 찾아낸 방법을 공유하려고 씁니다

 

진짜 ㅋㅋㅋ 내용들때문에 진짜 삽질을 어마어마하게 했네요 ㅋㅋㅋㅋ 

(인터넷도 잘 서핑을 해야됨 진짜... ㅋㅋ )

 

 

자 간단하게 먼저 설명 시작!!

 

일단 안드로이드 API29 즉 안드로이드 10버전 부터는 저장소 접근 방식이 변경되어 이전 방식처럼 바로 접근은 불가함

 

 

파일 Read/Write

Android 9 이하 (API 28 이하) Permission 획득 후 접근 가능
- WRITE_EXTERNAL_STORAGE
- READ_EXTERNAL_STORAGE
Android 10 이상 (API 29 이상) Scoped storage 정책 적용

Android 10 에서는 예외사항으로 속성값 적용 시 9버전처럼 사용가능(Target Build SDK가 29일 경우, 30일 경우는 해당 속성값 무시)
-> <application
      ....
      // 아래부분 추가
      android:requestLegacyExternalStorage="true"
    >

Private 영역은 권한 요청없이 생성, 삭제 가능
Public 영역은 MediaStore 함수를 이용하여 생성, 삭제 등 가능
반응형

 

일단 Storage Scope 에 대한 내용은 아래 링크 참조바랍니다.

[구글 공식내용]

 

Android 11의 저장소 업데이트  |  Android 개발자  |  Android Developers

Android 11의 저장소 업데이트 Android 11(API 수준 30)에서는 플랫폼을 한층 더 강화하여 외부 저장소의 앱 및 사용자 데이터를 더욱 안전하게 보호합니다. 이 버전에는 미디어의 원시 파일 경로 액세

developer.android.com

 

 

자 그러면 Private 영역, Public 영역은 뭐가 있는지 봅시다

 

이때까진 

// 루트경로
String path = Environment.getExternalStorageDirectory().getPath();

위 처럼 루트 경로에서 "패키지명" 폴더 를 만들어서 작업을 많이 했다면.... 이젠 불가....

 

공용 폴더 Path 정리

//공용 DCIM 폴더
String DCIM_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_DCIM).getPath();

//공용 다운로드 폴더
String Download_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS).getPath();

//공용 음악 폴더
String Music_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_MUSIC).getPath();

//공용 팟캐스트 폴더
String Podcast_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_PODCASTS).getPath();

//공용 벨소리 폴더
String Ring_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_RINGTONES).getPath();

//공용 알람 폴더
String Alarm_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_ALARMS).getPath();

//공용 알림음 폴더
String Noti_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_NOTIFICATIONS).getPath();

//공용 사진 폴더
String Picture_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES).getPath();

//공용 영상 폴더
String Movie_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_MOVIES).getPath();

//공용 문서 폴더
String Document_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS).getPath();

//공용 스크린샷 폴더
String Screenshot_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_SCREENSHOTS).getPath();

//공용 오디오북 폴더
String Audio_Path = Environment.getExternalStoragePublicDirectory(DIRECTORY_AUDIOBOOKS).getPath();

 

Private 영역 Path 정리

// 내부폴더 Path : /Android/data/패키지명/files/

//내부 DCIM 폴더
String DCIM_Path = context.getExternalFilesDir(DIRECTORY_DCIM).getPath();

//내부 다운로드 폴더
String Download_Path = context.getExternalFilesDir(DIRECTORY_DOWNLOADS).getPath();

//내부 음악 폴더
String Music_Path = context.getExternalFilesDir(DIRECTORY_MUSIC).getPath();

//내부 팟캐스트 폴더
String Podcast_Path = context.getExternalFilesDir(DIRECTORY_PODCASTS).getPath();

//내부 벨소리 폴더
String Ring_Path = context.getExternalFilesDir(DIRECTORY_RINGTONES).getPath();

//내부 알람 폴더
String Alarm_Path = context.getExternalFilesDir(DIRECTORY_ALARMS).getPath();

//내부 알림음 폴더
String Noti_Path = context.getExternalFilesDir(DIRECTORY_NOTIFICATIONS).getPath();

//내부 사진 폴더
String Picture_Path = context.getExternalFilesDir(DIRECTORY_PICTURES).getPath();

//내부 영상 폴더
String Movie_Path = context.getExternalFilesDir(DIRECTORY_MOVIES).getPath();

//내부 문서 폴더
String Document_Path = context.getExternalFilesDir(DIRECTORY_DOCUMENTS).getPath();

//내부 스크린샷 폴더
String Screenshot_Path = context.getExternalFilesDir(DIRECTORY_SCREENSHOTS).getPath();

//내부 오디오북 폴더
String Audio_Path = context.getExternalFilesDir(DIRECTORY_AUDIOBOOKS).getPath();

 

내부폴더를 생성하고 사진, 영상 파일 생성 후 갤러리에 표출하기위해 

// 특정 사진을 갤러리에 동기화
sendBroadcast(new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)) );

동기화를 실행.... 그러나 실패....

 

왜????  Private 영역은 애시당초 Android 폴더 내에 .nomedia 라는 파일이 자동생성 되어있어 갤러리에 동기화가 되질 않는다....하아.... 

 

그래서 DCIM 폴더에 파일을 옮겨야 하는 일이 생겼는데... 

 

그런데... 여기서 공용 폴더에 파일을 옮기려고 했더니 문제가 생겼다.

 

 

 

그냥 옮기면 안되고 MediaStore 함수를 이용하여 보내야 하는 상황이 발생!!!

 

ㅋㅋㅋㅋㅋ 그럼 MediaStore 를 또 알아봐야함 ㅋㅋ 끝이없네 ㅋㅋ 

 

간단하게 함수먼저 똭!!

 

사진파일 이동

   /**
     * @param context : Context
     * @param file : 원본 파일 정보
     * @param SavePath : 옮길 폴더 디렉토리  (ex : DCIM/TEST/사진저장)
     */
    public static void ImageFileMove(Context context, File file, String SavePath){
        try {
            Log.e("ImageFileMove", file.getName());
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                ContentValues values = new ContentValues();
                values.put(MediaStore.Images.Media.TITLE, file.getName());
                values.put(MediaStore.Images.Media.DISPLAY_NAME, file.getName());
                values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
                values.put(MediaStore.Images.Media.RELATIVE_PATH, SavePath);
                values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");

                Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
                OutputStream fos = context.getContentResolver().openOutputStream(uri);

                Bitmap bmp = BitmapFactory.decodeFile(file.getPath() );
                bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);

                fos.flush();
                fos.close();
            } else {
                String dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString() + File.separator + SavePath;
                File dir_file = new File(dir);
                if(!dir_file.exists()){
                    dir_file.mkdirs();
                }

                File imgfile = new File(dir_file, file.getName());
                FileOutputStream os = new FileOutputStream(imgfile);
                Bitmap bmp = BitmapFactory.decodeFile(file.getPath() );
                bmp.compress(Bitmap.CompressFormat.JPEG, 100, os);
                os.flush();
                os.close();

                ContentValues values = new ContentValues();
                values.put(MediaStore.Images.Media.TITLE, file.getName());
                values.put(MediaStore.Images.Media.DISPLAY_NAME, file.getName());
                values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
                values.put(MediaStore.Images.Media.BUCKET_ID, file.getName());
                values.put(MediaStore.Images.Media.DATA, imgfile.getAbsolutePath());
                values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");

                context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

 

이거면 로컬에 생성한 사진파일을 DCIM 에 특정폴더로 이동하여 갤러리에 갱신시킬 수 있다 ㅎㅎ 

설명은 편하게 툭툭 쓰지만 삽질을 쫌 했음.... 후우.... 

 

속성값 중에 특히 중요한건 아래내용이다.

속성값 설명
DISPLAY_NAME 저장할 파일 이름
DATE_TAKEN 파일생성 시간
DATA 원본파일 정보
MIME_TYPE 파일 저장 형식
RELATIVE_PATH 저장할 폴더 경로

 

위에 함수를 사용하면 사진파일 이동은 별 문제 없이 가능할 것이다.

 

그럼....영상파일 이동은?!!

 

함수 또 나옵니다~~~~

 

영상파일 이동

    /**
     * @param context : Context
     * @param file : 원본 파일 정보
     * @param SavePath : 옮길 폴더 디렉토리  (ex : DCIM/TEST/영상저장)
     */
    public static void VideoFileMove(Context context, File file, String SavePath){
        try {
            Log.e("VideoFileMove", file.getName());
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                ContentValues values = new ContentValues();
                values.put(MediaStore.Video.Media.TITLE, file.getName());
                values.put(MediaStore.Video.Media.DISPLAY_NAME, file.getName());
                values.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
                values.put(MediaStore.Video.Media.RELATIVE_PATH, SavePath);
                values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");

                Uri uri = context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
                ParcelFileDescriptor pdf = context.getContentResolver().openFileDescriptor(uri, "w", null);
                if(pdf == null){
                    Log.e("asdf", "null");
                }
                else{
                    FileOutputStream fos = new FileOutputStream(pdf.getFileDescriptor());
                    fos.write(FileUtils.readFileToByteArray(file));
                    fos.close();
                    pdf.close();
                    context.getContentResolver().update(uri, values, null, null);
                }
            } else {
                MOVE_FILE(context, MP4_Path, file.getName(), MP4_Path_orig);
            }
            Sub_FileDelete(file, 81);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void MOVE_FILE(Context context, String inputPath, String inputFile, String outputPath){
        InputStream in = null;
        OutputStream out = null;
        try{
            File dir = new File(outputPath);
            Log.e("dir", dir.getPath());
            if(!dir.exists()){
                dir.mkdirs();
            }

            Log.e("MOVE_FILE", outputPath + "/" + inputFile + "______" + dir.getPath());

            in = new FileInputStream(inputPath + inputFile);
            out = new FileOutputStream(outputPath + "/" + inputFile);

            byte[] buffer = new byte[1024];
            int read;
            while((read = in.read(buffer)) != -1){
                out.write(buffer, 0, read);
            }
            in.close();
            in = null;

            out.flush();
            out.close();
            out = null;

            new File(inputPath + inputFile).delete();

            File tmp_file = new File(outputPath + "/" + inputFile);
            context.sendBroadcast(new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(tmp_file)) );
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
SMALL

 

이러면 영상파일 이동도 완료 ㅎㅎㅎ 

 

기본적인 내용은 사진 이동하고 비슷함 ㅎㅎ 

 

안드로이드 8, 9, 10, 11 까지 시험 진행 완료된 함수이니 사용해도 무방할 것임 ㅎㅎ 

 

 

 

여기까지 하고 나니 DCIM 폴더에 저장한 사진 리스트를 받아와서 삭제해야하는 경우가 생김!!!

(산넘어 산.... 역시 가볍게 끝내주질 않음....)

 

그래서 DCIM 폴더에 접근해서 전체 파일 정보를 읽어오는

File dir = new File(path);
File[] fileList = dir.listFiles();

했지만... 결과는 Null !!!!!!! 왜?!!! 

 

그럼 아래처럼 파일 존재 확인은???

File file = new File(path);
Log.e("file", "Result : " + file.exists());

가능하다.... 

 

음.... 그래서 다시 검색 시작.....

근데 거의 대다수 사이트에서 똑같이 말하는 내용이 있다.....

아래처럼 WRITE_EXTERNAL_STORAGE Permission 에서 maxSdkVersion 을 28로 설정하라고.....

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"/>

 

아래처럼 maxSdkVersion 을 29로 설정하면 10버전에서 파일 리스트 호출이 정상적으로 동작함.....

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="29"/>

 

누구냐 도대체.... 28로 설정하라해서 삽질하게 만든 사람들은.....하아.....

 

오늘의 삽질은 여기까지.... ㅎㅎ 

 

다 필요한 함수는 아니겠지만 누군가에겐 도움이 되길 바라면서 오늘도 이만 ㅎㅎ 

반응형
반응형

네트워크를 통해서 HTTP 통신 후 Request 로 응답처리까지 처음하는 사람의 경우 어려울 수도 있어서 정리함

 

코드는 간단하게 정리해서 공유드리겠음

 

클래스는 2개를 추가해야한다.

RequestHttpConnection.java 와 NetworkTask.java 클래스 2개를 생성한다.

 

SMALL

 

1. RequestHttpConnection.java

   HTTP 연결 및 데이터 전달을 위한 클래스, 방식은 POST

public class RequestHttpConnection {
    public String request(String _url, ContentValues _params){

        // HttpURLConnection 참조 변수.
        HttpURLConnection urlConn = null;
        // URL 뒤에 붙여서 보낼 파라미터.
        StringBuilder sbParams = new StringBuilder();

        /**
         * 1. StringBuffer에 파라미터 연결
         * */
        // 보낼 데이터가 없으면 파라미터를 비운다.
        if (_params == null)
            sbParams.append("");
            // 보낼 데이터가 있으면 파라미터를 채운다.
        else {
            // 파라미터가 2개 이상이면 파라미터 연결에 &가 필요하므로 스위칭할 변수 생성.
            boolean isAnd = false;
            // 파라미터 키와 값.
            String key;
            String value;

            for(Map.Entry<String, Object> parameter : _params.valueSet()){
                key = parameter.getKey();
                value = parameter.getValue().toString();

                // 파라미터가 두개 이상일때, 파라미터 사이에 &를 붙인다.
                if (isAnd)
                    sbParams.append("&");

                sbParams.append(key).append("=").append(value);

                // 파라미터가 2개 이상이면 isAnd를 true로 바꾸고 다음 루프부터 &를 붙인다.
                if (!isAnd)
                    if (_params.size() >= 2)
                        isAnd = true;
            }
        }

        /**
         * 2. HttpURLConnection을 통해 web의 데이터를 가져온다.
         * */
        try{
            URL url = new URL(_url);
            urlConn = (HttpURLConnection) url.openConnection();
            urlConn.setConnectTimeout(HTTP_TIMEOUT_m);
            urlConn.setReadTimeout(HTTP_TIMEOUT_m);

            // [2-1]. urlConn 설정.
            urlConn.setRequestMethod("POST"); // URL 요청에 대한 메소드 설정 : POST.
            urlConn.setRequestProperty("Accept-Charset", "UTF-8"); // Accept-Charset 설정.
            urlConn.setRequestProperty("Context_Type", "application/x-www-form-urlencoded;cahrset=UTF-8");

            // [2-2]. parameter 전달 및 데이터 읽어오기.
            String strParams = sbParams.toString(); //sbParams에 정리한 파라미터들을 스트링으로 저장. 예)id=id1&pw=123;
            OutputStream os = urlConn.getOutputStream();
            os.write(strParams.getBytes(StandardCharsets.UTF_8)); // 출력 스트림에 출력.
            os.flush(); // 출력 스트림을 플러시(비운다)하고 버퍼링 된 모든 출력 바이트를 강제 실행.
            os.close(); // 출력 스트림을 닫고 모든 시스템 자원을 해제.

            // [2-3]. 연결 요청 확인.
            // 실패 시 null을 리턴하고 메서드를 종료.
            if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                Log.e("RequestHttpConnection : Err[2-3] " + urlConn.getResponseCode());
                return null;
            }

            // [2-4]. 읽어온 결과물 리턴.
            // 요청한 URL의 출력물을 BufferedReader로 받는다.
            BufferedReader reader = new BufferedReader(new InputStreamReader(urlConn.getInputStream(), StandardCharsets.UTF_8));

            // 출력물의 라인과 그 합에 대한 변수.
            String line;
            StringBuilder page = new StringBuilder();

            // 라인을 받아와 합친다.
            while ((line = reader.readLine()) != null){
                page.append(line);
            }

            return page.toString();

        } catch (IOException e) { // for URL.
            e.printStackTrace();
        }// for openConnection().
        finally {
            if (urlConn != null)
                urlConn.disconnect();
        }

        Log.e("RequestHttpConnection :  Err[END]");

        return null;

    }
}
반응형

2. NetworkTask.java

   AsyncTask 로 네트워크 Request 를 호출하여 처리할 수 있도록 함

public class NetworkTask extends AsyncTask<Void, Void, String> {

    private final String url;
    private final ContentValues values;
    private final Handler receiveHandler;  // 리턴 메세지를 반환할 Handler

    public NetworkTask(String url, ContentValues values, Handler receiveHandler) {
        this.url = url;
        this.values = values;
        this.receiveHandler = receiveHandler;
    }

    @Override
    protected String doInBackground(Void... params) {

        Log.d("URL : " + url );

        String result; // 요청 결과를 저장할 변수.
        RequestHttpConnection requestHttpURLConnection = new RequestHttpConnection();
        result = requestHttpURLConnection.request(url, values); // 해당 URL로 부터 결과물을 얻어온다.

        return result;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        //doInBackground()로 부터 리턴된 값이 onPostExecute()의 매개변수로 넘어오므로 s를 메세지에 담아 리턴한다.
        try {
            Log.d("onPostExecute : " + s );
            Message msg = receiveHandler.obtainMessage(SEND_WEB_RETURN);
            Bundle data = new Bundle();
            data.putString("WEB_RETURN", s);
            msg.setData(data);
            receiveHandler.sendMessage(msg);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 

위 클래스를 작성하였다면 이젠 사용하는 방법만 익히면 가능함

 

3. 호출하여 사용

    메인에서 호출을 진행한다고 하는 경우

    아래처럼
    1) POST 데이터를 ContentValues 클래스로 삽입 

    2) 리턴 값을 처리 할 Handler 를 생성

    3) 변수들을 삽입 후 실행~!!

    4) 리턴으로 돌아온 값을 처리하면 끝~!!

//POST 데이터를 삽입
ContentValues value = new ContentValues();
value.put("DATA1", data1);
value.put("DATA2", data2);

// 데이터 리턴값을 처리할 Handler
Handler receive_handler = new Handler(msg -> {
    if(msg.what == SEND_WEB_RETURN){
        String data = msg.getData().getString("WEB_RETURN");
        if(!data.equals("null")){
            try {
                Log.d("RecvData", data);
                JSONObject object = new JSONObject(data);  //JSON 파싱
                ... //그 이후 응답 데이터 처리 진행
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
    return true;
});

NetworkTask HTTP_REQ = new NetworkTask("URL 주소", value, receive_handler);
HTTP_REQ.execute();

 

복잡해 보이지만 RequestHttpConnection.java 와 NetworkTask.java 는 한번 만들어 두면 변경할 필요없이 계속 사용 가능하다.

 

그럼 오늘도 모두들 화이팅 하세요 ㅎㅎ 

반응형
반응형

안드로이드 개발을 하다보면 단말기의 Bluetooth Log 를 확인해야하는 경우가 발생

 

해당 내용을 간략하게 공유함

 

아래 스샷처럼 [개발자 옵션] -> [블루투스 HCI 스누프 로그 사용] 을 사용으로 변경해주면 된다.

참고. 개발자 모드 스크린샷

로그 사용을 켰다면 블루투스를 한번 껐다 켜주는게 좋다

 

그리고 시험하려는 단말기와 블루투스 통신을 진행한 뒤 .....

 

이제 로그를 추출해야함

SMALL

 

1. Android Studio 를 실행!!

   [Android Studio 다운로드]

 

Download Android Studio and SDK tools  |  Android Developers

<!-- hide description -->

developer.android.com

2. 폰을 USB 연결!!

3. CMD 창에서 ADB.exe 가 있는 곳으로 이동

cd C:\Users\USER\AppData\Local\Android\Sdk\platform-tools
C:\Users\USER\AppData\Local\Android\Sdk\platform-tools>adb bugreport test.zip
/data/user_de/0/com.android.shell/files/bugreports/bugreport-2021-11-18-14-27-54.zip: 1 file pulled, 0 skipped. 24.9 MB/s (9181748 bytes in 0.351s)
Bug report copied to test.zip

4. 3번에서 adb bugreport test.zip 을 입력하면 전체 버그 리포트가 추출된다

5. Android Studio 에서 File Explorer 를 클릭한다 보통 화면 우측 하단에 존재함

참고. 화면 우측 하단 File Explorer 위치

반응형

6. 버그리포트는 bugreports 라는 폴더에 존재함

참고. bugreport 파일 위치

7. 해당 파일 우클릭 -> Save As 로 내 PC로 저장하면 됨. 그리고 압축 해제!! 

8. FS/data/log/bt 에 btsnoop_hci.log 파일이 존재 확인

9. Wireshark 라는 프로그램으로 해당 파일을 열면 완료!!

  [Wireshark 다운로드]

 

Wireshark · Download

Download Wireshark The current stable release of Wireshark is 3.4.10. It supersedes all previous releases. You can also download the latest development release (3.6.0rc3) and documentation.

www.wireshark.org

참고. Wireshark 로 btsnoop_hci.log 검색

10. 로그 분석 시작 ㅎㅎ 수고하세요 ㅎㅎ 

반응형

+ Recent posts