APN 푸시3 - 서버

 

1. 인증서 만들기 https://ghj1001020.tistory.com/797
2. iOS 클라이언트 https://ghj1001020.tistory.com/798
3. 서버 https://ghj1001020.tistory.com/799

 

 

pom.xml

JavaPNS 라이브러리 추가

1
2
3
4
5
6
7
        <!-- APNS -->  
        <dependency>
            <groupId>com.github.mlaccetti</groupId>
            <artifactId>javapns</artifactId>
            <version>2.3.2</version>
        </dependency>
 

 

 

ApnsServer.java

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 
import javapns.notification.AppleNotificationServer;
import javapns.notification.AppleNotificationServerBasicImpl;
import javapns.notification.PushNotificationManager;
import javapns.notification.PushNotificationPayload;
import javapns.notification.PushedNotifications;
 
public class ApnsServer {
    
    private static final String CERT_FILE_PATH = "D:\\blog\\workspace\\web\\apns_ex.p12";    // 인증서 경로
    private static final String CERT_PASSWORD = "test1234";    // 인증서 만들때 입력한 패스워드
    private static final String DEVICE_TOKEN_ID = "1e1c9c3f4539288801101658251b3409222b20ff8b5507d7d9430985ebd96a74";    // iOS가 받은 푸시키
    
    public void sendApns() {
        PushNotificationManager pushManager = new PushNotificationManager();
        try {
            AppleNotificationServer pushServer = new AppleNotificationServerBasicImpl(CERT_FILE_PATH, CERT_PASSWORD, false);
            pushManager.initializeConnection(pushServer);
        
            List<Device> deviceList = new ArrayList<Device>();
            // device 추가
            Device device = new BasicDevice( DEVICE_TOKEN_ID );
            deviceList.add( device );
            
            PushNotificationPayload payload = PushNotificationPayload.complex();
            payload.addBadge(1);
            payload.addAlert("iOS 푸시 테스트 입니다.");
            payload.getPayload().put("message""메시지 내용을 입력해주세요.");
            
            PushedNotifications notis = pushManager.sendNotifications( payload, deviceList);
            
            int result = 0;
            if( notis != null && notis.getSuccessfulNotifications() != null ) {
                result = notis.getSuccessfulNotifications().size();
            }
        
            System.out.println"success size=" + result );
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        ApnsServer server = new ApnsServer();
        server.sendApns();
    }
}
 

 

 

결과

APN 푸시2 - iOS 클라이언트

 

1. 인증서 만들기 https://ghj1001020.tistory.com/797
2. iOS 클라이언트 https://ghj1001020.tistory.com/798
3. 서버 https://ghj1001020.tistory.com/799

 

 

Signing & Capabilities > Capability 추가 버튼 클릭

- Background Modes : Remote notifications 추가

- Push Notifications 추가

 

AppDelegate.swift

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
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        // 푸시 권한 획득
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (isGrant, error) in
            guard isGrant else {
                return
            }
            
            DispatchQueue.main.async {
                application.registerForRemoteNotifications()
            }
        }
        
        return true
    }
    
    
    // 푸시 토큰 받기 성공
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let deviceTokenString = deviceToken.map String(format: "%02x", $0) }.joined()
        print(deviceTokenString)
    }
    
    // 푸시 토큰 받기 실패
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print(error.localizedDescription)
    }
 
 

 

APN 푸시1 - 인증서 만들기

 

1. 인증서 만들기 https://ghj1001020.tistory.com/797
2. iOS 클라이언트 https://ghj1001020.tistory.com/798
3. 서버 https://ghj1001020.tistory.com/799

 

 

푸시 인증서 발급하기

1. 애플 개발자 사이트 로그인 > Certificates, IDs & Profiles 클릭

 

2. Certificates, Identifiers & Profiles > Certificates 추가 버튼 클릭

 

3. Create Certificate

개발용 : Services > Apple Push Notification service SSL (Sandbox) 선택

배포용 : Services > Apple Push Notification service SSL (Sandbox & Production) 선택

Platform 과 푸시 기능을 추가할려는 앱의 App ID 선택

Certificate Sigining Request 파일 선택

인증서 파일 다운로드 후 인증서를 더블 클릭하여 실행

 

 

서버 인증서 만들기

키체인 접근 실행

위에서 설치한 인증서 파일과 키를 선택하여 마우스 오른쪽 버튼 > 2개 항목 내보내기 선택

파일명, 저장 위치 선택

