[英]How to extract API and DB calls from Android Activity?
我是Android世界的新手。 在开发了漂亮的Rest API之后,我认为Android开发将很容易,但是我仍然坚持基础知识。
在我的Android应用程序上,我创建了Login(登录),该API调用了API,并在提供有效凭据时返回令牌。 此令牌存储在共享首选项中,并且用户被重定向到主要活动:HomeActivity。
该活动有很多工作要做:
它具有BottomNavigationBar,因此当用户单击它的按钮时,将加载新的片段。
调用API端点以获取资源并根据片段进行显示。
将API响应存储在数据库中,以避免服务器过载。
当然,对于Android开发人员而言,这将非常容易,但是对我来说是这样的:
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.
}
}
}
为了简要介绍一下,在方法loadContents()
我正在进行API调用; 和类InsertContents
这是我在本地数据库中玩的地方。
最后,问题是:
1)如您所见,我正在使用Volley进行API调用。 是否有最佳实践将任何“ api端点”放在单独的类上,并在Activity中使用该类? 如何在Android开发中分离此代码?
2)与数据库管理相同。 如何将代码放在单独的类上,然后从Activity调用它? 目前已经完成,但是...我无法从AsyncTask更新TextView(更新TextView只是一个简单的尝试,我的最终目标是使用ListView或ReciclerView。
欢迎提出任何改进建议。
您可以尝试使用存储库模式。
这个想法或多或少如下,假设您有一个Car
域类,并且您的数据库或api交互执行典型的CRUD操作,例如插入一辆汽车,通过其车牌号检索一张或全部卡的列表。
您可以创建一个类似的界面
public interface CarRepository {
void insertCar(@NonNull Car car);
List<Car> getAllCars();
Car getCarByPlate(@NonNull String plateNumber);
}
然后,您可以根据使用哪个源存储数据来创建所述接口的具体实现。
例如,如果使用排球,则可以创建一个RestCarRepository
来扩展CarRepository
并使用Volley从rest api获取/存储数据。 或使用SQLite的DBCarRepository
(或任何其他数据库引擎)。
最后,您可以在活动中声明存储库,从而抽象出提取数据的逻辑。
免责声明:关于存储库模式的文章很多(如答案开头的文章所述),当添加更多模式作为DI或MVP时,此答案可能会变得更加复杂,因此您可以掌握这个想法。
两种情况的简短答案:最好将视图(活动/片段)与模型或数据分开。 您将活动中的所有内容混合在一起,在这种小情况下可以,但是如果您的应用程序扩展,则很难阅读和理解活动并导致活动/片段生命周期出现问题。
为了进行更简洁的代码,有很多不同的方法可以将android中的关注点分开。
我建议您在这个回购中谈论android应用中的干净架构https://github.com/android10/Android-CleanArchitecture
Google还拥有一个相对较新的库来实现多种模式
https://developer.android.com/topic/libraries/architecture/adding-components.html
在这里,您可以获得有关数据库,页面调度,视图模型等的帮助。
编辑:详细回答:1)您可以遵循Model View Presenter(MVP)模式,将活动/片段视为仅一个View(仅负责一项职责的组件,呈现组件),并创建一个类(Presenter)了解模型/数据(您的api调用),并充当View和Model之间的桥梁。 视图将在Presenter中委派对模型的调用(例如,如果按下某些按钮),并且Presenter将数据返回给View,并且View仅具有绘制屏幕的方式。
2)您可以遵循调用Presenter的相同方法来检索信息并在RecyclerView中绘制数据。 您可以使用ThreadPoolExecutor来将数据与活动分离。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.