반응형


이번 포스팅은 Fragment를 좀 더 유연하게 구축하는 방법에 대하여 알아보도록 하겠습니다.


다양한 화면 크기를 지원하도록 애플리케이션을 설계할 때 다양한 레이아웃 구성에서 Fragment를 다시 사용하여 사용 가능한 화면 공간을 기준으로 사용자 환경을 최적화할 수 있습니다.

예를 들어 핸드셋 기기인 경우 단일 창 사용자 인터페이스에 Fragment를 한 번에 하나씩만 표시하는 것이 적합할 수 있습니다. 반대로, 화면 너비가 큰 태블릿에서는 Fragment를 나란히 설정하여 사용자에게 더 많은 정보를 표시할 수 있습니다.

이 FragmentManager 클래스는 동적 경험(dynamic experience)을 만들기 위해 런타임시 Activity에 Fragment를 추가, 제거 및 대체 할 수있는 메소드를 제공합니다.

Fragment 구현에 관한 자세한 정보는 다음 리소스를 참조하세요.


Runtime 시 Activity에 Fragment 추가


<fragment> 요소를 사용하는 이전 과정의 내용대로 레이아웃 파일의 Activity에 대한 Fragment를  정의하는 대신 Activity Runtime 중 Activity에 Fragment를 추가할 수 있습니다. Activity 사용 기간 중 Fragment를 변경하려는 경우에는 이 과정이 필요합니다.

프래그먼트 추가 또는 삭제와 같은 트랜잭션을 수행하려면 FragmentManager를 사용하여 기타 Fragment 트랜잭션을 추가, 삭제, 교체, 수행하기 위한 API를 제공하는 FragmentTransaction를 만들어야 합니다.

Activity에서 Fragment를 삭제하거나 교체할 수 있는 경우 Activity의 onCreate() 메서드 중에 Activity의 초기 Fragment를 추가해야 합니다.

Fragment 작업 시, 특히 런타임에 Fragment를 추가할 때 유념해야 할 중요한 규칙은 Fragment를 삽입할 수 있는 컨테이너 View를 Activity 레이아웃에 포함해야 하는 것입니다.

이전 과정에 나와 있는 대로 Fragment를 한 번에 하나씩만 표시하는 레이아웃 대신 다음 레이아웃을 사용할 수 있습니다. 한 Fragment 를 다른 Fragment 로 교체할 수 있도록 Activity 레이아웃에는 Fragment  컨테이너 역할을 하는 빈 FrameLayout이 포함되어 있습니다.

파일 이름은 이전 과정의 레이아웃 파일과 동일하지만, 레이아웃 디렉터리에 large 한정자가 없기 때문에, 이 레이아웃은 기기 화면이 대형보다 작아 두 Fragment 를 동시에 표시할 수 없는 경우에 사용됩니다.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
       
android:id="@+id/fragment_container"
       
android:layout_width="match_parent"
       
android:layout_height="match_parent" />



Reference

1. https://developer.android.com/training/basics/fragments/fragment-ui

반응형

'Development > Android' 카테고리의 다른 글

[Android] Touch 이벤트 순서  (0) 2019.09.10
[Android] Intent Filter  (0) 2019.09.10
[Android] App Fragment Test  (0) 2019.09.06
[Android] Fragment의 생성  (0) 2019.09.06
[Android] Fragment의 개요  (0) 2019.09.06
반응형

이번 포스팅은 App Fragment Test 에 대하여 알아보도록 하겠습니다.


Fragment는 앱 내에서 재사용 가능한 컨테이너 역할을 하며, 다양한 Activity와 레이아웃 구성에서 동일한 사용자 인터페이스 레이아웃을 표시할 수 있습니다. Fragment는 다양한 용도로 사용되기 때문에 일관적이고 효율적으로 리소스를 사용하는 환경을 제공하는지 검증하는 것이 중요합니다.

Fragment의 외형은 대형 스크린 화면이나 기기에서 가로 모드를 지원하는 Fragment를 포함하여 모든 레이아웃 구성에서 일관적이어야 합니다.

Fragment가 사용자에게 보일 경우에만 Fragment의 뷰 계층 구조를 생성하세요.


Fragment의 상태 변경



이 테스트를 쉽게 설정할 수 있도록 AndroidX는 Fragment를 생성하고 상태를 변경하기 위한 FragmentScenario 라이브러리를 제공합니다.

