Start fragment in landscape mode - java

Thanks in advance for your help.
I'm trying to load one of my fragments which is loaded from a drawerlayout activity in landscape mode by invoking
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
The fragment does load in landscape mode. I also have a runnable thread which starts from onStart() after the fragment binds to a background service and the runnable is killed onStop().
private final Runnable initCommands = new Runnable() {
#Override
public void run() {
Log.d(TAG,"running thread");
if(isServiceBound) {
//some code...
new Handler().postDelayed(initCommands,800);
}
}
};
The runnable is launched after the fragment is bound to service:
private ServiceConnection serviceConn = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
ELMScanner.ELMScannerServiceBinder serviceBinder = (ELMScanner.ELMScannerServiceBinder) binder;
scanner = serviceBinder.getService();
isServiceBound = true;
if (scanner.isConnected()) {
Log.d(TAG,"init commands");
new Handler().post(initCommands);
}
}
public void onServiceDisconnected(ComponentName className) {
isServiceBound = false;
scanner.resetCommands();
}
};
#Override
public void onStop() {
super.onStop();
Log.d(TAG,"onStop");
FRAGMENT_RUNNING = 0;
if (isServiceBound) {
getActivity().getApplicationContext().unbindService(serviceConn);
Log.d(TAG,"unbinding service");
isServiceBound = false;
scanner.resetCommands();
}
}
The problem I'm facing is coming from setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE). For some reason this method reloads the fragment not killing the first thread and as a result there are two instances of the thread running.
07-30 18:38:53.054 2940-2940/com.package/FuelFragment﹕ on Attach
07-30 18:38:53.054 2940-2940/com.package/FuelFragment﹕ onCreate
07-30 18:38:53.054 2940-2940/com.package/FuelFragment﹕ onCreatView
07-30 18:38:53.054 2940-2940/com.package/FuelFragment﹕ onstart
07-30 18:38:53.062 2940-2940/com.package/FuelFragment﹕ onStop
07-30 18:38:53.062 2940-2940/com.package/FuelFragment﹕ on Detach
07-30 18:38:53.070 2940-2940/com.package/ApplicationPackageManager﹕ cscCountry is not German : THR
07-30 18:38:53.070 2940-2940/com.package/FuelFragment﹕ on Attach
07-30 18:38:53.070 2940-2940/com.package/FuelFragment﹕ onCreate
07-30 18:38:53.242 2940-2940/com.package/FuelFragment﹕ onCreatView
07-30 18:38:53.250 2940-2940/com.package/FuelFragment﹕ onstart
07-30 18:38:53.257 2940-2940/com.package/FuelFragment﹕ init commands
07-30 18:38:53.257 2940-2940/com.package/FuelFragment﹕ init commands
07-30 18:38:53.328 2940-2940/com.package/FuelFragment﹕ running thread
My question: is there an alternative to setRequestedOrientation() that does not reload fragment?
Thanks.
Solution:
In case you're wondering how to fix this.
In the method onStart(), add the condition
int orientation = getResources().getConfiguration().orientation;
if (orientation == 2) {
Intent serviceIntent = new Intent(getActivity(), ELMScanner.class);
getActivity().getApplicationContext().bindService(serviceIntent, serviceConn, Context.BIND_AUTO_CREATE);
}
This will ensure that the fragment only binds to service and runs the thread when it starts on landscape mode. Also make sure to add
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
#Override
public void onDetach() {
super.onDetach();
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}

AFAIK, no. Orientation changes need to restart the Activity/Fragment so the system gets a chance to reload all the resources.
From the docs for setRequestedOrientation:
Change the desired orientation of this activity. If the activity is
currently in the foreground or otherwise impacting the screen
orientation, the screen will immediately be changed (possibly causing
the activity to be restarted). Otherwise, this will be used the next
time the activity is visible.
Your code should be able to handle being destroyed and recreated without messing up and creating two threads. It's not clear what you're trying to accomplish so I can't give you any further suggestions, but properly handling the activity lifecycle is necessary.

Related

Cannot Update Image from Runnable - Android

