Android Remote Service does not work on Real device - java

Remote Service
I'm doing a test for Android remote Service.
In the first app module, I make a service, complete as below:
AppService
package com.hqyj.dev.aidltest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class AppService extends Service {
public AppService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new IAppServiceRemoteBinder.Stub() {
#Override
public void basicTypes(
int anInt, long aLong,
boolean aBoolean, float aFloat,
double aDouble, String aString)
throws RemoteException {
}
#Override
public void setData(String data)
throws RemoteException {
setRealData(data);
}
#Override
public void registerCallback(IRemoteServiceCallback cb)
throws RemoteException {
AppService.this.callback = cb;
}
#Override
public void unregisterCallback(IRemoteServiceCallback cb)
throws RemoteException {
AppService.this.callback = null;
}
};
}
private IRemoteServiceCallback callback;
#Override
public void onCreate() {
super.onCreate();
System.out.println("Service started");
}
#Override
public void onDestroy() {
super.onDestroy();
System.out.println("Service stop");
}
public void setRealData(String data) {
this.data = data;
System.out.println("data = " + data);
try {
Thread.sleep(1000);
if (callback != null) {
callback.vlueChanged(data);
}
} catch (InterruptedException | RemoteException e) {
e.printStackTrace();
}
}
private String data = "default date";
}
And their are two AIDL files:
IAppServiceRemoteBinder.aild
// IAppServiceRemoteBinder.aidl
package com.hqyj.dev.aidltest;
// Declare any non-default types here with import statements
import com.hqyj.dev.aidltest.IRemoteServiceCallback;
interface IAppServiceRemoteBinder {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt,
long aLong,
boolean aBoolean, float aFloat,
double aDouble, String aString);
void setData(String data);
void registerCallback(IRemoteServiceCallback cb);
void unregisterCallback(IRemoteServiceCallback cb);
}
IRemoteServiceCallback.aild
// IRemoteServiceCallback.aidl
package com.hqyj.dev.aidltest;
// Declare any non-default types here with import statements
interface IRemoteServiceCallback {
/**
* return from server
*/
void vlueChanged(String value);
}
And in AndroidManifest.xml, this Server decleared as below:
AndroidManifest.xml
<service
android:name="com.hqyj.dev.aidltest.AppService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
And then, the in second module, copies all these aidl files with package name, as below:
And in MainActivity in anotherapp, complete as below:
package com.hqyj.dev.anotherapp;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import com.hqyj.dev.aidltest.IAppServiceRemoteBinder;
import com.hqyj.dev.aidltest.IRemoteServiceCallback;
public class MainActivity extends AppCompatActivity
implements View.OnClickListener, ServiceConnection {
private final String TAG = MainActivity.class.getSimpleName();
private Intent intent;
private IAppServiceRemoteBinder binder;
private int count = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_start).setOnClickListener(this);
findViewById(R.id.btn_stop).setOnClickListener(this);
findViewById(R.id.btn_set).setOnClickListener(this);
intent = new Intent();
intent.setComponent(new
ComponentName("com.hqyj.dev.aidltest",
"com.hqyj.dev.aidltest.AppService"));
}
#SuppressLint("DefaultLocale")
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start:
bindService(intent, this,
Context.BIND_AUTO_CREATE);
break;
case R.id.btn_set:
if (binder != null) {
try {
binder.setData(
String.format("the %d times",
++count));
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case R.id.btn_stop:
try {
binder.unregisterCallback(callback);
} catch (RemoteException e) {
e.printStackTrace();
}
unbindService(this);
break;
}
}
#Override
public void onServiceConnected(
ComponentName name, IBinder service) {
binder =
IAppServiceRemoteBinder.Stub.asInterface(service);
Log.d(TAG, "onServiceConnected: " + 1);
try {
binder.registerCallback(callback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
private IRemoteServiceCallback.Stub callback =
new IRemoteServiceCallback.Stub() {
#Override
public void
vlueChanged(String value) throws RemoteException {
Log.e(TAG, "vlueChanged: " + value);
}
};
}
As you see, I called the remote service by using bindService();
It works well, when I push these two apps into an emulator which using Android 7.0 as platform.
But
When I push these app into an real device(using Android 6.0), the flowing mistake happened:
AIDL failed!!
Why??

Related

Targeting 31+ SDK requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent?

I'm trying to run my app on an android 12 device. the app has a GPS functionality.
When I run my app, I get the usual allow GPS pop up on the phone but then as soon as I 'allow' it, the app crashes and I see this error in Android Studio:
2022-10-07 18:08:53.471 11854-11854/com.example.app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.app, PID: 11854
java.lang.RuntimeException: Unable to start service com.marianhello.bgloc.service.LocationServiceImpl#936d3d2 with null: java.lang.IllegalArgumentException: com.example.app: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.
edit:
even though the code is large, I share it as suggested in the comments.
package com.marianhello.bgloc.service;
import android.accounts.Account;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.marianhello.bgloc.Config;
import com.marianhello.bgloc.ConnectivityListener;
import com.marianhello.bgloc.sync.NotificationHelper;
import com.marianhello.bgloc.PluginException;
import com.marianhello.bgloc.PostLocationTask;
import com.marianhello.bgloc.ResourceResolver;
import com.marianhello.bgloc.data.BackgroundActivity;
import com.marianhello.bgloc.data.BackgroundLocation;
import com.marianhello.bgloc.data.ConfigurationDAO;
import com.marianhello.bgloc.data.DAOFactory;
import com.marianhello.bgloc.data.LocationDAO;
import com.marianhello.bgloc.data.LocationTransform;
import com.marianhello.bgloc.headless.AbstractTaskRunner;
import com.marianhello.bgloc.headless.ActivityTask;
import com.marianhello.bgloc.headless.LocationTask;
import com.marianhello.bgloc.headless.StationaryTask;
import com.marianhello.bgloc.headless.Task;
import com.marianhello.bgloc.headless.TaskRunner;
import com.marianhello.bgloc.headless.TaskRunnerFactory;
import com.marianhello.bgloc.provider.LocationProvider;
import com.marianhello.bgloc.provider.LocationProviderFactory;
import com.marianhello.bgloc.provider.ProviderDelegate;
import com.marianhello.bgloc.sync.AccountHelper;
import com.marianhello.bgloc.sync.SyncService;
import com.marianhello.logging.LoggerManager;
import com.marianhello.logging.UncaughtExceptionLogger;
import org.chromium.content.browser.ThreadUtils;
import org.json.JSONException;
import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.containsCommand;
import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.containsMessage;
import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.getCommand;
import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.getMessage;
public class LocationServiceImpl extends Service implements ProviderDelegate, LocationService {
public static final String ACTION_BROADCAST = ".broadcast";
public static final int MSG_ON_ERROR = 100;
public static final int MSG_ON_LOCATION = 101;
public static final int MSG_ON_STATIONARY = 102;
public static final int MSG_ON_ACTIVITY = 103;
public static final int MSG_ON_SERVICE_STARTED = 104;
public static final int MSG_ON_SERVICE_STOPPED = 105;
public static final int MSG_ON_ABORT_REQUESTED = 106;
public static final int MSG_ON_HTTP_AUTHORIZATION = 107;
private static int NOTIFICATION_ID = 1;
private ResourceResolver mResolver;
private Config mConfig;
private LocationProvider mProvider;
private Account mSyncAccount;
private org.slf4j.Logger logger;
private final IBinder mBinder = new LocalBinder();
private HandlerThread mHandlerThread;
private ServiceHandler mServiceHandler;
private LocationDAO mLocationDAO;
private PostLocationTask mPostLocationTask;
private String mHeadlessTaskRunnerClass;
private TaskRunner mHeadlessTaskRunner;
private long mServiceId = -1;
private static boolean sIsRunning = false;
private boolean mIsInForeground = false;
private static LocationTransform sLocationTransform;
private static LocationProviderFactory sLocationProviderFactory;
private class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
#Override
public IBinder onBind(Intent intent) {
logger.debug("Client binds to service");
return mBinder;
}
#Override
public void onRebind(Intent intent) {
logger.debug("Client rebinds to service");
super.onRebind(intent);
}
#Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
logger.debug("All clients have been unbound from service");
return true; // Ensures onRebind() is called when a client re-binds.
}
#Override
public void onCreate() {
super.onCreate();
sIsRunning = false;
UncaughtExceptionLogger.register(this);
logger = LoggerManager.getLogger(LocationServiceImpl.class);
logger.info("Creating LocationServiceImpl");
mServiceId = System.currentTimeMillis();
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
if (mHandlerThread == null) {
mHandlerThread = new HandlerThread("LocationServiceImpl.Thread", Process.THREAD_PRIORITY_BACKGROUND);
}
mHandlerThread.start();
// An Android service handler is a handler running on a specific background thread.
mServiceHandler = new ServiceHandler(mHandlerThread.getLooper());
mResolver = ResourceResolver.newInstance(this);
mSyncAccount = AccountHelper.CreateSyncAccount(this, mResolver.getAccountName(),
mResolver.getAccountType());
String authority = mResolver.getAuthority();
ContentResolver.setIsSyncable(mSyncAccount, authority, 1);
ContentResolver.setSyncAutomatically(mSyncAccount, authority, true);
mLocationDAO = DAOFactory.createLocationDAO(this);
mPostLocationTask = new PostLocationTask(mLocationDAO,
new PostLocationTask.PostLocationTaskListener() {
#Override
public void onRequestedAbortUpdates() {
handleRequestedAbortUpdates();
}
#Override
public void onHttpAuthorizationUpdates() {
handleHttpAuthorizationUpdates();
}
#Override
public void onSyncRequested() {
SyncService.sync(mSyncAccount, mResolver.getAuthority(), false);
}
}, new ConnectivityListener() {
#Override
public boolean hasConnectivity() {
return isNetworkAvailable();
}
});
registerReceiver(connectivityChangeReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
NotificationHelper.registerServiceChannel(this);
}
#Override
public void onDestroy() {
logger.info("Destroying LocationServiceImpl");
// workaround for issue #276
if (mProvider != null) {
mProvider.onDestroy();
}
if (mHandlerThread != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mHandlerThread.quitSafely();
} else {
mHandlerThread.quit(); //sorry
}
}
if (mPostLocationTask != null) {
mPostLocationTask.shutdown();
}
unregisterReceiver(connectivityChangeReceiver);
sIsRunning = false;
super.onDestroy();
}
#Override
public void onTaskRemoved(Intent rootIntent) {
logger.debug("Task has been removed");
// workaround for issue #276
Config config = getConfig();
if (config.getStopOnTerminate()) {
logger.info("Stopping self");
stopSelf();
} else {
logger.info("Continue running in background");
}
super.onTaskRemoved(rootIntent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
// when service was killed and restarted we will restart service
start();
return START_STICKY;
}
boolean containsCommand = containsCommand(intent);
logger.debug(
String.format("Service in [%s] state. cmdId: [%s]. startId: [%d]",
sIsRunning ? "STARTED" : "NOT STARTED",
containsCommand ? getCommand(intent).getId() : "N/A",
startId)
);
if (containsCommand) {
LocationServiceIntentBuilder.Command cmd = getCommand(intent);
processCommand(cmd.getId(), cmd.getArgument());
} else {
// Could be a BOOT-event, or the OS just randomly restarted the service...
startForegroundService();
}
if (containsMessage(intent)) {
processMessage(getMessage(intent));
}
return START_STICKY;
}
private void processMessage(String message) {
// currently we do not process any message
}
private void processCommand(int command, Object arg) {
try {
switch (command) {
case CommandId.START:
start();
break;
case CommandId.START_FOREGROUND_SERVICE:
startForegroundService();
break;
case CommandId.STOP:
stop();
break;
case CommandId.CONFIGURE:
configure((Config) arg);
break;
case CommandId.STOP_FOREGROUND:
stopForeground();
break;
case CommandId.START_FOREGROUND:
startForeground();
break;
case CommandId.REGISTER_HEADLESS_TASK:
registerHeadlessTask((String) arg);
break;
case CommandId.START_HEADLESS_TASK:
startHeadlessTask();
break;
case CommandId.STOP_HEADLESS_TASK:
stopHeadlessTask();
break;
}
} catch (Exception e) {
logger.error("processCommand: exception", e);
}
}
#Override
public synchronized void start() {
if (sIsRunning) {
return;
}
if (mConfig == null) {
logger.warn("Attempt to start unconfigured service. Will use stored or default.");
mConfig = getConfig();
// TODO: throw JSONException if config cannot be obtained from db
}
logger.debug("Will start service with: {}", mConfig.toString());
mPostLocationTask.setConfig(mConfig);
mPostLocationTask.clearQueue();
LocationProviderFactory spf = sLocationProviderFactory != null
? sLocationProviderFactory : new LocationProviderFactory(this);
mProvider = spf.getInstance(mConfig.getLocationProvider());
mProvider.setDelegate(this);
mProvider.onCreate();
mProvider.onConfigure(mConfig);
sIsRunning = true;
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
#Override
public void run() {
mProvider.onStart();
if (mConfig.getStartForeground()) {
startForeground();
}
}
});
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_SERVICE_STARTED);
bundle.putLong("serviceId", mServiceId);
broadcastMessage(bundle);
}
#Override
public synchronized void startForegroundService() {
start();
startForeground();
}
#Override
public synchronized void stop() {
if (!sIsRunning) {
return;
}
if (mProvider != null) {
mProvider.onStop();
}
stopForeground(true);
stopSelf();
broadcastMessage(MSG_ON_SERVICE_STOPPED);
sIsRunning = false;
}
#Override
public void startForeground() {
if (sIsRunning && !mIsInForeground) {
Config config = getConfig();
Notification notification = new NotificationHelper.NotificationFactory(this).getNotification(
config.getNotificationTitle(),
config.getNotificationText(),
config.getLargeNotificationIcon(),
config.getSmallNotificationIcon(),
config.getNotificationIconColor());
if (mProvider != null) {
mProvider.onCommand(LocationProvider.CMD_SWITCH_MODE,
LocationProvider.FOREGROUND_MODE);
}
super.startForeground(NOTIFICATION_ID, notification);
mIsInForeground = true;
}
}
#Override
public synchronized void stopForeground() {
if (sIsRunning && mIsInForeground) {
stopForeground(true);
if (mProvider != null) {
mProvider.onCommand(LocationProvider.CMD_SWITCH_MODE,
LocationProvider.BACKGROUND_MODE);
}
mIsInForeground = false;
}
}
#Override
public synchronized void configure(Config config) {
if (mConfig == null) {
mConfig = config;
return;
}
final Config currentConfig = mConfig;
mConfig = config;
mPostLocationTask.setConfig(mConfig);
ThreadUtils.runOnUiThread(new Runnable() {
#Override
public void run() {
if (sIsRunning) {
if (currentConfig.getStartForeground() == true && mConfig.getStartForeground() == false) {
stopForeground(true);
}
if (mConfig.getStartForeground() == true) {
if (currentConfig.getStartForeground() == false) {
// was not running in foreground, so start in foreground
startForeground();
} else {
// was running in foreground, so just update existing notification
Notification notification = new NotificationHelper.NotificationFactory(LocationServiceImpl.this).getNotification(
mConfig.getNotificationTitle(),
mConfig.getNotificationText(),
mConfig.getLargeNotificationIcon(),
mConfig.getSmallNotificationIcon(),
mConfig.getNotificationIconColor());
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, notification);
}
}
}
if (currentConfig.getLocationProvider() != mConfig.getLocationProvider()) {
boolean shouldStart = mProvider.isStarted();
mProvider.onDestroy();
LocationProviderFactory spf = new LocationProviderFactory(LocationServiceImpl.this);
mProvider = spf.getInstance(mConfig.getLocationProvider());
mProvider.setDelegate(LocationServiceImpl.this);
mProvider.onCreate();
mProvider.onConfigure(mConfig);
if (shouldStart) {
mProvider.onStart();
}
} else {
mProvider.onConfigure(mConfig);
}
}
});
}
#Override
public synchronized void registerHeadlessTask(String taskRunnerClass) {
logger.debug("Registering headless task");
mHeadlessTaskRunnerClass = taskRunnerClass;
}
#Override
public synchronized void startHeadlessTask() {
if (mHeadlessTaskRunnerClass != null) {
TaskRunnerFactory trf = new TaskRunnerFactory();
try {
mHeadlessTaskRunner = trf.getTaskRunner(mHeadlessTaskRunnerClass);
((AbstractTaskRunner) mHeadlessTaskRunner).setContext(this);
} catch (Exception e) {
logger.error("Headless task start failed: {}", e.getMessage());
}
}
}
#Override
public synchronized void stopHeadlessTask() {
mHeadlessTaskRunner = null;
}
#Override
public synchronized void executeProviderCommand(final int command, final int arg1) {
if (mProvider == null) {
return;
}
ThreadUtils.runOnUiThread(new Runnable() {
#Override
public void run() {
mProvider.onCommand(command, arg1);
}
});
}
#Override
public void onLocation(BackgroundLocation location) {
logger.debug("New location {}", location.toString());
location = transformLocation(location);
if (location == null) {
logger.debug("Skipping location as requested by the locationTransform");
return;
}
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_LOCATION);
bundle.putParcelable("payload", location);
broadcastMessage(bundle);
runHeadlessTask(new LocationTask(location) {
#Override
public void onError(String errorMessage) {
logger.error("Location task error: {}", errorMessage);
}
#Override
public void onResult(String value) {
logger.debug("Location task result: {}", value);
}
});
postLocation(location);
}
#Override
public void onStationary(BackgroundLocation location) {
logger.debug("New stationary {}", location.toString());
location = transformLocation(location);
if (location == null) {
logger.debug("Skipping location as requested by the locationTransform");
return;
}
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_STATIONARY);
bundle.putParcelable("payload", location);
broadcastMessage(bundle);
runHeadlessTask(new StationaryTask(location){
#Override
public void onError(String errorMessage) {
logger.error("Stationary task error: {}", errorMessage);
}
#Override
public void onResult(String value) {
logger.debug("Stationary task result: {}", value);
}
});
postLocation(location);
}
#Override
public void onActivity(BackgroundActivity activity) {
logger.debug("New activity {}", activity.toString());
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_ACTIVITY);
bundle.putParcelable("payload", activity);
broadcastMessage(bundle);
runHeadlessTask(new ActivityTask(activity){
#Override
public void onError(String errorMessage) {
logger.error("Activity task error: {}", errorMessage);
}
#Override
public void onResult(String value) {
logger.debug("Activity task result: {}", value);
}
});
}
#Override
public void onError(PluginException error) {
Bundle bundle = new Bundle();
bundle.putInt("action", MSG_ON_ERROR);
bundle.putBundle("payload", error.toBundle());
broadcastMessage(bundle);
}
private void broadcastMessage(int msgId) {
Bundle bundle = new Bundle();
bundle.putInt("action", msgId);
broadcastMessage(bundle);
}
private void broadcastMessage(Bundle bundle) {
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtras(bundle);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
#Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return super.registerReceiver(receiver, filter, null, mServiceHandler);
}
#Override
public void unregisterReceiver(BroadcastReceiver receiver) {
try {
super.unregisterReceiver(receiver);
} catch (IllegalArgumentException ex) {
// if was not registered ignore exception
}
}
public Config getConfig() {
Config config = mConfig;
if (config == null) {
ConfigurationDAO dao = DAOFactory.createConfigurationDAO(this);
try {
config = dao.retrieveConfiguration();
} catch (JSONException e) {
logger.error("Config exception: {}", e.getMessage());
}
}
if (config == null) {
config = Config.getDefault();
}
mConfig = config;
return mConfig;
}
public static void setLocationProviderFactory(LocationProviderFactory factory) {
sLocationProviderFactory = factory;
}
private void runHeadlessTask(Task task) {
if (mHeadlessTaskRunner == null) {
return;
}
logger.debug("Running headless task: {}", task);
mHeadlessTaskRunner.runTask(task);
}
/**
* Class used for the client Binder. Since this service runs in the same process as its
* clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public LocationServiceImpl getService() {
return LocationServiceImpl.this;
}
}
private BackgroundLocation transformLocation(BackgroundLocation location) {
if (sLocationTransform != null) {
return sLocationTransform.transformLocationBeforeCommit(this, location);
}
return location;
}
private void postLocation(BackgroundLocation location) {
mPostLocationTask.add(location);
}
public void handleRequestedAbortUpdates() {
broadcastMessage(MSG_ON_ABORT_REQUESTED);
}
public void handleHttpAuthorizationUpdates() {
broadcastMessage(MSG_ON_HTTP_AUTHORIZATION);
}
/**
* Broadcast receiver which detects connectivity change condition
*/
private BroadcastReceiver connectivityChangeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
boolean hasConnectivity = isNetworkAvailable();
mPostLocationTask.setHasConnectivity(hasConnectivity);
logger.info("Network condition changed has connectivity: {}", hasConnectivity);
}
};
private boolean isNetworkAvailable() {
ConnectivityManager cm =
(ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
}
public long getServiceId() {
return mServiceId;
}
public boolean isBound() {
LocationServiceInfo info = new LocationServiceInfoImpl(this);
return info.isBound();
}
public static boolean isRunning() {
return sIsRunning;
}
public static void setLocationTransform(#Nullable LocationTransform transform) {
sLocationTransform = transform;
}
public static #Nullable LocationTransform getLocationTransform() {
return sLocationTransform;
}
}
Add the following to your build.gradle(app) dependencies.
dependencies {
// For Java
implementation 'androidx.work:work-runtime:2.7.1'
// For Kotlin
implementation 'androidx.work:work-runtime-ktx:2.7.1'
}
Some of dependency targets 31+ but didn't updated their flag. If you are not using any pending intent with such flag, we need to add WorkManager dependency in our build.gradle(app) and that's it.
// Kotlin + coroutines
val work_version = "2.7.1"
implementation("androidx.work:work-runtime-ktx:$work_version")
Google Mobile Ads SDK also start managing their library after 20.4.0 release.
This release and all previous versions require an explicit dependency
on androidx.work:work-runtime:2.7.0 to fix a bug causing app crashes
on Android S with the following stack trace.
solved the problem by adding:
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.work:work-runtime-ktx:2.7.1'
implementation 'com.google.firebase:firebase-messaging:23.0.4'
implementation 'com.google.firebase:firebase-iid:21.1.0'
implementation platform('com.google.firebase:firebase-bom:30.0.0')
implementation 'androidx.work:work-runtime-ktx:2.7.1'
implementation 'com.firebaseui:firebase-ui-auth:8.0.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation("com.google.android.gms:play-services-auth:20.2.0")
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'com.google.firebase:firebase-database-ktx:20.0.5'
implementation 'com.google.firebase:firebase-auth-ktx:21.0.3'
implementation 'com.google.firebase:firebase-auth:21.0.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