암호 입력 (서버에서 푸시 보낼때 입력한 암호가 필요)

 

에러메시지

응용 프로그램을 위한 유효한 ‘aps-environment’ 인타이틀먼트 문자열을 찾을 수 없습니다.

 

 

해결

Signing & Capabilities > Capability 추가 버튼 클릭 > Push Notifications 을 추가한다

'IT > Ⅰ. IOS' 카테고리의 다른 글

iOS APN 푸시2 - iOS 클라이언트  (0) 2020.05.05
iOS APN 푸시1 - 인증서 만들기  (0) 2020.05.05
iOS message app 열기  (0) 2020.05.01
iOS PickerView 예제  (0) 2020.04.30
IOS (Swift) HTTP 통신하기2  (0) 2017.10.09

message app 열기

 

URL형태 : sms:전화번호&body=메시지

 

 

ViewController.swift

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
import UIKit
 
class ViewController: UIViewController {
    
    @IBOutlet var editMessage: UITextField!
 
    
    override func viewDidLoad() {
        super.viewDidLoad()
 
    }
 
    // 메시지앱으로 이동
    @IBAction func moveToMessageApp(_ sender: UIButton) {
        let message = editMessage.text
        var sms : String = "sms:01012341234"
 
        // 메시지 내용이 있으면 전화번호 뒤에 &body=메시지를 붙이고 인코딩을 한다
        if let msg = message , !msg.isEmpty {
            sms = sms + "&body=" + msg
        }
        sms = sms.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!   // 인코딩
 
        UIApplication.shared.open( URL.init(string: sms)!, options: [:], completionHandler: nil )
    }
}
 
 

 

 

결과

버튼 클릭시 메시지앱으로 이동하여 전화번호와 메시지를 설정한다

PickerView 예제

 

텍스트 필드 입력시 피커뷰 띄우기

피커뷰 위에 툴바를 추가하여 확인/취소 버튼 구현

 

 

ViewController.swift

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import UIKit
 
class ViewController: UIViewController , UIPickerViewDelegate , UIPickerViewDataSource {
    
    @IBOutlet var editCity: UITextField!
    let pickerView = UIPickerView()
    var selectCity = "" // 피커뷰 선택시 값을 갖고 있다가 확인버튼 클릭시 텍스트필드에 세팅한다
    
    
    let pickerData = ["서울" , "경기도" , "인천" , "부산" , "대구" , "광주" , "대전" , "제주도"]  // 피커뷰에 보여줄 테스트 데이터
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        pickerView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 220)
        pickerView.delegate = self
        pickerView.dataSource = self
        
        // 피커뷰 툴바추가
        let pickerToolbar : UIToolbar = UIToolbar()
        pickerToolbar.barStyle = .default
        pickerToolbar.isTranslucent = true  // 툴바가 반투명인지 여부 (true-반투명, false-투명)
        pickerToolbar.backgroundColor = .lightGray
        pickerToolbar.sizeToFit()   // 서브뷰만큼 툴바 크기를 맞춤
        // 피커뷰 툴바에 확인/취소 버튼추가
        let btnDone = UIBarButtonItem(title: "확인", style: .done, target: self, action: #selector(onPickDone))
        let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
        let btnCancel = UIBarButtonItem(title: "취소", style: .done, target: self, action: #selector(onPickCancel))
        pickerToolbar.setItems([btnCancel , space , btnDone], animated: true)   // 버튼추가
        pickerToolbar.isUserInteractionEnabled = true   // 사용자 클릭 이벤트 전달
        
        editCity.inputView = pickerView // 피커뷰 추가
        editCity.inputAccessoryView = pickerToolbar // 피커뷰 툴바 추가
        
    }
    
    // 피커뷰 > 확인 클릭
    @objc func onPickDone() {
        editCity.text = selectCity
        editCity.resignFirstResponder()
        selectCity = ""
    }
    
    // 피커뷰 > 취소 클릭
    @objc func onPickCancel() {
        editCity.resignFirstResponder() // 피커뷰를 내림 (텍스트필드가 responder 상태를 읽음)
        selectCity = ""
    }
 
    
    // 피커뷰의 구성요소(컬럼) 수
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1    // 구성요소(컬럼)로 지역만 있으므로 1을 리턴
    }
    
    // 구성요소(컬럼)의 행수
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int-> Int {
        return pickerData.count
    }
 
    // 피커뷰에 보여줄 값 전달
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int-> String? {
        return pickerData[row]
    }
    
    // 피커뷰에서 선택시 호출
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        selectCity = pickerData[row]
    }
}
 
 

 

 