I have the following class that implements runnable. When I start the thread I get an error preventing me from updating the image. I have tried setting the image as you will see in the runOnUiThread function but it doesn't work. I would like to ultimately update the image every x seconds but I cannot seem to do this unless I put the thread inside the main activity class. Once I move it out nothing I try works.
public class Test implements Runnable {
MainActivity activity;
ArrayList<Media> media = new ArrayList<Media>();
public Test(MainActivity activity){
this.activity = activity;
}
public void run(){
activity.runOnUiThread(new Runnable(){
#Override
public void run() {
ImageView img= (ImageView) activity.findViewById(R.id.imageView1);
img.setImageResource(R.drawable.ic_launcher_foreground);
}
});
}
}
In MainActivity.java's onCreate() function I start the thread.
Test p = new Test(this);
Thread t = new Thread(p);
t.start();
Then my error:
2019-10-31 19:26:47.274 5407-5407/com.desotomatrix.digitalsignageplayer E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.desotomatrix.digitalsignageplayer, PID: 5407
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setImageResource(int)' on a null object reference
at com.desotomatrix.digitalsignageplayer.Test$1.run(Test.java:30)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:209)
at android.app.ActivityThread.main(ActivityThread.java:7021)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:486)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:872)

java.lang.IllegalStateException: AssetManager has been finalized

I went to sleep yesterday with my app working and today when I tried to run it won't start at all. As soon as I try to open it crashes with a java.lang.IllegalStateException. I've gone several commits back in my code just to rule out it was something I did recently and still. This makes no sense, how can an app just stop working over night? I've looked for the error in the internet and there is not a lot of useful information about it. Is this really an odd error?
Here's the complete stack trace:
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.IllegalStateException: AssetManager has been finalized!
at android.os.Parcel.readException(Parcel.java:1439)
at android.os.Parcel.readException(Parcel.java:1385)
at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:1947)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1419)
at android.app.Activity.startActivityForResult(Activity.java:3390)
at android.app.Activity.startActivity(Activity.java:3583)
at com.android.launcher2.Launcher.startActivity(Launcher.java:2442)
at com.android.launcher2.Launcher.startActivitySafely(Launcher.java:2469)
at com.android.launcher2.AppsCustomizePagedView.onClick(AppsCustomizePagedView.java:584)
at android.view.View.performClick(View.java:4240)
at android.view.View$PerformClick.run(View.java:17721)
at android.os.Handler.handleCallback(Handler.java:730)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5136)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Since like I said it doesn't seem to be anything that I did I'm not sure what code to post. But given that the app crashes on start here's the code for the two main classes that are supposed to start first:
App
public class App extends Application {
private static App instance;
private static final String TAG = "Starter";
#Override
public void onCreate() {
super.onCreate();
instance = this;
// Enable Local Datastore.
Parse.enableLocalDatastore(this);
//TODO: Register subclasses
// ParseObject.registerSubclass(Challenge.class);
//Parse server
Log.d(TAG, "Initializing Parse");
Parse.initialize(new Parse.Configuration.Builder(this)
.applicationId(getString(R.string.parse_app_id))
.clientKey(getString(R.string.parse_client_key))
.server(getString(R.string.server_address)).build()
);
//Facebook
if (AccessToken.getCurrentAccessToken() == null)
ParseFacebookUtils.initialize(this);
ParseUser.enableAutomaticUser();
ParseACL defaultACL = new ParseACL();
// Optionally enable public read access.
defaultACL.setPublicReadAccess(true);
defaultACL.setPublicWriteAccess(true);
ParseACL.setDefaultACL(defaultACL, true);
Log.d(TAG, "Parse ready");
}
public static App getInstance(){
return instance;
}
}
SplashActivity
public class SplashActivity extends AppCompatActivity {
private static final String TAG = "Splash";
private boolean firstTime = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_splash);
firstTime = getSharedPreferences(Constants.GENERAL_SHARED_PREFS, MODE_PRIVATE)
.getBoolean(Constants.FIRSTTIME, true);
if (isLoggedIn())
if (firstTime)
startActivity(new Intent(SplashActivity.this, FirstTimeActivity.class));
else
startActivity(new Intent(SplashActivity.this, MenuActivity.class));
else {
Log.d(TAG, "Calling Home");
startActivity(new Intent(SplashActivity.this, WelcomeActivity.class));
finish();
}
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onPause() {
super.onPause();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
public boolean isLoggedIn() {
AccessToken accessToken = AccessToken.getCurrentAccessToken();
String parseSession = ParseUser.getCurrentUser().getSessionToken();
return parseSession != null;
}
}
Your stacktrace links to this class in the AOSP.
I think this crash has nothing to do with your app, but as an error in the Launcher class. Try installing from USB debugging and see if that works.
But there are still some details that are blurry. These lines are (from bottom of the stacktrace to the top) the lines that cause problems in com.android.launcher2 package:
https://android.googlesource.com/platform/packages/apps/Launcher2/+/android-4.2.2_r1/src/com/android/launcher2/AppsCustomizePagedView.java#584
https://android.googlesource.com/platform/packages/apps/Launcher2/+/master/src/com/android/launcher2/Launcher.java#2469
https://android.googlesource.com/platform/packages/apps/Launcher2/+/master/src/com/android/launcher2/Launcher.java#2442
From this error, I assume you are using a Nexus or Pixel (or any device with the unaltered source code, meaning stock android.).
From what I can tell from this error, this is not an error related to your app. It appears to be an issue with the launcher you are using. Try installing from USB debugging, or change launcher, and see if that works. Try rebooting your device as well.
Further, from what I see of your code, there are no parcelable classes in use
This error can also be caused when Instant Run loses connection with the Android Emulator as a result of which new app changes are not persisted in the emulator.
Running the app again will solve the issue.

