I am developing an application in which i am getting white screen before splash screen when ever i run the app.
On splash screen i created database in background also i am having the push notification registration code. For push notification registration i refer this link. So my code of splash screen code is as follows:
public class SplashScreenActivity extends AppCompatActivity {
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
private BroadcastReceiver mRegistrationBroadcastReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
mRegistrationBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean sentToken = sharedPreferences.getBoolean(QuickstartPreferences.SENT_TOKEN_TO_SERVER, false);
if (sentToken) {
// TODO token sent to server
} else {
// TODO show error that token not sent to server
}
}
};
if (checkPlayServices()) {
// Start IntentService to register this application with GCM.
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
}
InitializeScreen();
}
private boolean checkPlayServices() {
GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (apiAvailability.isUserResolvableError(resultCode)) {
apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.i("Splash screen activity", "This device is not supported.");
finish();
}
return false;
}
return true;
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver, new IntentFilter(QuickstartPreferences.REGISTRATION_COMPLETE));
}
private void InitializeScreen() {
new LoadDataBase(SplashScreenActivity.this).execute(SplashScreenActivity.this);
}
private class LoadDataBase extends AsyncTask<Context, Void, Void> {
Context context;
public LoadDataBase(Context context){
this.context = context;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Context... arg0) {
// Create data base from assets folder.
DatabaseHelper databaseHelper = new DatabaseHelper(arg0[0]);
try {
databaseHelper.createDataBase();
} catch (IOException e) {
e.printStackTrace();
}
// Closing the Data base.
databaseHelper.close();
return null;
}
#Override
protected void onPostExecute(Void result) {
pauseSplashScreen(context);
}
}
public void pauseSplashScreen(final Context context) {
// New Thread call.
new Thread() {
// Running Thread.
public void run() {
int count = 0;
while (count < 5) {
try {
Thread.sleep(1000);
} catch (Throwable e) {
e.printStackTrace();
}
count++;
}
Intent intent = new Intent(context, Activity2.class);
startActivity(intent);
finish();
}
}.start();
}
}
The issue is : I am getting white screen at app start before splash screen and may be that is due to push notification registration given in above link.
What should i do to avoid that white screen. Please guide me.
You are using a private inner class to create the database. And from there you call pauseSplashScreen(context). That then starts a new thread? Why?
I recommend giving the async task it's own class with a interface to give a signal back to your calling activity.
if you use
Intent intent = new Intent(context, Activity2.class);
startActivity(intent);
You don't have to call finish(), that call is better used to finish the current activity and get back to the previous one. (I get you use a thread to not block the rest of you app running, is it not better to let that thread get back to you, and then move to the next Activity?)
So, to answer your question, my guess is that the startup of your Activity takes some time, and what you see is a blanc layout loaded.
It's just because you are using the debug version, when you change to the release version, it will be gone don't wry about it!
Related
I have a very strange problem for updating UI. I have a foreground started bounded service which my main process in background. When I start app, I like to check if service is already running and change state of a toggle button. For this problem, I bind to my started service when starting app in OnResume() and service sends a value back to my app which shows running status of service and I update UI based on this value. But the problem is that UI is not updated in this situation.
Because this bug is shown in a very complex situation, I have written a sample code that reproduce this problem. Here are these codes (sorry for bad names and missing a lot of error checks, I have quickly written this code just to reproduce problem). I have discussed each code a little as an overview.
activity_main layout:
<ToggleButton
android:id="#+id/ui_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOff="Off State"
android:textOn="On State"
android:checked="false" />
<Button
android:id="#+id/start_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start"/>
MyTestService.java
At first, this is my sample foreground started bounded service. As you see, when we start service, we create a foreground service which just runs a small thread that toggles a mStatus variable every 10 seconds for 10 times and then stops. Whenever we bind to this service, we use ResultReceiver which is sent through binding intent in order to send mStatus to app. We also allow rebinding, because app may be closed several times and reopened again.
public class MyTestService extends Service {
private volatile boolean mStatus = false;
private MyThread mTh = new MyThread();
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mTh.start();
Intent notintent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notintent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentText("Test").setContentIntent(pendingIntent).setContentTitle("title").setSmallIcon(R.drawable.ic_launcher);
Notification notification = builder.build();
startForeground(100, notification);
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
ResultReceiver recv = (ResultReceiver)intent.getParcelableExtra("myrecvextra");
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
recv.send(0, data);
}
return null;
}
#Override
public boolean onUnbind(Intent intent) {
return true;
}
#Override
public void onRebind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
ResultReceiver recv = (ResultReceiver)intent.getParcelableExtra("myrecvextra");
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
recv.send(0, data);
}
}
public class MyThread extends Thread {
#Override
public void run() {
try {
for (int i = 0; i < 10; i++ ) {
Thread.sleep(10000);
mStatus = !mStatus;
Log.i("ASD", String.format("%d", mStatus? 1 : 0));
}
}catch (Exception e) {
}
stopSelf();
}
}
}
MyServiceAccessClass.java
This class is used for accessing service. start() starts service, bind() and unbind() are using for binding and unbinding service. mRecv is the ResultReceiver which send to service while binding and is used for getting status. When status is received after binding, ResultReceiver updates UI via a callback.
public class MyServiceAccessClass {
private MyResultRecv mRecv = new MyResultRecv(new Handler(Looper.getMainLooper()));
private OnUpdateRequest mCallback = null;
private Context mCtx = null;
private ServiceConnection mCon = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {}
#Override
public void onServiceDisconnected(ComponentName name) {}
};
public MyServiceAccessClass(Context ctx) {
mCtx = ctx;
mCallback = (OnUpdateRequest)ctx;
}
public void bind() {
Intent intent = new Intent(mCtx, MyTestService.class);
intent.setAction("checkstatus");
intent.putExtra("myrecvextra", mRecv);
mCtx.bindService(intent, mCon, Context.BIND_AUTO_CREATE);
}
public void unbind() {
mCtx.unbindService(mCon);
}
public void start() {
Intent intent = new Intent(mCtx, MyTestService.class);
mCtx.startService(intent);
}
private class MyResultRecv extends ResultReceiver {
public MyResultRecv(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 0) {
mCallback.updateUi(resultData.getBoolean("status"));
}
}
}
}
MainActivity.java
This is main class of test app. Start button starts service. and this class binds in OnResume() and unbinds in OnPause(). If app is run when service is already running and its mStatus is true, then updateUi will be called with true value and sets status of toggle button.
interface OnUpdateRequest {
public void updateUi(boolean state);
}
public class MainActivity extends AppCompatActivity implements OnUpdateRequest{
private MyServiceAccessClass mTest = new MyServiceAccessClass (this);
private ToggleButton mBtn = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtn = (ToggleButton)findViewById(R.id.ui_btn);
((Button)findViewById(R.id.start_btn)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mTest.start();
}
});
}
#Override
protected void onResume() {
super.onResume();
mTest.bind();
}
#Override
protected void onPause() {
super.onPause();
mTest.unbind();
}
#Override
public void updateUi(boolean state) {
mBtn.setChecked(state);
}
}
Ok, now in theory everything is all right. But if you try to use this code, when service is started and mStatus is true, toggle button's setChecked() will be called with true (which is correct till now) but UI will not be updated to show correct text and status. Funny part is that if you run isChecked for this toggle button, it will return true, but UI shows something else.
Any idea why this happens? Sorry for a lot of codes, this problem occured is this complex situation.
Update
I noticed something that I should mention. if I use isChecked right after setCheck, I get true which is correct. But if I use isChecked again some time later (for example in another button event handler), it returns false while I have not called setChecked anymore. I think this situation is related to my problem but I don't know how this is happened.
In addition, I think this problem is related to updating UI when you are in binding process to a service. because if I try to update app main UI with same ResultReceiver when I'm not in binding process, everything works correctly.
Possibly need to call View.requestLayout() or View.forceLayout() on the buttons View to refresh the buttons state.
I finally found the problem with my code. It took me a lot of time to solve this problem, so I post it here for other android developers.
Sending result back from a service is somehow obvious via ResultReceiver. But most of examples in internet does not show service rebinding, and I never found sending result back after rebinding service.
OK, now what is the problem? look at the following part of code from my service:
#Nullable
#Override
public IBinder onBind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
ResultReceiver recv = (ResultReceiver)intent.getParcelableExtra("myrecvextra");
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
recv.send(0, data);
}
return null;
}
#Override
public boolean onUnbind(Intent intent) {
return true;
}
#Override
public void onRebind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
ResultReceiver recv = (ResultReceiver)intent.getParcelableExtra("myrecvextra");
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
recv.send(0, data);
}
}
This is a common approach for making a rebinding service, based on simple binding to service that you find in internet. This has been done with returning true in onUnbind() and using onRebind(). But this approach is completely wrong.
Why? Because of a strange design in android. In Service OnRebind(), there is a small 18 word comment:
Note that any extras that were included with the Intent at that point
will not be seen here.
Now What this means? It means that extra which carries ResultReceiver will not be available on rebinding which in turn means that results will not be sent back after rebinding. But for unknown reason, this code does not make any exception and you even see result back in app while debugging, so it is super ambiguous why this code does not work.
Now what is the solution? Never send ResultReceiver when you bind to a service with bindService() intent. Even though this is correct for a non-rebinding service, but I highly suggest to avoid it. Send ResultReceiver via a separate message to service when onServiceConnected is called and then everything works like a piece of cake. Here are my modifications for code:
MyTestService.java
public static int SERVICE_SET_RECV = 1;
public static String SERVICE_RECV = "SERVICE_RECV";
private Messenger mMessenger = new Messenger(new MyHandler(this));
private ResultReceiver mRecv = null;
#Nullable
#Override
public IBinder onBind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
return mMessenger.getBinder();
}
return null;
}
#Override
public boolean onUnbind(Intent intent) {
mRecv = null;
return true;
}
#Override
public void onRebind(Intent intent) {}
public void setRecv(ResultReceiver recv) {
mRecv = recv;
// Example to send some result back to app
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
mRecv.send(0, data);
}
private static class MyHandler extends Handler {
private final WeakReference<MyTestService> mService;
public MyHandler(MyTestService service) {
mService = new WeakReference<>(service);
}
#Override
public void handleMessage(Message msg) {
MyTestService service = mService.get();
Bundle data = msg.getData();
switch (msg.what) {
case SERVICE_SET_RECV: {
ResultReceiver recv = data.getParcelable(SERVICE_RECV);
service.setRecv(recv);
break;
}
default:
super.handleMessage(msg);
}
}
}
MyServiceAccessClass.java
private ServiceConnection mCon = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Messenger messenger = new Messenger(service);
Bundle data = new Bundle();
data.putParcelable(MyTestService.SERVICE_RECV, mRecv);
Message msg = Message.obtain(null, MyTestService.SERVICE_SET_RECV, 0, 0);
msg.setData(data);
messenger.send(msg);
}
#Override
public void onServiceDisconnected(ComponentName name) {}
};
public void bind() {
Intent intent = new Intent(mCtx, MyTestService.class);
mCtx.bindService(intent, mCon, Context.BIND_AUTO_CREATE);
}
Finding this ridiculous problem took me a lot of time. I wish everyone likes this solution and solves a lot of problems for rebinding a service.
I have a fragment with a button. When clicked it tells a service to start polling sensors and then insert the sensor data into a database on a background thread. When the button is pushed again, the service will stop. When the Stop button is pushed, there may still be tasks in the executor queue that is inserting into the DB, so during this time I want to display a progress dialog, and dismiss it once the entire queue is clear. The fragment with the button looks like this:
public class StartFragment extends Fragment implements View.OnClickListener {
Button startButton;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_start, container, false);
startButton = (Button) view.findViewById(R.id.startButton);
startButton.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
if (recording has not yet started){
mainActivity.startService(new Intent(mainActivity, SensorService.class));
} else {
//I want to display a progress dialog here when the service is told to stop
//Once all executor task queue is clear, I want to dismiss this dialog
mainActivity.stopService(new Intent(mainActivity, SensorService.class));
}
}
}
When the button is clicked the first time, the following service will start:
public class SensorService extends Service implements SensorEventListener {
public static final int SCREEN_OFF_RECEIVER_DELAY = 100;
private SensorManager sensorManager = null;
private WakeLock wakeLock = null;
ExecutorService executor;
Runnable insertHandler;
private void registerListener() {
//register 4 sensor listeners (acceleration, gyro, magnetic, gravity)
}
private void unregisterListener() {
sensorManager.unregisterListener(this);
}
public BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive("+intent+")");
if (!intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
return;
}
Runnable runnable = new Runnable() {
public void run() {
Log.i(TAG, "Runnable executing...");
unregisterListener();
registerListener();
}
};
new Handler().postDelayed(runnable, SCREEN_OFF_RECEIVER_DELAY);
}
};
public void onSensorChanged(SensorEvent event) {
//get sensor values and store into 4 different arrays here
//insert into database in background thread
executor.execute(insertHandler);
}
#Override
public void onCreate() {
super.onCreate();
//get sensor manager and sensors here
PowerManager manager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
//Executor service and runnable for DB inserts
executor = Executors.newSingleThreadExecutor();
insertHandler = new InsertHandler();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startForeground(Process.myPid(), new Notification());
registerListener();
wakeLock.acquire();
return START_STICKY;
}
#Override
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
if (executor.isTerminated()){
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
//Once the queue is clear, I want to send a message back to the fragment to dismiss the progress dialog here
}
}
class InsertHandler implements Runnable {
public void run() {
//get sensor values from 4 arrays, and insert into db here
}
}
So I want to display the dialog on the 2nd button press. Then once it is pressed again, service will stop, and I want to wait until the queue is clear and then send a dismiss event back to the fragment to dismiss the progress dialog.
Showing the dialog is easy. I can just add progress dialog code in the onClick method of the fragment, before stopService is called
I'm having difficulty with figuring out how to send a message back in onDestroy of the SensorService to dismiss that dialog
Whats the best way of doing this without resorting to external libraries?
Is there some way that the BroadcastReceiver I'm using in SensorService can be used? Or maybe it's better to create a new Handler in the fragment and somehow pass it through to the service so it can send a message back to the fragment?
EDIT:
I have tried the following based on one of the answers below:
Added a MessageHandler class to my fragment class:
public static class MessageHandler extends Handler {
#Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case 0:
stopDialog.dismiss();
break;
case 1:
stopDialog = new ProgressDialog(mainActivity);
stopDialog.setMessage("Stopping...");
stopDialog.setTitle("Saving data");
stopDialog.setProgressNumberFormat(null);
stopDialog.setCancelable(false);
stopDialog.setMax(100);
stopDialog.show();
break;
}
}
}
Created a new instance of MessageHandler in my fragment (tried placing this in a variety of places...same results):
public static Handler messageHandler = new MessageHandler();
The service is then started from my fragment using:
Intent startService = new Intent(mainActivity, SensorService.class);
startService.putExtra("MESSENGER", new Messenger(messageHandler));
getContext().startService(startService);
In my SensorService BroadcastReceiver I create the messageHandler:
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
Then I show the dialog at the very beginning of SensorService onDestroy:
sendMessage("SHOW");
and dismiss it at the very end of that same method:
sendMessage("HIDE");
My sendMessage method looks like this:
public void sendMessage(String state) {
Message message = Message.obtain();
switch (state) {
case "SHOW":
message.arg1 = 1;
break;
case "HIDE" :
message.arg1 = 0;
break;
}
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
So I can start the Service OK, but when I press it again to stop, I get this:
java.lang.RuntimeException: Unable to stop service com.example.app.SensorService#21124f0: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.os.Messenger.send(android.os.Message)' on a null object reference
and its referring to Line 105 of SensorService where I have messageHandler.send(message)
Thoughts on what might be wrong?
In activity:
protected BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, final Intent intent) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(intent.hasExtra("someExtraMessage")){
doSomething(intent.getStringExtra("someExtraMessage"));
}
}
});
}
};
#Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("message-id"));
}
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
public void doSomething(){
//...
}
Then somewhere from service:
Context context = BamBamApplication.getApplicationContext(); // Can be application or activity context.
// BamBamApplicaiton extends Application ;)
Intent intent = new Intent("message-id");
intent.putExtra("someExtraMessage", "Some Message :)");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
Actually you are doing wrong from the very beginning :) all the services are running on main thread, so here you must better start all hard processing to async task to move this in background otherwise you will stuck your app, or you will get sudden unexpected crashes.
Here are you sample of async task that parses json api response in background with Typed result by parameter.
class ParseJsonInBackground<T> extends AsyncTask<String, Void, ApiResponseModel<T>> {
private ProcessResponse<T> func;
private Type inClass;
public ParseJsonInBackground(ProcessResponse<T> f, Type inClass){
this.func = f;
this.inClass = inClass;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected ApiResponseModel<T> doInBackground(String... json) {
Gson gson = new Gson();
try {
ApiResponseModel<T> result = (ApiResponseModel<T>) gson.fromJson(json[0], inClass);
return result;
}catch(Exception e){
ApiResponseModel<T> result = new ApiResponseModel<T>();
result.data = null;
result.success = false;
result.error = new ArrayList<>();
result.error.add(new ErrorModel(0, "Parsing error", "Parsing error"));
return result;
}
}
#Override
protected void onPostExecute(ApiResponseModel<T> result) {
Utils.hideLoadingProgress(mContext);
if(result != null && func != null){
if(result.success){
func.onSuccess(result);
}else{
func.onError(result);
}
}
}
}
and sample how to call:
new ParseJsonInBackground<T>(responseFunc, inClass).execute(json.toString());
make attention! - don't use any views in processing coz this will stuck main thread, make database processing in similar async task, don't write to often to database make recording with transactions.
I would propose doing this via Handler messages: you send a message from the Service to your Activity which has to register as a callback handler (implement http://developer.android.com/reference/android/os/Handler.Callback.html). Use a custom message code (message.what) and listen for it. Keep in mind to send this to the main looper of your application (from the service).
You may also check this comment which illustrates this kind of interaction with some more code: https://stackoverflow.com/a/20595215/4310905
It turns out that the code in the Edit of my original question works, but I have to shuffle around some of my code:
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
The above needs to be moved to onStartCommand of SensorService instead of being in the BroadcastReceiver
I have a service and an activity. in service I broadcast messages received from network and in the activity show these messages. this works fine. but all messages will lost when the activity is in the background.
How can I get last messages(if exists) from server, in activity onResume(or onCreate)?
EDIT :
in service:
public class server extends Service implements Runnable
{
#Override
public void onCreate()
{
}
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#Override
public void onStart(Intent intent, int startID)
{
//initializing socket and begining listen
new Thread(this).start();
}
public void run()
{
String readed;
while (true)
{
if(reader == null) continue;
try
{
if ((readed = reader.readLine()) != null)
{
Intent intent = new Intent(SEND_DATA_INTENT);
intent.putExtra("type", "message");
intent.putExtra("content", readed.substring(1));
sendBroadcast(intent);
Thread.sleep(100);
}
}
catch (Exception ee) { }
}
}
}
and in activity:
public class menhaj extends Activity
{
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
#Override
protected void onResume()
{
if (dataUpdateReceiver == null) dataUpdateReceiver = new DataReciver();
IntentFilter intentFilter = new IntentFilter(server.SEND_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);
super.onResume();
};
#Override
protected void onPause()
{
if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
super.onPause();
};
private class DataReciver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(server.SEND_DATA_INTENT))
{
Bundle bdl = intent.getExtras();
String type = bdl.getString("type");
if (type.equals("message"))
{
String message = bdl.getString("content");
db.addMessage(message);
showMessage(message);
}
}
}
}
}
If that activity is in the background, the user probably doesn't want to see messages from it. Show notifications from your services instead. Generally, an activity should de-register itself onPause() and register again onResume() when it comes back to the foreground.
I would declare BroadcastReceivers , those can receive messages and bring back to from your avtivity / app
I've got a class called Download which extends the AsyncTask.
The OnPreExecute method does the following :
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
this.dialog.setTitle("Check updates...");
this.dialog.show();
}
The dialog listed is instantied in the constructor of the class and has the following charateristics:
dialog = new ProgressDialog(activity);
dialog.setCancelable(false);
In the doInBackground method I'll do a lot of network operations and I'll call the progress update method every time I'm able to download an image from a desired url :
protected void onProgressUpdate(String... values)
// TODO Auto-generated method stub
super.onProgressUpdate(values);
//call the onprogress update
publishProgress("1000");
//do a lot of stuff with the network
}
In the onprogressupdate I'll dismiss the first dialog created and I'll show another one:
protected void onProgressUpdate(String... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
if(values[0].equals("1000")){
dialog.dismiss();
progress_brand.show();
progress_brand.setProgress(progress_brand.getProgress()+1);
if(progress_brand.getProgress() == progress_brand.getMax()){
progress_brand.dismiss();
}
}
}
So basically : at the start of the asynctask I'm showing a dialog with a title "check updates" ... then I'll search for those updates in the doinbackground method and if I'll find some, I'll use the publish progress to dismiss the "old dialog" and create a new one with the ProgressDialog.STYLE_HORIZONTAL. This last dialog is updated everytime I'll download something from the net.
So here's the problem. If I'll run the application with eclipse and then during a download I'll pause the application everything works fine. If I re-enter the application in a second time I can see that the download continues perfectly and I can see the second progress bar continuing to update itself as expected.
If however I make a signed apk --> install the application through that apk --> start the app --> put it on pause during a download -->re-enter the app, then the first dialog is showed again and the download can't proceed properly.
I've seen from the logcat that if I'll run the app from eclipse the onpreexecute method is called only once, even if I'll exit and re-enter in the app.
However if I'll install the app through the apk the onpreexecute method is called everytime I'll exit and then re-start the app.
Why is that happening? I've tried to clean the project and other basic operations to see if the problem was the creation of that apk, but with no results.
no, you do not use ProgressDialog in your AnyTask
try this (for example)
public class Updated extends Activity {
/**
* ProgressDialog which is shown
*/
private ProgressDialog progessDialog_g;
private boolean downloadUses = false;
/**
* Instance of the BroadcastReceiver
*/
private BroadcastReceiver receiver_g;
private IntentFilter iFilter;
protected ServiceConnection mServerConn = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder binder) {
}
public void onServiceDisconnected(ComponentName name) {
}
};
private Intent sI;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progress);
progessDialog_g = new ProgressDialog(this);
// Reads and sets the settings for the ProgressDialog
// Create the IntentFilter for the different broadcast messages
iFilter = new IntentFilter(
ProgressService.PROGRESS_DIALOG_BROADCAST_FINISH);
// Creates the BroadcastReceiver
receiver_g = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (ProgressService.PROGRESS_DIALOG_BROADCAST_FINISH
.equals(intent.getAction())) {
// Finishs the ProgressDialog
progessDialog_g.cancel();
Finish();
}
}
};
}
#Override
protected void onDestroy() {
super.onDestroy();
}
#Override
protected void onResume() {
sI = new Intent(this, ProgressService.class);
this.bindService(sI, mServerConn, Context.BIND_AUTO_CREATE);
this.startService(sI);
// Registers the BroadcastReceiver
registerReceiver(receiver_g, iFilter);
if (downloadUses) {
downloadUses = false;
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else {
progessDialog_g.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progessDialog_g.setMessage("you messege");
progessDialog_g.show();
new DownloadJSONTask(this, sI)
.execute(Initialize.server_url+"/changes/update/1");
}
super.onResume();
}
#Override
protected void onPause() {
this.stopService(new Intent(this, ProgressService.class));
this.unbindService(mServerConn);
unregisterReceiver(receiver_g);
super.onPause();
}
private void Finish() {
Intent in = new Intent(this, RegionsActivity.class);
startActivity(in);
downloadUses = true;
}
}
}
and
public class ProgressService extends IntentService {
public static final String PROGRESS_DIALOG_BROADCAST_FINISH = "Dialog.Progress.MyKey.Finish";
public ProgressService() {
super("ExampleProgressService");
}
/**
* Send the finish message.
*/
private void closeProgressActivity() {
Intent intent = new Intent(PROGRESS_DIALOG_BROADCAST_FINISH);
sendBroadcast(intent);
}
#Override
protected void onHandleIntent(Intent intent) {
// extractVariablesFromIntentAndPrepare(intent);
String action = intent.getStringExtra("Action");
if ("0".equals(action)) {
closeProgressActivity();
}
}
}
and in you AnyTask
sI.putExtra("Action", "0");
context.startService(sI);
and in you manifest
<service android:name=".Intent.ProgressService" />
I'm new to Android and I'm stuck on a conception problem.
I have an application with several activities, some of those activities are critical and require the user to be logged in a webApp.
When the user clicks a button to reach one of those critical activities, I call a background Service which ask if the user is still connected to the webApp (not timeout). If the user is logged in, the activity is started, otherwise a dialog pops up and ask for username and password. The problem is that there is several protected activities, and I want to use the same service to do the verification. The way I do it for the moment works but it's kind of kludgy.
public class A_Activity extends Activity {
Context context;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getApplicationContext();
setButtonClickListener();
}
private void setButtonClickListener() {
button_1 = (Button)findViewById(R.id.button1);
button_1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intentCall = new Intent(context,com.them.cp.ConnexionManagerService.class);
intentCall.putExtra("WHO_IS_CALLED","FIRST_ACTIVITY");
context.startService(intentCall);
}
});
button_2 = (Button)findViewById(R.id.button2);
button_2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intentCall = new Intent(context,com.them.cp.ConnexionManagerService.class);
intentCall.putExtra("WHO_IS_CALLED","SECOND_ACTIVITY");
context.startService(intentCall);
}
});
}
}
And my service
public class ConnexionManagerService extends Service{
public class IsConnectedAsync extends AsyncTask<String , Void, Void>{
protected Void doInBackground(String... whoIsCalled) {
String redirectedURL = getRedirectedURL();
if(redirectedURL.equalsIgnoreCase(IF_NOT_CONNECTED_URL)){
if(whoIsCalled[0].equalsIgnoreCase("FIRST_ACTIVITY")){
Intent trueIntent = new Intent(getBaseContext(), FirstActivity.class);
trueIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(trueIntent);
}
else if(whoIsCalled[0].equalsIgnoreCase("SECOND_ACTIVITY")){
Intent trueIntent = new Intent(getBaseContext(), SecondActivity.class);
trueIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(trueIntent);
}
}
else{
Intent falseIntent = new Intent(getBaseContext(), PopUpLoginActivity.class);
falseIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(falseIntent);
}
}
return null;
}
}
#Override
public void onCreate() {
Toast.makeText(this, "My Service Created", Toast.LENGTH_LONG).show();
Log.d("service onCreate", "onCreate");
}
public int onStartCommand(Intent intent, int flags, int startId){
String whoIsCalled = intent.getStringExtra("WHO_IS_CALLED");
new IsConnectedAsync().execute(whoIsCalled);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
with my little knowledge I wish i could just send an intent, but it seems that it's not possible as it's not the UI thread.
My question is: What can I do to make this service more generic ?
I want to use the same service to do the verification.
If you don't destroy the service it will be the same service object. If an activity which started your service finishes or stops the service it could be destroyed if it was the unique activity that started the service. If you want to ensure that the service reminds on background start it on you application class (extending Application) and in each activity you need. When an activity stops the service or finishes the service will not be destroyed because your application class is still connected.
EDIT:
To avoid write putExtra again and again:
public class StartOrder1 extends Intent {
public StartOrder(Context ctx, String activity_name){
super(ctx, ServiceName.class);
if(activity_name != null)
super.putExtra("WHO", activity_name);
else
super.putExtra("WHO", "UNKNOWN");
}
public String getWho(){
reurn.getIntExtra("WHO");
}
}
To start it:
this.startService(new StartOrder1(this, "My activity name"));
The best solution:
public class StartOrder2 extends Intent {
public StartOrder(Activity a){
super(a, ServiceName.class);
super.putExtra("WHO", a.toString());
}
public String getWho(){
reurn.getIntExtra("WHO");
}
}
And you can override toString method in each Activity passing the activity name, class name, whatever you want. Then when you start an intent:
this.startService(new StartOrder2(this));
Or extends Activity with this utility:
public class EnhancedActivity extends Activity{
protected startMyService(String name){
Intent i = new Intent(this, MyService.class);
i.putExtra("who", name);
startService(i);
}
}
And call it on your final activity
[...]
super.startMyService("activity_name");
[...]