Why is my service getting destroyed while mediaplayers are looping?

In my App's MainActivity I am playing some sounds using MediaPlayers and in The onPause() in that activity I realease the players and I am starting a service to continue playing these sounds by creating them again and I send the resourcesNames and the Volume of every player from the activity to the service , I am using setLooping() to make these sounds loop and I am running the service in a new thread (outside the main thread) uding Handler and HandlerThread.
when the activity gets paused the service starts and the sounds are playing but the problem is that they just loop for 3 times and then the service is getting destroyed without stopService() or stopSelf() are being called and also without exiting from the app(the app still in the recent apps)?
Here the sevice's code :
package com.example.naturesounds;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
public class SoundSPlayingService extends Service implements MediaPlayer.OnPreparedListener
{
private static final String PACKAGE_NAME = "com.example.naturesounds";
private float playerVolume = 0.0f;
serviceHandler serviceHandler;
Looper serviceLooper;
HandlerThread thread;
Intent intent;
Bundle playersVolume = new Bundle() ;
ArrayList<String> runningResourceNames = new ArrayList<>();
HashMap<String, MediaPlayer> playersMap = new HashMap<>();
#Override
public void onCreate() {
thread = new HandlerThread("ServiceThread", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
serviceLooper = thread.getLooper();
serviceHandler = new serviceHandler(serviceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message message = serviceHandler.obtainMessage();
message.obj = intent;
serviceHandler.sendMessage(message);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
#Override
public void onDestroy() {
releasePlayers();
Log.d("serviceLifeCycle","onDestroy() is running");
}
public void createPlayer(String resourceName)
{
playersMap.put(resourceName,new MediaPlayer());
try {
playersMap.get(resourceName).setDataSource(getApplicationContext(),
Uri.parse("android.resource://com.example.naturesounds/raw/" + resourceName));
playersMap.get(resourceName).setOnPreparedListener(this);
playersMap.get(resourceName).prepareAsync();
playersMap.get(resourceName).setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
setPlayerLooping(resourceName);
setPlayerVolume(resourceName);
}
catch (IOException e1)
{
e1.printStackTrace();
}
catch (IllegalArgumentException e2)
{
e2.printStackTrace();
}
}
public void releasePlayers()
{
for(int i=0 ; i<runningResourceNames.size(); i++)
{
String resourceName = runningResourceNames.get(i);
if(playersMap.get(resourceName) != null)
{
playersMap.get(resourceName).release();
playersMap.put(resourceName,null);
}
}
}
public void setPlayerVolume(String resourceName)
{
playerVolume = playersVolume.getFloat(resourceName);
Log.d("playerVolume",String.valueOf(playerVolume));
playersMap.get(resourceName).setVolume(playerVolume,playerVolume);
}
public void setPlayerLooping(String resourceName)
{
playersMap.get(resourceName).setLooping(true);
}
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
class serviceHandler extends Handler
{
public serviceHandler(Looper looper)
{
super(looper);
}
#Override
public void handleMessage(#NonNull Message msg) {
Log.d("serviceHandlerChecking","handleMessage() is executing");
intent = (Intent) msg.obj;
runningResourceNames = intent.getStringArrayListExtra(PACKAGE_NAME + ".MainActivity.runningResourceNames");
playersVolume = intent.getBundleExtra(PACKAGE_NAME + ".MainActivity.playersVolume");
for(int i=0; i<runningResourceNames.size(); i++)
{
String resourceName = runningResourceNames.get(i);
createPlayer(resourceName);
}
}
}
}

ADK toolkit Android+Arduino store variable issue

I'm writing a program which is supposed to run "forever". The program is Android application for tablet which exchanges data with Arduino. I have already implemented the code for Arduino and Android, and it exchanges data very well. However, after 2 cycles of work, my instance of AdkManager becomes NULL. As I've read before, Android will null variables from time to time because it has limited resources. However here's the problem - the AdkManager has confirmed bug that once it has been closed, it can't be reopened. Thus I can't re-initiate the AdkManager instance and I need to store it somehow. So far I've been using Application extension. The code is below:
MyApplication:
package org.udoo.androidadkdemobidirect;
import android.app.Application;
import android.content.Context;
import android.hardware.usb.UsbManager;
import me.palazzetti.adktoolkit.AdkManager;
/**
* Created by admin on 8/18/16.
*/
public class MyApplication extends Application {
private String someVariable;
public String getSomeVariable() {
return someVariable;
}
public void setSomeVariable(String someVariable) {
this.someVariable = someVariable;
}
public static class sAdkManager{
private static sAdkManager ourInstance = null;
public static sAdkManager getInstance() {
if (ourInstance==null)
ourInstance = new sAdkManager();
return ourInstance;
}
private static AdkManager mAdkManager = null;
public void write(String s){
mAdkManager.writeSerial(s);
}
public String read(){
return mAdkManager.readSerial();
}
public void open(){
mAdkManager.open();
}
public void close(){
mAdkManager.close();
}
public boolean checkNull(){
return mAdkManager==null;
}
public static void init(Context context){
if(mAdkManager==null) {
mAdkManager = new AdkManager((UsbManager) context.getSystemService(Context.USB_SERVICE));
context.registerReceiver(mAdkManager.getUsbReceiver(), mAdkManager.getDetachedFilter());
}
}
private sAdkManager() {
}
}
}
MainActivity:
package org.udoo.androidadkdemobidirect;
import me.palazzetti.adktoolkit.AdkManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.TextView;
import android.widget.ToggleButton;
//import org.udoo.androidadkdemobidirect.sAdkManager;
public class MainActivity extends Activity{
// private static final String TAG = "UDOO_AndroidADKFULL";
private static String mAdkManager=null;
private ToggleButton buttonLED;
private TextView distance;
private AdkReadTask mAdkReadTask;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//
//// register a BroadcastReceiver to catch UsbManager.ACTION_USB_ACCESSORY_DETACHED action
// registerReceiver(mAdkManager.getUsbReceiver(), mAdkManager.getDetachedFilter());
buttonLED = (ToggleButton) findViewById(R.id.toggleButtonLed);
distance = (TextView) findViewById(R.id.textViewIntro);
// mAdkManager.open();
TextView tv = (TextView) findViewById(R.id.ppm);
if (mAdkManager==null){
tv.setText("ADK is null. init()");
mAdkManager = new String ("sometext");
}
else{
tv.setText("ADK is not null.");
}
if (MyApplication.sAdkManager.getInstance().checkNull()) {
distance.setText("Null before init");
MyApplication.sAdkManager.init(this);
}
if (MyApplication.sAdkManager.getInstance().checkNull()) {
distance.setText("Null after init");
}
MyApplication.sAdkManager.getInstance().open();
mAdkReadTask = new AdkReadTask();
mAdkReadTask.execute();
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onDestroy() {
MyApplication.sAdkManager.getInstance().close();
// unregisterReceiver(mAdkManager.getUsbReceiver());
super.onDestroy();
}
// ToggleButton method - send message to SAM3X
public void blinkLED(View v){
if (buttonLED.isChecked()) {
TextView tvdbg = (TextView) findViewById(R.id.ppm);
tvdbg.setText("send 1");
// writeSerial() allows you to write a single char or a String object.
//mAdkManager.writeSerial("1");
MyApplication.sAdkManager.getInstance().write("1");
// mAdkManager.writeSerial("8");
} else {
//mAdkManager.writeSerial("0");
MyApplication.sAdkManager.getInstance().write("0");
}
}
/*
* We put the readSerial() method in an AsyncTask to run the
* continuous read task out of the UI main thread
*/
private class AdkReadTask extends AsyncTask<Void, String, Void> {
private boolean running = true;
public void pause(){
running = false;
}
protected Void doInBackground(Void... params) {
// Log.i("ADK demo bi", "start adkreadtask");
while(running) {
// if (mAdkManager.serialAvailable())
// publishProgress(mAdkManager.readSerial()) ;
publishProgress(MyApplication.sAdkManager.getInstance().read());
}
return null;
}
protected void onProgressUpdate(String... progress) {
distance.setText("You put "+((int)progress[0].charAt(0)-48) + " iqos butts\tRFID OK");
next();
// Log.i(TAG, "received: " + (int)progress[0].charAt(0));
}
}
private void next() {
final Intent intent = new Intent(this, BRActivity.class );
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
mAdkReadTask.pause();
mAdkReadTask = null;
startActivity(intent);
}
},
3000);
}
}
There are just 2 Activities for now - MainActivity and BRActivity. BRActivity is just a view with "return" button which comes back to MainActivity.
Also what I find interesting - I output the readSerial in TextView to see what I got in reader thread. However on cycle#2 i don't get any output to TextView, but Activity still changes to the next one.
[EDIT]
Apparently the problem was solved when the thread was nulling. However, I still don't get the text update, but I magically get to another screen. Please advice.