Calling getActivity() inside a Fragment returns null

excuse the trouble I was trying to make a listview for parsing a page for my app, I'll explain: I have a actionbar formed by Fragment.
When instazia the fragment, opening the app the first time "getActivity" does not return null, is the next time, when I start the AsyncTask
In the fragment in which I will stop parsing this error:
java.lang.NullPointerException
at android.widget.ArrayAdapter.init(ArrayAdapter.java:310)
at android.widget.ArrayAdapter.<init>(ArrayAdapter.java:104)
at com.megadown.megacodownloader.ParsingArrayAdapter.<init>(ParsingArrayAdapter.java:30)
at com.megadown.megacodownloader.Tab_Search$ParsingPaginaWeb.onPostExecute(Tab_Search.java:264)
at com.megadown.megacodownloader.Tab_Search$ParsingPaginaWeb.onPostExecute(Tab_Search.java:125)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4898)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
at dalvik.system.NativeStart.main(Native Method)
Code Java that refers:
protected void onPostExecute(String result)
{
// dopo che ho eseguito il parsing mostro i dati nella listview
adapter = new ParsingArrayAdapter(myActivity, titoli, descrizioni,immagini);
lista.setAdapter(adapter);
}
If you want I can post the code of the ArrayAdapter.
Thank you in advance
First, make sure myActivity has the correct value:
protected void onPostExecute(String result) {
Activity myActivity = getActivity();
adapter = new ParsingArrayAdapter(myActivity, titoli, descrizioni,immagini);
lista.setAdapter(adapter);
}
Next, point is, you can't be sure that your activity and fragment is still active in onPostExecute().
For example, the user could have pressed 'back' button or rotated the screen, which would re-create the activites and fragments.
So, the good code is:
protected void onPostExecute(String result) {
Activity myActivity = getActivity();
if (myActivity==null) return; // Fragment not active anymore, bail out
adapter = new ParsingArrayAdapter(myActivity, titoli, descrizioni,immagini);
lista.setAdapter(adapter);
}
Fragment's getActivity() will only return expectable value while the associated activity has done onCreate method.

Finishing an activity from another class

I am working on an app that requires a permanent internet connection. If no internet connection is present I want the user to be logged out of the app (taken to the login screen).
I have a network receiver class that detects network connectivity. I want this class to either terminate the activity on top of the stack OR to start a new login activity and delete the entire history stack.
The problem is that I can't finish the foreground activity from inside the my receiver class, and there is no way of knowing which activity the user is in when there is a network fail. And if I'm starting a new login activity from this class, when the user presses "back" he is taken back to the app(if he reconnects to a network), but the app is not logged in and crashes.
Tried using myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK || FLAG_ACTIVITY_CLEAR_TOP); when starting a new login activity from my NetworkStateReceiver class. But it doesn't work, to my understanding this creates a new task in which the only activity is the one I started (login), but the old task with all the other activities remain intact.
So I need :
-either a way to finish a foreground activity from a class
-or a way to start a new activity from a class and emptying the activity stack
Here's the receiver code for what it's worth:
public class NetworkStateReceiver extends BroadcastReceiver{
public void onReceive(Context context, Intent intent) {
// super.onReceive(context, intent);
Log.d("app","Network connectivity change");
if(intent.getExtras()!=null) {
Login.apiKey = null;
NetworkInfo ni=(NetworkInfo) intent.getExtras().get(ConnectivityManager.EXTRA_NETWORK_INFO);
if(ni!=null && ni.getState()==NetworkInfo.State.CONNECTED) {
Log.i("app","Network "+ni.getTypeName()+" connected");
}
}
if(intent.getExtras().getBoolean(ConnectivityManager.EXTRA_NO_CONNECTIVITY,Boolean.FALSE) && !Login.loginActive) {
Log.d("app","There's no network connectivity");
Toast.makeText(context, "No internet connection. Logging out", Toast.LENGTH_LONG).show();
//logout
Receiver.engine(context).halt();
Receiver.mSipdroidEngine = null;
Receiver.reRegister(0);
new Thread(ChatList.runnable).interrupt();
ChatList.buddyList.clear();
Login.apiKey = null;
Log.i("Logout", "Logged out!");
Login.loggedOut = true;
Intent myIntent = new Intent(context, Login.class);
myIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// myIntent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
context.startActivity(myIntent);
}
}
}
SOLUTION:
Passed reference from all activities to receiver
//random user_activity
#Override
protected void onPause() {
super.onPause();
NetworkStateReceiver.curActivity = null;
}
#Override
protected void onResume() {
super.onResume();
NetworkStateReceiver.curActivity = user_activity.this; //edited : getParent() doesn't always work
}
and in network receiver in onReceive() :
if(curActivity != null)
{
curActivity.finish();
}
One way is to pass the receiver a reference to the current activity(say, in onResume?). Make sure to null it in onPause, though, or you're looking at some ugly memory leaks. Then, in onReceive, you can curActivity.finish()(or do nothing if it's null) before you start the login activity.

