Protocol Buffer 사용 및 전달하기

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 사용해보면서 배운 점을 정리한 글입니다.

단순 protobuf 사용하기

저번 포스팅에서 다루었듯이 그냥 단순하게 클래스처럼 다른 자료형을 묶어서 관리하기 위해 사용할 수 있다.

package com.example.myprotobuf;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

import com.example.myprotobuf.UserInfo.Person;

public class MainActivity extends AppCompatActivity {

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

        Integer mId = 1;
        String mUserName = "rick";
        String mAddress = "asdf1234@gmail.com";

        Person protoPerson = Person.newBuilder() // 값을 입력할 경우 newBuilder로 열어야 함
                .setId(mId)
                .setName(mUserName)
                .setEmail(mAddress) // 사전에 정의한 값들을 정의된 자료형에 맞게 입력
                .build(); //입력을 끝내면 build로 저장한다.

        Log.e("get protobuf", protoPerson.toString()); // 확인을 위해 에러로그 출력

    }
}

Person 이라는 구조 안에 string, int 형을 자유롭게 넣어 관리할 수 있었다.

nest 구조 형태의 protobuf 사용하기

앞서 정의했듯이 Person 구조 안에 PhoneNumber란 구조가 정의되어 있고 해당 구조는 string의 번호 값과 enum 형태의 PhoneType을 담을 수 있다. 그리고 Person 구조는 PhoneNumber에 repeated 필드를 설정하여 여러 개 가질 수 있다. (사람이 갖는 폰 번호, 집 번호 등 연락처는 다양하므로...)

userinfo.proto 부분

proto 구조를 정의할 떄 연락처는 1개라고 지정했었는데 조금 수정해서 repeated 필드를 갖는다고 하자. 그럼 아래와 같이 proto 파일의 해당 부분을 수정한 뒤 빌드하여 갱신해야 한다.

message Person { // Person 구조 정의
  required string name = 1; // proto2에서는 required, oprional로 필수 여부 체크
  required int32 id = 2;
  optional string email = 3;
  repeated PhoneNumber phone = 4; // 하위 protobuf 구조를 자식으로 가질 수 있음


  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME]; // 입력되지 않으면 HOME으로 지정
  }

  enum PhoneType { // PhoneType의 각 속성값을 상수화
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
}

적당히 집번호와 휴대전화 연락처를 담은 1명의 protobuf 개체를 만드려면 아래와 같이 입력할 수 있다.

public class MainActivity extends AppCompatActivity {
    Person protoPerson;
    Person.Builder protoPersonBuilder;

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

        Integer mId = 1;
        String mUserName = "rick";
        String mAddress = "asdf1234@gmail.com";
        String[] mNumberList = {"010-1234-5678",  "064-123-4567"}; // rick의 번호들
        Person.PhoneType[] mTypeList = {Person.PhoneType.MOBILE, Person.PhoneType.HOME};

        protoPersonBuilder = Person.newBuilder(); // Builder객체 선언 후 입력 오픈
        protoPersonBuilder.setId(mId)
                .setName(mUserName)
                .setEmail(mAddress); // rick의 정보 입력

        for (int i = 0; i < 2; i++) {
            Person.PhoneNumber.Builder phoneNumberBuilder = Person.PhoneNumber.newBuilder(); // 임시로 사용할 PhonNumber Builder 객체 선언 후 오픈
            phoneNumberBuilder.setNumber(mNumberList[i])
                    .setType(mTypeList[i])
                    .build(); // 저장하여 닫기
            protoPersonBuilder.addPhone(phoneNumberBuilder); // repeated 필드는 add를 사용
        }

        protoPerson = protoPersonBuilder.build(); // Builder 객체 저장하며 Person 입력

        Log.e("MainAct: get person builder", protoPersonBuilder.toString());
        Log.e("MainAct: get person", protoPerson.toString());

    }
}

그리고 입력된 결과를 로그로 출력하면 아래와 같이 출력된다.

Android_Protocol_Buffer_002_01

