0

저는 Android 세계에서 매우 새로 왔습니다. 아름다운 Rest API를 개발 한 후에 안드로이드 개발이 쉬울 것이라고 생각했지만 기본에 매달렸다.Android 활동에서 API 및 DB 호출을 추출하는 방법은 무엇입니까?

내 Android 앱에서 올바른 자격 증명이 제공되면 토큰을 반환하는 API 호출을 만드는 로그인을 만들었습니다. 이 토큰은 공유 환경 설정에 저장되며 사용자는 주요 활동 : HomeActivity로 리디렉션됩니다. 그것의 버튼을 사용자가 클릭이 새로운 조각을로드 할 수 있도록하는 경우,

  • 그것은 BottomNavigationBar 있습니다

    이 활동은 할 일이 많이있다.

  • 리소스를 얻고 프래그먼트에 따라 표시하려면 API 끝점을 호출하십시오.

  • 과부하 서버를 피하기 위해 데이터베이스에 API 응답을 저장하십시오.

확실히, 안드로이드 개발자를위한이 매우 쉬운 것입니다,하지만 내이 같다 : 방법 나는 API 호출을 만들고있어 loadContents()에 빠른 요약은

import android.arch.persistence.room.Room; 
import android.content.Context; 
import android.content.SharedPreferences; 
import android.os.AsyncTask; 
import android.support.annotation.NonNull; 
import android.support.design.widget.BottomNavigationView; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.MenuItem; 
import android.view.View; 
import android.widget.TextView; 
import android.widget.Toast; 
import com.android.volley.AuthFailureError; 
import com.android.volley.Request; 
import com.android.volley.Response; 
import com.android.volley.VolleyError; 
import com.android.volley.toolbox.JsonObjectRequest; 
import com.ibosca.thub.database.AppDatabase; 
import com.ibosca.thub.helpers.BottomNavigationViewHelper; 
import com.ibosca.thub.models.Channel; 
import com.ibosca.thub.models.Content; 
import com.ibosca.thub.models.ContentList; 
import com.ibosca.thub.models.Town; 
import com.ibosca.thub.models.User; 
import com.ibosca.thub.parser.ContentParser; 
import com.ibosca.thub.volley.MySingleton; 
import org.json.JSONException; 
import org.json.JSONObject; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

public class HomeActivity extends AppCompatActivity { 

private String userToken; 
public TextView contentList; 
private ContentParser contentParser = new ContentParser(); 
public static AppDatabase db; 


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

    db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "townhub").build(); 

    contentList = (TextView) findViewById(R.id.contentList); 
    loadContents(); 

    SharedPreferences sharedPref = getSharedPreferences(MainActivity.PACKAGE_NAME, Context.MODE_PRIVATE); 
    userToken = sharedPref.getString("token", null); 

    BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation); 
    BottomNavigationViewHelper.disableShiftMode(bottomNavigationView); 

    View contentsButton = bottomNavigationView.findViewById(R.id.action_contents); 
    contentsButton.performClick(); 

    bottomNavigationView.setOnNavigationItemSelectedListener(
      new BottomNavigationView.OnNavigationItemSelectedListener() { 
       @Override 
       public boolean onNavigationItemSelected(@NonNull MenuItem item) { 

        Toast toast = Toast.makeText(getApplicationContext(), "UNDF", Toast.LENGTH_LONG); 

        switch (item.getItemId()) { 
         case R.id.action_towns: 
          toast = Toast.makeText(getApplicationContext(), "Towns", Toast.LENGTH_LONG); 
          break; 

         case R.id.action_channels: 
          toast = Toast.makeText(getApplicationContext(), "Channels", Toast.LENGTH_LONG); 
          break; 

         case R.id.action_contents: 
          loadContents(); 
          break; 

         case R.id.action_question: 
          toast = Toast.makeText(getApplicationContext(), "Questions", Toast.LENGTH_LONG); 
          break; 

         case R.id.action_user: 
          toast = Toast.makeText(getApplicationContext(), "Settings", Toast.LENGTH_LONG); 
          break; 

        } 

        toast.show(); 
        return true; 
       } 


      }); 

} 

public void ExecuteInsert(ContentList...lists){ 
    new InsertContents().execute(lists); 
} 

protected void loadContents() { 

    String url = MySingleton.BASE_URL + "/contents"; 


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

       @Override 
       public void onResponse(JSONObject response) { 
        try { 
         ContentList list = contentParser.fromContents(response); 
         ContentList[] lists = new ContentList[1]; 
         lists[0] = list; 
         ExecuteInsert(lists); 

        } catch (JSONException e) { 
         e.printStackTrace(); 
        } 

       } 
      }, new Response.ErrorListener() { 

       @Override 
       public void onErrorResponse(VolleyError error) { 
        Toast.makeText(getApplicationContext(), "Failed to connect", Toast.LENGTH_LONG).show(); 
       } 
      }) { 
     @Override 
     public Map<String, String> getHeaders() throws AuthFailureError { 
      Map<String, String> headers = new HashMap<>(); 

      headers.put("Authorization", "Bearer " + userToken); 
      return headers; 
     } 
    }; 

    MySingleton.getInstance(this).addToRequestQueue(jsObjRequest); 

} 


public static class InsertContents extends AsyncTask<ContentList, Void, Void> { 

    @Override 
    protected void onPreExecute() { 
     super.onPreExecute(); 

     //Perform pre-adding operation here. 
    } 

