In my IM application there is a chats window where loader.onContentChanged is called whenever message is sent or received.
It works perfectly fine but sometimes randomly even after calling 'loader.onContentChanged' there are no calls to onCreateLoader and onLoadFinished.
This is very random but users get a weird behavior wherein they click send and message disappears.
Logs indicate that initLoader is called but logs from onCreateLoader are missing.
I am using recycler view to show list of messages, but that code is not a issue.
I don't know the reason but this is how the defect got fixed:
Old code:
private void initLoader(){
LoaderManager.LoaderCallbacks<Cursor> callbacks = this;
LoaderManager lm = getSupportLoaderManager();
if (mLoader == null) {
mLoader = lm.initLoader(LOADER_ID, null, callbacks);
} else {
mLoader.onContentChanged();
}
}`
New code:
`private void initLoader() {
LoaderManager.LoaderCallbacks<Cursor> callbacks = this;
LoaderManager lm = getSupportLoaderManager();
if (mLoader == null) {
mLoader = lm.initLoader(LOADER_ID, null, callbacks);
} else {
if(mLoader.isStarted()) {
mLoader.onContentChanged();
}else {
try {
mLoader = lm.restartLoader(LOADER_ID, null, callbacks);
}catch( Exception e ){
mLoader = null;
initLoader();
}
}
}
}
Related
The code I tried to start callback for my background service.
public class PushService extends JobIntentService implements MethodCallHandler {
private static FlutterEngine backgroundFlutterEngine = null;
private void startPushService(Context context) {
synchronized (serviceStarted) {
if (backgroundFlutterEngine == null) {
final Long callbackHandle = PushStore.getInstance().getPreferenceLongValue(
PushPlugin.CALLBACK_DISPATCHER_ID, 0L);
if (callbackHandle == 0L) {
Log.e(TAG, "Fatal: no callback registered.");
return;
}
final FlutterCallbackInformation callbackInfo =
FlutterCallbackInformation.lookupCallbackInformation(callbackHandle);
if (callbackInfo == null) {
Log.e(TAG, "Fatal: failed to find callback info.");
return;
}
Log.i(TAG, "Starting PushService...");
backgroundFlutterEngine = new FlutterEngine(context);
DartCallback args = DartCallback(context.getAssets(), FlutterMain.findAppBundlePath(context), callbackInfo);
backgroundFlutterEngine.getDartExecutor().executeDartCallback(args);
...
}
}
}
}
But the FlutterMain is deprecated, how to do the new way to execute dart callback?
FlutterMain has been replaced by FlutterLoader, from what I understand.
Edit:
On the FlutterMain documentation page, right underneath the first horizontal divider, it mentions this replacement.
My requirements are to poll a web service every fixed interval. After instantiating the handler as a private field of the class, I'm doing this in the onCreate method of the main activity:
if (handler != null) {
handler.post(new Runnable() {
#Override
public void run() {
String sessionId = SharedPreferencesHelper.getSessionId(MainActivity.this);
if (sessionId != null && !sessionId.isEmpty()) {
if (Utils.isNetworkAvailable(MainActivity.this)) {
restHandler.getUnreadMessages(sessionId);
}
handler.postDelayed(this, Constants.CHAT_POLLING_REFRESH_DELAY);
} else {
handler.removeCallbacks(this);
}
}
});
}
Then onDestroy:
if (handler != null) {
handler.removeCallbacksAndMessages(null);
handler = null;
}
The backend is complaing that sometimes the sessionId passed is empty or null. How can this be possible? I'm using retrofit. Bonus question: can I have problems with MainActivity.this context reference when app goes in background?
I'm making a media controller app similar to this example made by google. https://github.com/googlesamples/android-media-controller
However, I want to make a function that can resume playing music or pause given package name. I managed to return a list of package names.
PS. I'm using react native that's why I need a fucntion that I can call from the react side.
public void getMediaApps (Callback callback) {
// = getPackageManager();
ArrayList<MediaAppDetails> mediaApps = new ArrayList<MediaAppDetails>();
Intent mediaBrowserIntent = new Intent(MediaBrowserServiceCompat.SERVICE_INTERFACE);
List<ResolveInfo> services = packageManager.queryIntentServices(
mediaBrowserIntent,
PackageManager.GET_RESOLVED_FILTER
);
if (services != null && !services.isEmpty()) {
for (ResolveInfo info : services) {
mediaApps.add(
new MediaAppDetails(info.serviceInfo, packageManager, resources)
);
}
}
WritableArray waPackagenames = Arguments.createArray();
// ArrayList<String> packagenames = ArrayList<String>()
if(mediaApps != null && !mediaApps.isEmpty()){
for(MediaAppDetails mediaApp : mediaApps){
waPackagenames.pushString(mediaApp.packageName);
}
}
callback.invoke(waPackagenames);
}
I've been trying to do this for 3 days now, but no luck.
Probably won't make such of a difference but this is where I got so far with the play function.
#ReactMethod
public void play (String packageName) {
PackageManager pm = this.packageManager;
Resources res = this.resources;
ServiceInfo serviceInfo = MediaAppDetails.findServiceInfo(packageName, pm);
mMediaAppDetails = new MediaAppDetails(serviceInfo, pm, res);
MediaSessionCompat.Token token = mMediaAppDetails.sessionToken;
if (token == null) {
if (mMediaAppDetails.componentName != null) {
mBrowser = new MediaBrowserCompat(this.reactContext, mMediaAppDetails.componentName,
new MediaBrowserCompat.ConnectionCallback() {
#Override
public void onConnected() {
setupMediaController();
// mBrowseMediaItemsAdapter.setRoot(mBrowser.getRoot());
}
#Override
public void onConnectionSuspended() {
//TODO(rasekh): shut down browser.
// mBrowseMediaItemsAdapter.setRoot(null);
}
#Override
public void onConnectionFailed() {
showToastAndFinish("connection failed .. shit!");
}
}, null);
mBrowser.connect();
} else if (mMediaAppDetails.sessionToken != null) {
setupMediaController();
}
token = mBrowser.getSessionToken();
Toast.makeText(this.reactContext, "no token can't open controller", Toast.LENGTH_SHORT).show();
// toast
}
// Toast.makeText(this.reactContext, "found token", Toast.LENGTH_SHORT).show();
if(mBrowser == null )mBrowser = new MediaBrowserCompat(this.reactContext, new ComponentName(packageName, "MainActivity"), null, null);
MediaControllerCompat.TransportControls transportControls;
try{
mController = new MediaControllerCompat(this.reactContext, token);
if(mController!= null) {
transportControls = mController.getTransportControls();
transportControls.play();
}
}catch(Exception E){
Log.w("Error",E);
Log.w("Error","couldn't create mediaControllerCompat");
// System.out.println(E);
// System.out.println("couldn't create mediaControllerCompat");
}
}
Is private copy ofBundle is passed to Fragment and the parent Activity?
I try to save key,value pair in onSaveInstanceState of Fragment and try to retrieve it in the onCreate of Activity. It is absent there.
But there also seems to be a connection between the two.
When I pass null to super.onCreate of Activity the Bundle passed to Fragment's onCreate is also null.
Bundle wich is sent to onCreate for Activity and Fragment are completely different. If you're sending null to super.onCreate -> activity will recreate all the fragments from the scratch. So your Fragment will receive null -> also. Cause this is NEW fragment
This is a part of code of FragmentActivity:
/**
* Perform initialization of all fragments and loaders.
*/
#SuppressWarnings("deprecation")
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mFragments.restoreLoaderNonConfig(nc.loaders);
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
// Check if there are any pending onActivityResult calls to descendent Fragments.
if (savedInstanceState.containsKey(NEXT_CANDIDATE_REQUEST_INDEX_TAG)) {
mNextCandidateRequestIndex =
savedInstanceState.getInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG);
int[] requestCodes = savedInstanceState.getIntArray(ALLOCATED_REQUEST_INDICIES_TAG);
String[] fragmentWhos = savedInstanceState.getStringArray(REQUEST_FRAGMENT_WHO_TAG);
if (requestCodes == null || fragmentWhos == null ||
requestCodes.length != fragmentWhos.length) {
Log.w(TAG, "Invalid requestCode mapping in savedInstanceState.");
} else {
mPendingFragmentActivityResults = new SparseArrayCompat<>(requestCodes.length);
for (int i = 0; i < requestCodes.length; i++) {
mPendingFragmentActivityResults.put(requestCodes[i], fragmentWhos[i]);
}
}
}
}
if (mPendingFragmentActivityResults == null) {
mPendingFragmentActivityResults = new SparseArrayCompat<>();
mNextCandidateRequestIndex = 0;
}
mFragments.dispatchCreate();
}
I'm using loaders to handle internet connections and it's working fine. Now I want to control the duration of the connection attempt, in other words to set a timeout.
I'm using the following code:
in onCreate method:
Uri SearchUri =
Uri.parse(urlString);
Bundle args = new Bundle();
args.putParcelable(ARGS_URI, SearchUri);
// Initialize the Loader.
getLoaderManager().initLoader(LOADER_SEARCH,
args, this);
The other methods:
#Override
public Loader<RESTLoader.RESTResponse> onCreateLoader(int i, Bundle args) {
a = args;
Log.v("TESTING","in OnCreateLoader");
if (args != null && args.containsKey(ARGS_URI)){
Log.v("TESTING","in OnCreateLoader, getting communication key");
Uri action = args.getParcelable(ARGS_URI);
return new RESTLoader(this, RESTLoader.HTTPVerb.GET,action);
}
return null;
}
#Override
public void onLoadFinished(Loader<RESTLoader.RESTResponse> loader, RESTLoader.RESTResponse data) {
int code = data.getCode();
String json = data.getData();
if (code == 200){
Log.v("TESTING","Inside onLoadFinished, code = 200");
}
else {
Log.v("TESTING","Inside onLoadFinished, code != 200");
Toast.makeText(getApplicationContext(), "Connection Problem", Toast.LENGTH_SHORT).show();
}
// Check to see if we got an HTTP 200 code and have some data.
if (code == 200 && !json.equals("")) {
...
}
}
So how can I set a timeout on the loader? For instance give a 10 sec duration, if there's no response yet, stop the attempt.