I've an Android app that has a login capabilities, and the login box has a TextView that displays messages to the user when trying to login(ie. wrong name, wrong pass, etc..).
I have two methods, the first one check if the fields is filled or not, and if filled it redirects the app to the second method that will check the user/pass from the local server.
the problem is when resetting the text in the second method, as when i set the text at the first method everything is OK, but when changing it in the second method it doesn't change, I can set it like million times in the first method and everything going well, another thing is when i set the text at the first time from the second method it works perfectly.
Hint1: this first method is the onClick method of an OnClickListener.
Hint2: the printed log is prented like million times in the logcat so the while condition verified
public class Login extends Activity {
public EditText user, pw;
public TextView errorMessage;
private static String response = null;
private static String data;
the first method :
public void onClick(View v) {
if (v == login) {
String userName = Login.this.user.getText().toString();
String Password = Login.this.pw.getText().toString();
if (userName.equals(null_string)
|| Password.equals(null_string)) {
errorMessage.setText(R.string.request);
} else {
protocol = protocol_login;
boolean status = false;
try {
status = checkLogin(userName, Password);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
if (status) {
Intent intent = new Intent(v.getContext(),
MainPage.class);
go(intent);
} else {
errorMessage.setText(R.string.login_error);
}
}
}
}
the second method:
private String connect(String data) throws UnknownHostException,
IOException, JSONException {
setData(data);
Thread connect = new Thread(new ConnectToServer(getData()));
connect.start();
while (response == null) {
System.out.println("waiting");
errorMessage.setText(R.string.waiting);
}
return response;
}
}
Your problem lies in the fact that in the second method you are trying to update the GUI while actually being a second thread.
U can use the runOnUIThread method
Activity.runOnUiThread(new Runnable() {
#Override
public void run() {
errorMessage.setText(R.string.waiting);
}
});
while (response == null) {
System.out.println("waiting");
}
U also shouldn't set the text in a while-loop if the text isn't changing so you don't use unnecessary resources.
I'm not sure what exactly is causing your problem (you are blocking the UI thread somewhere), but there are better ways of getting a response from the server. You are essentially synchronously checking for an asynchronous response (below), because you are continuously polling whether response is not null.
Android has a useful class called AsyncTask. You give an AsyncTask some work to do on a background thread (what ConnectToServer(..) does), and when it is done, another method on the AsyncTask (called onPostExecute(..)) is called. The benefit of this over your approach is that it handles all the threading for you, and doesn't poll. There is also a method onPreExecute() which you would set your waiting text in.
N.B. checking synchronously for an asynchronous response
What I mean by this is that the response can come back at any time (asynchronously), yet you are checking for it at any point you can (synchronously). This is going to waste valuable resources on the CPU - you should get the response to tell you when it is finished rather than continually ask whether it is.
First, these two string variables are declared globally:
String userName,Password
Try this easy Asyntask method:
private class SetDataOfWebService extends AsyncTask<Void, Void,Boolean> {
ProgressDialog pDialog;
boolean success = false;
ConnectivityManager connectivity;
#Override
protected void onPreExecute() {
pDialog = new ProgressDialog(MailSettings.this);
pDialog.setMessage("Please Wait..");
pDialog.show();
}
#Override
protected Boolean doInBackground(Void... params) {
if (isNetworkAvailable()) {
success = true;
if (userName.length()>0 || Password.length()>0) {
status = checkLogin(userName, Password);
}
else
{
errorMessage.setText(R.string.request);
}
} else {
success = false;
}
return success;
}
#Override
protected void onPostExecute(Boolean result) {
if (pDialog != null && pDialog.isShowing())
pDialog.dismiss();
if (result) {
if (status) {
Intent intent = new Intent(v.getContext(),
MainPage.class);
go(intent);
} else {
errorMessage.setText(R.string.login_error);
}
} else {
return;
}
}
public boolean isNetworkAvailable() {
connectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null) {
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
Log.i("Class", info[i].getState().toString());
if (info[i].getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
}
}
return false;
}
}
////Calling This Function On Button CLick Like This
userName = Login.this.user.getText().toString();
Password = Login.this.pw.getText().toString();
new SetDataOfWebService().execute();
Related
I'm currently making a small app and I'm getting stuck on changing fragments using an onClick listener. I've searched the site and could find similar situations, but none of the proposed solutions worked.
So, when a user logs in, it sets a few values in SharedPreferences such as username, email and account level using a method from a class used to set and get SharedPreferences values. Afterwards, it should automatically redirect the user to a different Fragment. What's not happening, is redirecting the user to the other fragment.
I'm using AsyncTask for accessing the database. This is my code for the Login AsyncTask:
public class LoginSync extends AsyncTask <String, Void, String> {
AlertDialog dialog;
Context context;
String result;
JSONObject jObject;
String username, password;
String jEmail, jLevel;
public LoginSync(Context context){
this.context = context;
}
#Override
protected void onPreExecute() {
dialog = new AlertDialog.Builder(context).create();
dialog.setTitle("Login Status");
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if(result.equals("login")) {
dialog.setMessage("Logged in successfully!");
}else{
dialog.setMessage("Failed to login! Please check username/password.");
}
dialog.show();
}
#Override
protected String doInBackground(String... voids) {
username = voids[0];
password = voids[1];
String connstr = "URL HERE";
try{
URL url = new URL(connstr);
HttpURLConnection http = (HttpURLConnection) url.openConnection();
http.setRequestMethod("POST");
http.setDoInput(true);
http.setDoOutput(true);
OutputStream ops = http.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(ops, StandardCharsets.UTF_8));
String data = URLEncoder.encode("username","UTF-8")+"="+URLEncoder.encode(username,"UTF-8")
+"&&"+URLEncoder.encode("password","UTF-8")+"="+URLEncoder.encode(password,"UTF-8");
writer.write(data);
writer.flush();
writer.close();
ops.close();
InputStream ips = http.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(ips, StandardCharsets.ISO_8859_1));
String line;
while((line = reader.readLine()) != null){
jObject = new JSONObject(line);
result = "false";
if (jObject != null){
jEmail = jObject.getString("email");
jLevel = jObject.getString("account_level");
result = "login";
}
}
if(result.equals("login")) {
AppPreferences.setUserInfo(context.getApplicationContext(), username,jEmail,jLevel);
AppPreferences.setLoggedStatus(context.getApplicationContext(), true);
}
reader.close();
ips.close();
http.disconnect();
return result;
}catch (MalformedURLException e){
result = e.getMessage();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
}
Using the debugger, I see that the values are being set as intended in the SharedPreferences. However, in the onClick check on the Login Fragment, it's set to false until the onClick method ends.
This is my Login Fragment code:
public class LoginFragment extends Fragment {
private FragmentLoginBinding binding;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentLoginBinding.inflate(inflater, container, false);
View root = binding.getRoot();
binding.btnLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String user = binding.username.getText().toString().trim();
final String pass = binding.password.getText().toString().trim();
LoginSync login = new LoginSync(getActivity());
login.execute(user,pass);
if(AppPreferences.getLoggedStatusBool(getActivity()).equals(true)){
NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_home);
}
}
});
binding.lnkRegister.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_register);
}
});
return root;
}
#Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
On the first click, the values are set correctly, but checking the onClick with the debugger tells me that it's still false, after running the AsyncTask, and it doesn't trigger the fragment change in the if clause. On the second click, it changes the fragment.
What am I doing wrong? How can I make it change the fragment on the same click as it sets the information?
Thank you.
You are getting correct value from sharedPreference, only your timing to get that value is not correct. You are using async task, which works on a different thread. in your onCLick you have these lines:
LoginSync login = new LoginSync(getActivity());
login.execute(user,pass);
if(AppPreferences.getLoggedStatusBool(getActivity()).equals(true)){
NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_home);
}
you must have assumed that your if statement will execute after your login async task is completed, but this will not happen, it will execute straight after starting the login process and will check the sharedPref before the value is even set. You are doing network call and IO operation which will take some time and shared pref should be checked after the async task has been completed. So yo should write your if statement in async class's onPostExecute method like this:
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if(result.equals("login")) {
dialog.setMessage("Logged in successfully!");
if(AppPreferences.getLoggedStatusBool(getActivity()).equals(true)){
NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_home);
}
}else{
dialog.setMessage("Failed to login! Please check username/password.");
}
dialog.show();
}
I am writing an IRC Client. The socket connection to the IRC Server is handled via a service. I have managed to stabilize all the UI elements of the Activities in question during the orientation change, but somehow the socket that is maintained by the service is being closed during the change.
Here is what I believe to be the relevant code. Please let me know if you need to see more.
//This is the Service in question
public class ConnectionService extends Service{
private BlockingQueue<String> MessageQueue;
public final IBinder myBind = new ConnectionBinder();
public class ConnectionBinder extends Binder {
ConnectionService getService() {
return ConnectionService.this;
}
}
private Socket socket;
private BufferedWriter writer;
private BufferedReader reader;
private IRCServer server;
private WifiManager.WifiLock wLock;
private Thread readThread = new Thread(new Runnable() {
#Override
public void run() {
try {
String line;
while ((line = reader.readLine( )) != null) {
if (line.toUpperCase().startsWith("PING ")) {
SendMessage("PONG " + line.substring(5));
}
else
queueMessage(line);
}
}
catch (Exception e) {}
}
});
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(MessageQueue == null)
MessageQueue = new LinkedBlockingQueue<String>();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
return myBind;
}
#Override
public boolean stopService(Intent name) {
try {
socket.close();
wLock.release();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return super.stopService(name);
}
#Override
public void onDestroy()
{//I put this here so I had a breakpoint in place to make sure this wasn't firing instead of stopService
try {
socket.close();
wLock.release();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
super.onDestroy();
}
public void SendMessage(String message)
{
try {
writer.write(message + "\r\n");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public String readLine()
{
try {
if(!isConnected())
return null;
else
return MessageQueue.take();
} catch (InterruptedException e) {
return "";
}
}
public boolean ConnectToServer(IRCServer newServer)
{
try {
//create a new message queue (connecting to a new server)
MessageQueue = new LinkedBlockingQueue<String>();
//lock the wifi
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "LockTag");
wLock.acquire();
server = newServer;
//connect to server
socket = new Socket();
socket.setKeepAlive(true);
socket.setSoTimeout(60000);
socket.connect(new InetSocketAddress(server.NAME, Integer.parseInt(server.PORT)), 10000);
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//run basic login scripts.
if(server.PASS != "")
SendMessage("PASS " + server.PASS);
//write nickname
SendMessage("NICK " + server.NICK);
//write username login
SendMessage("USER " + server.NICK + " 0 * :Fluffy IRC");
String line;
while ((line = reader.readLine( )) != null) {
if (line.indexOf("004") >= 0) {
// We are now logged in.
break;
}
else if (line.indexOf("433") >= 0) {
//change to alt Nick
if(!server.NICK.equals(server.ALT_NICK) && !server.ALT_NICK.equals(""))
{
server.NICK = server.ALT_NICK;
SendMessage("NICK " + server.NICK);
}
else
{
queueMessage("Nickname already in use");
socket.close();
return false;
}
}
else if (line.toUpperCase().startsWith("PING ")) {
SendMessage("PONG " + line.substring(5));
}
else
{
queueMessage(line);
}
}
//start the reader thread AFTER the primary login!!!
CheckStartReader();
if(server.START_CHANNEL == null || server.START_CHANNEL == "")
{
server.WriteCommand("/join " + server.START_CHANNEL);
}
//we're done here, go home everyone
} catch (NumberFormatException e) {
return false;
} catch (IOException e) {
return false;
}
return true;
}
private void queueMessage(String line) {
try {
MessageQueue.put(line);
} catch (InterruptedException e) {
}
}
public boolean isConnected()
{
return socket.isConnected();
}
public void CheckStartReader()
{
if(this.isConnected() && !readThread.isAlive())
readThread.start();
}
}
//Here are the relevant portions of the hosting Activity that connects to the service
//NOTE: THE FOLLOWING CODE IS PART OF THE ACTIVITY, NOT THE SERVICE
private ConnectionService conn;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
conn = ((ConnectionService.ConnectionBinder)service).getService();
Toast.makeText(main_tab_page.this, "Connected", Toast.LENGTH_SHORT)
.show();
synchronized (_serviceConnWait) {
_serviceConnWait.notify();
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
conn = null;
}
};
#Override
protected void onSaveInstanceState(Bundle state){
super.onSaveInstanceState(state);
state.putParcelable("Server", server);
state.putString("Window", CurrentTabWindow.GetName());
unbindService(mConnection);
}
#Override
protected void onDestroy()
{
super.onDestroy();
if(this.isFinishing())
stopService(new Intent(this, ConnectionService.class));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_tab_page);
localTabHost = (TabHost)findViewById(R.id.tabHostMain);
localTabHost.setup();
localTabHost.setOnTabChangedListener(new tabChange());
_serviceConnWait = new Object();
if(savedInstanceState == null)
{//initial startup, coming from Intent to start
//get server definition
server = (IRCServer)this.getIntent().getParcelableExtra(IRC_WINDOW);
server.addObserver(this);
AddTabView(server);
startService(new Intent(this, ConnectionService.class));
}
else
{
server = (IRCServer)savedInstanceState.getParcelable("Server");
String windowName = savedInstanceState.getString("Window");
//Add Needed Tabs
//Server
if(!(windowName.equals(server.GetName())))
AddTabView(server);
//channels
for(IRCChannel c : server.GetAllChannels())
if(!(windowName.equals(c.GetName())))
AddTabView(c);
//reset each view's text (handled by tabChange)
if(windowName.equals(server.GetName()))
SetCurrentTab(server.NAME);
else
SetCurrentTab(windowName);
ResetMainView(CurrentTabWindow.GetWindowTextSpan());
//Rebind to service
BindToService(new Intent(this, ConnectionService.class));
}
}
#Override
protected void onStart()
{
super.onStart();
final Intent ServiceIntent = new Intent(this, ConnectionService.class);
//check start connection service
final Thread serverConnect = new Thread(new Runnable() {
#Override
public void run() {
if(!BindToService(ServiceIntent))
return;
server.conn = conn;
conn.ConnectToServer(server);
server.StartReader();
if(server.START_CHANNEL != null && !server.START_CHANNEL.equals(""))
{
IRCChannel chan = server.FindChannel(server.START_CHANNEL);
if(chan != null)
{
AddTabView(chan);
}
else
{
server.JoinChannel(server.START_CHANNEL);
chan = server.FindChannel(server.START_CHANNEL);
AddTabView(chan);
}
}
}
});
serverConnect.start();
}
private boolean BindToService(Intent ServiceIntent)
{
int tryCount = 0;
bindService(ServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
while(conn == null && tryCount < 10)
{
tryCount++;
try {
synchronized (_serviceConnWait) {
_serviceConnWait.wait(1500);
}
}
catch (InterruptedException e) {
//do nothing
}
}
return conn != null;
}
Im not entirely certain what I am doing wrong there. Obviously there's something I'm missing, haven't found yet, or haven't even thought to check. What happens though is that after the orientation change my Send command gives me this message and nothing happens:
06-04 22:02:27.637: W/System.err(1024): java.net.SocketException: Socket closed
06-04 22:02:27.982: W/System.err(1024): at com.fluffyirc.ConnectionService.SendMessage(ConnectionService.java:90)
I have no idea when the socket is getting closed, or why.
Update
I have changed the code so that rather than binding to the service and using that to start it, instead I call startService and stopService at appropriate points as well as binding to it, on the thought that the service was being destroyed when the binding was lost. This is working exactly like it was before I changed it. The socket still closes on an orientation change, and I have no idea why.
Update :- Code and description
I added the code changes recently made for Start/Stop service and START_STICKY. I also recently read a very good article explaining how the orientation change process flow works and why its NOT a bad idea to add the android:configChanges="orientation|screenSize" line to your manifest. So this fixed the orientation issue, but its still doing the same thing if I put the activity into background mode, and then bring it back to the foreground. That still follows the same Save/Destroy/Create process that the orientation does without that manifest line...and it still closes my socket, and I still don't know why.
I do know that it doesn't close the socket until the re-create process...I know this because the message queue will display messages that were received while the app was in the background, but once I bring it back forward it closes the socket and nothing else can be sent or received.
'Socket closed' means that you closed the socket and then continued to use it. It isn't a 'disconnect'.
You need to put something into that catch block. Never just ignore an exception. You might get a surprise when you see what the exception actually was.
NB Socket.isConnected() doesn't tell you anything about the state of the connection: only whether you have ever connected the Socket. You have, so it returns true.
SO currently i have an AsyncTask class that runs and POST's data to my server when I click a button(which works great).
What im trying to do now is handle what happens when the user is not connected to the internet. so i have set up these classes to notify the app when internet has connected so that the data can be sent automatically to the server.
AsyncTask class(inner class)
private class HttpAsyncTask extends AsyncTask<String, Void, String> {
ProgressDialog dialog = new ProgressDialog(getActivity());
final AlertDialog finishedDialog = new AlertDialog.Builder(getActivity())
.setCancelable(false)
.setPositiveButton(android.R.string.ok, null)
.create();
#Override
protected String doInBackground(String... urls) {
onProgressUpdate("Uploading Data...");
return POST(urls[0]);
}
#Override
protected void onPreExecute() {
this.dialog.show();
finishedDialog.setOnShowListener(new DialogInterface.OnShowListener(){
#Override
public void onShow(DialogInterface dialog) {
Button b = finishedDialog.getButton(AlertDialog.BUTTON_POSITIVE);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// navigate to match summary.....
finishedDialog.dismiss();
}
});
}
});
}
protected void onProgressUpdate(String msg) {
dialog.setMessage(msg);
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(String result) {
if (result != ""){
finishedDialog.setTitle("Upload Complete!");
finishedDialog.setMessage("Data Sent Successfully");
finishedDialog.show();
dialog.dismiss();
editor.clear();
editor.commit();
//Toast.makeText(getActivity().getBaseContext(), result, Toast.LENGTH_LONG).show();
}else
{
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
finishedDialog.setTitle("Upload Failed!");
finishedDialog.setMessage("Data Will Automatically Be Uploaded When Internet Connection Is Available");
finishedDialog.show();
dialog.dismiss();
}}, 1000);
setFlag(true);
}
}
}
public static boolean getFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String POST(String url){
InputStream inputStream = null;
String result = "";
try {
// 1. create HttpClient
HttpClient httpclient = new DefaultHttpClient();
// 2. make POST request to the given URL
HttpPost httpPost = new HttpPost(url);
if(adapter.updateNeeded()){
JSONObject main = new JSONObject(exmaplePrefs.getString("jsonString", "cant find json"));
JSONObject dbUpdates = new JSONObject(exmaplePrefs.getString("ChangesJSON", "cant find Changejson"));
main.put("Team_Updates", dbUpdates);
json = main.toString();
}else{
json = exmaplePrefs.getString("jsonString", "cant find json");
// String json = "{\"twitter\":\"test\",\"country\":\"test\",\"name\":\"test\"}";
}
// 5. set json to StringEntity
StringEntity se = new StringEntity(json);
se.setContentType("application/json;charset=UTF-8");
// 6. set httpPost Entity
httpPost.setEntity(se);
// 7. Set some headers to inform server about the type of the content
// httpPost.setHeader("Accept", "application/json");
// httpPost.setHeader("Content-type", "application/json");
// httpPost.setHeader("json", json);
// 8. Execute POST request to the given URL
HttpResponse httpResponse = httpclient.execute(httpPost);
// 9. receive response as inputStream
inputStream = httpResponse.getEntity().getContent();
String status = httpResponse.getStatusLine().toString();
// 10. convert inputstream to string
if (!status.equals("HTTP/1.1 500 Internal Server Error")){
if(inputStream != null){
result = convertInputStreamToString(inputStream);
}
else{
result = "Did not work!";
}
}else{
System.out.println("500 Error");
}
} catch (Exception e) {
Log.d("InputStream", e.getLocalizedMessage());
System.out.println("eerroorr "+e);
}
// 11. return result
System.out.println(result);
return result;
}
private static String convertInputStreamToString(InputStream inputStream) throws IOException{
BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(inputStream));
String line = "";
String result = "";
while((line = bufferedReader.readLine()) != null)
result += line;
inputStream.close();
return result;
}
}
NetworkUtil class
public class NetworkUtil {
public static int TYPE_WIFI = 1;
public static int TYPE_MOBILE = 2;
public static int TYPE_NOT_CONNECTED = 0;
public static int getConnectivityStatus(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (null != activeNetwork) {
if(activeNetwork.getType() == ConnectivityManager.TYPE_WIFI)
return TYPE_WIFI;
if(activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE)
return TYPE_MOBILE;
}
return TYPE_NOT_CONNECTED;
}
public static String getConnectivityStatusString(Context context) {
int conn = NetworkUtil.getConnectivityStatus(context);
String status = null;
if (conn == NetworkUtil.TYPE_WIFI) {
status = "Wifi enabled";
} else if (conn == NetworkUtil.TYPE_MOBILE) {
status = "Mobile data enabled";
} else if (conn == NetworkUtil.TYPE_NOT_CONNECTED) {
status = "Not connected to Internet";
}
return status;
}
}
BroadcastReceiver class
public class NetworkChangeReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
intent.getExtras();
String status = NetworkUtil.getConnectivityStatusString(context);
Toast.makeText(context, status, Toast.LENGTH_LONG).show();
if(MatchFragment.getFlag()){
//send data
}
}
}
So in the BroadcastReceiver class I check the flag that gets set to true when the app attempts to send data but there is not internet (onPostExecute in AsyncTask Class).
so what want to do is some how call the POST method. do i have to create a new Async task class? Im a bit stumped here .
Thanks
Using AsyncTask in BroadcastReceiver is a bad practice.
You should use Service because Android OS may kill your process or onReceive() may run to completion before asyncTask will return result, so there is no guarantee you will get the expected result.
You shouldn't use AsyncTask in Broadcast Receiver because the system can kill your process after returning from onReceive method (if there is no any active service or activity).
Proof link
Official documentation recommends IntentService for such cases (see paragraph about Broadcast Receivers).
The other answers are not correct according to Google's documentation. The Broadcast Receivers developer guide explicitly calls out that you can use AsyncTasks from BroadcastReceivers if you call goAsync() first and report the status to the pending result in the AsyncTask
For this reason, you should not start long running background threads from a broadcast receiver. After onReceive(), the system can kill the process at any time to reclaim memory, and in doing so, it terminates the spawned thread running in the process. To avoid this, you should either call goAsync() (if you want a little more time to process the broadcast in a background thread) or schedule a JobService from the receiver using the JobScheduler, so the system knows that the process continues to perform active work.
And later it clarifies how much time you actually get:
Calling goAsync() in your receiver's onReceive() method and passing
the BroadcastReceiver.PendingResult to a background thread. This keeps
the broadcast active after returning from onReceive(). However, even
with this approach the system expects you to finish with the broadcast
very quickly (under 10 seconds). It does allow you to move work to
another thread to avoid glitching the main thread.
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadcastReceiver";
#Override
public void onReceive(final Context context, final Intent intent) {
final PendingResult pendingResult = goAsync();
AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
#Override
protected String doInBackground(String... params) {
StringBuilder sb = new StringBuilder();
sb.append("Action: " + intent.getAction() + "\n");
sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
Log.d(TAG, log);
// Must call finish() so the BroadcastReceiver can be recycled.
pendingResult.finish();
return data;
}
};
asyncTask.execute();
}
}
I added the AsyncTask to offload network operations to a background thread.I need to make sure the UI operations are on the UI thread.So i want to Use runOnUiThread() in my Activity.
Thanks for your help
WifiApManager
public class WifiApManager {
private final WifiManager mWifiManager;
public WifiApManager(Context context) {
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
}
public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
try {
if (enabled) { // disable WiFi in any case
mWifiManager.setWifiEnabled(false);
}
Method method = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
return (Boolean) method.invoke(mWifiManager, wifiConfig, enabled);
} catch (Exception e) {
Log.e(this.getClass().toString(), "wifi", e);
return false;
}
}
public WIFI_AP_STATE getWifiApState() {
try {
Method method = mWifiManager.getClass().getMethod("getWifiApState");
int tmp = ((Integer)method.invoke(mWifiManager));
// Fix for Android 4
if (tmp > 10) {
tmp = tmp - 10;
}
return WIFI_AP_STATE.class.getEnumConstants()[tmp];
} catch (Exception e) {
Log.e(this.getClass().toString(), "wifi", e);
return WIFI_AP_STATE.WIFI_AP_STATE_FAILED;
}
}
public boolean isWifiApEnabled() {
return getWifiApState() == WIFI_AP_STATE.WIFI_AP_STATE_ENABLED;
}
public WifiConfiguration getWifiApConfiguration() {
try {
Method method = mWifiManager.getClass().getMethod("getWifiApConfiguration");
return (WifiConfiguration) method.invoke(mWifiManager);
} catch (Exception e) {
Log.e(this.getClass().toString(), "wifi", e);
return null;
}
}
public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
try {
Method method = mWifiManager.getClass().getMethod("setWifiApConfiguration", WifiConfiguration.class);
return (Boolean) method.invoke(mWifiManager, wifiConfig);
} catch (Exception e) {
Log.e(this.getClass().toString(), "wifi", e);
return false;
}
}
public ArrayList<ClientScanResult> getClientList(boolean onlyReachables) {
return getClientList(onlyReachables, 10);
}
public ArrayList<ClientScanResult> getClientList(boolean onlyReachables, int reachableTimeout) {
BufferedReader br = null;
ArrayList<ClientScanResult> result = null;
try {
result = new ArrayList<ClientScanResult>();
br = new BufferedReader(new FileReader("/proc/net/arp"));
String line;
while ((line = br.readLine()) != null) {
String[] splitted = line.split(" +");
if ((splitted != null) && (splitted.length >= 4)) {
// Basic sanity check
String mac = splitted[3];
if (mac.matches("..:..:..:..:..:..")) {
boolean isReachable = InetAddress.getByName(splitted[0]).isReachable(reachableTimeout);
if (!onlyReachables || isReachable) {
result.add(new ClientScanResult(splitted[0], splitted[3], splitted[5], isReachable));
}
}
}
}
} catch (Exception e) {
Log.e(LOGTAG, e.toString());
} finally {
try {
br.close();
} catch (IOException e) {
Log.e(LOGTAG, e.toString());
}
}
return result;
}
}
connect.java
public class connect extends Activity{
WifiApManager wifiApManager;
TextView tv;
Button scan;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.connect);
tv =(TextView) findViewById(R.id.iptv);
new scan().execute();
}
public class scan extends AsyncTask<String, Integer, String> {
public Object WIFI_SERVICE;
#Override
protected void onProgressUpdate(Integer...integers) {
ArrayList<ClientScanResult> clients = wifiApManager.getClientList(false);
tv.setText("WifiApState: " + wifiApManager.getWifiApState() + "\n\n");
tv.append("Clients: \n");
for (ClientScanResult clientScanResult : clients) {
tv.append("####################\n");
tv.append("IpAddr: " + clientScanResult.getIpAddr() + "\n");
tv.append("Device: " + clientScanResult.getDevice() + "\n");
tv.append("HWAddr: " + clientScanResult.getHWAddr() + "\n");
tv.append("isReachable: " + clientScanResult.isReachable()+ "\n");
}
}
#Override
protected void onPostExecute(String result){
tv.setText(result);
}
#Override
protected String doInBackground(String... params) {
wifiApManager = new WifiApManager(this);
// the above line shows a Error
return null;
}
}
}
EDIT
I want to Display the processed text in a TextView
class scan extends AsyncTask<String, Void, Void> {
public Context context;
ArrayList<ClientScanResult> clients;
public scan(Context c) // constructor to take Context
{
context = c; // Initialize your Context variable
}
protected Void doInBackground(String... params) {
wifiApManager = new WifiApManager(context); // use the variable here
clients = wifiApManager.getClientList(false);
return null;
}
}
protected void onPostExecute(Void result){
ArrayList<ClientScanResult> clients;
tv.setText("WifiApState: " + wifiApManager.getWifiApState() + "\n\n");
tv.append("Clients: \n");
for (ClientScanResult clientScanResult : clients)//showin error in clients
{
tv.append("####################\n");
tv.append("IpAddr: " + clientScanResult.getIpAddr() + "\n");
tv.append("Device: " + clientScanResult.getDevice() + "\n");
tv.append("HWAddr: " + clientScanResult.getHWAddr() + "\n");
tv.append("isReachable: " + clientScanResult.isReachable()+ "\n");
}
}
}
I added the AsyncTask to offload network operations to a background thread.I need to make sure the UI operations are on the UI thread.So i want to Use runOnUiThread() in my Activity.
Ugh! No!!! Every method of AsyncTask runs on the UI Thread except for doInBackground(). So do your network operations in doInBackground() and update the UI in onPostExecute() or in onProgressUpdate() by calling publishProgress() from doInBackground().
Do not use runOnUiThread() with AsyncTask. There is no reason, at least known to me, to use that with AsyncTask since it has methods that already run on the UI Thread. I have never seen it do anything but cause trouble.
You can either call publishProgress() from your loop and update your TextView in onProgressUpdate() or add the values to an ArrayList and update in onProgressUpdate().
Please read the docs several times. AsyncTask is a bit tricky at first but once you learn what it does then it can be a beautiful thing.
Edit
Create an instance of your AsyncTask and pass your Activity Context to it
Scan myScan = new scan(this); // pass the context to the constructor
myScan.execute();
Then create a constructor in your AsyncTask and have it accept a Context.
public class scan extends AsyncTask<String, Integer, String>
{
public Object WIFI_SERVICE;
public Context context; // Context variable
public scan(Context c) // constructor to take Context
{
context = c; // intialize your Context variable
}
Now use that variable
#Override
protected String doInBackground(String... params)
{
wifiApManager = new WifiApManager(context); // use the variable here
return null;
}
Another Edit
class scan extends AsyncTask<String, Void, Void> {
ArrayList<ClientScanResult> clients;
Context context;
...
then initialize your `clients` in `doInBackground()`
clients = wifiApManager.getClientList(false);
change onPostExecute() to not accept anything
protected void onPostExecute(Void result){
and put your code that updates the TextView in there.
You should not be accessing any UI elements within doInBackground, This method runs in background thread. What you should do is override onPostExecute() method and access your TextView there. onPostExecute runs in UI thread, so you don't need to call runOnUiThread()
I have an app that crashes when the wifi on phone goes out of range. It gets strings from an online txt mainly and I do disconnect the HTTPURLConnection after it gets done, so I was not expecting the crash. Below is the relevant code;
To check network availablity(all code that uses an internet connection gets checked by this first):
public boolean isNetworkAvailable() {
ConnectivityManager cm =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
//if (netInfo != null && netInfo.isConnected()) {
return true;
}
return false;
}
My checkForPromptPassword asynctask that runs on onResume:
private class CheckForPromptPasswordAgain extends AsyncTask<Void, Void, Boolean>
{
// ProgressDialog pdLoading = new ProgressDialog(MainScreenActivity.this);
#Override
protected void onPreExecute() {
super.onPreExecute();
//this method will be running on UI thread
// pdLoading.setMessage("\tFetching Database...");
// pdLoading.show();
}
#Override
protected Boolean doInBackground(Void... params) {
//this method will be running on background thread so don't update UI frome here
//do your long running http tasks here,you dont want to pass argument and u can access the parent class' variable url over here
//view GONE by default of update button
if (isNetworkAvailable()){
if (PromptForPasswordAgain()){
//TODO: //show update button or dialog
return true;
}else{
//TODO: //proceed as normal
return false;
}
}
return false;
}
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
//this method will be running on UI thread
if (result==true){
promptForPassword();
}else{
// showDialog("string", "string",0);
}
// pdLoading.dismiss();
}
}
My checkForUpdate AsyncTask that runs on OnResume():
Private class CheckForUpdate extends AsyncTask<Void, Void, Boolean>
{
// ProgressDialog pdLoading = new ProgressDialog(MainScreenActivity.this);
#Override
protected void onPreExecute() {
super.onPreExecute();
//this method will be running on UI thread
// pdLoading.setMessage("\t string.");
// pdLoading.show();
}
#Override
protected Boolean doInBackground(Void... params) {
//this method will be running on background thread so don't update UI frome here
//do your long running http tasks here,you dont want to pass argument and u can access the parent class' variable url over here
//view GONE by default of update button
if (fileExistance("data.txt")){
try {
if (isNetworkAvailable()){
if (isDatabaseContentDifferent()){
//TODO: //show update button or dialog
return true;
}else{
//TODO: //proceed as normal
return false;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
Log.i("error","file data.txt does not exist in internal");
return false;
}
return false;
}
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
//this method will be running on UI thread
Button updateButton = (Button) findViewById(R.id.UpdateDatabase);
updateButton.setVisibility(View.GONE);
if (result==true){
showDialog("Database Updated On Server", "The Database App has detected a change in the database, press \"Update Database\" to account for the change(s). ",0);
updateButton.setVisibility(View.VISIBLE);
}else{
updateButton.setVisibility(View.GONE);
// showDialog("No Update Detected", "The Database App has detected a change in the database, press \"Update Database\" to account for the change(s). ",0);
}
// pdLoading.dismiss();
}
}
Example of the HTTPURLClient that I am using, many functions but this is the basic structure:
public boolean isDatabaseContentDifferent() throws IOException{
String page = null;
try{
URL url = new URL(_data);
HttpURLConnection.setFollowRedirects(true);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(false);
con.setReadTimeout(20000);
con.setRequestProperty("Connection", "keep-alive");
//get etag for update check
//String etag= "";
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0");
((HttpURLConnection) con).setRequestMethod("GET");
//System.out.println(con.getContentLength()) ;
con.setConnectTimeout(5000);
BufferedInputStream in = new BufferedInputStream(con.getInputStream());
//make seperate function for etag it doesn't work with GET
//String etag = con.getHeaderField("etag");
int responseCode = con.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
System.out.println(responseCode);
}
StringBuffer buffer = new StringBuffer();
int chars_read;
//int total = 0;
while ((chars_read = in.read()) != -1)
{
char g = (char) chars_read;
buffer.append(g);
}
page = buffer.toString();
//create password.txt to internal
//TODO: checkkk
con.disconnect();
}catch(Exception e){
showDialog("Database Fetch Failure","Unable to Fetch Password Database, check your internet" +
" connection and try again later.",0);
Log.i("Page", "Error in isDatabaseContentDifferent()");
return false;
}
if (fileExistance("data.txt")){
if (isTextInFileDifferent(page,"data.txt")){
return true;
}else{
return false;
}
}else{
Log.i("Page","file data.txt does not exist IN isDatabaseContentDifferent()");
return false;
}
}
Any help will be highly appreciated. Thanks.