Ringster's Techblog

Tech, Mobile, Internet

Posts Tagged ‘Android

Layout XML의 Background 이미지에 할당된 메모리 즉시 반환하기

with one comment

앱을 제작하는데 있어 Layout XML의 Background로 이미지를 포함시키는 작업은 상당히 빈번하게 이루어진다.

public class MainActivity extends ActionBarActivity {
    private Button launchBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        launchBtn = (Button) findViewById(R.id.button);
        launchBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(i);
                finish();
            }
        });
    }
}

위는 액티비티를 생성하고 Launch 버튼을 클릭하면 다음 액티비티가 실행되는 간단한 코드이다.
실행 결과는 다음과 같다.

xml_activity

Background로 포함된 이미지가 OOM을 유발한 만큼 크지 않은 이상 위와 같이 정상 실행된다.
여기서 문제는 아래의 화살표 부분에서 첫번째 Activity를 종료시키고 두번째 Activity를 실행했음에도 첫번째 Activity의 background XML에 할당된 이미지가 계속 메모리 공간을 점유하고 있고, 이 메모리가 언제 반환될지 알 수 없다는 것이다.
memory_monitor

이는 이미지가 커서 많은 메모리를 점유하고 있거나, 다음 Activity에서 메모리가 많이 필요한 작업을 할 경우에 OOM을 유발시킬 수 있는 요인이 될 수 있는데, 이를 간단히 해결하는 방법은 다음과 같다.

@Override
    protected void onDestroy() {
        super.onDestroy();
        findViewById(R.id.rootView).setBackground(null);
        System.gc();
    }

memory_monitor2

OnDestroy에서 Activity가 destroy될 때 View의 Background를 null로 설정하고 명시적으로 gc를 호출했다.
이로써 두번째 Activity가 시작될 때 첫번째 Activity의 XML에 할당된 메모리를 회수했음을 확인할 수 있다.

Written by Ringster

2015/03/10 at 6:55 pm

안드로이드 개발에 게시됨

Tagged with , , ,

SporTracker 제작 후기 (2) – Amazon Web Service, Elastic Beanstalk 사용기

with 2 comments

SporTracker를 제작하며 개인 기록을 저장할 간단한 서버가 필요했다.
AWS EC2에는 어느 정도 익숙한 편이지만, 일일히 EC2 인스턴스에 DB를 설치하고 웹서버를 설정하고 다시 앱과 연동하는 것은 간단히 빠르게 구현해 업로드 하려던 프로젝트의 취지에 맞지 않았다.

그래서 눈을 돌리게 된 것이 Elastic Beanstalk다. 콘솔을 통한 몇번의 클릭만으로도 Auto Scaling이 지원되는 서버를 쉽게 설정할 수 있고, mysql을 지원하는 RDS를 통해 DB 로그 보기나 DB 백업등의 관리를 손쉽게 가능하게 해준다. Cloud watch와 연동하여 여러 조건에 대해 이메일 알람을 받는것도 쉽게 설정할 수 있으며, 자체적인 버전 관리 기능도 가지고 있다.

어플리케이션 배포는 콘솔을 통해 zip 파일을 올리는 것으로도 가능하고, 이와 더불어 git을 통한 퍼블리싱도 가능하다.
나의 경우에는 Beanstalk-CLI (링크)를 설치하고, 파이썬과 boto 인터페이스(링크)를 설치 한 후 git을 통해 beanstalk 서버에 직접 업로드 하는 방식을 사용했는데, 설정도 간편하고 git을 통해 푸시하는 방식은 익숙하다 보니 어렵지 않게 이용할 수 있었다.

DB에 접속할 때, php의 경우

      $dbhost = $_SERVER['RDS_HOSTNAME'];
      $dbport = $_SERVER['RDS_PORT'];
      $dbname = $_SERVER['RDS_DB_NAME'];
      $dsn = "mysql:host={$dbhost};port={$dbport};dbname={$dbname}";
      $username = $_SERVER['RDS_USERNAME'];
      $password = $_SERVER['RDS_PASSWORD'];


      $dbh = new PDO($dsn, $username, $password);

와 같이 접근하면 DB 핸들러를 얻을 수 있다.
그 이후에는 다른 서버에서 작업할때와 별반 다른점이 없는데, 다만 고생했던 점은.. DB 테이블의 인코딩을 UTF-8로 설정하고, 앱에서도 제대로된 인코딩 값을 보내주는데 DB에서 계속 한글이 깨지게 출력되어 알고보니 RDS DB의 기본 encoding 설정이 utf-8이 아닌 latin1으로 되어었었다.

문제는 이 설정값은 Beanstalk에 의해 생성된 인스턴스에 접근해서 my.cnf 파일을 수정해도 바꿀 수가 없다는 것인데, 온갖 방법으로 시도해봐도 영 바뀌지가 않아 고생하고 있다가 결국 해결책을 찾아냈다.

AWS관리 콘솔에 들어가서 RDS 대쉬보드에 들어가 Parameter charset을 UTF-8로 적용한 그룹을 생성하고, RDS 인스턴스 중에서 Elastic beanstalk가 생성한 인스턴스에서 instance action를 선택, 이후 modify -> Database options 하위에서 해당 Parameter group을 적용한 후 RDS 인스턴스를 재부팅하면 된다.

이후 다시 DB에 접속하여 show variables like ‘c%’를 쳐보면