참고: FragmentScenario 객체가 포함된 테스트 실행을 성공하기 위해 Test 계측 스레드에서 각 API 메서드를 실행하세요. Android 테스트에서 사용한 여러 가지 스레드에 대한 자세한 내용은 테스트의 스레드 이해를 참조하세요.


Test artifact 위치 구성

FragmentScenario를 의도한 대로 사용하려면 다음 소스 코드와 같이 앱의 테스트 APK에서 Fragment test artifact를 app/build.gradle 안에 정의합니다.

dependencies {
   
// ...
   
debugImplementation 'androidx.fragment:fragment-testing:{{ fragment_version }}'
}


Fragment 만들기

FragmentScenario에는 다음 유형의 Fragment를 실행하기 위한 메서드가 포함됩니다.

사용자 인터페이스 포함하는 그래픽 Fragment 입니다. 이 유형의 Fragment를 실행하려면 launchFragmentInContainer()를 호출합니다. FragmentScenario는 Fragment를 Activity의 루트 뷰 컨트롤러에 연결합니다. 그 외의 경우에는 Activity가 포함된 이 작업은 비어 있습니다.

▶ 여러 Activity에 포함된 정보를 단기적으로 실행하거나 저장하는 비 그래픽 Fragment 입니다. 이 유형의 Fragment를 실행하려면 launchFragment()를 호출합니다. FragmentScenario는 이 유형의 조각을 루트 보기가 없는 완전히 빈 활동에 연결합니다.

이런 Fragment 유형 중 하나를 실행한 이후 FragmentScenario는 테스트 중인 Fragment를 RESUMED 상태로 변경합니다. 이 상태는 Fragment가 실행 중이라는 것을 나타냅니다. 그래픽 Fragment를 테스트할 때 사용자에게도 테스트가 보이므로 Espresso UI 테스트를 사용하여 UI 요소에 대한 정보를 평가할 수 있습니다.

다음 소스 코드는 각 유형의 Fragment를 실행하는 방법을 보여줍니다.

<그래픽 Fragment 예시>

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
   
@Test fun testEventFragment() {
       
// The "state" and "factory" arguments are optional.
       
val fragmentArgs = Bundle().apply {
            putInt
("selectedListItem", 0)
       
}
       
val factory = MyFragmentFactory()
       
val scenario = launchFragmentInContainer<MyFragment>(
                fragmentArgs
, factory)
        onView
(withId(R.id.text)).check(matches(withText("Hello World!")))
   
}
}

<비 그래픽 Fragment의 예시>

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
   
@Test fun testEventFragment() {
       
// The "state" and "factory" arguments are optional.
       
val fragmentArgs = Bundle().apply {
            putInt
("numElements", 0)
       
}
       
val factory = MyFragmentFactory()
       
val scenario = launchFragment<MyFragment>(fragmentArgs, factory)
   
}
}


Fragment 다시 생성

기기에 리소스가 부족할 경우 시스템이 Fragment가 포함된 Activity를 소멸할 수 있으며, 이때 사용자가 앱에 돌아왔을 때 앱이 해당 Fragment를 다시 생성해야 합니다. 이 상황을 시뮬레이션하려면 recreate()를 호출하세요.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
   
@Test fun testEventFragment() {
       
val scenario = launchFragmentInContainer<MyFragment>()
       
scenario.recreate()
   
}
}

FragmentScenario 클래스가 테스트 중인 Fragment를 다시 생성하면 Fragment는 다시 생성하기 이전의 수명 주기 상태로 돌아옵니다.


Fragment를 새로운 상태로 변경

앱 UI 테스트에서는 대개 테스트 중인 Fragment를 실행하고 다시 생성하는 것만으로 충분합니다. 그러나 세밀한 유닛 테스트에서는 Fragment가 하나의 수명 주기에서 다른 수명 주기로 넘어갈 때 Fragment의 동작을 평가할 수 있습니다.

Fragment를 다른 수명 주기 상태로 변경하려면 moveToState()를 호출하세요. 이 메서드는 CREATED, STARTED, RESUMED, DESTROYED의 상태를 인수로 지원합니다. 이 작업은 Fragment가 포함된 Activity가 다른 앱이나 시스템 작업에 중단되어 상태를 변경하는 상황을 시뮬레이션합니다.

 참고: Fragment를 DESTROYED 상태로 전환할 경우 해당 Fragment를 다른 상태로 변경할 수 없고 Fragment를 다른 Activity에 연결할 수도 없습니다.

