我正在尝试构建一个依赖于Web服务的应用程序。为此,我决定遵循Model-View-ViewModel体系结构以及Repository模式。我尝试根据Android开发者官方网站Guide to App architecture中显示的指南来构建这种架构。
我使用OkHttp库使用WebService,并使用Room作为电话数据库。
我进行了一些测试,以查看该应用程序是否通过Web服务从Main Activity内部成功获取了数据,并且该程序正常工作;该应用程序成功接收到数据。
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
connected = false;
Log.i("MainActivity", "MyWebService DISconnected successfully.");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myweb_service = ((MyWebService.LocalBinder)service).getService();
connected = true;
Log.i("MainActivity", "MyWebService connected successfully.");
}
};
void doBindMyWebService() {
if (bindService(new Intent(this, MyWebService.class),
connection, Context.BIND_AUTO_CREATE))
{
mShouldUnbind = true;
}
else {
Log.e("MainActivity", "ERROR --> Service instance doesn't exist, or this Client doesn't have permission to access.");
}
}
void doUnbindMyWebService() {
if (mShouldUnbind) {
unbindService(connection);
}
}
doBindMyWebService();
/* I am not trying to extract this method at all; I just included it for
* the sake of completeness
*/
@Override
protected void onDestroy() {
super.onDestroy();
doUnbindMyWebService();
}
现在,为了坚持选择的体系结构,我试图将在主Activity中创建ServiceConnection的代码移到Activity之外,以使其与View逻辑脱钩。为此,我尝试包装构成ServiceConnection的代码并将其绑定到用于处理Services的新类中。在提取代码并尝试将其与Repository类结合使用时,出现了下一个问题:它是否需要绑定Service以使其运行并从中检索数据,或者,我可以在不进行绑定的情况下进行绑定所有? (记录:这不是问题本身;我只是指出编写代码时的想法)
请注意,我知道我可以将某些上下文(例如MainActivity的上下文或Application上下文)传递给类的构造函数并将其存储在某些属性中,但这确实是我要避免的事情这样做是为了防止内存泄漏。长话短说:
这是MyWebService的代码:
package com.example.myapp;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import com.example.myapp.model.MyDataModel;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MyWebService extends Service {
private String url= "https://someserver.here/api/data/read.php";
private String response = "";
private JSONArray response_jsonarray;
private boolean running = false;
final static int MESSAGE = 1;
private final IBinder mBinder = new LocalBinder();
public boolean isRunning() {
return this.running;
}
public class LocalBinder extends Binder {
MyWebService getService() {
return MyWebService.this;
}
}
public String getResponse() {
if (!running) return null;
return this.response;
}
public JSONArray getResponseAsJsonArray() {
if (!running) return null;
return this.response_jsonarray;
}
public List<MyDataModel> getResponseAsObject() {
if (!running) return null;
ArrayList<MyDataModel> child_nodes = new ArrayList<MyDataModel>();
for (int i = 0; i < this.response_jsonarray.length(); i++) {
try {
JSONObject parent_obj = this.response_jsonarray.getJSONObject(i);
long parent_id = parent_obj.getLong("id_parent");
short type_id = (short)parent_obj.getInt("type");
short status_id = (short)parent_obj.getInt("status_id");
JSONObject childs_list = parent_obj.getJSONObject("childs");
Iterator<String> iter_childs = childs_list.keys();
while (iter_childs.hasNext()) {
JSONObject child_obj = childs_list.getJSONObject(iter_child.next());
long i_id = child_obj.getLong("id");
String i_name = child_obj.getString("name");
double i_lat = child_obj.getDouble("lat");
double i_long = child_obj.getDouble("long");
short i_order = (short)child_obj.getInt("order");
MyDataModel i_child = new MyDataModel(i_id, i_name, i_lat, i_long, 0, 0, type_id, status_id, 0, parent_id, i_order);
child_nodes.add(i_child);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
return child_nodes;
}
/*
* SERVICE LIFECYCLE
*/
@Override
public void onCreate() {
Log.i("MyWebService", "OnCreate TRIGGERED");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("MyWebService", "ERROR --> " + e.getMessage());
e.printStackTrace();
call.cancel();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i("MyWebService", "GOOD -> Response received! :-)");
final String myResponse = response.body().string();
response = myResponse;
try {
response_jsonarray = new JSONArray(response);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
this.running = true;
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Log.i("MyWebService", "OnStartCommand TRIGGERED");
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("MyWebService", "OnBind TRIGGERED");
return mBinder;
}
}
在这里,我称之为“ RemoteDataSource”的类的代码:
package com.example.myapp;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;
public class RemoteDataSource {
private boolean connected;
private boolean mShouldUnbind;
private MyWebService mWebService;
private final int INTENT_WEBSERVICE = 1;
public boolean isConnected() {
return this.connected;
}
public boolean isBound() {
return this.mShouldUnbind;
}
public ServiceConnection getConnection() {
return this.connection;
}
private ServiceConnection connection;
public MyWebService getMyWebService() {
return this.mWebService;
}
public RemoteDataSource() {
this.connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
connected = false;
Log.i("RemoteDataSource", "MyWebService DISconnected successfully");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mWebService = ((MyWebService.LocalBinder) service).getService();
connected = true;
Log.i("RemoteDataSource", "MyWebService connected successfully");
}
};
}
public boolean isRunning() {
if (this.mWebService == null) return false;
return this.mWebService.isRunning();
}
public void prepareWebService() {
//TODO -> Consider this empty method as a symbol of what I am trying to achieve
}
public void startMyWebService() {
this.mWebService.onStartCommand(new Intent(Intent.ACTION_SYNC), 0, INTENT_WEBSERVICE);
}
public void stopMyWebService() {
this.mWebService.stopSelf();
}
public void doBindMyWebService (Context context) {
if (context.bindService(new Intent(context, MyWebService.class),
this.connection, Context.BIND_AUTO_CREATE)) {
this.mShouldUnbind = true;
} else {
Log.e("RemoteDataSource", "ERROR --> Service instance doesn't exist, or this Client doesn't have permission to access.");
}
}
public void doUnbindMyWebService (Context context) {
if (this.mShouldUnbind) {
context.unbindService(connection);
}
}
}
这是我的Repository类的构造函数的一部分:
public Repository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
mParentDao = db.parentDao();
mMyDataModelDao = db.myDataModelDao();
mRemoteDataSource = new RemoteDataSource();
/* XXX It was a bad idea, it gets stuck in an infinite loop; I just had to try anyway
* The point is, if I don't wait until MyWebService has made it's work,
* the app crashes with NullPointerException (or it seems so, at least)
*/
while (!mRemoteDataSource.isRunning()) { continue; }
mRemoteDataSource.startMyWebService();
// [...]
}
这时我的目标是从RemoteDataSource类处理我的MyWebService,避免将其绑定到上下文。 WebService可能值得一个专用的线程,但是我仍然是一个带有线程的菜鸟。所以我宁愿保持简单,直到我看到它起作用为止;以后我会担心会提高性能。
无论如何,我认为我对应该如何启动服务感到困惑(请注意,我对服务及其处理方式不太熟悉)。我尝试处理服务的方式是否错误?如果是,我在俯视什么?为了使MyWebService与View逻辑脱钩,应该注意什么?