character_set_client utf8
character_set_connection utf8
character_set_database utf8
character_set_filesystem utf8
character_set_results utf8
character_set_server utf8
character_set_system utf8
character_sets_dir /rdsdbbin/mysql-5.5.40b.R1/share/charsets/
collation_connection utf8_general_ci
collation_database utf8_general_ci
collation_server utf8_general_ci
completion_type NO_CHAIN
concurrent_insert AUTO
connect_timeout 10
위와 같이 캐릭터 셋이 utf8로 적용된 것을 확인할 수 있다.
아마존에서는 RDS의 파라미터 값을 바꾸는 것을 권장하지 않아 이런식으로 파라미터를 잠가 놓았다는데, 기왕이면 utf-8로 설정해주지 latin1이라니.. 그 탓에 Elastic beanstalk를 포기하고 EC2 인스턴스를 그냥 하나 생성할까도 고민했었다.
Elastic Beanstalk를 처음 사용해본 탓에 생각보다 고생했지만, git을 이용한 빠른 배포와 빠른 서버 설정에는 이만한 서비스가 없는 것 같다. EC2 인스턴스를 설치해 웹서버를 설치하고, DB를 설치해 권한 및 원격 접속을 설정하고, 여러 로그들을 관리하고, Auto Scaling을 적용하는데는 너무 많은 손이 간다.
간단한 서버의 배포를 위해 이 모든것을 설정하는데, EB를 이용하면 5분이 채 걸리지 않는다. (나처럼 헤매지 않는다는 가정하에..)
aws_management
또한 위에 보이는 AWS Console와 같이 공식적으로 지원되는 앱을 통해 모바일에서도 쉽게 서비스의 상태를 파악하고, 관리 가능하다는 것은 5분도 걸리지 않는 노력에 비해 충분한 메리트가 있다.
더구나 Free tier에서 이용 가능하므로 이 모든 것을 이용하면서도 비용이 청구되지 않는다. (서비스 트래픽이 증가해 Auto Scaling이 되지 않는 이상, Auto Scaling 되더라도 기본 설정은 t1.micro 4대까지 이므로 적절히 한달 가동시간 750시간을 넘기지 않게 잘 관리해주면 된다.)
서비스가 사용자가 몰리게 되면서 트래픽이 증가하게 되면 Elastic Beanstalk의 사용에서도 불편함을 느낄테지만, 간단한 서비스를 작성해 런칭하고, 반응을 보는데는 적합한 서비스다. Twitch가 Beanstalk를 사용해 개발되었으니, 생각만큼 소규모 서비스에만 적합한 서비스도 아니다. 사실 백엔드에 대한 복잡한 고민 없이 아이디어와 서비스의 질 자체에 집중하고 싶다면 Beanstalk가 좋은 선택이 될 것이라 생각한다.

Written by Ringster

2015/03/02 at 5:30 pm

SporTracker 제작 후기 (1) – Twitter Fabric, Crashlytics kit 사용기

with 2 comments

지난달 남는 시간을 들여 만든 앱을 하나 앱스토어에 등록했다.
Eclipse ADT만 사용하다가 Android studio로 갈아탄 후 제작한 첫 앱인데, 처음엔 IDE가 익숙하지 않아 고생했지만 이런저런 시행착오를 한 덕에 Android studio에 익숙해졌다.

앱에 대한 설명을 간단히 하자면, 개인적으로 수영을 즐겨 하기에 내 수영 기록을 로컬 DB에 저장 관리하면서 그래프로 기록 추이를 나타내서 보여주면 좋겠단 생각에서 시작했다. 1주일 정도 짬짬히 시간을 내서 오픈소스와 기본 위젯들을 간단히 변형하여 완성했는데, 켤때마다 투박한 모습이 거슬려서 material 디자인 스타일의 위젯으로 전부다 갈아 엎었다.
기왕 깔끔하게 만들기로 작정한 참에 여러 사람이 쓸 수 있도록 공개해야겠다는 생각이 들어, 여러 사람들의 기록을 저장할 수 있도록 AWS를 이용해서 DB와 웹서버도 붙이고, 구글플러스 로그인 연동을 붙여 구색을 맞추어 놓았다.
IDE에 익숙해지려고 시작한 작은 프로젝트가 너무 커진다는 생각이 들어서, 웹서버쪽의 세세한 구현은 추후 반응이 있으면 추가 구현하기로 마음먹고 버전을 릴리즈했다. 첫 커밋을 한 후 정확히 한달 째 되던 날 코딩을 마무리하고 앱을 마켓에 등록했다.

나름 마켓에 등록한 앱이니 Crash report도 붙여놨는데, 작은 프로젝트들에 대하여 자주 사용했던 URQA (링크) 대신, Twitter Fabric(링크)을 대신 이용해 보았다.

Android Studio에 Fabric을 설치하는 것은 설명이 필요 없을 정도로 쉽다. 플러그인만 설치하면 되고, 이마저도 튜토리얼이 제공된다. 튜토리얼을 따라서 Fabric을 설치하고 나면 안드로이드 스튜디오가 알아서 프로젝트에 Crashlytics를 추가해준다. 추가한 후 에는 Fabric Answer 탭에서 아래와 같은 창을 볼 수 있다.

fabric_answer

DAU등과 더불어 각각의 Activity에 대한 세션 길이또한 보여준다.
덕지덕지 붙여놓은 Google Analytics가 무색해질만큼 간단한 정보는 편리하게 받아 볼 수 있다.

사실 이러한 기능보다 더욱 더 편리하다고 생각되는 것은 Beta라는 기능이다.
소규모 앱개발자들의 경우 베타테스터에게 앱을 릴리즈하고, 테스트 리포트를 받아보는 과정은 상당히 까다로운 과정이다.
APK를 일일히 보내서 업데이트하기도 번거롭고, 그렇다고 구글 플레이 스토어의 베타 업로드 기능을 사용하려면, 한번 업로드 하고 그 내용이 적용되기까지 몇시간은 그냥 날려먹어야 한다.

하지만 Beta를 이용하면 아래의 인터페이스를 통해 간단히 베타 버전을 릴리즈 할 수 있다.
fabric_beta

Add Tester 버튼을 통해 이메일 주소만 추가하면 해당 메일을 통해 베타 테스트 앱 링크가 날아가고,  해당 메일을 선택하여 Accept를 선택하면 Beta 앱이 베타테스터의 디바이스에 설치된다.mobile_beta-side

링크를 선택해 Beta를 실행시키면 위와 같이 베타버전 접속이 가능하다.
권한이 허가되면 마지막 화면과 같이 릴리즈 노트와 베타버전을 다운받을 수 있는 버튼이 표시된다.
베타 테스터들의 테스트 결과는 Crash report와 Fabric Answer에 바로 적용되고, Fabric 인터페이스를 통해 베타테스터의 권한을 회수하거나, 테스터마다 다른 권한을 주어 관리하고, 테스터 그룹을 생성/관리할 수 있다.