moveToState()의 사용 예시가 다음 소스 코드에서 나타납니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
   
@Test fun testEventFragment() {
       
val scenario = launchFragmentInContainer<MyFragment>()
       
scenario.moveToState(State.CREATED)
   
}
}

주의: 테스트 중인 Fragment를 현재의 상태로 전환하려고 할 경우 FragmentScenario는 이 요청을 예외가 아니라 ‑작업 불능으로 취급합니다. 특히, 이 API를 사용하면 Fragment를 DESTROYED 상태로 연속해서 여러 번 전환할 수 있습니다.


Fragment에서 작업 자르기

테스트 중인 Fragment에서 작업을 트리거하려면 Espresso 뷰 매처를 사용하여 뷰에서 요소와 상호작용하세요.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
   
@Test fun testEventFragment() {
       
val scenario = launchFragmentInContainer<MyFragment>()
       
onView(withId(R.id.refresh))
               
.perform(click())

   
}
}

Fragment 자체에서 메서드를 호출해야 할 경우 (예: 옵션 메뉴에서의 선택에 응답) FragmentAction을 구현하면 안전하게 호출할 수 있습니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
   
@Test fun testEventFragment() {
       
val scenario = launchFragmentInContainer<MyFragment>()
       
scenario.onFragment(fragment ->
            fragment
.onOptionsItemSelected(clickedItem) {
               
// Update fragment's state based on selected item.
           
}
       
}

   
}
}

참고: 테스트 클래스에서는 onFragment()로 전달한 객체에 대한 참조를 그대로 두지 마세요. 이 참조는 시스템 리소스를 사용하고, 프레임워크가 콜백 메서드로 전달된 Fragment를 다시 생성했기 때문에 참조 자체가 최신이 아닐 수 있습니다.


Reference

1. https://developer.android.com/training/basics/fragments/testing


반응형

'Development > Android' 카테고리의 다른 글

[Android] Intent Filter  (0) 2019.09.10
[Android] Flexable Fragment UI 구축  (0) 2019.09.06
[Android] Fragment의 생성  (0) 2019.09.06
[Android] Fragment의 개요  (0) 2019.09.06
[Android] Button의 배경을 투명하게 하는 방법  (0) 2019.09.05
반응형

이번 포스팅은 Fragment 생성하는 방법에 대하여 알아보도록 하겠습니다.


Fragment의 생성



Fragment를 생성하려면 Fragment 클래스를 extend 한 후 Activity 클래스에서와 마찬가지로 주요 수명 주기 메서드를 재정의하여 자체 앱 로직을 적용합니다.

Fragment 생성 시 한 가지 차이점은 레이아웃 정의에 onCreateView() 콜백을 사용해야 한다는 점입니다. 사실 Fragment 실행에 필요한 콜백은 이것뿐입니다. 다음은 자체 레이아웃을 지정하는 간단한 Fragment의 예입니다.

import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.ViewGroup

class ArticleFragment : Fragment() {
   
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup,
                     savedInstanceState
: Bundle?): View {
       
// Inflate the layout for this fragment
       
return inflater.inflate(R.layout.article_view, container, false)
   
}
}

Activity와 마찬가지로, Fragment는 Activity에 추가되거나 Activity에서 삭제될 때, 그리고 Activity가 수명 주기 상태 간에 전환될 때 Fragment의 상태를 관리할 수 있는 다른 수명 주기 콜백을 구현해야 합니다. 예를 들어 Activity의 onPause() 메서드가 호출되면 Activity 내 모든 Fragment도 onPause() 호출을 받게 됩니다.

Fragment 수명 주기 및 콜백 메서드에 대한 자세한 내용은 Fragments 개발자 가이드를 참조하세요.


XML을 이용하여 Activity에 Fragment 추가



Fragment는 재사용이 가능한 모듈식 UI 구성 요소인 반면, Fragment 클래스의 각 인스턴스는 상위 FragmentActivity와 연결되어야 합니다. Activity 레이아웃 XML 파일 안에 각각의 Fragment를 정의하여 이 연결을 설정할 수 있습니다.