결과

텍스트 필드 클릭시 피커뷰가 나타나고 피커뷰에서 선택 후 확인 버튼 클릭시 텍스트 필드에 값이 세팅된다

 

 

WebView를 스크롤하면 툴바 숨기기

 

CoordinatorLayout 과 NestedScrollView 사용

 

 

build.gradle (:app)

1
2
3
    implementation 'androidx.appcompat:appcompat:1.1.0'
 
 

- Material Design 라이브러리를 추가

 

 

styles.xml

1
2
3
4
5
6
7
8
9
10
11
12
<resources>
 
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
 
</resources>
 
 

- 툴바를 레이아웃 xml에 추가할 것이므로 styles에서는 액션바가 없는 스타일을 상속한다

 

 

activity_main.xml

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?xml version="1.0" encoding="utf-8"?>
<!--
CoordinatorLayout : 부모뷰-자식뷰 , 자식뷰들간의 여러 인터렉션을 지원하는 컨테이너
NestedScrollView : 한 화면에 여러개의 스크롤 사용
 
fitsSystemWindows : status bar와 ui가 안겹치도록 조정 (뷰가 상태바와 소프트키 영역을 제외한 영역까지만 차지한다)
-->
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">
 
 
        android:fitsSystemWindows="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
        <!--
        layout_scrollFlags : AppBarLayout 및 하위의 스크롤 동작 담당
            scroll :  내용과 같이 스크롤, 툴바를 다시 볼려면 스크롤을 위로 끝까지 올려야함
            enterAlways : 맨위에 없어도 위로 스크롤시 바로 툴바가 보임
            enterAlwaysCollapsed : enterAlways와 동작이 비슷하지만 스크롤을 위로 끝까지 올려야 전체뷰가 보임 (스크롤하면 툴바도 같이 스크롤되어 안보임)
            exitUntilCollapsed : 아래/위로 스크롤시 축소된 툴바가 보이고 스크롤을 위로 끝까지 올려야 전체뷰가 보임 (스크롤해도 축소된 툴바가 보임)
            snap : 툴바가 위쪽에서 얼마나 떨어져 있는지에 따라 숨겨지거나 보여짐
         -->
            android:layout_width="match_parent"
            android:layout_height="?android:attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways"
            app:contentInsetStart="0dp"
            app:contentInsetEnd="0dp"
            app:contentInsetLeft="0dp"
            app:contentInsetRight="0dp"
            app:contentInsetStartWithNavigation="0dp">
 
            <LinearLayout
                android:paddingLeft="8dp"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
                <TextView
                    android:text="툴바"
                    android:textSize="16dp"
                    android:textColor="#ffffff"
                    android:layout_gravity="center_vertical"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" />
            </LinearLayout>
 
 
 
 
    <!-- layout_behavior : 자식뷰의 변화 상태를 부모뷰/다른 자식뷰한테 전달 (여기서는 스크롤했을 경우 다른 자식뷰에게 전달한다) -->
        android:layout_width="match_parent"
        android:layout_height="match_parent">
 
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
 
            <WebView
                android:id="@+id/webView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
 
        </LinearLayout>
 
 
 
 
 

- 아래로 스크롤하면 툴바가 사라졌다가 위로 스크롤하면 툴바가 나타난다

 

 

MainActivity.java

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
32
 
 
public class MainActivity extends AppCompatActivity {
 
    // ui
    WebView webView;
 
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        webView = (WebView)findViewById( R.id.webView );
        webView.setNetworkAvailable( true );
 
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled( true );
 
        webView.setWebChromeClient( new WebChromeClient() );
        webView.setWebViewClient( new WebViewClient() );
 
        webView.loadUrl( "https://www.naver.com");
    }
}
 
 

- 테스트를 위해 웹뷰에 네이버를 띄운다

 

 

결과

처음에는 툴바가 보였다가 아래로 스크롤하면 툴바가 사라졌다가 위로 조금만 스크롤해도 툴바가 다시 보인다

Glide 라이브러리로 gif이미지 로딩 

 

github : https://github.com/bumptech/glide

 

 

build.gradle (:app)

1
2
3
4
5
    // Glide
    // annotationProcessor : annotation processor classpath 에서 compile processor classpath를 분리하여 빌드 성능개선
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
 
  • glide 라이브러리 추가

 

 

MainActivity.java

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
 
 
 