마지막으로 Crash report 항목이다.fabric_crash_reporting

위와 같이 어디서 크래쉬가 발생했고, 몇명의 유저가 영향을 받았는지 표시된다.
버전별, 시간대별로 정렬 가능하고 Jira와 같은 Issue 트래킹 시스템 만큼은 아니지만 간단히 Issue의 Open/Closed 상태도 관리가 가능하다.
issue_detail

상세 issue정보를 보면, 기기의 루팅 상태나 Storage, memory 상태들을 볼 수 있고 좀더 자세히 살펴보면crash_detail

위와 같이 기기 정보나, 배터리 정보, UI orientation 에 대한 정보도 출력된다. 커스텀 키를 설정해서 추가 정보를 받을 수도 있는 것 같은데, 여러모로 이슈 트래킹에 도움이 될 것으로 보인다.
issue_detail_closed

이슈를 close 하게되면 위와 같이 표시되며, Crash report 리스트 상에서는 취소선이 표시된다.

간단히 사용할 수 있고, 편리하지만 그렇다고 기능이 가볍지는 않다.
아직 세세한 기능까지는 사용해보지 않았고, Fabric 자체도 아직은 베타 버전이지만 지금까지의 상태만으로도 충분히 강력하고, 기대되는 모습을 보여준다. 특히 Beta의 강력한 베타테스터 배포 기능은 Fabric이 앞으로 널리 쓰이게 될 것이라는 확신을 갖게 해준다.

얼마나 사용히 간편한지 간단한 튜토리얼을 보고 싶다면, 아래의 링크에 접속해보시길 바란다.
http://www.crashlytics.com/blog/launching-beta-by-crashlytics/

Written by Ringster

2015/03/02 at 4:12 pm

Mac 안드로이드 스튜디오 단축키

leave a comment »

Android Studio의 정식버전이 릴리즈 된 이후로 간간히 Android Studio에 익숙해지려고 노력중이다.
Eclipse가 익숙하기도 하고, 형상관리 연동 설정 등이 모두 되있는터라 작업의 효율을 핑계로 계속해서 프로젝트를 Android Studio로 옮기는 것을 주저하고 있었는데 슬슬 더 이상 미룰 수 없는 시기가 다가오고 있는 것 같다.

새로운 툴을 사용하다보면 가장 먼저 부딫치는 것이 생소한 핫키들인데, Eclipse에서 편리했던 기능들에 대한 핫키들에 대해 정리해 보았다. 전체 설정된 핫키는 Android Studio -> Preference -> IDE Settings : Keymap 에서 찾을 수 있으며, 이 중 개인적으로 많이 쓰는 핫키들을 정리한 것임을 참고하길 바란다.

Option + Enter : 빠른 수정 (이클립스 코드에 빨간줄 생길 때 수정 항목 추천과 같은 기능)
Control + Space : 기본 코드 자동 완성
Control + O : Override / Implement methods
Control + Option + O : Optimize imports
Command + N : Generate code( Getters, Setters, Constructors, hashCode/equals, toString )
Control + Shift + Space : 스마트 코드 완성(예상되는 타입의 메소드또는 변수명 )
Command + Option + L : Reformat code

Command + Option + T : Surround with… (if..else, try..catch, for, synchronized, etc.)
Command + / : 한줄주석
Control + Shift + / : 블럭주석
Control + W : 연속적인 코드블럭 선택
Command + Shift + V : 클립보드 히스토리
Control + mouse over code : 간단한 설명
Shift + mouse over code : 약간 더 자세한 설명 (API version, superclass, interface)


며칠전 IR transmitter 관련 앱을 Android Studio로 작성해 보았는데, 라이브러리 추가나 핫키 등이 익숙하지 않아 애를 먹었다.
사실 Keymap 옵션을 보면 Eclipse 핫키에 맞추어 Android Studio 핫키를 변경시켜주는 옵션이 있는데, 일부러 해당 옵션을 사용하고 있지 않다.
사소한 것이지만 하나의 툴이나 하나의 언어에 익숙해지는 것보다, 새로운 것들에  계속 적응하고 익숙한 분야를 넓혀가는 것이 중요하다고 생각하기 때문이다.

P.S

– 아직은 Android Studio 자체에 자잘한 버그가 많은것 같기는 하다. IR transmitter를 사용하기 위해서

ConsumerIrManager irManager = (ConsumerIrManager)getSystemService(Context.CONSUMER_IR_SERVICE);

위 코드를 추가했는데, 계속 Context.CONSUMER_IR_SERVICE 부분에 구문 오류가 표시되어 뭐가 잘못되었는지 한참을 찾았는데, 그냥 컴파일을 수행하니 문제없이 컴파일이 수행되었다. (동일한 코드를 이클립스에서 사용 시에는 구문 오류가 출력되지 않는다.)
계속해서 개선되다보면 이런 버그들은 언젠가는 처리되겠지만, 사실 이런 사소한 문제들에 시간을 뺏길때마다 여러 사람들에 의해 검증된 이후 사용하고 싶기는 하다…

P.S 2
– 조만간 https://developer.android.com/tools/studio/index.html 에 대해서도 한번 훑어봐야겠다.

Written by Ringster

2015/01/22 at 4:23 pm

KaKaoLink를 이용하여 앱에서 카카오톡 통해 메시지 전송하기

with one comment

요즘 많은 앱들이 KaKaoTalk api를 이용하여 카카오톡과 연동한다.
여기서는 카카오톡 api를 이용하여 카카오톡을 통해 메시지 전송하는 법을 알아보겠다.
자세한 설명은 KakaoTalk developers(여기)를 참조하기로 하자.

먼저 https://developers.kakao.com/apps/new 를 통해 카카오톡에서 사용할 앱을 생성하고 네이티브 앱키를 발급받는다.
설정 → 플랫폼 추가에 앱이나 웹 정보를 추가한다. 해당 정보가 비어있으면 앱/웹으로 이동 버튼을 포함한 카카오 링크가 전송되지 않는다.


1. 카카오톡 sdk 추가하기 및 설정

카카오톡 sdk를 다운로드 받아 이클립스에 추가한다. sdk에서 라이브러리 관련 에러가 생기면 Google play service라이브러리 경로를 새로 지정해 준다.

