I'm having a problem writing a service, that should work with multiple activities.
I wrote a simple service and a mediator class the makes the bind and can return a service object. this is the simple service class:
public class ServerConnectionService extends Service{
private static final String TAG = "ServerConnectionService";
private final Binder binder=new LocalBinder();
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy(){
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class LocalBinder extends Binder {
ServerConnectionService getService() {
return ServerConnectionService.this;
}
}
}
this is the mediator class:
public class ServiceConnectionBinder{
private ServerConnectionService m_SrvConnection=null;
private ServiceConnection m_OnService;
private boolean m_IsBound;
private Activity m_Client;
public ServiceConnectionBinder(Activity i_Activity)
{
m_IsBound = false;
this.m_Client = i_Activity;
this.m_OnService=new ServiceConnection() {
public void onServiceConnected(ComponentName className,IBinder rawBinder) {
m_SrvConnection=((ServerConnectionService.LocalBinder)rawBinder).getService();
}
public void onServiceDisconnected(ComponentName className) {
m_SrvConnection=null;
}
};
doBindService();
Log.d("ServiceConnectionBinder", "finished Ctor");
}
private void doBindService() {
if(!m_IsBound)
{
m_Client.bindService(new Intent(m_Client, ServerConnectionService.class), m_OnService, Context.BIND_AUTO_CREATE);
m_IsBound = true;
}
if(m_SrvConnection == null)
{
Log.d("ServiceConnectionBinder",".doBindService cannot bind " + ServerConnectionService.class.toString() + " to " + this.toString());
}
}
public void doUnbindService() {
if (m_IsBound) {
// Detach our existing connection.
m_Client.unbindService(m_OnService);
m_IsBound = false;
}
}
public ServerConnectionService getServerConnectionService()
{
if(m_IsBound)
{
Log.d("ServiceConnectionBinder", "getServerConnectionService m_IsBound = " + m_IsBound);
}
return m_SrvConnection;
}
}
The client Activity has the following data members:
private ServiceConnectionBinder m_SrvcConnectionBinder=null;
private ServerConnectionService m_SrvConnection=null;
And in onCreate() the following code:
m_SrvcConnectionBinder = new ServiceConnectionBinder(this);
m_SrvConnection = m_SrvcConnectionBinder.getServerConnectionService();
problem is that after the onCreate(), the m_SrvConnection is always null.
If you have any other ways to implement this you are more than welcome to share..
problem is that after the onCreate(), the m_SrvConnection is always null.
Of course. The binding request will not even begin until the main application thread gets control again (i.e., you return control to the OS).
You cannot use m_SrvConnection until onServiceConnected() is called.
Resurrecting the old post as I had the similar question, but there is no clear answer here.
One of the ways to address this is like this:
protected void onCreate(Bundle savedInstanceState){
//... some stuff #1...
new AsyncTask<Void, Void, Integer>(){
protected void onPreExecute() { }
protected Integer doInBackground(Void... params) {
while(m_SrvConnection==null);
return new Integer(1);
}
protected void onPostExecute(Integer result) {
// service is up, m_SrvConnection is set
// what you wanted to do with the service in onCreate() goes here
}
}.execute();
//...some stuff #2...
}
Note that "some stuff #1" will run right when onCreate() is called, "some stuff #2" will be executed almost right after that, but what you put in onPostExecute() will be run much later.
The reason for doing it this way and not just putting the code into onServiceConnected() is that the ServiceConnectionBinder can now be put outside of the Activity (in some singleton, or Application for example) and be used by multiple activities without the need for each of them to bind to the service.
Note, it may not be obvious, but things in onPostExecute() may (will) actually be run after all other standard callbacks (like onResume() etc.).
Related
I am trying to implement an architecture similar to the one presented at the Android Developer Summit 2015: https://github.com/yigit/dev-summit-architecture-demo. My application has a simple class that handles the network requests. The class uses Retrofit 2 for the requests. I am also using Dagger 2 for dependency injection.
I am trying to achieve something very simple. My activity tells a controller to fetch data. The controller then makes a call to my REST client to perform a network request. When the request completes successfully I want to broadcast an event to my Activity so that it can update the UI. However, the event is not being broadcast.
I am using the LocalBroadcastManager to broadcast events. My activity registers/unregisters for broadcasts in the onResume/onPause methods. My REST client has an instance of the application context which is provided through dependency injection. The REST client uses the application context to send the broadcast.
My first suspicion was that the broadcasts were not being sent because the network requests are executed on a worker thread whereas the activity is expecting broadcasts on the main thread. However, this type of scenario shouldn't be a problem if the Android documentation is correct.
This is my activity.
public class BaseActivity extends AppCompatActivity {
private ApplicationComponent mApplicationComponent;
private EventBroadcastReceiver mEventBroadcastReceiver = new EventBroadcastReceiver();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mApplicationComponent = getMovieManagerApplication().getApplicationComponent();
}
#Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("now_playing");
LocalBroadcastManager.getInstance(this).registerReceiver(mEventBroadcastReceiver, intentFilter);
}
#Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mEventBroadcastReceiver);
}
protected MovieManagerApplication getMovieManagerApplication() {
return (MovieManagerApplication) getApplication();
}
protected ApplicationComponent getApplicationComponent() {
return mApplicationComponent;
}
protected void update(Intent intent) {
}
private class EventBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
update(intent);
}
}
}
And this is my REST client.
public class MovieRestClient extends BaseRestClient implements Callback<MovieResponse> {
#Inject
public MovieApiService mMovieApiService;
#Inject
public Context mApplicationContext;
public MovieRestClient(ApplicationComponent applicationComponent) {
super(applicationComponent);
getApplicationComponent().inject(this);
}
#Override
public void onResponse(Response<MovieResponse> response) {
if (response.isSuccess()) {
Parcelable parcelable = Parcels.wrap(response.body());
MovieResponse movieResponse = Parcels.unwrap(parcelable);
Intent intent = new Intent("now_playing");
intent.putExtra(MovieResponse.class.getName(), parcelable);
LocalBroadcastManager.getInstance(mApplicationContext).sendBroadcast(intent);
}
}
#Override
public void onFailure(Throwable t) {
}
public void getNowPlaying() {
mMovieApiService.getNowPlaying(API_KEY).enqueue(this);
}
public void getPopular() {
mMovieApiService.getPopular(API_KEY).enqueue(this);
}
public void getTopRated() {
mMovieApiService.getTopRated(API_KEY).enqueue(this);
}
public void getUpcoming() {
mMovieApiService.getUpcoming(API_KEY).enqueue(this);
}
}
Any help would be greatly appreciated.
I am new to Android. I am using Sockets in an asynchronous task and I wish to pass data back to the activity that called it. But I do not want the asynchronous task to handle the UI. I just wish to pass data.
The class that e![enter image description here][1]xtends async task is not a part of the class that extends activity
My activity has 2 buttons. When the button is clicked, async task is called and corresponding changes should be made to rest of the activity.
From How do I send data back from OnPostExecute in an AsyncTask:
class YourActivity extends Activity {
private static final int DIALOG_LOADING = 1;
public void onCreate(Bundle savedState) {
setContentView(R.layout.yourlayout);
new LongRunningTask1().execute(1,2,3);
}
private void onBackgroundTaskDataObtained(List<String> results) {
//do stuff with the results here..
}
private class LongRunningTask extends AsyncTask<Long, Integer, List<String>> {
#Override
protected void onPreExecute() {
//do pre execute stuff
}
#Override
protected List<String> doInBackground(Long... params) {
List<String> myData = new ArrayList<String>();
for (int i = 0; i < params.length; i++) {
try {
Thread.sleep(params[i] * 1000);
myData.add("Some Data" + i);
} catch(InterruptedException ex) { }
}
return myData;
}
#Override
protected void onPostExecute(List<String> result) {
YourActivity.this.onBackgroundTaskDataObtained(result);
}
}
}
Yes you can use handler to communicate between AsyncTask and Activity, see following example, it will help,
#Override
protected void onPostExecute(Object result) {
super.onPostExecute(result);
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putString("file", pdfPath);
message.setData(bundle);
handler.sendMessage(message); // pass handler object from activity
}
put following code into Activity class
Handler handler = new android.os.Handler() {
#Override
public void handleMessage(Message msg) {
String filePath = msg.getData().getString("file"); // You can change this according to your requirement.
}
};
If you dont't aware of Handler class then first read following link, it will help you
https://developer.android.com/training/multiple-threads/communicate-ui.html
There are different way to pass data back to activity. As explained below
Suppose u have one class
public class Socket {
private Activity activity;
//create interface
public interface OnAyscronusCallCompleteListener{
public void onComplete(/*your data as parameter*/);
}
private void setAsyncListener(Activity activity){
this.activity = activity;
}
//rest of your code
// send back data to activity
activity.onComplete(/* your data */)
}
//Now your activity
class YourActivity extends Activity implements Socket.OnAyscronusCallCompleteListener {
// rest of your activity life cycle methods
onCreate(Bundle onSaved)
{Socket socket = new Socket();
socket.setAsyncListener(this);
}
public void onComplete(/*your data*/){
// perform action on data
}
}
In your Activity Class
new YourAsyncTask().execute("String1","String2","12");
Your AsyncTask
AsyncTask<Params, Progress, Result>
private class YourAsyncTask extends AsyncTask<String, Void, Void > {
protected Long doInBackground(String... s) {
String s1 = s[0]; //="String1";
String s2 = s[1]; //="String2";
int s1 = Integer.parseInt(s[2]); //=3;
}
protected void onProgressUpdate(Void... values) {
}
protected void onPostExecute() {
}
}
A great explanation is here
Example to implement callback method using interface.
Define the interface, NewInterface.java.
package javaapplication1;
public interface NewInterface {
void callback();
}
Create a new class, NewClass.java. It will call the callback method in main class.
package javaapplication1;
public class NewClass {
private NewInterface mainClass;
public NewClass(NewInterface mClass){
mainClass = mClass;
}
public void calledFromMain(){
//Do somthing...
//call back main
mainClass.callback();
}
}
The main class, JavaApplication1.java, to implement the interface NewInterface - callback() method. It will create and call NewClass object. Then, the NewClass object will callback it's callback() method in turn.
package javaapplication1;
public class JavaApplication1 implements NewInterface{
NewClass newClass;
public static void main(String[] args) {
System.out.println("test...");
JavaApplication1 myApplication = new JavaApplication1();
myApplication.doSomething();
}
private void doSomething(){
newClass = new NewClass(this);
newClass.calledFromMain();
}
#Override
public void callback() {
System.out.println("callback");
}
}
Then regarding your answer, in actually you have a 2 possibilities... The first one is the answer from #Rodolfoo Perottoni and the other possibility are correctly, read this post please!
I prefer the second way because I can update when I need it.
I would create a inner class in the MainActivity that extends the AsyncTask and voila all data is there already by getters.
I'm trying to access sqlite DB (that is filled on diffrent part of the package) on AIDL stub implementation but - there is no context there. how can I get the context?
there are 2 projects (applications) - A,B.
Project A contains keeps records on sqlite DB. and contains aidl service.
Project B needs to ask project A (diffrent package, there can be many projects like B) if a record exists. the only way for project A to answer project B from the stub is to check the DB is to have a Context (the "?????" in the code below) - how can I get the project A's context from the stub?
The AIDL's implementation:
public class IRecordServiceImpl extends IRecordService.Stub{
#Override
public boolean RecordExists(String recordKey)
throws RemoteException {
boolean returnValue = false;
RecordDataSource rds = new RecordDataSource(??????);
rds.Open();
returnValue = rds.isRecordExists(recordKey);
rds.Close();
return returnValue;
}
}
The Service code:
public class IRecordService extends Service {
private IRecordServiceImpl service;
#Override
public void onCreate() {
super.onCreate();
this.service = new IRecordServiceImpl();
}
#Override
public IBinder onBind(Intent intent) {
return this.service;
}
#Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
Thanks!
Problem solved - you can pass the Service's Context via the constructor and keep it in IRecordServiceImpl as private field
into my application i use an intent:
private Handler mHandler = new Handler();
.
.
mServiceIntent = new Intent(this, ObdGatewayService.class);
mServiceConnection = new ObdGatewayServiceConnection();
mServiceConnection.setServiceListener(mListener);
// bind service
Log.d(TAG, "Binding service..");
bindService(mServiceIntent, mServiceConnection,
Context.BIND_AUTO_CREATE);
here my activity at onCreate start a new service. this is my onDestroy:
#Override
protected void onDestroy() {
super.onDestroy();
mServiceIntent = null;
mServiceConnection = null;
mListener = null;
mHandler = null;
}
this is mServiceConnection:
public class ObdGatewayServiceConnection implements ServiceConnection{
private static final String TAG = "com.echodrive.io.ObdGatewayServiceConnection";
private IPostMonitor service = null;
private IPostListener listener = null;
public void onServiceConnected(ComponentName name, IBinder binder) {
service = (IPostMonitor) binder;
service.setListener(listener);
}
public void onServiceDisconnected(ComponentName name) {
service = null;
Log.d(TAG, "Service disconnesso.");
}
public boolean isRunning() {
if (service == null) {
return false;
}
return service.isRunning();
}
public void addJobToQueue(ObdCommandJob job) {
if (null != service)
service.addJobToQueue(job);
}
public void setServiceListener(IPostListener listener) {
this.listener = listener;
}
mListener is a listener from interface:
public interface IPostListener {
void fineTest(DatiTest risultati);
void startAcquisizione();
void aquisizioneTerminata();
void aquisizioneInterrotta(String motivo);
void connessioneCorretta();
void gpsStato(boolean stato);
}
my problem is.. how save all this code after rotation? thanks!
The recommended way to save state across rotations is to save them on the outState. This is accomplished by overriding the onSaveInstanceState method. This method gives you a Bundle outState object that you can add Parcelable and Serializable objects to. This should work fine for your Intent object since it implements Parcelable but it may not work for say Handler because it only extends Object.
Another solution is to make these members static. However, be very careful if you decide to do this. Make sure that the value of the static member never holds on to a Context or a view hierarchy, etc, or you could easily introduce memory leaks.
If neither of these is acceptable to you, there is the option suggested by Tushar. However, unless you're careful this will make your life very difficult very fast. A large reason why activities are destroyed and re-created is so that resources can be re-loaded. So if you have layouts, strings, colors, dimens, or basically any resource specifically for landscape, or tablets, or different versions, you'll have to reload the entire UI yourself.
I have a Service that creates AsyncTasks for downloading files. In activities, we create Runnables or Threads that we pass to Activity.runOnUiThread(). I can't access that method from a service, so how do I use AsyncTask correctly, (do heavy work without blocking the UI Thread)?
If your service is only called from your application, and you can make it a singleton, then try this:
public class FileDownloaderService extends Service implements FileDownloader {
private static FileDownloaderService instance;
public FileDownloaderService () {
if (instance != null) {
throw new IllegalStateException("This service is supposed to be a singleton");
}
}
public static FileDownloaderService getInstance() {
// TODO: Make sure instance is not null!
return instance;
}
#Override
public void onCreate() {
instance = this;
}
#Override
public IBinder onBind(#SuppressWarnings("unused") Intent intent) {
return null;
}
#Override
public void downloadFile(URL from, File to, ProgressListener progressListener) {
new Thread(new Runnable() {
#Override
public void run() {
// Perform the file download
}
}).start();
}
}
Now you can directly call methods on your service. So just call downloadFile() to put the service to work.
About your real question of how to update the UI. Notice that this method receives a ProgressListener instance. It could look like this:
public interface ProgressListener {
void startDownloading();
void downloadProgress(int progress);
void endOfDownload();
void downloadFailed();
}
Now you just update the UI from the activity (not from the service, which remains unaware of how the UI looks like).