I got an null pointer exception on Android 3.2 emulator on a simple Android project created by Wizerd with a service in separated process, when I switch between "Stretch to fill screen" to "Zoom to fill screen". This crash will not happen if the service is put into the same process with main activity, namely "android:process" attribute not specified. While it only happens when I add "android:process" to manifest file for my test service.
The exception is:
FATAL EXCEPTION: main
java.lang.NullPointerException
at android.view.WindowManagerImpl.reportNewConfiguration(WindowManagerImpl.java:427)
at android.app.ActivityThread.handleUpdatePackageCompatibilityInfo(ActivityThread.java:2801)
at android.app.ActivityThread.access$2700(ActivityThread.java:122)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1151)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4123)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)
My test code:
TestActivity.java (Generated by Wizerd)
package com.test;
import android.app.Activity;
public class TestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent intent = new Intent(this, TestService.class);
startService(intent);
}
}
TestService.java (Most of the functions are empty)
Package com.test;
import android.content.ComponentName;
public class TestService extends Service {
private boolean m_connected = false;
private ServiceConnection m_connInitService = new ServiceConnection() {
public void onServiceConnection(ComponentName className, IBinder service) {
m_connected = true;
}
public void onServiceDisconnected(ComponentName className) {
}
};
public static class TestServiceBinder extends Binder {
}
public IBinder onBind(Intent intent) {
return new TestServiceBinder();
}
public void onDestroy() {
super.onDestroy();
}
public int onStartCommand(Intent intent, int flags, int startId) {
return 1;
}
}
If I run the test app without service or with service in the same process the screen compatibility switch will not cause any problem. However, I do not understand why service can cause system exception during screen compatibility switch. Is this because service process is a non-UI process? Which could potentially triggers a bug inside the Android core code?
I've found in the documentation the following thing:
android:process
If the name assigned to this attribute begins with a colon (':'), a
new process, private to the application, is created when it's needed
and the service runs in that process. If the process name begins with
a lowercase character, the service will run in a global process of
that name, provided that it has permission to do so. This allows
components in different applications to share a process, reducing
resource usage.
Thus, if you assign process name that starts with lowercase you should have permissions to do this. But I do not know where these permissions are checked. Maybe they are checked in the WindowManagerImpl and it cannot find this global process and thus returns null. This is just an assumption.
The reason causing this issue is Screen Compatibility Mode, see how to disable it for your app:
http://developer.android.com/guide/practices/screen-compat-mode.html#Disable
Related
I have an app and if the app crashes in a particular activity, it restarts at one of the intermediate parent activities.
This is a problem for me since I have some inputted user info that is lost upon crash.
Is there any way to force the app to start from the launcher screen after restarting from a crash?
Thanks.
Proposed Solution 1 -
Add this tag android:clearTaskOnLaunch="true" in the manifest.xml file to your main activity which should always launch.
Probable Reason why it did not work
When the application crashes, it throws an Exception and we need to handle the Exception and otherwise we would not get the expected behavior
Proposed Solution 2
Try to handle any uncaught Exception and tell the system what to do. To implement this, try the below steps.
Create a class extending Application Class
Handle uncaughtException in your Application subclass.
In your launcher Activity, call your Application class.
After catching an Exception, start your main Activity (as per your requirement).
Code Sample
Step 1 and 2
package com.casestudy.intentsandfilter;
import android.app.Application;
import android.content.Intent;
public class MyApplication extends Application
{
#Override
public void onCreate() {
super.onCreate();
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException (Thread thread, Throwable e) {
handleUncaughtException (thread, e);
}
});
}
private void handleUncaughtException (Thread thread, Throwable e) {
// The following shows what I'd like, though it won't work like this.
Intent intent = new Intent (getApplicationContext(),DrawView.class);
startActivity(intent);
// Add some code logic if needed based on your requirement
}
}
Step 3
public class MainActivity extends Activity {
protected MyApplication app;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Get the application instance
app = (MyApplication)getApplication();
.............
}
}
Step 4
Modify the below method as per your requirement
private void handleUncaughtException (Thread thread, Throwable e) {
// The following shows what I'd like, though it won't work like this.
Intent intent = new Intent (getApplicationContext(), HomeActivity.class);
startActivity(intent);
// Add some code logic if needed based on your requirement
}
I would recommend using library such as
https://github.com/Ereza/CustomActivityOnCrash
As the library takes care of other stuff along with different versions of android.
First, create and set the App class in your AndroidManifest.xml and
android:name=".App"
android:clearTaskOnLaunch="true"
then put this code in the App class
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable e) {
Log.d("AppCrash", "Error just lunched ");
}
});
}}
Debug Log Screenshot:
Maybe there's no way to do that but you can flag it so you know if the activity was started through user action or if it was just started after a crash.
i.e when you start the parent activity, pass something into the startActivity intent. If that isn't there then it was started after the crash.
I managed to start my main activity with intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); like this:
private void handleUncaughtException (Thread thread, Throwable e)
{
Intent intent = new Intent (getApplicationContext(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
I was wondering how to keep a record of launched activites for logging purposes. what broadcast receiver I have to subscribe to intercept this intent? or what intent-filter to use? I figure that I must use some type of long-running service in the background.
My first objetive is to track main-focus applications, some sort of history.
Want to get finally some similar to:
- Launched app com.android.xxx
- Launched app xx.yy.zz
- App xx.yy.zz lost focus
Thanks in advance
EDIT - Just see that app MyAppRank , that does exactly what i mean
What i'm able to figure out from your question is that you want to keep track of all the activities when they are launched in your application. If that is correct, the solution may work for you:
Crate a BaseActivity which all of your Activities should extend
public class BaseActivity extends Activity
{
private Activity activity;
public static final String INTENTFILTER_TRACK_MY_ACTIVITIES="INTENTFILTER_TRACK_MY_ACTIVITIES";
public static final String INTENTFILTER_REMOVE_MY_ACTIVITIES="INTENTFILTER_REMOVE_MY_ACTIVITIES";
public void setActivity(Activity act)
{
activity = act;
}
public Activity getActivity()
{
return activity;
}
#Override protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Intent intent = new Intent();
intent.setAction(INTENTFILTER_TRACK_MY_ACTIVITIES);
intent.putExtra("activityName", activity.getClass().getSimpleName());
sendBroadcast(intent);
}
#Override protected void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
Intent intent = new Intent();
intent.setAction(INTENTFILTER_REMOVE_MY_ACTIVITIES);
intent.putExtra("activityName", activity.getClass().getSimpleName());
sendBroadcast(intent);
setActivity(null);
}
}
Now extend above BaseActivity for all your activities. i.e instead of extending your Activities should extend BaseActivity and call setActivity(this); in onCreate like below:
public class MyActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setActivity(this);
//write your other code form here
}
}
3.Then write a BroadcastReceiver like below:
class TrackActivitiesReceiver extends BroadcastReceiver
{
private static final Object SEPERATOR = ",";// use , as seperator
String sb="";
#Override
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equalsIgnoreCase(BaseActivity.INTENTFILTER_TRACK_MY_ACTIVITIES))
{
sb+=intent.getStringExtra("activityName");
sb+=SEPERATOR;
}
else if(intent.getAction().equalsIgnoreCase(BaseActivity.INTENTFILTER_REMOVE_MY_ACTIVITIES))
{
sb=sb.replace(intent.getStringExtra("activityName")+SEPERATOR, "");
}
}}
4Finally, Register above Receiver in your AndroidManifest.xml
<receiver
android:name="TrackActivitiesReceiver"
android:exported="false" >
<intent-filter>
<action android:name="INTENTFILTER_TRACK_MY_ACTIVITIES" />
</intent-filter>
</receiver>
Hope this solves your problem. cheers!
There are no Intents broadcast when applications are started or when applications come to the foreground. There isn't anything that you can hook into as a listener to get these events.
The way you can do this (which is the way apps like MyAppRank do it) is to use the methods of the ActivityManager:
getRunningTasks()
getRunningAppProcesses()
getRecentTasks()
You create a Service which runs all the time and at regular intervals calls methods of the ActvityManager to determine which task is in the foreground and you can "infer" what the user has done (or is doing). It isn't an exact science.
Note: You will need android.permission.GET_TASKS and none of this works anymore as of API 21 (Android 5, Lollipop). As of API 21 the security has been tightened and an application can only get information about its own tasks, not other tasks in the system.
I pass a handler created on mainUI thread from Activity and passed to a thread which performs some network operation and when i obtain result i send the result back to the activity using the handler.
This approach had issue in memory leaks when i went through these links: Inner ClassHandler Memory Leak Android Developers
So i had implemented WeakReference, and kept the activity instance using WeakReference. But i am still seeing Activity instance alive even after activity is destroyed.
I created a Handler inside activity and passed activity instance as weakreference to handler.
By the time my Handler responds with a message delivered to it after 10secs, Activity is destroyed. But the weak reference still has the Activity instance and i am seeing the Toast, after Activity is destroyed.
Is there some where my understanding wrong ? Can someone explain how to handle messages delivered to a handler,but the UI is not around ?
import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Message;
public abstract class SingleParamHandler <T> extends Handler
{
private WeakReference<T> mActivityReference;
public SingleParamHandler(T activity) {
mActivityReference = new WeakReference<T>(activity);
}
#Override
public void handleMessage(Message msg) {
if (mActivityReference.get() == null) {
return;
}
handleMessage(mActivityReference.get(), msg);
}
protected abstract void handleMessage(T activity, Message msg);
}
import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.widget.Toast;
public class MainActivity extends Activity {
MyHandler<MainActivity> handler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main1);
handler = new MyHandler<MainActivity>(this);
new Thread(new MyRunnable(handler)).start();
}
public void onDestroy() {
super.onDestroy();
System.out.println("######## Activity onDestroy() ###### ");
}
private class MyRunnable implements Runnable {
private Handler mHandler;
public MyRunnable(Handler handler) {
mHandler = handler;
}
public void run() {
try {
Thread.sleep(10000);
mHandler.sendMessage(Message.obtain(handler, 1));
} catch ( Exception e) {
e.printStackTrace();
}
}
}
private static class MyHandler<T> extends SingleParamHandler<T> {
public MyHandler(T activity) {
super(activity);
}
#Override
public void handleMessage(T act, Message msg) {
if(msg.what == 1) {
Toast.makeText((MainActivity)act, "Called after activity destroyed", Toast.LENGTH_LONG).show();;
}
}
}
}
Based on the response obtained, i am updating the answer here. You may do it in the way u liked. But this is one way.
Added the below function in SingleParamHandler
public void clear() {
mActivityReference.clear();
}
And in Activity onDestroy()
public void onDestroy() {
super.onDestroy();
System.out.println("######## Activity onDestroy() ###### ");
handler.clear();
}
You don't need a WeakReference here. The Handler can just contain a reference to the Activity. In activity's onDestroy() just call a method on MyHandler that sets the reference to the Activity to null. Check for null in handleMessage().
Another choice would be this: in activity's onDestroy() call a method that interrupts the sleeping thread so that it shuts down before sending the message.
There's no guarantee that Android will really delete an object from memory if it's not required to do so. In other words, Activity objects can stay in memory even after onDestroy() has been called (if there's enough memory available). On the other hand, there's no guarantee that onDestroy() will be called if there's not enough memory; quite to the contrary, Android is allowed to kill your whole process after calling onPause() on your current Activity (depending on the Android version).
I think there's a better path to follow for your purpose. What you may want to do is attach, detach and possibly re-attach (e.g. on configuration changes) Activities to your Service. Don't hope for the garbage collector to do the work for you. Rather, make it explicitly.
Subclass Activity and override the lifecycle methods as well as startActivity() and startActivityForResult() to let your Service know who's in charge right now. Of course, that's only a best-effort approach since some callbacks aren't guaranteed, but that only matters in certain situations which aren't dangerous. For example, your Activity won't detach from your Service in onPause(), but it could get killed right afterwards. But either your Service runs in the same process, so it gets killed at the same time. Or it runs in a different process, but then Android will notice the broken connection and may or may not kill the service as well; if not, then all you need to do is implement it in a robust fashion to be able to deal with the connection loss.
Update
After reading your comment: You're right, I didn't address that specifically.
i am figuring out how to avoid messages being sent to a handler which is created in a activity which is destroyed
Given your code above, and assuming that you really just want to display Toasts with an Activity as long as it exists, the following approach should help.
If your Thread is supposed to serve more than one Activity, extend it such that Activities can register with the Thread after it is created. If your Thread just serves one Activity, pass the Activity reference along with the Handler reference upon your Thread's (Runnable's) construction.
Before your Thread sends the message via the Handler, check activity.isDestroyed(). If the Activity is not destroyed, send the message. If the Activity is destroyed, do not send the message.
Depending on whether your Thread should server more than one Activity, either exit it's Runnable's run() method or set it's Activity reference to null if it finds that the Activity has been destroyed.
This should fix your above code. However, if your scenario grows, other approaches may be more suitable.
// PPS.java
package com.domain.Servicecrasher;
import android.app.Service;
import android.view.Gravity;
import android.widget.Toast;
public abstract class PPS extends Service
{
#Override
public void onCreate()
{
Toast toasty = Toast.makeText(PPS.this, "service created!", 500);
toasty.setGravity(Gravity.CENTER, 0, 200);
toasty.show();
};
public void onDestroy()
{
Toast toasted = Toast.makeText(PPS.this, "service destroyed!", 500);
toasted.setGravity(Gravity.CENTER, 0, -200);
toasted.show();
};
};
tried to call the service using two methods:
method 1.
{
{
{
startService(new Intent(MainMenu.this, PPS.class));
}
}
}
method 2
{
{
{
Intent startPPS = new Intent(MainMenu.this, PPS.class);
startPPS.putExtra("com.domain.Servicecrasher.PPS", false);
startService(startPPS);
}
}
}
these both return an error on the emulator saying the app quit unexpectedly and i click force close, but the main activity doesn't close, so i'm assuming it's the service that i am force closing. below is the DDMS output:
java.lang.RuntimeException: Unable to instantiate service com.domain.Servicecrasher.PPS
at android.app.ActivityThread.handleCreateService(ActivityThread.java:1929)
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:3683)
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(Zygote.java:839)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
at dalvik.system.NativeStart.main(Native Method)
caused by: java.lang.InstantiationException: com.domain.Servicecrasher.PPS
at java.lang.Class.newInstaceImp1(Native Method)
at java.lang.Class.newInstaceI(Class.java:1409)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:1926)
... 10 more
just a couple simple questions.
what am i doing wrong?
what is the proper way to do this?
eventually i need the service to be capable of loading settings from a SQL base and continue recording audio to a file after the main activity loses focus or is closed.
for now i'd be happy if i could just launch a simple service.
Abstract classes, be they Services or other things, cannot be instantiated. Eclipse no doubt suggested that you add the abstract keyword to your class because you had not implemented all the necessary methods to make a concrete instance.
I don't know if Eclipse has an option like this (I assume it does), but IntelliJ IDEA has an option where you can select "implement methods" when you have an incomplete class and it'll add all the stubs you need to fill in. Try that, or consult the documentation for a full list.
Strange problem I'm facing. I want to start a Service when my Activity is created. Here is my Service's code (very simple, as you'll see):
package com.nblmedia.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class ServiceSynchro extends Service {
public ServiceSynchro()
{
Log.i(XXX.LOG, "ServiceSynchro");
}
#Override
public IBinder onBind(Intent intent) {
Log.i(XXX.LOG, "onBind");
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
// TODO Auto-generated method stub
Log.i(XXX.LOG, "onCreate");
super.onCreate();
}
#Override
public void onDestroy() {
Log.i(XXX.LOG, "onDestroy");
// TODO Auto-generated method stub
super.onDestroy();
}
}
In my AndroidManifest.xml I have declared the Service as follow :
<service android:name=".ServiceSynchro"></service>
Finaly, to start my Service, I call the startService method in the onCreate :
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
startService(new Intent(UserMenuActivity.this, ServiceSynchro.class));
}
And nothing happens. If I check the device's running services, there are no service named ServiceSyncho running. If I check the Logcat, nothing is outputted. Same for the breakpoints, the app doesn't cycle thru the Service's lifecycle events. Any idea what could be my problem?
I found the problem. In my AndroidManifest.xml file, the service:name attribute wasn't refering to the fully qualified classname of the service. As example, I was using this :
<service android:name=".ServiceSynchro"></service>
When I needed :
<service android:name="com.xxx.service.ServiceSynchro"></service>
For the people interested in the application from the documentation, here is the link to view it.
android:name
The name of the Service subclass that implements the service. This should be a fully qualified class name (such as, "com.example.project.RoomService"). However, as a shorthand, if the first character of the name is a period (for example, ".RoomService"), it is appended to the package name specified in the element.
Lastly, I updated the code to override the onStartCommand command since the onStart method is deprecated.
public int onStartCommand(Intent intent, int flags, int startId)
Get rid of your constructor. Never implement a constructor in a component (sole current exception: IntentService).
Also, when you do implement constructors elsewhere in Java, chain to the superclass.
I looked everywhere when my service didnt start.
Nothing worked out.Finally debugging of the third hour ı found out that my package name was not the standart format xx.xx.xx package names.I fixed and it worked.
For your information.