샘플로 시작할 새로운 프로젝트를 만들고, 카카오톡 sdk 라이브러리를 추가한다.
카카오톡 페이지에서 발급받은 네이티브 앱키를 넣는다.
스킴 부분은 kakao는 그대로 두고 뒷부분만 앱키로 교체하고, kakaolink_host는 그대로 둔다.

<resources>

    <string name="kakao_app_key">발급받은 appkey</string>
    <string name="kakao_scheme">kakao발급받은 appkey</string>
    <string name="kakaolink_host">kakaolink</string>

</resources>

이후 메니페스트를 수정한다. 인텐트 필터를 추가하고, 메타 데이터를 추가한다.
수정된 메니페스트는 다음과 같다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.kakaosample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="9"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.kakaosample.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
	            <category android:name="android.intent.category.LAUNCHER" />
	      </intent-filter>
      	  <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="@string/kakao_scheme" android:host="@string/kakaolink_host" />
            </intent-filter>
        </activity>

        <meta-data
	        android:name="com.kakao.sdk.AppKey" android:value="@string/kakao_app_key" />
    </application>

</manifest>

이제 메인 레이아웃을 작성해보자. 메시지를 입력받을 EditText하나와 링크를 전송할 버튼을 하나 생성하자.

<LinearLayout 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:orientation="vertical"
    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.example.kakaosample.MainActivity" >

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/test" />

    <Button
        android:id="@+id/btnSend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="전송" />

</LinearLayout>

2. KakaoLink 이용하여 텍스트 메시지 보내기

KakaoLink를 사용하는 방법은 크게 3단계로 나뉜다.

1) KakaoLink 객체 얻기

final KakaoLink kakaoLink = KakaoLink.getKakaoLink(Context c);

2) KakaoTalkLinkMessageBuilder로 메시지 스트링 생성하기

먼저 다음과 같이 KakaoTalkLinkMessageBuilder를 생성한다.

kakaoTalkLinkMessageBuilder = kakaoLink.createKakaoTalkLinkMessageBuilder();

그 이후 KakaoTalkLinkMessageBuilder를 통해 텍스트를 추가한다.

kakaoTalkLinkMessageBuilder.addText(String str);

3) 생성한 메시지를 sendMessage 메소드를 통해 전송하기

다음 메소드를 통해 메시지를 전송한다.

kakaoLink.sendMessage(String msg, context c);

다음은 위의 방법을 종합하여 완성된 코드이다.


public class MainActivity extends ActionBarActivity {

    private KakaoLink kakaoLink;
    private KakaoTalkLinkMessageBuilder kakaoTalkLinkMessageBuilder;
    private EditText mEditText;
    private Button mSendBtn;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		try {
			kakaoLink = KakaoLink.getKakaoLink(getApplicationContext());
			kakaoTalkLinkMessageBuilder = kakaoLink.createKakaoTalkLinkMessageBuilder();
		} catch (KakaoParameterException e) {
			e.getMessage();
		}

		mEditText = (EditText) findViewById(R.id.editText);
		mSendBtn = (Button) findViewById(R.id.btnSend);
		mSendBtn.setOnClickListener(new Button.OnClickListener() {
			@Override
			public void onClick(View v) {
				sendLink();
			}
		});
	}

	private void sendLink(){
		try {
			kakaoTalkLinkMessageBuilder.addText(mEditText.getText().toString());
			final String linkContents = kakaoTalkLinkMessageBuilder.build();
			kakaoLink.sendMessage(linkContents, this);
		} catch (KakaoParameterException e) {
			e.getMessage();
		}
	}
}

EditText에 메시지를 입력하고 전송버튼을 누르면 카카오톡이 실행되며 메시지가 잘 전달된다.

3. 이미지를 포함한 메시지 보내기

이미지를 포함한 메시지 보내기도 위와 동일한 순서를 따른다. 이미 만들어놓은 코드를 수정하여 이미지도 포함시켜 보자.

public class MainActivity extends ActionBarActivity {

    private KakaoLink kakaoLink;
    private KakaoTalkLinkMessageBuilder kakaoTalkLinkMessageBuilder;
    private final String imgSrcLink = "https://www.google.co.kr/images/srpr/logo11w.png";
    private EditText mEditText;
    private Button mSendBtn;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		try {
			kakaoLink = KakaoLink.getKakaoLink(getApplicationContext());
			kakaoTalkLinkMessageBuilder = kakaoLink.createKakaoTalkLinkMessageBuilder();
		} catch (KakaoParameterException e) {
			e.getMessage();
		}

		mEditText = (EditText) findViewById(R.id.editText);
		mSendBtn = (Button) findViewById(R.id.btnSend);
		mSendBtn.setOnClickListener(new Button.OnClickListener() {
			@Override
			public void onClick(View v) {
				sendLink(imgSrcLink);
			}
		});
	}

	private void sendLink(String imgSrcLink){
		try {
			kakaoTalkLinkMessageBuilder.addText(mEditText.getText().toString());
			kakaoTalkLinkMessageBuilder.addImage(imgSrcLink, 269, 95);
			final String linkContents = kakaoTalkLinkMessageBuilder.build();
			kakaoLink.sendMessage(linkContents, this);
		} catch (KakaoParameterException e) {
			e.getMessage();
		}
	}
}

kakaolink_sample   스크린샷 2014-12-10 오후 6.10.24

정상적으로 이미지 및 텍스트가 전송되었다.

4. 앱/웹으로 연결 버튼 포함한 메시지 보내기

링크를 전송하거나, 앱/웹으로 연결 버튼 추가 또한 간단하고, 위와 동일한 순서를 따른다.
다만 서두에서 말했듯이 카카오톡 홈페이지에서 웹/앱 플랫폼이 추가되있지 않다면 전송에 실패하게 된다.
앱/웹 연결 버튼 또한 추가해보자.
다른 부분은 모두 동일하고 sendLink 메소드에서 appbutton이나 webbutton을 추가하는 메소드를 호출하면 된다.
인자를 전달할 수도 있는데 자세한 것은 카카오톡 개발자 페이지를 참고하자.