각각에 대해 접근하기 위해 사전에 정의한 protobuf를 build하면서 안드로이드가 자체적으로 생성한 메소드를 사용한다. 입력한 Person 개체에 어떤 메소드가 있는지 살펴보면 get-속성값 형태로 접근할 수 있음을 확인할 수 있고 repeated 필드의 경우 idx 값을 넣어 조회할 수 있다.

Android_Protocol_Buffer_002_02

사람 이름과 두 번째 연락처에 접근하여 로그를 찍으면

        Log.e("proto person name: ", protoPerson.getName());
        Log.e("second phoneNumber", protoPerson.getPhone(1).getNumber());

아래와 같이 출력된다.

Android_Protocol_Buffer_002_03

다음 Activity로 protobuf 전달하기

값을 입력하고 다음 페이지에서 입력했던 값들을 로드해 사용해야 할 때가 있다. 그럴 땐 평소 다른 값들을 intent에 넣어 전달했듯이 똑같이 해주면 된다. 다만 protobuf 자료형이 없으므로 Base64 String으로 Encoding 후 ByteArray로 변환하여 전달한다. 수신하는 Activity에서도 ByteArray를 받은 후 Base64 decoding을 하면 정상적으로 불러왔음을 확인할 수 있다.

MainActivity 부분

        Log.e("MainAct: get person builder", protoPersonBuilder.toString());
        Log.e("MainAct: get person", protoPerson.toString());

        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        intent.putExtra("person", Base64.encodeToString(protoPerson.toByteArray(), Base64.DEFAULT));
        startActivity(intent);
        finish();

SecondActivity 부분

public class SecondActivity extends AppCompatActivity {
    UserInfo.Person mPerson;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        try {
            mPerson = UserInfo.Person.parseFrom(Base64.decode(getIntent().getStringExtra("person") ,Base64.DEFAULT));
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.e("SecondAct: get Person", mPerson.toString());
    }
}

로그를 통해 확인하면 아래와 같다.

Android_Protocol_Buffer_002_04

전체 코드

MainActivity.class

package com.example.myprotobuf;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;

import com.example.myprotobuf.UserInfo.Person;

public class MainActivity extends AppCompatActivity {
    Person protoPerson;
    Person.Builder protoPersonBuilder;

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

        Integer mId = 1;
        String mUserName = "rick";
        String mAddress = "asdf1234@gmail.com";
        String[] mNumberList = {"010-1234-5678",  "064-123-4567"};
        Person.PhoneType[] mTypeList = {Person.PhoneType.MOBILE, Person.PhoneType.HOME};

        protoPersonBuilder = Person.newBuilder();
        protoPersonBuilder.setId(mId)
                .setName(mUserName)
                .setEmail(mAddress);

        for (int i = 0; i < 2; i++) {
            Person.PhoneNumber.Builder phoneNumberBuilder = Person.PhoneNumber.newBuilder();
            phoneNumberBuilder.setNumber(mNumberList[i])
                    .setType(mTypeList[i])
                    .build();
            protoPersonBuilder.addPhone(phoneNumberBuilder);
        }

        protoPerson = protoPersonBuilder.build();

        Log.e("MainAct: get person builder", protoPersonBuilder.toString());
        Log.e("MainAct: get person", protoPerson.toString());
        Log.e("proto person name: ", protoPerson.getName());
        Log.e("second phoneNumber", protoPerson.getPhone(1).getNumber());

        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        intent.putExtra("person", Base64.encodeToString(protoPerson.toByteArray(), Base64.DEFAULT));
        startActivity(intent);
        finish();
    }
}

SecondActicity.class

package com.example.myprotobuf;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;

import com.example.myprotobuf.UserInfo.Person;

public class SecondActivity extends AppCompatActivity {
    Person mPerson;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        try {
            mPerson = Person.parseFrom(Base64.decode(getIntent().getStringExtra("person") ,Base64.DEFAULT));
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.e("SecondAct: get Person", mPerson.toString());
    }
}

참고자료

+ Recent posts