★ 참고: FragmentActivity는 지원 라이브러리에서 제공하는 특수한 Activity이며, API 레벨 11보다 낮은 시스템 버전에서 Fragment를 처리하는 데 사용합니다. 지원하는 가장 낮은 시스템 버전이 API 레벨 11 이상일 경우, 일반 Activity를 사용해도 됩니다.

다음은 기기 화면을 "large"로 간주할 때, 두 개의 Fragment를 Activity에 추가하는 레이아웃 파일의 예입니다. (디렉토리 이름에 large 한정자가 지정되어 있음).

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:orientation="horizontal"
   
android:layout_width="fill_parent"
   
android:layout_height="fill_parent">

   
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
             
android:id="@+id/headlines_fragment"
             
android:layout_weight="1"
             
android:layout_width="0dp"
             
android:layout_height="match_parent" />

   
<fragment android:name="com.example.android.fragments.ArticleFragment"
             
android:id="@+id/article_fragment"
             
android:layout_weight="2"
             
android:layout_width="0dp"
             
android:layout_height="match_parent" />

</LinearLayout>

그런 다음 레이아웃을 Activity에 적용합니다.

import android.os.Bundle
import android.support.v4.app.FragmentActivity

class MainActivity : FragmentActivity() {
   
override fun onCreate(savedInstanceState: Bundle?) {
       
super.onCreate(savedInstanceState)
        setContentView
(R.layout.news_articles)
   
}
}

위 소스에서 v7 appcompat library를 사용하는 경우, Activity는 FragmentActivity의 하위 클래스인 AppCompatActivity를 대신 확장해야 합니다. 자세한 내용은 App bar 추가를 참조하세요.

참고: 레이아웃 XML 파일에서 Fragment를 정의하여 Activity 레이아웃에 Fragment를 추가하는 경우, 런타임에 Fragment를 삭제할 수 없습니다. 사용자 상호작용 중 Fragment를 추가 및 삭제하려면, Activity를 처음 시작할 때 Fragment를 Activity에 추가해야 합니다. 



Reference

1. https://developer.android.com/training/basics/fragments/creating

반응형
반응형

이번 포스팅은 Fragment 에 대하여 자세히 알아보도록 하겠습니다.


Fragment FragmentActivity 내의 어떤 동작 또는 사용자 인터페이스의 일부를 나타냅니다. 여러 개의 프래그먼트를 하나의 액티비티에 결합하여 창이 여러 개인 UI를 빌드할 수 있으며, 하나의 프래그먼트를 여러 액티비티에서 재사용할 수 있습니다. 프래그먼트는 액티비티의 모듈식 섹션이라고 생각하면 됩니다. 이는 자체적인 수명 주기를 가지고, 자체 입력 이벤트를 수신하고, 액티비티 실행 중에 추가 및 삭제가 가능합니다(다른 액티비티에 재사용할 수 있는 "하위 액티비티"와 같은 개념).


프래그먼트는 항상 액티비티 내에서 호스팅되어야 하며 해당 프래그먼트의 수명 주기는 호스트 액티비티의 수명 주기에 직접적으로 영향을 받습니다. 예를 들어 액티비티가 일시정지되는 경우, 그 안의 모든 프래그먼트도 일시정지되며 액티비티가 소멸되면 모든 프래그먼트도 마찬가지로 소멸됩니다. 그러나 액티비티가 실행 중인 동안(onResume 수명 주기 상태에 있을 경우)에는 각 프래그먼트를 추가 또는 제거하는 등 개별적으로 조작할 수 있습니다. 그와 같은 프래그먼트 트랜잭션을 수행할 때는 이를 액티비티가 관리하는 백 스택에도 추가할 수 있습니다. 각 백 스택 항목이 발생한 프래그먼트 트랜잭션의 기록이 됩니다. 이 백 스택을 사용하면 사용자가 프래그먼트 트랜잭션을 거꾸로 돌릴 수 있습니다(뒤로 이동). 이때 Back 버튼을 누르면 됩니다.


프래그먼트를 액티비티 레이아웃에 추가하면, 해당 프래그먼트는 액티비티의 뷰 계층 내에서 ViewGroup에 들어가고 자체적인 뷰 레이아웃을 정의합니다. 액티비티의 레이아웃 파일에서 <fragment> 요소로 프래그먼트를 선언하거나 기존 ViewGroup에 추가하는 방법으로 애플리케이션 코드에서 프래그먼트를 선언하면 액티비티 레이아웃에 프래그먼트를 삽입할 수 있습니다.