private void sendLink(String imgSrcLink){
	try {
		kakaoTalkLinkMessageBuilder.addText(mEditText.getText().toString());
		kakaoTalkLinkMessageBuilder.addImage(imgSrcLink, 269, 95);
		kakaoTalkLinkMessageBuilder.addAppButton("앱으로 이동");
//		kakaoTalkLinkMessageBuilder.addWebButton("웹으로 이동");
		final String linkContents = kakaoTalkLinkMessageBuilder.build();
		kakaoLink.sendMessage(linkContents, this);
	} catch (KakaoParameterException e) {
		e.getMessage();
	}
}

kakaolink_applink

앱으로 이동 버튼과 아이콘까지 추가되었다. 아이콘은 카카오톡 홈페이지에서 등록해놓은 앱의 이미지가 출력된다.
링크를 선택할 경우 웹버튼의 경우 웹사이트로 앱 버튼의 경우 앱이 설치되었을 때는 앱으로, 그렇지 않은 경우는 마켓으로 연결된다.

Written by Ringster

2014/12/10 at 6:27 pm

Volley를 이용해 Network Data 전송하기 (3) – Standard Request 생성하기

with one comment

지난 포스팅(여기)에서 Volley를 이용하여 custom RequestQueue를 생성하는 방법을 알아보았다.
이번에는 volley가 지원하는 common request 의 종류와 사용법에 대해 정리해 보자.
1) StringRequest – URL을 지정해주고 Raw string으로 된 응답을 받는다. 지난 두번의 포스팅에서 계속해서 사용한 Request이다.
2) ImageRequest – URL을 지정해주고 이미지 응답을 받는다.
3) JsonObjectRequest, JsonArrayRequest – URL을 지정해주고 JsonObject나 JsonArray를 응답으로 받는다.
App에서 사용되는 대부분의 http 요청은 위의 3가지 범주에 포함되므로 그 경우에는 단순히 해당 Request를 사용하면 되고, 그렇지 않을 경우에는 다음 포스팅에서 진행할  Custom Request 생성하기를 참조하자.


1. ImageRequest 사용하기

먼저 ImageRequest를 사용해보자.
지난번 포스팅에서 생성한 Singleton class와 StringRequest를 생성한 코드에 추가하여 ImageRequest를 생성해 보았다.

public class MainActivity extends ActionBarActivity {
	private final String url = "http://www.google.com";
	private final String imgurl = "http://www.google.co.kr/logos/doodles/2014/anna-freuds-119th-birthday-5664856720015360-hp.jpg";
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		final TextView mTextView = (TextView) findViewById(R.id.text);
		final ImageView mImageView = (ImageView) findViewById(R.id.imageView);
		
		// Get a RequestQueue
		RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
		    getRequestQueue();
		
		// Formulate the request and handle the response.
		StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
		        new Response.Listener<String>() {
		    @Override
		    public void onResponse(String response) {
		    	mTextView.setText("Response is: "+ response.substring(0,500));
		    }
		},
		    new Response.ErrorListener() {
		        @Override
		        public void onErrorResponse(VolleyError error) {
		        	mTextView.setText(error.toString());
		    }
		});
		
		// Retrieves an image specified by the URL, displays it in the UI.
		ImageRequest imageRequest = new ImageRequest(imgurl,
		    new Response.Listener<Bitmap>() {
		        @Override
		        public void onResponse(Bitmap bitmap) {
		            mImageView.setImageBitmap(bitmap);
		        }
		    }, 0, 0, null,
		    new Response.ErrorListener() {
		        public void onErrorResponse(VolleyError error) {
		            mImageView.setImageResource(R.drawable.ic_launcher);
		        }
		    });
		// Access the RequestQueue through your singleton class.
		MySingleton.getInstance(this).addToRequestQueue(imageRequest);

		// Add a request (in this example, called stringRequest) to your RequestQueue.
		MySingleton.getInstance(this).addToRequestQueue(stringRequest);
		
	}
}

2. ImageLoader 및 NetworkImageView 이용하기

Volley에서는 ListView와 같이 view가 재사용되는 컴포넌트에서 여러장의 이미지를 효과적으로 나타내기 위해 NetworkImageView라는 imageView의 subclass를 지원한다.
ImageRequest를 viewholder에 저장하고, view를 재활용 할 시에 해당 이미지 요청들을 취소할 수도 있지만
NetworkImageView를 사용하면 해당 View가 detach 될때 알아서 요청을 취소해준다.
그럼 위에서 작성한 코드를 NetworkImageView를 이용하여 다시 작성해보자.

<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.example.bitmaphandlingtest.MainActivity$PlaceholderFragment" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <com.android.volley.toolbox.NetworkImageView
            android:id="@+id/networkImageView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#000000" />

        <TextView
            android:id="@+id/text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Test String" />

    </LinearLayout>

</RelativeLayout>

ImageView의 subclass인 만큼 사용방법은 동일하다. 이제 ImageLoader를 사용하는 방법을 알아보자.
ImageLoader는 image Loading 및 caching을 담당하는 helper class로써 다음과 같이 자체적으로 사용할 수도 있고,

ImageLoader mImageLoader;
ImageView mImageView;
// The URL for the image that is being loaded.
private static final String IMAGE_URL = "http://developer.android.com/images/training/system-ui.png";
...
mImageView = (ImageView) findViewById(R.id.regularImageView);

// Get the ImageLoader through your singleton class.
mImageLoader = MySingleton.getInstance(this).getImageLoader();
mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,
         R.drawable.def_image, R.drawable.err_image));

아래와 같이 NetworkImageView의 인자로 넘겨주면서 사용 또한 가능하다.
그럼 위에서 작성한 xml과 imageLoader를 이용하여 코드를 수정해 보자.

