Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Start service at boot, but do not launch GUI

I'm writing an Android app that has two major components: a service which starts up at boot time, and a GUI that I want to start only when I manually launch it via its icon, not when the device boots. I know how to start the service at boot time, but it also launches the GUI at boot time, which I don't want.

I presume that this has something to do with the settings in my manifest, but despite trying a number of things, I haven't figured out how to prevent the GUI from also starting at boot time.

I should add that I do not programmatically launch the GUI at boot time. I do reference static public variables within the GUI's activity class, but I do not make any method calls or send any intents to the GUI's activity.

Here is my manifest. What am I doing wrong? Thank you very much.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="my.package.name"
      android:versionCode="0"
      android:versionName="0.1.0">

    <uses-sdk
        android:minSdkVersion="11" 
        android:targetSdkVersion="17"/>

    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.WRITE_SMS"/>
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application android:icon="@drawable/icon"
                 android:label="@string/app_name"
                 android:allowBackup="true" >

        <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
               I want MainActivity to only start when I
               select its icon, NOT at boot time. However,
               it always starts up at boot.
             XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        -->
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop">
           <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
        </activity>

        <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
               MyBootReceiver properly starts up at boot time,
               and it properly invokes MyBootService. At
               the appropriate time, MyBootService invokes
               RegisterActivity. 
             XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        -->
        <activity android:name=".RegisterActivity"
                  android:label="@string/app_name">
        </activity>
        <receiver
            android:name=".MyBootReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="my.package.name" />
            </intent-filter>
        </receiver>
        <service android:name=".MyBootService" />

    </application>
</manifest>

Adding broadcast receiver class:

package my.package.name;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyBootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent bootIntent = new Intent(context, MyBootService.class);
        bootIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startService(bootIntent);
    }
}

Adding service class ...

package my.package.name;

import java.util.ArrayList;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.telephony.TelephonyManager;

public class MyBootService extends Service {

    private static final String GOOGLE_ACCOUNT_TYPE    = "com.google";
    private static final String GOOGLE_ACCOUNT_FEATURE = "service_ah";

    private Context context = null;

    @Override
    public IBinder onBind(Intent intent) {
        this.display("onBind");
        return (null);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);

        AccountManager am = AccountManager.get(this);
        am.getAccountsByTypeAndFeatures(GOOGLE_ACCOUNT_TYPE, new String[]{GOOGLE_ACCOUNT_FEATURE},
                new AccountManagerCallback<Account[]>() {
                    @Override
                    public void run(AccountManagerFuture<Account[]> acclist) {
                        MyBootService parent = MyBootService.this;
                        Intent regIntent = new Intent(parent.getApplicationContext(), RegisterActivity.class);
                        try {
                            ArrayList<String> accountNameList = new ArrayList<String>();
                            for (Account a: acclist.getResult()) {
                                accountNameList.add(a.name);
                            }
                            regIntent.putStringArrayListExtra("accountNames", accountNameList);
                            try {
                                TelephonyManager tmgr = (TelephonyManager) parent.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
                                String phoneNo = tmgr.getLine1Number();
                                regIntent.putExtra("phoneNumber", phoneNo);
                            }
                            catch (Throwable t) {
                            }
                        }
                        catch (Throwable t) {
                            // put error message here
                        }
                        regIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        parent.startActivity(regIntent);
                    } 
                }, null);

        return (START_STICKY);
    }

    @Override
    public void onDestroy() {
        this.display("onDestroy");
        super.onDestroy();
    }
}

MORE INFO

I figured out some of what is going on. First of all, I was mistaken in saying that my MainActivity was starting. Upon more detailed debugging, I see that its onCreate() and onResume() methods were not being called. However, the app's view was showing up: a black screen with the name of the app and the default icon. I originally mistook that for an indication of a full startup.

This, of course, raises the question of why that view showed up on boot, in the first place. I have some info about this, although I'm still confused as to what is going on. I stripped down the onCreate() method of the RegisterActivity class that gets invoked by MyBootService. When this.getIntent() is called, the application's view shows up on boot. When this.getIntent() is commented out, the application's view does not show up on boot.

Note that this is onCreate() of the RegisterActivity class, NOT of MainActivity.

Do any of you know what could be causing the application's view to show up when this.getIntent() is called?

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // If the following line is commented out, the
    // application's view does not show up on boot;
    // if the following line is not commented out,
    // the application's view shows up.

    Intent intent = this.getIntent();
}
like image 764
NYCHippo Avatar asked Oct 22 '22 10:10

NYCHippo


1 Answers

I think what you are seeing is the preview used by Android to make the app startup seems faster. This preview is made based on the theme of your activity.

Try setting a custom theme for your RegisterActivity which more closely looks like the final result. For example, if your activity is a dialog, create a theme extending Theme.Dialog or Theme.Light.Dialog

You can get more information on this blog post from Cyril Mottier: Android App Launching Made Gorgeous


EDIT: changed to actually answer the question

like image 137
nicopico Avatar answered Oct 24 '22 04:10

nicopico