모범 사례 가이드를 포함한 수명 주기 처리에 대한 자세한 내용은 다음 참고 자료를 참조하세요.

수명 주기 인식 구성 요소로 수명 주기 처리

앱 아키텍처에 대한 가이드

태블릿 및 핸드셋 지원


Fragment 디자인 철학



Android가 프래그먼트를 처음 도입한 것은 Android 3.0(API 레벨 11)부터입니다. 기본적으로 태블릿과 같은 큰 화면에서 보다 역동적이고 유연한 UI 디자인을 지원하는 것이 목적이었습니다. 태블릿의 화면은 핸드셋 화면보다 훨씬 크기 때문에 UI 구성 요소를 조합하고 상호 교환할 공간이 더 많습니다. 프래그먼트는 개발자가 뷰 계층에 복잡한 변경 내용을 관리하지 않아도 이러한 디자인을 사용할 수 있도록 해줍니다. 액티비티의 레이아웃을 여러 프래그먼트로 나누면 런타임에서 액티비티의 외관을 수정할 수도 있고, 그러한 변경 내용을 해당 액티비티가 관리하는 백 스택에 보존할 수도 있습니다. 이제 프래그먼트는 Fragment suppoert library를 통해 폭넓게 제공됩니다.


예를 들어 뉴스 애플리케이션이라면 하나의 프래그먼트를 사용하여 왼쪽에 기사 목록을 표시하고, 또 다른 프래그먼트로 오른쪽에 기사 내용을 표시할 수 있습니다. 두 프래그먼트 모두 한 가지 액티비티에서 양쪽으로 나란히 나타나며, 각 프래그먼트에 나름의 수명 주기 콜백 메서드가 있고 각자 사용자 입력 이벤트를 따로 처리하게 됩니다. 따라서 사용자는 기사를 선택하는 데 하나의 액티비티를 쓰고 기사를 읽는 데 다른 액티비티를 선택하는 대신, 같은 액티비티 안에서 기사를 선택하고 읽는 과정을 모두 끝낼 수 있습니다.


각 프래그먼트는 모듈식이고 재사용 가능한 액티비티 구성 요소로 디자인해야 합니다. 다시 말해, 각 프래그먼트가 자체적인 수명 주기 콜백으로 레이아웃과 동작을 정의하기 때문에 한 프래그먼트를 여러 액티비티에 포함할 수 있습니다. 그러므로 다시 사용할 것을 염두에 두고 디자인하고 하나의 프래그먼트를 다른 프래그먼트에서 직접 조작하는 것은 삼가야 합니다. 이것이 특히 중요한 이유는 모듈식 프래그먼트를 사용하면 프래그먼트 조합을 여러 가지 화면 크기에 맞춰 변경할 수 있기 때문입니다. 태블릿과 핸드셋을 모두 지원하는 애플리케이션을 디자인하는 경우, 사용 가능한 화면 공간을 사용자 환경에서 최적화하도록 프래그먼트를 여러 레이아웃 구성에 재사용할 수 있습니다.


여러 가지 화면 구성에 맞게 여러 가지 프래그먼트 조합으로 애플리케이션을 디자인하는 법에 대한 보다 더 자세한 내용은 화면 호환성 개요에 대한 가이드를 참조 부탁드립니다.


Reference

1. https://developer.android.com/guide/components/fragments

반응형
반응형


이번 포스팅은 Android에서 Button 위젯의 배경을 투명하게 하는 방법에 대하여 알아보도록 하겠습니다.


1. 첫번째 방법은 android:background="#00ff0000" 으로 xml에 적용하는 것입니다.


<Button

        android:id="@+id/bt_confirm"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:background="#00ff0000" />


2. 두번째 방법은 Android 내부에 이미 선언된 transparent color를 사용하는 것입니다.


<Button

        android:id="@+id/bt_confirm"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:background="@android:color/transparent" />


위의 첫번째 방법과 두번째 방법이 있지만 두번째 방법의 경우 Theme 에 따라 투명도가 정상적으로 작동하지 않을 수도 있으니 참고 하시기 바랍니다.