public class MainActivity extends ActionBarActivity {
	private final String url = "http://www.google.com";
	private final String imgurl = "http://www.google.co.kr/logos/doodles/2014/anna-freuds-119th-birthday-5664856720015360-hp.jpg";
	ImageLoader mImageLoader;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		final TextView mTextView = (TextView) findViewById(R.id.text);
		final NetworkImageView mNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView);

		// Get a RequestQueue
		RequestQueue queue = MySingleton.getInstance(
				this.getApplicationContext()).getRequestQueue();

		// Formulate the request and handle the response.
		StringRequest stringRequest = new StringRequest(Request.Method.GET,
				url, new Response.Listener<String>() {
					@Override
					public void onResponse(String response) {
						mTextView.setText("Response is: "
								+ response.substring(0, 500));
					}
				}, new Response.ErrorListener() {
					@Override
					public void onErrorResponse(VolleyError error) {
						mTextView.setText(error.toString());
					}
				});

		// Get the ImageLoader through your singleton class.
		mImageLoader = MySingleton.getInstance(this).getImageLoader();
		mNetworkImageView.setImageUrl(imgurl, mImageLoader);

		// Add a request (in this example, called stringRequest) to your RequestQueue.
		MySingleton.getInstance(this).addToRequestQueue(stringRequest);
	}
}

NetworkImageView 및 ImageLoader를 이용하여 이미지를 네트워크 상에서 불러오는 예제가 완성되었다.
여기에서도 중요한점은 ImageLoader를 single instance로 application의 lifetime동안 존재하도록 해야한다는 것이며, 만약 imageLoader를 activity에서 생성하게되면 user가 activity를 재생성시키는 동작을 할때 (예를 들어 화면 회전) flicker를 유발하게 된다.

3. LRU cache 사용하기

Volley toolbox는 DiskBasedCache 클래스를 통해서 standard cache구현을 제공한다. 이 클래스는 지정된 디렉토리에 파일들을 캐쉬한다. 하지만 ImageLoader를 사용하기 위해서는 ImageLoader.ImageCache 인터페이스를 구현하는 custom in-memory LRU bitmap cache를 제공해야한다.

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import com.android.volley.toolbox.ImageLoader.ImageCache;

public class LruBitmapCache extends LruCache<String, Bitmap>;
        implements ImageCache {

    public LruBitmapCache(int maxSize) {
        super(maxSize);
    }

    public LruBitmapCache(Context ctx) {
        this(getCacheSize(ctx));
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }

    // Returns a cache size equal to approximately three screens worth of images.
    public static int getCacheSize(Context ctx) {
        final DisplayMetrics displayMetrics = ctx.getResources().
                getDisplayMetrics();
        final int screenWidth = displayMetrics.widthPixels;
        final int screenHeight = displayMetrics.heightPixels;
        // 4 bytes per pixel
        final int screenBytes = screenWidth * screenHeight * 4;

        return screenBytes * 3;
    }
}

해당 코드를 Singleton class에 적용해보자.

private MySingleton(Context context) {
		mCtx = context;
		mRequestQueue = getRequestQueue();

		/* 기존 LruCache
		mImageLoader = new ImageLoader(mRequestQueue,
				new ImageLoader.ImageCache() {
					private final LruCache<String, Bitmap> cache = new LruCache<String, Bitmap>(
							20);

					@Override
					public Bitmap getBitmap(String url) {
						return cache.get(url);
					}

					@Override
					public void putBitmap(String url, Bitmap bitmap) {
						cache.put(url, bitmap);
					}
				});
				*/
                // 새로운 LruCache 초기화
		mImageLoader = new ImageLoader(mRequestQueue, new LruBitmapCache(
	            LruBitmapCache.getCacheSize(context)));
	}

4. Json Request 생성하기

Volley는 JSONArray를 위한 JsonArrayRequest와, JSONObject를 위한 JsonObjectRequest를 제공한다.
이중 JsonObjectRequest를 이용하여 Json Request를 요청하고, 해당 결과를 parsing해서 보여주는 예제를
기존 예제를 변경하여 작성해 보겠다.

public class MainActivity extends ActionBarActivity {
	private final String imgurl = "http://www.google.co.kr/logos/doodles/2014/anna-freuds-119th-birthday-5664856720015360-hp.jpg";
	private final String jsonurl = "http://pipes.yahooapis.com/pipes/pipe.run?_id=giWz8Vc33BG6rQEQo_NLYQ&_render=json";
	ImageLoader mImageLoader;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		final TextView mTextView = (TextView) findViewById(R.id.text);
		final NetworkImageView mNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView);

		// Get a RequestQueue
		RequestQueue queue = MySingleton.getInstance(
				this.getApplicationContext()).getRequestQueue();

		// Formulate the JSON request and handle the response.

		JsonObjectRequest jsObjRequest = new JsonObjectRequest(
				Request.Method.GET, jsonurl, null,
				new Response.Listener<JSONObject>() {

					@Override
					public void onResponse(JSONObject response) {
						Log.i("Response", response.toString());
						String stringFromJson = parseJSON(response);
						mTextView.setText(stringFromJson);
					}
				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError error) {
						mTextView.setText(error.toString());
					}
				});

		// Get the ImageLoader through your singleton class.
		mImageLoader = MySingleton.getInstance(this).getImageLoader();
		mNetworkImageView.setImageUrl(imgurl, mImageLoader);

		// Add a JsonObjectRequest to your RequestQueue.
		MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);

	}

	private String parseJSON(JSONObject json) {
		String mText = "";
		try {
			JSONObject value = json.getJSONObject("value");
			JSONArray items = value.getJSONArray("items");
			for (int i = 0; i < items.length(); i++) {
				JSONObject item = items.getJSONObject(i);
				mText = "Title: "+item.optString("title")+"\n";
				mText += "Description: "+item.optString("description")+"\n";
				mText += "Link: "+item.optString("link")+"\n";
				mText += "Pub date: "+item.optString("pubDate");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return mText;
	}
}

2014-12-04-15-42-02

StringRequest와 동일하게 url을 제공하고, 받아온 response를 처리하면 된다.
Log로 올라온 source response와 좌측 Application의 TextView에 출력된 결과를 비교해보면 정상적으로 Json이 parsing되었음을 확인할 수 있다.

물론, Custom LruCache를 이용한 이미지 출력또한 정상적으로 동작하고 있다.

Written by Ringster

2014/12/04 at 6:52 am

Volley를 이용해 Network Data 전송하기 (2) – Request Queue 설정하기

leave a comment »

지난 포스팅(여기)에서 Volley를 이용하여 Network Data 전송을 하기 위해서는
1) RequestQueue 생성, 2) Request Object 생성,  3) 생성한 Object를 RequestQueue로 전달의 3단계가 필요하다고 하였다.
지난 포스팅에서는 RequestQueue를 생성하지 않고, Volley의 편의 메소드를 이용하여 RequestQueue를 생성하였는데,
이번에는 직접 RequestQueue를 생성하여 custom action을 가능하도록 하는 법을 알아보겠다.


