I want to create a notification for my android app. I have it working fine when I run it with the rest of my code inside a class, everything executes when I click a button and the notification works fine. However, because there is a lot of work to be done, the application is running slowly when I click the button. To help this, I have separated the notification part into a class of it's own and I am running it on a different thread.
I do not have a lot of experience with threads, or notifications for that matter and I would appreciate some help with this problem. Should I be separating the notification part into a class of it's own?
HomeActivity class is where the thread gets called to start, using the start() method when the button is clicked:
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import Model.Data;
public class HomeActivity extends FragmentActivity {
private com.atmlocator.hooper.kenneth.atmlocator.Notification n;
public void addListenerOnButton() {
locationSpinner = (Spinner) findViewById(R.id.spinner1);
options1Spinner = (Spinner) findViewById(R.id.spinner2);
options2Spinner = (Spinner) findViewById(R.id.spinner3);
Button locate = (Button) findViewById(R.id.locateBtn);
locate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = getApplicationContext();
String text = "Loading...";
//call notification thread here
n.start();
//rest of code here...
}
}
}
}
and then the Notification class is:
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentActivity;
public class Notification extends FragmentActivity implements Runnable {
private Thread t;
private String name;
Context context;
Notification(String name, Context c)
{
this.name = name;
this.context = c;
}
public void run()
{
// Intent is triggered if the notification is selected
Intent intent = new Intent(context, HomeActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, (int) System.currentTimeMillis(), intent, 0);
// Build notification
android.app.Notification not = new android.app.Notification.Builder(context)
.setContentTitle("Notification")
.setContentText("New email").setSmallIcon(R.drawable.atm)
.setContentIntent(pIntent).build();
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
not.flags |= android.app.Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, not);
}
public void start()
{
t = new Thread (this, name);
t.start ();
}
}
Should I be separating the notification part into a class of it's own?
There should not be any problem with this!
Also you should show your notification outside a thread.
You can use a handler instead to show notification instead of thread
like this :
}
....
// write this outside your code block
Handler mHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
showNotification();
}
};
// call it like this instead of thread start
mHandler.sendEmptyMessage(0);
check this link for more info : https://developer.android.com/training/multiple-threads/communicate-ui.html
This will virtually always work:
public static void toast(final String text, final int length, final Context context) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(context, text, length).show();
}
});
}
Related
I have a service type class which starts a timertask in onCreate method and I need to stop the timer from MainActivity when user press the button. I know I have to keep the reference to the timer in my service, but I cannot figure out how to do that and need some help, please!
please take a look at my code
package com.example.timertest;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(this);
startService(new Intent(this, TimeService.class));
}
#Override
public void onClick(View v) {
if(v.getId() == R.id.button){
// i need here to call mTimer.cancel() in TimeService.class
//
}
}
}
// and here is the TimeService.java
package com.example.timertest;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TimeService extends Service {
public Timer mTimer;
private Handler mHandler = new Handler();
long NOTIFY_INTERVAL = 60 * 1000 * 1; // 1 min
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
if (mTimer != null) {
mTimer.cancel();
} else {
mTimer = new Timer();
}
mTimer.scheduleAtFixedRate(new RefreshDataTimerTask(), 0, NOTIFY_INTERVAL);
}
class RefreshDataTimerTask extends TimerTask {
#Override
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), getDateTime(), Toast.LENGTH_LONG).show();
}
});
}
private String getDateTime() {
SimpleDateFormat sdf = new SimpleDateFormat("[yyyy/MM/dd - HH:mm:ss]");
return sdf.format(new Date());
}
}
}
//and service registered in manifest
<service android:name=".TimeService"/>
I have tried to call the mTimer.cancel(), but got a null reference, because seems I have created a new instance of the service class.
This example shows a Toast with date and time each minute, i want when for example 15 seconds have past and i press the button, the timer to be canceled and to start counting 60 seconds again from the beginning. Inside this service i have many other things like notifications,channels, shared preferences and so on, so it will be good if i can operate only with the timer object.
Take a look on this API documentaion and this. There is example of how to bind service to activity after what you can get direct access to service methods.
After successful binding you can add method inside your service to stop the timer:
public void stopMyTimer() {
mTimer.cancel();
}
And call this method from Activity
This might not be best practice, but you can access public values by directly referring to the class:
TimeService.mTimer.cancel()
Which should work because Services function similarly to Singletons, in that each call of startService should be calling the same instance.
I'm working on an Android app so I can learn mobile dev and I'm stuck with this problem.
Basically in my SampleFragment class I have an adapter called mAdapter and when my TestService gets my data objects and updates my dataObjects arrayList and notifies the adapter that the data has changed the adapter isn't initialized and is null at the time. Is it a thread issue or is it associated with the fragment lifecycle?
SampleFragment.java
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class SampleFragment extends Fragment {
private static final int SEND_DELAY = 1500;
private String userName, sEventId;
private EditText etMessage;
private ImageButton btSend;
private Context applicationContext;
private View view;
private Handler handler = new Handler();
private ArrayList<Message> dataObjects = new ArrayList<>();
private MessageListAdapter mAdapter;
private Runnable initMessageAdapter = new Runnable() {
#Override
public void run() {
initMessageAdapter();
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String eventName = CurrentActiveEvent.getInstance().getEventName();
Activity activity = getActivity();
activity.setTitle(eventName);
mAdapter = new MessageListAdapter(context, userName, dataObjects);
CurrentActiveUser currentUser = CurrentActiveUser.getInstance();
userName = currentUser.getUsername();
Intent intent = new Intent(activity, TestService.class);
activity.startService(intent);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_messaging, container, false);
applicationContext = getActivity();
sEventId = CurrentActiveEvent.getInstance().getEventID();
btSend = (ImageButton) view.findViewById(R.id.btSend);
handler.post(initMessageAdapter);
btSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handler.post(new Runnable() {
#Override
public void run() {
saveMessage(body);
}
});
}
});
return view;
}
// Setup message field and posting
private void initMessageAdapter() {
etMessage = (EditText) view.findViewById(R.id.etMessage);
ListView lvChat = (ListView) view.findViewById(R.id.lvChat);
lvChat.setAdapter(mAdapter);
}
public void updatedataObjects(List<objs> newdataObjects){
this.dataObjects.clear();
this.dataObjects.addAll(newdataObjects);
mAdapter.notifyDataSetChanged();
}
}
TestService.java
import android.app.IntentService;
import android.content.Intent;
import java.util.Collections;
import java.util.List;
public class TestService extends IntentService {
private static final int MAX_RESULTS = 50;
private static final String CLASS_NAME = TestService.class.getSimpleName();
private final String sEventId = CurrentActiveEvent.getInstance().getEventID();
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*/
public TestService() {
super(CLASS_NAME);
}
#Override
#SuppressWarnings("unchecked")
protected void onHandleIntent(Intent intent) {
if (NetworkState.isConnected(getApplicationContext())) {
Query query = new Query(Data.class);
query.whereEqualTo(Events.ID, sEventId);
query.orderByDESC(Data.CREATED_AT);
query.setLimit(MAX_RESULTS);
List objs = queryDB(query, Data.class.getSimpleName());
if (objs != null) {
Collections.reverse(objs);
new SampleFragment().updateMessages(objs);
}
}
}
}
Your problem comes from this line:
new SampleFragment().updateMessages(objs);
You are creating a new instance of your fragment inside your service. Since you are not attaching the fragment anywhere, it's lifecycle is not started and the onCreate() method is never called, which results in the NullPointerException.
IntentServices are great for executing tasks on a background thread, but they are components, that are meant to be separated from the UI - related components, like Activities and Fragments. You should never have direct communication between an IntentService and a Fragment or Activity. If you need to return a result from your IntentService, you should consider using the LocalBroadcastManager. It will fire an intent, containing the result, and you can register receivers to intercept it.
There are also other options, like Bound Services - they are created to provide an interface for their clients, and you can use this to return your result. But bear in mind, that, unlike IntentService they don't work in a background thread by default.
Since you are trying to work with a database, I recommend you take a look and the ContentProvider and ContentResolver classes. They are the recommended way of working with DBs in Android and come with loads of neat stuff.
I must write a program with android which can find other ssid's and show them. I desinged it with 2 xml pages. I create an imagebutton in page2 and want to make a relation between imagebutton and searching method. It means i want to click on imagebutton and seaching method begin it's work and search ssid's and show them...
My problem is, I download my search method and because of that i can not recognize which method i must call on my setonclick method that i write for an imagebutton in second page? I try to create another class seperately for search method and call it from the second class of page2.
but i dont know how can i make a relation between these 2 calss(i mean the second and third class). Or i must write the method of searching and on click of my imagebutton in one class?
Thanks for your suggestion.this is the code that i was copy:
import java.util.Date;
import java.util.List;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class wifiScan extends Activity {
private class WifiReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context c, Intent intent) {
List<ScanResult> results = wifi.getScanResults();
Date tempDate=new Date();
String info=testNumber+" "+(tempDate.getTime()-testDate.
getTime()) +" "+results.size();
Log.i("wifiScan", info);
wifiText.setText(info);
testNumber++;
testDate=new Date();
wifi.startScan();
}
}
private TextView wifiText;
private WifiManager wifi;
private WifiReceiver receiver;
private Date testDate;
private static int testNumber=0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
testNumber=0;
wifiText = (TextView) findViewById(R.id.wifiText);
receiver=new WifiReceiver();
registerReceiver(receiver, new IntentFilte
(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
wifi =(WifiManager)getSystemService(Context.WIFI_SERVICE);
if(!wifi.isWifiEnabled()){
wifi.setWifiEnabled(true);
}
startScan();
}
#Override
public void onStop(){
super.onStop();
finish();
}
public void startScan(){
testDate=new Date();
wifi.startScan();
}
}
you_image_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(this, wifiScan.class);
getApplicationContext().startActivity(i);
}
});
Im just making a simple app that will vibrate when the button is clicked, but for some reason when i click the button the app says it unexpectedly stopped and needed to force close, below is the source code to the main java file and i have used the android vibrate permission in my manifest. can someone tell me why every time I click the vibrate button it gives me the error that it unexpectedly stopped?
package com.test;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.View;
import android.widget.EditText;
public class Main extends Activity {
public final static String EXTRA_MESSAGE = "com.test.MESSAGE";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
/* Called when the user clicks the button */
public void sendMessage(View view) {
// do something in response to button
Intent intent = new Intent(this, DisplayMessageActivity.class);
EditText editText = (EditText) findViewById(R.id.edit_message);
String message = editText.getText().toString();
intent.putExtra(EXTRA_MESSAGE, message);
startActivity(intent);
}
public void vibrateMe() {
Vibrator vibrate = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
vibrate.vibrate(500);
}
public void stopVibrating(Vibrator vibrate) {
vibrate.cancel();
}
}
You have to change your vibrateMe() to vibrateMe(View v) if you use android:onClick="vibrateMe"
For instance, if you specify android:onClick="sayHello", you must
declare a public void sayHello(View v) method of your context
(typically, your Activity).
Check the developer page
public void stopVibrating(Vibrator vibrate) {
vibrate.cancel();
}
remove this and then check.
I'm trying to start a new activity from a non-activity class.
From the main menu:
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class Menu extends Activity {
Button start, options;
GameLoop Game = new GameLoop();
#Override
public void onCreate(Bundle mainStart) {
super.onCreate(mainStart);
setContentView(R.layout.menu);
start = (Button) findViewById(R.id.bStart);
options = (Button) findViewById(R.id.bOptions);
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent openStart = new Intent(Menu.this, Game.class);
startActivity(openStart);
}
});
options.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context mContext = null; //Error called for mContext to be initialized so just tried setting to null. This is most likely the error cause it would make more sense for it to be equal to "getContext()" or something like that
Game.Start(mContext);//Here
}
});
}
}
I'm trying to open an activity from the Game.Start() method.
import android.content.Context;
import android.content.Intent;
public class GameLoop extends Menu{
boolean hello = false;
public void Start(Context sContext){
Intent openOptions = new Intent(sContext, Options.class);
startActivity(openOptions);
}
}
I'm not sure if using context would be the right way of going about this but I figured it was worth a try. Im entirely new to java and android so I'm pretty much lost on where to go next. Any help in what direction to take would be throughly appreciated.
Activity extends Context, so you can just use this when inside Activity.
Game.Start(Menu.this);
I use Menu.this because you are inside inner anonymous class (View.OnClickListener) where this refers to this inner class.
Do you added the new activities to the androidmanifest.xml?