여기에서 첫번째 방법에 사용한 "#00ff0000" 값에 대하여 궁금하신 분도 계실 수도 있어 간단하게 설명드리도록 하겠습니다.

"#" 이하부터 "00ff0000" 라는 총 8자리 문자열을 00 / ff / 00 / 00 두 자리씩 나눕니다.

- 첫번째 문자열 "00" 은  투명도를 나타냅니다. 

- 두번째 문자열 "ff" 는  적색을 나타냅니다.

- 세번째 문자열 "00" 은 녹색을 나타냅니다.

- 네번째 문자열 "00" 은 청색을 나타냅니다.


적색과 녹색, 청색의 조화로 인해 새로운 Color 가 나타나게 됩니다.


반응형
반응형

이번 포스팅은 FileProvider를 사용하여 파일 공유하는 방법에 대하여 알아보도록 하겠습니다.


1. FileProvider 란 


ContentProvider는 데이터를 캡슐화하여 다른 응용 프로그램에 제공하는 Android 구성 요소입니다. 여러 응용 프로그램간에 데이터를 공유해야하는 경우에만 필요합니다. 예를 들어 연락처 데이터는 ContentProvider의 하위 클래스 인 ContactsProvider를 사용하여 다른 응용 프로그램과 공유됩니다.

FileProvider는 ContentProvider의 하위 클래스입니다. ContentProvider는 모든 종류의 데이터를 안전하게 공유 할 수있는 구성 요소이지만 FileProvider는 특히 앱의 내부 파일을 공유하는 데 사용됩니다. FileProvider 클래스는 v4 지원 라이브러리의 일부이므로 프로젝트에 포함시켜야합니다.

FileProvider가 작동하도록하려면 다음 세 단계를 수행하십시오.

    ▶ AndroidManifest 파일에서 FileProvider 정의

    ▶ FileProvider가 다른 응용 프로그램과 공유 할 모든 경로가 포함 된 XML 파일을 만듭니다.

    ▶ Intent에 유효한 URI를 번들로 묶어 활성화


2. FileProvider 정의


FileProvider 정의

AndroidManifest 내에서 FileProvider를 정의하려면 다음 속성 및 요소에 익숙해야합니다.

    ▶ android : authorities

    ▶ android : exported

    ▶ android : grantUriPermissions

    ▶ android : name

    ▶ <meta-data> subelement

이들 모두가 매우 친숙해 보인다면 FileProvider를 둘러 보는 방법이 약간 쉬울 것입니다. 그렇지 않으면 각 속성과 그 목적에 대한 자세한 설명을 준비했습니다.

android : authorities

적어도 하나의 고유 권한을 반드시 정의합니다. Android 시스템은 모든 제공자의 목록을 유지하며 권한별로 이를 구분합니다. 권한은 애플리케이션 ID가 Android 애플리케이션을 정의하는 것처럼 FileProvider를 정의합니다.

일반적으로 Android 시스템은 ContentProviders에 특정 URI 체계를 사용합니다. 체계는 content : // <authority> / <path>로 정의 되므로 시스템은 URI의 권한을 ContentProvider의 권한과 일치시켜 어떤 ContentProvider가 요청되는지 알 수 있습니다.


android : exported

이 속성은 이름이 잘못되어 쉽게 오용 될 수 있습니다. 이 속성을 이해하려면 FileProvider를 door가 잠긴 방으로 생각하십시오. 값을 true로 설정하면 기본적으로 모든 사람에게 문이 열립니다. 모든 것이 귀하의 관점에서 작동하지만 다른 모든 앱이 권한을 부여받지 않고 FileProvider를 사용할 수 있기 때문에 방금 큰 보안 문제가 발생했습니다.

이를 통해 우연의 일치로 프로그램하지 말고 항상 코드의 부작용을 인식 할 수 있습니다. 또한 SDK 16 이하의 기본값이 true 이므로 항상이 속성을 정의하십시오 .


android : grantUriPermissions

FileProvider를 잠긴 방으로 계속 생각하면이 속성은 외부 앱에 임시 일회성 키를 제공하는 데 사용됩니다. 이 속성을 사용하면 앱의 내부 저장소를 안전하게 공유 할 수 있습니다. FLAG_GRANT_READ_URI_PERMISSION 또는 FLAG_GRANT_WRITE_URI_PERMISSION을 구성 요소를 활성화하여 앱의 내부 파일을 여는 의도에 추가하기 만하면됩니다. 이 플래그를 사용하려면 해당 값을 true로 설정하십시오 .

