如何正确激活活动外的服务?

我正在尝试构建一个依赖Web服务工作的应用程序。 为了实现它,我决定遵循Model-View-ViewModel架构和Repository模式。 我尝试使这个架构受到Android开发人员官方网站的应用程序架构指南中所示指南的启发。

我使用OkHttp库来使用WebService,并使用Room作为手机的数据库。

Android推荐的架构

我做了一些测试,看看应用程序是否通过Web服务从主要活动中成功获得了数据,并且它有效; 应用程序成功收到数据。

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之外的Main Activity中移动创建ServiceConnection的代码,以便将它与View逻辑分离。 为此,我尝试包装生成ServiceConnection的代码并将其绑定到一个用于处理Services的新类。 在提取代码并尝试将其与Repository类接合时,接下来的问题出现了:它是否需要绑定服务以使其工作,并从中检索数据,或者相反,我可以在不绑定它的情况下执行此操作所有? (对于记录:这不是问题本身;我只是在制作代码时指出我在想什么)

请注意,我知道我可以将一些上下文(例如MainActivity的上下文或Application上下文)传递给类的构造函数并将其存储在某些属性中,但这确实是我试图避免按顺序执行的操作防止内存泄漏。 长话短说:

  • 上面的代码在MainActivity中有效
  • 我正在尝试激活Activity类之外的服务,因此我创建了一个专用的类来处理其中的服务。 我称这个类为“RemoteDataSource”
  • 一旦服务完成HTTP调用并收到响应,我试图使Repository类从服务中检索数据。
  • 我希望MyWebService继续运行,即使没有任何Activity实例化。
  • 在这些条件下尝试将服务绑定到某些上下文似乎毫无意义,因为它可能会导致内存泄漏。
  • 似乎调用onStartCommand不足以让它运行服务。

这是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 getResponseAsObject() { if (!running) return null; ArrayList child_nodes = new ArrayList(); 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 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逻辑分离,我应该记住什么?