I have a service that I'm trying to unit test using ServiceTestCase. In my setUp(), I'm creating the IBinder, but I get a NullPointerException when creating the intent. I'm using the application's context and setting it to the test case, but the package name seems to be null. Any ideas as to why it's doing this and what the solution might be?
ServiceTestCase Code:
public class MyActivityTest extends ServiceTestCase<ImageDownloadTaskService> {
ImageDownloadTaskService service;
/**
* Constructor
*/
public MyActivityTest() {
super(ImageDownloadTaskService.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
setApplication(new MyApplication());
getApplication().onCreate();
setContext(getApplication());
Intent intent = new Intent(getContext(), ImageDownloadTaskService.class);
IBinder binder = bindService(intent);
service = ((ImageDownloadTaskService.LocalBinder) binder).getService();
}
public void testService(){
assertTrue(service.returnTrue());
}
}
Service code snippet:
public boolean returnTrue(){
return true;
}
public class LocalBinder extends Binder {
ImageDownloadTaskService getService() {
return ImageDownloadTaskService.this;
}
}
private final IBinder binder = new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return binder;
}
Error:
java.lang.NullPointerException
at android.content.ContextWrapper.getPackageName(ContextWrapper.java:127)
at android.content.ComponentName.<init>(ComponentName.java:75)
at android.content.Intent.<init>(Intent.java:3004)
at com.example.untitled2.MyActivityTest.setUp(MyActivityTest.java:43)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:537)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)
Try using getSystemContext() instead of getContext(). Docs say:
It returns the real system context that is saved by setUp(). Use it to create mock or other types of context objects for the service under test.
Hope this helps.
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'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
I am working on adding in-app billing and working from this official documentation
And I am on the section Binding to IInAppBillingService
Here is my code:
public class CommunityActivity extends BaseActivity implements ServiceConnection
{
ArrayAdapter<ChatMessage> adapter;
Dialog dialog;
ArrayList<ChatMessage> chat = new ArrayList <ChatMessage>( );
IInAppBillingService mService;
ServiceConnection mServiceConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
#Override
public void onServiceConnected(ComponentName name,
IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
FlurryAgent.onStartSession(this, "8CA5LTZ5M73EG8R35SXG");
setContentView(R.layout.community);
bindService(new
Intent("com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn, Context.BIND_AUTO_CREATE);
But I get compile errors saying I have to implement the onServiceConnected and onServiceDisconnected methods. But I thought I already added them in a way that the example suggested.
Where did I go wrong here? Thanks!
The error is because you have declared your class as follows
public class CommunityActivity extends BaseActivity implements ServiceConnection
now compiler expects that u have these two functions onServiceConnected and on ServiceDisconnected implemented in CommunityActivity. but it cannot find them in this class.
remove this implements ServiceConnection and code should compile successfully.
It seems that Bundle has deprecated putIBinder and getIBinder which I thought was very useful for passing binders (through Bundles) over to my service. Since these are deprecated, is there an alternative to this?
I really need to pass an IBinder object over to my service, and I thought the Bundle approach was the easiest (best) solution for this.
Thanks,
J
Posting this which may help someone who deals with binders. I use this approach, still works with Bundle
// write
bundle.putParcelable(key, new ParcelableBinder(binder));
// read
ParcelableBinder value = bundle.getParcelable(key);
IBinder binder = value == null ? null : value.getBinder();
// or with possible NPE
IBinder binder = bundle.<ParcelableBinder>getParcelable(key).getBinder()
public static class ParcelableBinder implements Parcelable {
IBinder mBinder;
public ParcelableBinder(IBinder binder) {
mBinder = binder;
}
private ParcelableBinder(Parcel in) {
mBinder = in.readStrongBinder();
}
public IBinder getBinder() {
return mBinder;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongBinder(mBinder);
}
public static final Creator<ParcelableBinder> CREATOR = new Creator<ParcelableBinder>() {
#Override
public ParcelableBinder createFromParcel(Parcel in) {
return new ParcelableBinder(in);
}
#Override
public ParcelableBinder[] newArray(int size) {
return new ParcelableBinder[size];
}
};
}
No need to use Bundles to pass IBinders across services (I can't even see the Bundle.{put,get}IBinder methods you mention in the javadoc).
You can use IBinder objects directly in AIDL, after you import them. For example, if you want to pass a reference to an IOtherService into a method on IService, then IService.aidl could look like this:
package com.yourpackage;
import com.yourpackage.IOtherService;
interface IService {
void doSomething(IOtherService service);
}
Would using implementation of Parcelable instead be applicable in your case (Bundle is just an implementation of Parcelable anyways)?
Check out this post on Bundle v Parcelable:
Passing a custom Object from one Activity to another Parcelable vs Bundle
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.).