<provider> 요소는 <grant-uri-permission> 하위 요소도 가질 수 있습니다. 유일한 차이점은이 속성을 사용하면 앱의 내부 저장소 내에서 무엇이든 공유 할 수 있고 하위 요소를 사용하면 공유 할 특정 데이터 하위 집합을 선택할 수 있다는 것입니다. 하위 요소를 대신 사용하려면 값을 false로 설정하십시오 .

<메타 데이터> 하위 요소

파일 경로 XML 생성

FileProvider를 사용할 때이 하위 요소를 정의해야합니다. FileProvider가 외부 앱과 공유 할 수있는 모든 데이터 경로가 포함 된 XML 파일의 경로를 정의해야합니다.

XML 파일에는 루트로 <paths> 요소가 있어야합니다. <paths> 요소에는 다음 중 하나 일 수있는 하나 이상의 하위 요소가 있어야합니다.

    <files-path />-내부 앱 스토리지, Context # getFilesDir ()

    <cache-path />-내부 앱 캐시 스토리지, Context # getCacheDir ()

    <external-path />-공용 외부 저장소, Environment.getExternalStorageDirectory ()

    <external-files-path />-외부 앱 스토리지, 컨텍스트 #getExternalFilesDir (null)

    <external-cache-path />-외부 앱 캐시 스토리지, 컨텍스트 #getExternalCacheDir ()

그것들은 그들이 정의한 앱의 디렉토리에 따라 다르다는 것을 알았을 것입니다.

각 요소에는 경로와 이름 속성이 있어야합니다. path 속성은 공유하려는 서브 디렉토리를 정의하며 와일드 카드를 지원 하지 않습니다 . name 속성은 보안상의 이유로 사용되며 하위 디렉토리 이름을 해당 값으로 대체합니다.

안드로이드 : 이름

이 값을 android.support.v4.content.FileProvider로 설정했습니다 .

번들 유효 URI

AndroidManifest 파일에 FileProvider를 정의하면 사용할 준비가 된 것입니다. 파일을 공유하려면 인 텐트를 작성하고 유효한 URI를 제공해야합니다. URI는 FileProvider 클래스를 사용하여 생성됩니다.

코드 구현

AndroidManifest.xml

<provider
    android:name="android.support.v4.content.FileProvider"
    android:grantUriPermissions="true"
    android:exported="false"
    android:authorities="${applicationId}">

    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_provider_paths"/>

</provider>

권한을 위해 앱 ID를 사용하고 있습니다. 프로젝트에 여러 가지 특징이 있으며 동시에 장치에 설치할 수 있기 때문입니다. Android 시스템에서는 동일한 FileProvider로 여러 응용 프로그램을 설치할 수 없으므로 각 기능마다 고유 한 권한이 필요합니다.

file_provider_paths.xml

<paths>
    <cache-path name="cache" path="/" />
    <files-path name=”files” path=”/” />
</paths>

이와 같은 경로를 정의함으로써 FileProvider가 앱의 내부 캐시 및 파일 디렉토리에있는 모든 파일을 공유 할 수 있습니다.

FileProvider 사용

// create new Intent
Intent intent = new Intent(Intent.ACTION_VIEW);

// set flag to give temporary permission to external app to use your FileProvider
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

// generate URI, I defined authority as the application ID in the Manifest, the last param is file I want to open
String uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, file);

// I am opening a PDF file so I give it a valid MIME type
intent.setDataAndType(uri, "application/pdf");

// validate that the device can open your File!
PackageManager pm = getActivity().getPackageManager();
if (intent.resolveActivity(pm) != null) {
    startActivity(intent);
}

작동 방식을 이해하면 고유 한 FileProvider를 구현하는 것이 매우 간단합니다.

문제의 복잡성은 코드 자체가 아니라 모든 것이 상호 연결되는 방법에 대한 문서와 이해입니다. 이 기사가 자신의 유스 케이스에 FileProvider를 구현하고 공식 문서를 읽기 쉽게 만드는 데 도움이되기를 바랍니다.


https://infinum.co/the-capsized-eight/share-files-using-fileprovider

반응형

+ Recent posts