Android Services

I'm writing a web server for android. Actually it works with threads, but now I would use a service to make it running in background. I replaced the thread with a service, but I don't know if this design is ok...
When I press a button it starts then it stays in listening. When there is a request, the service create a thread to serve it.
This is my structure:
WebServer.java
class WebServer extends Service{
onCreate(){...}
onDestroy(){...}
}
DroidServer.java
class DroidServer extends WebServer{...}
MyActivity.java
MyActivity extends Activity{
boolean isOn=false;
btn.setOnClickListener(new OnClickListener(){
public void onClick(View V){
if(!isOn){
startService(new Intent(this, DroidWebServer.class));
btn.setText("Stop");
}else{
stopService(new Intent(this, DroidWebServer.class));
btn.setText("Start");
}}});
}
AndroidManifest.xml
... <service android:name='DroidWebServer'/> ...
But when I click on the button I get this exception:
FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to Instantiate service it.giox.ws.DroidWebServer: java.lang.InstantiationException: it.giox.ws.DroidWebServer
at android.app.ActivityThread.handleCreateService(ActivityThread.java:1933)
at android.app.ActivityThread.access$2500(ActivityThread.java:117)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:985)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:3729)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:874)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:632)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.InstantiationException: it.giox.ws.DroidWebServer
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1409)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:1930)
... 10 more
Where's the problem?
Have you seen this post? It basically says there is an error in your constructor. His fix was to remove the argument, and also make sure to call the super's constructor ...
InstantiationException occurs when an exception is thrown out of a
constructor. Generally there should be another exception reported as
causing the InstantiationException, and the stack trace should point
to the specific lines that are in error. Stack trace is your friend,
and you should be grateful -- their are poor children programming on
Symbian devices who have no stack trace.
Did you implement the callback method onStartCommand() in your subclass (either WerServer or DroidServer)? Check out Service.startService() for details.
Personally, I think use the bound form of service is more reasonable in your design, where you can replace a is-a relationship with a has-a relationship.
Your WebServer:
class WebServer implements IWebServer {
doSoming();
}
Your DroidServer:
class DroidServer extends Service {
private WebServer webServer;
private WebServerBinder myBinder;
onCreate(){...}
IBinder onBind(Intent intent) {
return myBinder;
}
onDestroy(){...}
protected class WebServerBinder extends Binder implements IWebServer {
doSomething() {
myServer.doSomething();
}
}
}
In your Activity, use your DroidServer like this:
public class MyActivity extends Activity {
private IWebServer myWebService;
private ServiceConnection myServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
myWebService = (IWebServer) service;
}
public void onServiceDisconnected(ComponentName className) {
if (myWebService != null)
myWebService = null;
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bindService(new Intent(this, DroidWebServer.class),
myServiceConnection, Context.BIND_AUTO_CREATE);
boolean isOn=false;
btn.setOnClickListener(new OnClickListener(){
public void onClick(View V){
if(!isOn){
myWebService.doSomething();
btn.setText("Stop");
}else{
myWebService.doSomethingElse(someParam);
btn.setText("Start");
}}});
}
#Override
public void onDestroy() {
super.onDestroy();
unbindService(myServiceConnection );
}
}

Categories