public class MainActivity extends AppCompatActivity {
 
    ImageView imgGif;
 
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        imgGif = (ImageView)findViewById( R.id.img_gif );
 
        Glide.with( this )
                .asGif()    // GIF 로딩
                .load( R.raw.loading )
                .diskCacheStrategy( DiskCacheStrategy.RESOURCE )    // Glide에서 캐싱한 리소스와 로드할 리소스가 같을때 캐싱된 리소스 사용
                .into( imgGif );
    }
}
 
 
  • GIF 이미지를 raw 폴더에 넣음

 

그외 glide 라이브러리 함수

  • override() : 지정한 크기로 이미지 사이즈 설정

  • placeholder() : 이미지가 로딩할 동안 보여줄 기본 이미지

  • error() : 이미지 로딩 실패시 보여줄 에러 이미지

 

 

activity_main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<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:padding="8dp"
    tools:context=".MainActivity">
 
    <ImageView
        android:id="@+id/img_gif"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
 
</RelativeLayout>
 
 

 

 

결과

Retrofit2 예제

안전한 타입의 HTTP Client 라이브러리로 Android 및 Java 애플리케이션에서 사용합니다

최소요구사항 : Java 8+ or Android API 21+

 

github : https://github.com/square/retrofit

 

 

build.gradle (:app)

1
2
3
4
    // retrofit2
    implementation group: 'com.squareup.retrofit2', name: 'retrofit', version: '2.8.1'
    implementation group: 'com.squareup.retrofit2', name: 'converter-gson', version: '2.8.1' // JSON을 직렬화
    implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6' // 직렬화된 JSON을 객체로 역직렬화
  • Retrofit2 라이브러리 추가

 

 

AndroidManifest.xml

1
<uses-permission android:name="android.permission.INTERNET" />
  • 인터넷 사용 권한 추가

 

 

ApiInterface.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
public interface ApiInterface {
 
    // base_url + "api/login" 으로 POST 통신
    @POST("api/login")
    Call<ResLoginData> requestPostLogin(@Body ReqLoginData reqLoginData );   // @Body : request 파라미터
 