1. Network 및 Cache 설정하기

RequestQueue는 작업을 위해 Request를 처리할 network 와 cache가 필요하다. volley는 toolbox에
one-file-per-response cache를 제공하는 DiskBasedCache와 개발자의 선택에 따라 AndroidHttpClient나
HttpURLConnection을 이용하는 BasicNetwork의 기본 구현을 제공한다. BasicNetwork는 HttpClient로
초기화되어야 하는데, 보통 HttpURLConnection이나 AndroidHttpClient가 쓰인다.

HttpURLConnection은 API Level 9 이하에서 안정적이지 않으므로 API Level 9 이하에서는 AndroidHttpClient를
사용하고, 이후에는 HttpURLConnection을 사용하도록 하려면 다음과 같이 기기가 어떤 Android Version에서
구동되는지 확인하는 코드를 이용한다.

HttpStack stack;
...
// If the device is running a version >= Gingerbread...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
    // ...use HttpURLConnection for stack.
} else {
    // ...use AndroidHttpClient for stack.
}
Network network = new BasicNetwork(stack);

 Volley에서는 기본적으로 API Level 9 이하에서는 AndroidHttpClient를 그 이후에는
HttpURLConnection을 이용한다.
그럼 Customized된 Request Queue를 이용해  SimpleRequest 전송 시와 동일한 동작을 하는 코드를 보자

public class MainActivity extends ActionBarActivity {
	private final String url = "http://www.google.com";
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		final TextView mTextView = (TextView) findViewById(R.id.text);
		
		RequestQueue mRequestQueue;

		// Instantiate the cache
		Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap

		// Set up the network to use HttpURLConnection as the HTTP client.
		Network network = new BasicNetwork(new HurlStack());
		

		// Instantiate the RequestQueue with the cache and network.
		mRequestQueue = new RequestQueue(cache, network);

		// Start the queue
		mRequestQueue.start();

		// Formulate the request and handle the response.
		StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
		        new Response.Listener<String>() {
		    @Override
		    public void onResponse(String response) {
		    	mTextView.setText("Response is: "+ response.substring(0,500));
		    }
		},
		    new Response.ErrorListener() {
		        @Override
		        public void onErrorResponse(VolleyError error) {
		        	mTextView.setText(error.toString());
		    }
		});

		// Add the request to the RequestQueue.
		mRequestQueue.add(stringRequest);
	}
}

1회성 네트워크 작업을 할때는 RequestQueue를 생성한 후 RequestQueue.stop() 을 통해 작업을 종료시킬 수
있지만, 이러한 단순한 작업은 volley.newRequestQueue()를 이용하고 커스톰 RequestQueue는 Singleton으로
생성하여 App의 Lifetime 동안 존재하도록 구현하는 것이 일반적이다.

2. Singleton Pattern 사용하기

Application이 지속적으로 네트워크 작업을 한다면,  App의 Lifetime동안 존재할 RequestQueue 인스턴스가 있는
것이 효과적이다. 이는 여러가지 접근 방법을 통해 구현 가능한데 하나의 Singleton class를 생성하여
RequestQueue 및 사용할 volley function 들을 캡슐화하거나, Application의 Subclass를 생성하여 Application의
onCreate()메소드에서 requestQueue를 설정하면 된다.
두번째 방법보다는 첫번째 방법이  S/W의 모듈화에 좋으므로 첫번째 방법을 사용하기로 하며,
요점은 RequestQueue를 Activity context가 아닌 Application의 context를 통해 초기화하여
계속해서 재생성되지 않도록 하는 것이다.

 RequestQueue와 ImageLoader를 제공하는 Singleton Class를 생성해보자. (ImageLoader는 다음편에 설명)

public class MySingleton {
    private static MySingleton mInstance;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private static Context mCtx;

    private MySingleton(Context context) {
        mCtx = context;
        mRequestQueue = getRequestQueue();

        mImageLoader = new ImageLoader(mRequestQueue,
                new ImageLoader.ImageCache() {
            private final LruCache<String, Bitmap>
                    cache = new LruCache<String, Bitmap>(20);

            @Override
            public Bitmap getBitmap(String url) {
                return cache.get(url);
            }

            @Override
            public void putBitmap(String url, Bitmap bitmap) {
                cache.put(url, bitmap);
            }
        });
    }

    public static synchronized MySingleton getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new MySingleton(context);
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request <T> req) {
        getRequestQueue().add(req);
    }

    public ImageLoader getImageLoader() {
        return mImageLoader;
    }
}

이제 작성한 Singleton Class를 이용해 위해서 한 작업과 동일한 코드를 작성해보자.

public class MainActivity extends ActionBarActivity {
	private final String url = "http://www.google.com";
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		final TextView mTextView = (TextView) findViewById(R.id.text);
		
		// Get a RequestQueue
		RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
		    getRequestQueue();
		
		// Formulate the request and handle the response.
		StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
		        new Response.Listener<String>() {
		    @Override
		    public void onResponse(String response) {
		    	mTextView.setText("Response is: "+ response.substring(0,500));
		    }
		},
		    new Response.ErrorListener() {
		        @Override
		        public void onErrorResponse(VolleyError error) {
		        	mTextView.setText(error.toString());
		    }
		});

		// Add a request (in this example, called stringRequest) to your RequestQueue.
		MySingleton.getInstance(this).addToRequestQueue(stringRequest);	
	}
}

이로서 Singleton pattern을 이용해 위에서 작성한 코드와 동일한 동작을 구현하였다.
다음 포스팅에서는 지금까지 사용해왔던 StringRequest를 비롯하여 ImageRequest, JSONObjectRequest 등
Standard Request를 사용하는 법을 알아보고, Singleton class에 추가한 ImageLoader의 사용법 또한 알아보겠다.

Written by Ringster