Memory leak in service android

I am implementing the bound service for the socket.io implementation in android for single socket maintenance to connect with nodejs server by this Gottox library. When I implementing this the memory of the service is not stable like while on starting of the service it takes around 30MB to 40MB, after some time it also leads to 200MB. So I thought it may be memory leak. But i don't get any single clue to find it.
Codes
DemoActivity.java
import org.json.JSONException;
import org.json.JSONObject;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import com.actionbarsherlock.app.SherlockActivity;
import com.devspark.appmsg.AppMsg;
import com.devspark.appmsg.AppMsg.Style;
import com.nuappz.Demo.DemoService.MyLocalBinder;
import com.nuappz.Demo.handler.ResponseHandler;
import com.nuappz.Demo.helper.MySharedPreferences;
public class DemoActivity extends SherlockActivity {
MySharedPreferences pref;
DemoService socketService;
boolean isBound;
EditText name, mobile_no, email, password;
Button Demo;
Style style_alert, style_success;
JSONObject json_Demo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_Demo);
isBound = false;
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
// start the bind service
if (!isBound) {
bindService(new Intent(DemoActivity.this,
DemoService.class), myConnection,
Context.BIND_AUTO_CREATE);
isBound = true;
startService(new Intent(this, DemoService.class));
socketService = DemoService.getInstance();
}
}
public ServiceConnection myConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
isBound = false;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
socketService = ((MyLocalBinder) service).getService();
isBound = true;
}
};
protected void onDestroy() {
if (isBound) {
// Disconnect from an application service. You will no longer
// receive calls as the service is restarted, and the service is
// now allowed to stop at any time.
unbindService(myConnection);
isBound = false;
}
stopService(new Intent(DemoActivity.this, DemoService.class));
super.onDestroy();
}
}
DemoService.java
import io.socket.IOAcknowledge;
import io.socket.IOCallback;
import io.socket.SocketIO;
import io.socket.SocketIOException;
import java.net.MalformedURLException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import com.nuappz.Demo.handler.ResponseHandler;
import com.nuappz.Demo.helper.MySharedPreferences;
/*
* This class is Background service for the Blood Drop application
*/
public class DemoService extends Service {
private static final String serverUrl = "http://nuappzdev.hello.com:8080/";
private static SocketIO socket;
private static DemoService instance;
private static ResponseHandler handler;
public boolean bound;
JSONObject jobj_in = new JSONObject();
#Override
public void onCreate() {
// TODO Auto-generated method stub
Log.d("Service", "Started");
super.onCreate();
// connecting socket
try {
DemoService.initInstance();
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
public DemoService() {
}
public static DemoService getInstance() {
return instance;
}
// start the service to handle the functions
public int onStartCommand(Intent intent, int flags, int startId) {
// HandleReceiveRequest();
return START_STICKY;
}
// Stop the services
public void onDestroy() {
Log.d("Service", "Stopped");
getSocket().disconnect();
}
// Binder class initialize
public class MyLocalBinder extends Binder {
DemoService getService() {
return DemoService.this;
}
}
private final IBinder myBinder = new MyLocalBinder();
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
bound = true;
return myBinder;
}
// initiate the socket connection
public static void initInstance() throws MalformedURLException {
if (instance == null) {
instance = new DemoService();
if (DemoService.getSocket() == null) {
DemoService.setSocket(new SocketIO());
}
DemoService.connectIO();
}
}
// Method to get socket
public static SocketIO getSocket() {
return socket;
}
// Method to set socket
public static void setSocket(SocketIO socket) {
DemoService.socket = socket;
}
// Method to ConnectIO to server
public static void connectIO() throws MalformedURLException {
try {
SocketIO.setDefaultSSLSocketFactory(SSLContext.getDefault());
} catch (NoSuchAlgorithmException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
DemoService.getSocket().connect(serverUrl, new IOCallback() {
#Override
public void onMessage(JSONObject json, IOAcknowledge ack) {
// TODO Auto-generated method stub
}
#Override
public void onMessage(String data, IOAcknowledge ack) {
}
#Override
public void onError(SocketIOException socketIOException) {
Log.d("Connection:", "Error in Connection");
}
#Override
public void onDisconnect() {
// TODO Auto-generated method stub
Log.d("Connection:", "disConnected");
}
#Override
public void onConnect() {
Log.d("Connection:", "Connected");
}
#Override
// Method to getting response from server
public void on(String event, IOAcknowledge ack, Object... args) {
JSONArray jarr_args = new JSONArray();
JSONObject jobj_in = new JSONObject();
try {
jarr_args.put(args[0]);
jobj_in = jarr_args.getJSONObject(0);
jobj_in.put("event", event);
Log.d("jobject: event", jobj_in.getString("event"));
try {
handler.handleObject(jobj_in);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
}
// Method to send request to server
public static void emit(String event, Object args,
ResponseHandler responseHandler) throws MalformedURLException {
handler = responseHandler;
if (DemoService.getSocket().isConnected() == false) {
DemoService.getSocket().reconnect();
}
DemoService.getSocket().emit(event, args);
}
// Method to send request to server with Acknowledge
public static void emitWithAcknowledge(String event, Object args)
throws MalformedURLException {
if (DemoService.getSocket().isConnected() == false) {
DemoService.getSocket().reconnect();
}
DemoService.getSocket().emit(event, new IOAcknowledge() {
#Override
public void ack(Object... args) {
// TODO Auto-generated method stub
}
}, args);
}
}
}
What are the chances of memory leak in this code.
You needs to unbind service in onStop of your activity and you should never call stopService from your activity. Let Android do the handling of life cycle of your service.

Post on Facebook Fan page through app

I've managed to create a class which posts on my facebook wall. But how do I change the code to share on my facebook fanpage instead? I can't find anything on google or stack overflow...
here is the class which shares on facebook:
package com.celticwolf.blahblah; <--- changed
import com.facebook.android.*;
import com.facebook.android.Facebook.DialogListener;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.Toast;
public class ShareOnFacebook extends Activity{
private static final String APP_ID = "35255389027859"; <--- changed
private static final String[] PERMISSIONS = new String[] {"publish_stream"};
private static final String TOKEN = "access_token";
private static final String EXPIRES = "expires_in";
private static final String KEY = "facebook-credentials";
private Facebook facebook;
private String messageToPost;
public boolean saveCredentials(Facebook facebook) {
Editor editor = getApplicationContext().getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();
editor.putString(TOKEN, facebook.getAccessToken());
editor.putLong(EXPIRES, facebook.getAccessExpires());
return editor.commit();
}
public boolean restoreCredentials(Facebook facebook) {
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences(KEY, Context.MODE_PRIVATE);
facebook.setAccessToken(sharedPreferences.getString(TOKEN, null));
facebook.setAccessExpires(sharedPreferences.getLong(EXPIRES, 0));
return facebook.isSessionValid();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
facebook = new Facebook(APP_ID);
restoreCredentials(facebook);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.facebook_dialog);
String facebookMessage = getIntent().getStringExtra("facebookMessage");
if (facebookMessage == null){
facebookMessage = "Test wall post";
}
messageToPost = facebookMessage;
}
public void doNotShare(View button){
finish();
}
public void share(View button){
if (! facebook.isSessionValid()) {
loginAndPostToWall();
}
else {
postToWall(messageToPost);
}
}
public void loginAndPostToWall(){
facebook.authorize(this, PERMISSIONS, Facebook.FORCE_DIALOG_AUTH, new LoginDialogListener());
}
public void postToWall(String message){
new MyAsyncTask().execute(message);
}
class MyAsyncTask extends AsyncTask<String,Void,Boolean>
{
public Boolean doInBackground(String ...message){
Bundle parameters = new Bundle();
parameters.putString("message", message[0]);
parameters.putString("description", "topic share");
try {
facebook.request("me");
String response = facebook.request("me/feed", parameters, "POST"); <--- I think here is the crucial part
Log.d("Tests", "got response: " + response);
if (response == null || response.equals("") ||
response.equals("false")) {
return Boolean.FALSE;
}
else {
return Boolean.TRUE;
}
} catch (Exception e) {
e.printStackTrace();
return Boolean.FALSE;
}
}
public void onPostExecute(Boolean result){
if(result == Boolean.TRUE){
showToast("posted successfully");
}else{
showToast("couldn't post to FB.");
}
finish();
}
}
class LoginDialogListener implements DialogListener {
public void onComplete(Bundle values) {
saveCredentials(facebook);
if (messageToPost != null){
postToWall(messageToPost);
}
}
public void onFacebookError(FacebookError error) {
showToast("Authentication with Facebook failed!");
finish();
}
public void onError(DialogError error) {
showToast("Authentication with Facebook failed!");
finish();
}
public void onCancel() {
showToast("Authentication with Facebook cancelled!");
finish();
}
}
private void showToast(String message){
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
}
thank you!
String response = facebook.request("me/feed", parameters, "POST");
me/feed becomes PAGE_ID/feed:
String response = facebook.request("PAGE_ID/feed", parameters, "POST");
Learn how to use the Graph API here: https://developers.facebook.com/docs/reference/api/
String response = facebook.request("PAGE_ID/feed", parameters, "POST");
This will be work but for posting in a Page you must need access token with Mangage_Pages and Publish_stream permissions.

Categories

Resources