    @Override 
    protected Void doInBackground(ContentList...lists) { 
     ContentList list = lists[0]; 

     //Insert towns, channels 
     db.townDao().insertArrayList(list.getTowns()); 
     db.channelDao().insertArrayList(list.getChannels()); 
     db.userDao().insertArrayList(list.getUsers()); 
     db.contentDao().insertArrayList(list.getContents()); 

     //Select data from DB 
     List<Town> towns = db.townDao().getAll(); 
     List<Channel> channels = db.channelDao().getAll(); 
     List<User> users = db.userDao().getAll(); 
     List<Content> contents = db.contentDao().getAll(); 

     for (int i = 0; i < contents.size(); i++) { 
      Content content = contents.get(i); 
      Town contentTown = db.townDao().findById(content.getTownId()); 
      Log.i("Poble: ", contentTown.getName()); 
     } 

     return null; 
    } 

    @Override 
    protected void onPostExecute(Void aVoid) { 
     super.onPostExecute(aVoid); 

     //To after addition operation here. 
    } 
} 
} 

; 클래스 InsertContents은 로컬 데이터베이스에서 재생할 수있는 곳입니다. 마지막으로

, 질문 :

1) 당신이 볼 수 있듯이, 내가 API 호출을 위해 발리를 사용하고 있습니다. 분리 된 클래스에 "api 끝점"을 넣고 Activity에서이 클래스를 사용하는 것이 가장 좋습니다. Android 개발시이 코드를 분리하는 방법은 무엇입니까?

2) 데이터베이스 관리와 동일합니다. 어떻게 분리 된 클래스에 코드를 넣고 Activity에서 호출 할 수 있습니까? 이 작업은 현재 완료되었지만 AsyncTask에서 TextView를 업데이트 할 수 없습니다. TextView를 업데이트하면 ListView 또는 ReciclerView를 사용하는 것이 최종 목표입니다.

개선을위한 제안 사항은 무엇입니까? 환영합니다.

+0

https://github.com/googlesamples/android-architecture – LordRaydenMK

+0

개선판을 보시기 바랍니다. 이것은 REST API를 Java API로 변환 할 수있는 라이브러리입니다. –

답변

0

당신은 Repository 패턴을 시도 할 수 있습니다.

아이디어는, 다음과 다소 같다 자동차를 삽입, 당신이 Car 도메인 클래스가 있고 데이터베이스 또는 API를 상호 작용이 tipical CRUD 작업 등을 수행 말할 수, 알 카드 또는 그 번호의 목록을 검색하십시오.

당신은 그럼 당신은 구체적인 구현을 만들 수 있습니다

public interface CarRepository { 
    void insertCar(@NonNull Car car); 

    List<Car> getAllCars(); 

    Car getCarByPlate(@NonNull String plateNumber); 
} 

같은 인터페이스를 만들 수있는 인터페이스가있는 소스의 당신이 당신의 데이터를 저장하기 위해 사용하고 따라했다.

예를 들어 발리를 사용하는 경우 CarRepository까지 확장되는 RestCarRepository을 만들고 발리를 사용하여 나머지 API에서 데이터를 가져 오거나 저장합니다. 또는 DBCarRepository은 SQLite (또는 다른 데이터베이스 엔진)를 사용합니다.

마지막으로 활동에서 저장소를 선언하여 데이터를 가져 오는 논리를 추상화 할 수 있습니다.

면책 조항 : 저장소 패턴과 관련하여 많은 기사가 있습니다 (답변의 시작 부분에 게시 됨). DI 또는 MVP로 패턴을 추가 할 때이 대답이 더 복잡해 질 수 있습니다. 이는 귀하가 생각.

0

두 가지 경우 모두에 대한 간단한 대답 : 모델 (데이터)과보기 (작업/조각)를 분리하는 것이 좋습니다. 액티비티의 모든 것을 혼합하고 있지만이 작은 경우에는 괜찮을 수 있지만 앱이 커지면 액티비티/프래그먼트 수명주기를 읽고 이해하는 것이 어려워집니다.

더 깨끗한 코드를 수행하기 위해 안드로이드에서 우려를 분리 할 수있는 많은 다른 aproaches가 있습니다.

나는 안드로이드 애플 리케이션 깨끗한 아키텍처 https://github.com/android10/Android-CleanArchitecture

또한 구글은 몇 가지 패턴

다음

https://developer.android.com/topic/libraries/architecture/adding-components.html

당신이 데이터베이스에 대한 도움을 구현하는 비교적 새로운 라이브러리를 가지고, 페이징에 대해 이야기 당신이 REPO 추천 , 뷰 모델 등

편집 : 더 많은 답변 세부 사항 : 1) 뷰 (하나의 책임 만있는 구성 요소, 구성 요소 렌더링)와 같은 활동/단편을 고려하고 MVP (Model View Presenter) 패턴을 따를 수 있으며 다음과 같은 지식이있는 클래스 (발표자)를 만들 수 있습니다. 모델/데이터 (API 호출) 및 뷰와 모델 간의 가교 역할을합니다. 보기는 Presenter에서 모델에 대한 호출을 위임합니다 (예를 들어 일부 버튼을 누르면) Presenter가 데이터를보기로 반환하고보기에 화면을 페인트하는 방법 만 있습니다.

2) 정보를 검색하고 RecyclerView에서 데이터를 페인팅하기 위해 Presenter를 호출하는 동일한 접근 방식을 따를 수 있습니다. 활동에서 데이터를 분리하기 위해 ThreadPoolExecutor를 사용할 수 있습니다.