2014/12/03 at 11:03 am

Volley를 이용해 Network Data 전송하기 (1) – Simple Request 전송

leave a comment »

안드로이드 App을 제작하다보면 이미지 파일 업로드/다운로드, Json 형식의 응답 주고 받기 등
네트워크를 통한 데이터 전송을 빈번하게 사용한다. 이를 위해 가장 많이 쓰이는 방법은 AsyncTask를 생성하여
DoitBackground 메소드 내에서 네트워크 작업을 처리하는 것인데, 이러한 작업들을 좀더 쉽고 빠르게 하기 위해
Google은 2013년 Google I/O에서 volley라는 Http 라이브러리를 공개했다.

Google Volley에 대한 자세한 설명은 Naver의 정상혁 님이 쓰신 글(여기)이나
Google I/O 발표자료 (110 – Volley- Easy,Fast Networking for Android)를 참조하기로 하고,
Google 개발자 튜토리얼(여기)을 참조하여 Volley를 이용해 네트워크 데이터를 처리하는 방법을 알아보도록 하자.


1. Volley Library Download 받기 및 프로젝트에 추가하여 사용준비 하기

먼저 다음 명령어를 실행하여 git repository에서 volley를 다운로드 받는다.

git clone https://android.googlesource.com/platform/frameworks/volley

다음 명령어를 실행하여 build 환경을 재설정하고,

/Applications/adt-bundle-mac-x86_64-20140321/sdk/tools/android update project -p .

volley를 다운받은 폴더에서 lib파일을 생성한다.

/Applications/apache-ant-1.9.4/bin/ant jar;

그 이후 Project의 libs 폴더 안에 생성된 volley.jar파일을 붙여넣는다. (Eclipse 기준)

마지막으로 AndroidMenifest.xml 파일에 android.permission.INTERNET 퍼미션을 추가한다.

2. newRequestQueue 사용하기

Volley를 사용하는 방법은 크게 3단계로 나뉜다.
1) RequestQueue를 생성한다. 2) Request Object를 생성한다. 3) 생성한 Object를 RequestQueue로 넘긴다.
이번 포스팅에서는 RequestQueue를 생성하는 편의 메소드인 newRequestQueue를 이용하여
간단한 네트워크 데이터 전송을 수행하는 방법을 알아보도록 하자.

final TextView mTextView = (TextView) findViewById(R.id.text);
...

// Instantiate the RequestQueue. 
// 1) 편의 메소드를 이용하여 RequestQueue 생성 및 초기화
RequestQueue queue = Volley.newRequestQueue(this);
String url = "http://www.google.com";

// Request a string response from the provided URL. 
// 2) Request Obejct인 StringRequest 생성
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        // Display the first 500 characters of the response string.
        mTextView.setText("Response is: "+ response.substring(0,500));
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        mTextView.setText("That didn't work!");
    }
});
// 3) 생성한 StringRequest를 RequestQueue에 추가
queue.add(stringRequest);

Volley를 이용한 네트워크 응답은 언제나 main Thread로 전송되므로
response Handler를 통해 바로 Main UI에 접근할 수 있다.

add() 메소드를 수행하면 volley는 하나의 cache thread와 network dispatch thread pool을 실행시키고,
cache dispatcher를 통해 cache가 이용 가능한 Request는 cache 쓰레드로 보내고, 그렇지 않은 Request는
network thread로 보내 round robin 방식으로 처리한다.
처리된 결과는 쓰레드에 상관없이 모두 main Thread로 보내진다 (구조도 보기)

2. Request 취소하기

Request를 취소하기 위해서는 생성한 Request Object에 대하여 cancel() 메소드를 수행하면 된다.
volley는 cancel() 된 Object의 Response Handler가 수행되지 않음을 보장한다.
이는 Activity의 onStop() 메소드에서 모든 pending Request를 취소할 수 있고 onSaveInstanceState()가
호출되었든지 아니든지 간에 response Handler에서 getActivity()==null 체크나 기타 방어코드를 사용해가며
노력을 낭비할 필요가 없음을 의미한다.

이러한 장점을 얻기위해서는 모든 request를 적절한 시기에 취소하기 위해 추적해야 하는데,
이를 위한 간단한 방법이 있다.

바로 tag를 이용하여 모든 request를 tagging 하고 그 request의 적절한 취소 시기에 requestQueue.cancelAll(this)
메소드를 호출하는 것이다. 예를 들어서 다음과 같이 모든 request에 Tag를 추가하고,

public static final String TAG = "MyTag";
StringRequest stringRequest; // Assume this exists.
RequestQueue mRequestQueue;  // Assume this exists.

// Set the tag on the request.
stringRequest.setTag(TAG);

// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);

Activity의 onStop 메소드에서 해당 Tag를 이용하여 모든 Request를 취소하는 것이다

@Override
protected void onStop () {
    super.onStop();
    if (mRequestQueue != null) {
        mRequestQueue.cancelAll(TAG);
    }
}

이와 유사하게 ViewPager 는 page가 swipe될때 mRequestQueue.cancelAll(this)를 호출하여
기존 Request들을 취소하고 새로운 Request를 추가할 수 있다.

Written by Ringster

2014/12/03 at 8:51 am

Yurim Jin, Programmer&Designer

Fork my brain because I'm ready to commit

jamesjungkuelee's biotech story

Biotech, entrepreneur, life

Communications as Ikor

기업 위기관리의 모든 것

Charles Pyo Ventures

시도와 실패, 성장의 기록. 2막에서도 계속되는 모험들.

techNeedle 테크니들

글로벌 테크 소식을 인사이트와 함께 전달합니다

Open API, Cloud, DevOps 와 eBook

Open API, eBook, Cloud, DevOps

Economics of almost everything

Tech, Mobile, Internet

cylee

Tech, Mobile, Internet

gorekun.log

고어쿤로그

Google Developers Korea Blog

Tech, Mobile, Internet

Android Developers Blog

Tech, Mobile, Internet

최피디의 앱스 개발기

기술, 앱스, SNS, 창업

D2 Blog

Tech, Mobile, Internet

All of Software

Tech, Mobile, Internet

'Startup's Story Platform’

'Startup's Story Platform’