    // base_url + "api/users" 으로 GET 통신
    @GET("api/users")
    Call<ResUsersData> requestGetUsersDetail( @Query(value = "page", encoded = trueString page );   // @Query : url에 쿼리 파라미터 추가, encoded - true
 
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
  • HTTP 통신 인터페이스

 

 

HttpClient.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import retrofit2.Retrofit;
 
public class HttpClient {
 
    private static Retrofit retrofit;
 
    // Http 통신을 위한 Retrofit 객체반환
    public static Retrofit getRetrofit() {
        if( retrofit == null )
        {
            Retrofit.Builder builder = new Retrofit.Builder();
            builder.baseUrl( "https://reqres.in/" );
            builder.addConverterFactory( GsonConverterFactory.create() );  // 받아오는 Json 구조의 데이터를 객체 형태로 변환
 
            retrofit = builder.build();
        }
 
        return retrofit;
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
  • Retrofit 객체를 생성

 

 

ReqLoginData.java

1
2
3
4
5
6
7
8
9
10
11
// api/login 요청 데이터
public class ReqLoginData {
 
    String email;
    String password;
 
    public ReqLoginData( String email, String password ) {
        this.email = email;
        this.password = password;
    }
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
  • api/login 통신의 요청 객체

 

 

ResLoginData.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
import androidx.annotation.NonNull;
 
// api/login 응답 데이터
public class ResLoginData {
 
    @Expose
    String token;
 
    @NonNull
    @Override
    public String toString() {
        return "[ResLoginData] token=" + token;
    }
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
  • api/login 통신의 응답 객체

  • api/login 통신 결과값을 ResLoginData 객체에 맵핑한다

 

 

ResUsersData.java

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 
 
import androidx.annotation.NonNull;
 
// api/users 응답 데이터
public class ResUsersData {
 
    @Expose
    int page;
    @Expose
    @SerializedName("per_page")
    int perPage;
    @Expose
    int total;
    @Expose
    @SerializedName("total_pages")
    int totalPages;
    @Expose
    List<Data> data;
    @Expose
    Ad ad;
 
 
    public class Data {
        int id;
        String email;
        @SerializedName("first_name")
        String firstName;
        @SerializedName("last_name")
        String lastName;
        String avatar;
 
        @NonNull
        @Override
        public String toString() {
            return "{id=" + id + " , email=" + email + " , firstName=" + firstName + " , lastName=" + lastName + " , avatar}";
        }
    }
 
    public class Ad {
        String company;
        String url;
        String text;
 
        @NonNull
        @Override
        public String toString() {
            return "{company=" + company + " , url=" + url + " , text=" + text + "}";
        }
    }
 
    @Override
    public String toString() {
        String str = "[ResUsersData] page=" + page + " , perPage=" + perPage + " , total=" + total + " , totalPages=" + totalPages + " , data= [";
        forint i=0; i<data.size(); i++ ) {
            str += data.get(i).toString();
            if( i < data.size()-1 ) {
                str += ",";
            }
        }
        str += "]";
        str += " , ad=" + ad.toString();
 
        return str;
    }
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
  • api/users 통신의 응답 객체

  • api/users 통신 결과값을 ResUsersData 객체에 맵핑한다

 

 

MainActivity.java

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
 
import retrofit2.Callback;
import retrofit2.Response;
 
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 
    private static String TAG = "MainActivity";
 
    ApiInterface api;
 
    // ui
    Button btnGet;
    Button btnPost;
    TextView txtResult;
 
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
 
        btnGet = (Button)findViewById( R.id.btnGet );
        btnGet.setOnClickListener( this );
        btnPost = (Button)findViewById( R.id.btnPost );
        btnPost.setOnClickListener( this );
        txtResult = (TextView)findViewById( R.id.txtResult );
 
        api = HttpClient.getRetrofit().create( ApiInterface.class );
    }
 
    @Override
    public void onClick(View view) {
        switch ( view.getId() ) {
            case R.id.btnPost:
                txtResult.setText("");
                requestPost();
                break;
 
            case R.id.btnGet:
                txtResult.setText("");
                requestGet();
                break;
        }
    }
 
    // POST 통신요청
    public void requestPost() {
        ReqLoginData reqLoginData = new ReqLoginData( "eve.holt@reqres.in" , "cityslicka" );
        Call<ResLoginData> call = api.requestPostLogin( reqLoginData );
 
        // 비동기로 백그라운드 쓰레드로 동작
        call.enqueue( new Callback<ResLoginData>() {
            // 통신성공 후 텍스트뷰에 결과값 출력
            @Override
            public void onResponse(Call<ResLoginData> call, Response<ResLoginData> response) {
                txtResult.setText( response.body().toString() );    // body() - API 결과값을 객체에 맵핑
            }
 
            @Override
            public void onFailure(Call<ResLoginData> call, Throwable t) {
                txtResult.setText( "onFailure" );
            }
        } );
    }
 
    // GET 통신요청
    public void requestGet() {
        Call<ResUsersData> call = api.requestGetUsersDetail( "2" );
 
        // 비동기로 백그라운드 쓰레드로 동작
        call.enqueue(new Callback<ResUsersData>() {
            // 통신성공 후 텍스트뷰에 결과값 출력
            @Override
            public void onResponse(Call<ResUsersData> call, Response<ResUsersData> response) {
                txtResult.setText( response.body().toString() );
            }
 
            // 통신실패
            @Override
            public void onFailure(Call<ResUsersData> call, Throwable t) {
                txtResult.setText( "onFailure" );
            }
        });
    }
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
  • POST통신과 GET통신 결과값을 TextView에 출력

 

 

activity_main.xml

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
32
33
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
        <Button
            android:id="@+id/btnPost"
            android:text="POST"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
 
        <Button
            android:id="@+id/btnGet"
            android:text="GET"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
 
    </LinearLayout>
 
    <TextView
        android:id="@+id/txtResult"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
 
 
</LinearLayout>
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

 

 

결과

ADB Path 환경변수로 설정

 

 

1. ADB의 경로를 확인합니다

* [Android 설치경로]/sdk/platform-tools

 

 

2. VI로 .bash_profile 파일을 열고 수정합니다

1
ghj:~ ghj$ vi ~/.bash_profile
cs


1
2
export ADB_HOME=/Users/ghj/Library/Android/sdk/platform-tools
export PATH=${ADB_HOME}:$PATH
cs

* ADB_HOME 변수에 ADB 경로를 설정하고 PATH에 추가합니다

 

 

3. 터미널을 재실행하고 ADB 명령어로 확인합니다

1
2
3
4
ghj:~ ghj$ adb --version
Android Debug Bridge version 1.0.40
Version 4797878
Installed as /Users/ghj/Library/Android/sdk/platform-tools/adb
cs





+ Recent posts