Can't get a fixed value on a final hashmap in android - java

I am developing a an android app that uses service discovery over wifi direct,based on the provided google sample code, the problem is i can't get a fixed value of the advertised TXT record even though i'm using a final hashmap to store the value, the Hashmap is modified every time the onDnsSdTxtRecordAvailable callback is called, even though i'm using a final hashmap declared globally to store the first value, it gets replaced by null. here is the code i'm using. thanks
private WifiP2pManager manager;
private final IntentFilter intentFilter = new IntentFilter();
private Channel channel;
private BroadcastReceiver receiver = null;
private WifiP2pDnsSdServiceRequest serviceRequest;
private TextView statusTxtView;
private TextView services;
private Button register;
private Button broadcast;
private LocationManager locationManager;
final HashMap<String, String> buddies = new HashMap<String, String>();
private TextView locationTxt;
public String value1;
private Button loc;
private final String name = new String();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
statusTxtView = (TextView) findViewById(R.id.status);
services = (TextView) findViewById(R.id.services);
register = (Button) findViewById(R.id.discover);
broadcast = (Button) findViewById(R.id.bd);
locationTxt = (TextView)findViewById(R.id.location);
loc = (Button)findViewById(R.id.loc);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter
.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
intentFilter
.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
channel = manager.initialize(this, getMainLooper(), null);
startRegistrationAndDiscovery();
register.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startRegistrationAndDiscovery();
}
});
broadcast.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
discoverService();
}
});
loc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
doIt();
}
});
}
#Override
public void onResume() {
super.onResume();
receiver = new MyReceiver(manager, channel, this);
registerReceiver(receiver, intentFilter);
}
#Override
public void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
public void appendStatus(String status) {
String current = statusTxtView.getText().toString();
statusTxtView.setText(current + "\n" + status);
}
//service discvery handllng methods
private void startRegistrationAndDiscovery() {
Map<String, String> record = new HashMap<String, String>();
record.put(TXTRECORD_PROP_AVAILABLE, "visible");
WifiP2pDnsSdServiceInfo service = WifiP2pDnsSdServiceInfo.newInstance(
SERVICE_INSTANCE, SERVICE_REG_TYPE, record);
manager.addLocalService(channel, service, new ActionListener() {
#Override
public void onSuccess() {
appendStatus("Added Local Service");
}
#Override
public void onFailure(int error) {
appendStatus("Failed to add a service");
}
});
discoverService();
}
private void discoverService() {
/*
* Register listeners for DNS-SD services. These are callbacks invoked
* by the system when a service is actually discovered.
*/
manager.setDnsSdResponseListeners(channel,
new DnsSdServiceResponseListener() {
#Override
public void onDnsSdServiceAvailable(String instanceName,
String registrationType, WifiP2pDevice srcDevice) {
// A service has been discovered. Is this our app?
if (instanceName.equalsIgnoreCase(SERVICE_INSTANCE)) {
// update the UI and add the item the discovered
// device.
/* WiFiP2pService service = new WiFiP2pService();
service.device = srcDevice;
service.instanceName = instanceName;
service.serviceRegistrationType = registrationType;*/
//put data to textview here
services.setText(srcDevice.status+" Instance name " + instanceName + " type " + registrationType);
Log.d(TAG, "onBonjourServiceAvailable "
+ instanceName);
Http_client http_client = new Http_client(SERVICE_INSTANCE);
appendStatus("data sent to server successfully");
}
}
}, new DnsSdTxtRecordListener() {
/**
* A new TXT record is available. Pick up the advertised
* buddy name.
*/
#Override
public void onDnsSdTxtRecordAvailable(String fullDomainName, Map<String, String> record,WifiP2pDevice device) {
Log.d(TAG, device.deviceName + " is " + record.get(TXTRECORD_PROP_AVAILABLE));
buddies.put("mm",record.get(TXTRECORD_PROP_AVAILABLE));
Toast.makeText(getBaseContext(),record.get(TXTRECORD_PROP_AVAILABLE),Toast.LENGTH_LONG).show();
//
}
});
// After attaching listeners, create a service request and initiate
// discovery.
serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
manager.addServiceRequest(channel, serviceRequest,
new ActionListener() {
#Override
public void onSuccess() {
appendStatus("Added service discovery request");
}
#Override
public void onFailure(int arg0) {
appendStatus("Failed adding service discovery request");
}
});
manager.discoverServices(channel, new ActionListener() {
#Override
public void onSuccess() {
appendStatus("Service discovery initiated");
}
#Override
public void onFailure(int arg0) {
appendStatus("Service discovery failed");
}
});
}
public void doIt(){
buddies.put("mm","value1");
Toast.makeText(getBaseContext(),buddies.get("mm"),Toast.LENGTH_LONG).show();
buddies.put("mm","value2");
Toast.makeText(getBaseContext(),buddies.get("mm"),Toast.LENGTH_LONG).show();
}

A final variable will not help you, as I have commented. Based on your question, what you need to do is to always check if the value is set in your hashmap, before saving the new returned variable. Something like
if (!buddies.containKey("mm"))
buddies.put("mm", .....)

Related

display api data with realm not working android studios

I am trying to use realm database to display my api data. I want to display the company name, however the data is saids it is inserted in the log but cant seem to display the data on the UI. Here is the code..
Any help would be greatly appreciated with this problem. The variables are at the top and the problem is when it hits on success, ive written the code "write to DB", but it doesnt display the data but tells me the data has been inserted.
// Variables for the search input field and results TextViews.
private EditText mCompanyInput;
private TextView mTitleText;
private TextView mDescriptionText;
private TextView mOfficerText;
private TextView mTitleText1;
private TextView mDescriptionText1;
private OkHttpClient okHttpClient;
private static final String TAG = "MainActivity";
private Request request;
private String url = "https://api.companieshouse.gov.uk/search/companies?q=";
Button save;
TextView log;
Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCompanyInput = findViewById(R.id.companyInput);
log = findViewById(R.id.log);
mDescriptionText = findViewById(R.id.descriptionText);
mOfficerText = findViewById(R.id.officerText);
mTitleText1 = findViewById(R.id.titleText1);
mTitleText = findViewById(R.id.titleText);
mDescriptionText1 = findViewById(R.id.descriptionText1);
save = findViewById(R.id.searchButton);
realm = Realm.getDefaultInstance();
save.setOnClickListener(this);
}
public void onClick(View view){
okHttpClient = new OkHttpClient();
request = new Request.Builder().url(url).header("Authorization", "k6DNRbTp-AnQWn51JBz5VuPiTl8jv4_etdzoMyhf") .method("GET", null).build();
Log.d(TAG, "onClick:"+url);
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.i(TAG, e.getMessage());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG,response.body().string());
Log.d(TAG, "onResponse:"+response.code());
}
});
writeToDB(mCompanyInput.getText().toString().trim(), (mDescriptionText.getText().toString().trim()));
showData();
}
public void showData(){
RealmResults<Company> guests = realm.where(Company.class).findAll();
// Use an iterator to invite all guests
String op="";
for (Company guest : guests) {
op+=guest.getName();
op+=guest.getAppointments();
}
log.setText(op);
}
public void writeToDB(final String mTitleText1, final String mDescriptionText1){
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
Company user = new Company(mTitleText1, mDescriptionText1);
bgRealm.insert(user);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
writeToDB(mCompanyInput.getText().toString().trim(), (mOfficerText.getText().toString().trim()));
showData();
// Transaction was a success.
Log.v("Database", "Data Inserted");
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
Log.e("Database", error.getMessage());
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
realm.close();
}
Why are you calling writeToDB() from the onSuccess() method? This will cause recursion and keep writing the same data into the realm. It's correct to call showData() from onSuccess(), but there's not much point calling it directly from onClick().
I think your problem though is that you're trying to update the UI from a thread: it's called from an async transaction thread and not the main thread. See this answer (and there are others you can find easily once you know the problem: Updating UI / runOnUiThread / final variables: How to write lean code that does UI updating when called from another Thread.

SharedPreferences gives default values on some devices

I am using this code to save key-value pair in shared preferences and its working fine on my device but on emulators and other real devices, it always returns the default values.
public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
public static final String USER_PREFS = "com.aamir.friendlocator.friendlocator.USER_PREFERENCE_FILE_KEY";
SharedPreferences sharedPreferences;
private static String userKey="";
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
static final int PERMISSION_ACCESS_FINE_LOCATION = 1;
boolean FINE_LOCATION_PERMISSION_GRANTED = false;
TextView textViewLocationData;
TextView textViewKeyDisplay;
Button buttonRefresh;
Button btnCopyKey;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
goToActivityFriends();
}
});
fab.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_people_white_48dp));
textViewLocationData = (TextView) findViewById(R.id.textViewLocationData);
textViewKeyDisplay =(TextView) findViewById(R.id.tvKeyDisplay);
buttonRefresh = (Button) findViewById(R.id.buttonRefresh);
btnCopyKey = (Button) findViewById(R.id.btnCopyKey);
sharedPreferences = getApplicationContext().getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE);
String key = sharedPreferences.getString("key", "");
if(!key.equals("")) {
textViewKeyDisplay.setText(key);
}
// Create an instance of GoogleAPIClient.
buildGoogleApiClient();
//user_sp = getSharedPreferences(USER_PREFS, 0);
buttonRefresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
displayLocation();
}
});
btnCopyKey.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("userKey", textViewKeyDisplay.getText().toString());
clipboard.setPrimaryClip(clip);
Toast.makeText(getBaseContext(), "Key copied !", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
protected void onStart() {
super.onStart();
if (mGoogleApiClient != null) mGoogleApiClient.connect();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
}
private void displayLocation() {
int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
if ( permissionCheck != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},PERMISSION_ACCESS_FINE_LOCATION);
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocation != null) {
double latitude = mLastLocation.getLatitude();
double longitude = mLastLocation.getLongitude();
textViewLocationData.setText(latitude + ", " + longitude);
sharedPreferences = getApplicationContext().getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE);
String key = sharedPreferences.getString("key", "");
Log.d("User Key",key);
updateServers(latitude, longitude,key);
} else {
textViewLocationData
.setText("Couldn't get the location. Make sure location is enabled on the device");
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_ACCESS_FINE_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
FINE_LOCATION_PERMISSION_GRANTED = true;
//displayLocation();
} else {
FINE_LOCATION_PERMISSION_GRANTED = false;
}
return;
}
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.i("", "Connection failed: ConnectionResult.getErrorCode() = "
+ result.getErrorCode());
}
#Override
public void onConnected(Bundle arg0) {
// Once connected with google api, get the location
//displayLocation();
}
#Override
public void onConnectionSuspended(int arg0) {
mGoogleApiClient.connect();
}
public void goToActivityFriends () {
Intent intent = new Intent(this, com.aamir.friendlocator.friendlocator.Friends.class);
startActivity(intent);
}
public void updateServers(Double lat,Double lon,String Key) {
if (Key.equals("")) {
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
SendLocation cleint = retrofit.create(SendLocation.class);
Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call = cleint.registerUser(String.valueOf(lat), String.valueOf(lon), Key);
call.enqueue(new Callback<com.aamir.friendlocator.friendlocator.Models.SendLocation>() {
#Override
public void onResponse(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Response<com.aamir.friendlocator.friendlocator.Models.SendLocation> response) {
Log.d("Response", response.body().getUserKey());
if (!response.body().getUserKey().isEmpty()) {
String key_user = response.body().getUserKey();
textViewKeyDisplay.setText(key_user);
// Writing data to SharedPreferences
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("key", userKey);
if(editor.commit()){
Log.d("saved","saved");
}
}
}
#Override
public void onFailure(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Throwable t) {
Log.e("Response", t.toString());
}
});
}
else {
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("http://demoanalysis.com/pro03/FriendLocator/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
SendLocation cleint = retrofit.create(SendLocation.class);
Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call = cleint.updateLocation(String.valueOf(lat), String.valueOf(lon), Key);
call.enqueue(new Callback<com.aamir.friendlocator.friendlocator.Models.SendLocation>() {
#Override
public void onResponse(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Response<com.aamir.friendlocator.friendlocator.Models.SendLocation> response) {
Log.d("Response", response.body().getLocationStatus());
if (!response.body().getLocationStatus().isEmpty()) {
Toast.makeText(MainActivity.this,response.body().getLocationStatus(),Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Throwable t) {
Log.e("Response", t.toString());
}
});
}
}
}
On some devices, it's working perfectly. I did change context from this to getApplicationContext but no progress. I have updated the code.
Edit:
tl;dr : you write the wrong variable into the preferences.
Your variable userKey is never written and always an empty string.
In your retrofit onResponse you put userKey as value of "key" into the
preferences. This writes an empty string into the preferences. This will work and give you no error.
Please assign userKey with the value of key_user.
Your response is only stored to key_user.
Or directly remove the local variable key_user as follows:
public void onResponse(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Response<com.aamir.friendlocator.friendlocator.Models.SendLocation> response) {
Log.d("Response", response.body().getUserKey());
if (!response.body().getUserKey().isEmpty()) {
String userKey = response.body().getUserKey();
textViewKeyDisplay.setText(userKey);
// Writing data to SharedPreferences
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("key", userKey);
if(editor.commit()){
Log.d("saved","saved");
}
}
}
Before:
In your code to save, you directly try to gather the previously saved value using editor.apply();
As documentation states out, apply will save your changes in background on a different thread.
Therefore your changes might not be saved at the time you try to get the value,
some lines below.
Try to use editor.commit(); instead and check if the problem is still there.
I'm share here my own Preference Class it's too easy so you can put in any project.
Put this class into your util folder or anywhere.
AppPreference.java
package util;
import android.content.Context;
import android.content.SharedPreferences;
/**
* Created by Pranav on 25/06/16.
*/
public class AppPreference {
public static final String PREF_IS_LOGIN = "prefIsLogin";
public static final class PREF_KEY {
public static final String LOGIN_STATUS = "loginstatus";
}
public static final void setStringPref(Context context, String prefKey, String key, String value) {
SharedPreferences sp = context.getSharedPreferences(prefKey, 0);
SharedPreferences.Editor edit = sp.edit();
edit.putString(key, value);
edit.commit();
}
public static final String getStringPref(Context context, String prefName, String key) {
SharedPreferences sp = context.getSharedPreferences(prefName, 0);
return sp.getString(key, "");
}
}
Set Preference Value in Login.java when user Login set value like this :
AppPreference.setStringPref(context, AppPreference.PREF_IS_LOGIN, AppPreference.PREF_KEY.LOGIN_STATUS, "0");
Then you will get Login Status Value in any Class by Calling like this :
String LoginStatus = AppPreference.getStringPref(context, AppPreference.PREF_IS_LOGIN, AppPreference.PREF_KEY.LOGIN_STATUS);

How do I return a value from onResponse

Basically this it the code structure, I would like to know how i can modify my codes so that I can get the value inside onResponse and returning it. As of now, my mainReply variable return "(blank)" but im expecting it to pass the data in the arraylist called details inside my onResponse segment. Rest assure, there are values returned as I have checked, but i just cant get the value to be passed out of the onResponse segment.
I have checked for alternatives and they mentioned to use interface. However, I do not know how to modify my codes to use the solution that mentioned interface and use of callBacks.
public class MainActivity extends AppCompatActivity {
EditText et_message;
FloatingActionButton fab_send;
API api;
ListView list_view_conversation;
List<ChatModel> list_chat = new ArrayList<>();
RevealDetailsCallbacks callback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_message = (EditText) findViewById(R.id.et_message);
fab_send = (FloatingActionButton) findViewById(R.id.fab_send);
list_view_conversation = (ListView) findViewById(R.id.list_view_conversation);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
api = retrofit.create(API.class);
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//this method ultimately is to get response and send back to user
String s = et_message.getText().toString();
ChatModel model = new ChatModel(s, true);
list_chat.add(model);
new retrieveDetails().execute(list_chat);
et_message.setText("'");
}
});
}
public class retrieveDetails extends AsyncTask<List<ChatModel>, Void, String> {
String text = et_message.getText().toString();
String mainReply = "";
List<ChatModel> models;
List<String> details = new ArrayList<String>();
#Override
public String doInBackground(List<ChatModel>[] lists) {
Call<List<Patient>> call = api.getPatients();
models = lists[0];
call.enqueue(new Callback<List<Patient>>() {
public String reply;
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
List<Patient> patients = response.body();
for (int i = 0; i < patients.size(); i++) {
if (patients.get(i).getNric().equals(text)) {
details.add("Name: " + patients.get(i).getName() + "\nNRIC: " + patients.get(i).getNric()
+ "\nDOB: " + patients.get(i).getDob() + "\nContact No: " + patients.get(i).getContactno());
}
}
this.mainReply = details.get(0);
Log.i("Here Log i", reply);
}
#Override
public void onFailure(Call<List<Patient>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
return mainReply;//I want to reply with the data added into the details arraylist in the onResponse segment
}
#Override
public void onPostExecute(String s) {
ChatModel chatModel = new ChatModel(s, false);
models.add(chatModel);
CustomAdapter adapter = new CustomAdapter(models, getApplicationContext());
list_view_conversation.setAdapter(adapter);
}
}
}
If you wanted to modify your existing code, you would add an interface like the one I added up top (RevealDetailsCallbacks), pass it into the asynctask constructor, and run it. The code would look like this:
public class MainActivity extends AppCompatActivity {
//Interface callback here
interface RevealDetailsCallbacks {
public void getDataFromResult(List<String> details);
}
EditText et_message;
FloatingActionButton fab_send;
API api;
ListView list_view_conversation;
List<ChatModel> list_chat = new ArrayList<>();
RevealDetailsCallbacks callback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_message = (EditText) findViewById(R.id.et_message);
fab_send = (FloatingActionButton) findViewById(R.id.fab_send);
list_view_conversation = (ListView) findViewById(R.id.list_view_conversation);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
this.callback = new RevealDetailsCallbacks() {
#Override
public void getDataFromResult(List<String> details) {
//Do stuff here with the returned list of Strings
}
};
api = retrofit.create(API.class);
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//this method ultimately is to get response and send back to user
String s = et_message.getText().toString();
ChatModel model = new ChatModel(s, true);
list_chat.add(model);
new retrieveDetails(callback).execute(list_chat);
et_message.setText("'");
}
});
}
public class retrieveDetails extends AsyncTask<List<ChatModel>, Void, String> {
String text = et_message.getText().toString();
String mainReply = "";
List<ChatModel> models;
List<String> details = new ArrayList<String>();
private RevealDetailsCallbacks listener;
retrieveDetails(RevealDetailsCallbacks listener){
this.listener = listener;
}
#Override
public String doInBackground(final List<ChatModel>[] lists) {
Call<List<Patient>> call = api.getPatients();
models = lists[0];
call.enqueue(new Callback<List<Patient>>() {
public String reply;
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
List<Patient> patients = response.body();
for (int i = 0; i < patients.size(); i++) {
if (patients.get(i).getNric().equals(text)) {
details.add("Name: " + patients.get(i).getName() + "\nNRIC: " + patients.get(i).getNric()
+ "\nDOB: " + patients.get(i).getDob() + "\nContact No: " + patients.get(i).getContactno());
}
}
this.mainReply = details.get(0);
Log.i("Here Log i", reply);
if(listener != null) {
listener.getDataFromResult(details);
}
}
#Override
public void onFailure(Call<List<Patient>> call, Throwable t) {
//Don't make a toast here, it will throw an exception due to it being in doInBackground
//Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
return mainReply;//I want to reply with the data added into the details arraylist in the onResponse segment
}
#Override
public void onPostExecute(String s) {
ChatModel chatModel = new ChatModel(s, false);
models.add(chatModel);
CustomAdapter adapter = new CustomAdapter(models, getApplicationContext());
list_view_conversation.setAdapter(adapter);
}
}
}
However, there is no need for asynctask here since you are running Retrofit and calling .enqueue, which runs on a background thread. A simpler version would look like this:
public class MainActivity extends AppCompatActivity {
//Interface callback here
interface RevealDetailsCallbacks {
public void getDataFromResult(List<String> details);
}
//Keep your same variables here
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Same setup here
this.callback = new RevealDetailsCallbacks() {
#Override
public void getDataFromResult(List<String> details) {
//Do stuff here with the returned list of Strings
}
};
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Same setup here, then call the method
makeWebCalls();
}
});
}
private void makeWebCalls(){
Call<List<Patient>> call = api.getPatients();
models = lists[0];
call.enqueue(new Callback<List<Patient>>() {
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
//Run your response code here. When done, pass to the callback
}
#Override
public void onFailure(Call<List<Patient>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
You can just enqueue the Retrofit call immediately in the OnClick and handle its response there
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String text = et_message.getText().toString();
// if you're trying to filter data, add a parameter to getPatients()
api.getPatients().enqueue(new Callback<List<Patient>>() {
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
// Here you have a full list of patients
final List<Patient> patients = response.body();
// adapter = new PatientAdapter(MainActivity.this, patients);
// mListView.setAdapter(adapter);
}

How do I disconnect from a device when I close my app?

I am working on a project in Android Studio where I am supposed to connect two devices using Wifi-Direct.
I have managed to connect to another device with Wifi-direct. My problem is that when I close the app manually on my phone, the devices do not disconnect. How can I achieve this? I am quite new with Android Studio and java.
Here is my WifiDirectActivity class:
public class WiFiDirectActivity extends Activity implements WifiP2pManager.ChannelListener, DeviceListFragment.DeviceActionListener {
public static final String TAG = "wifidirectdemo";
private WifiP2pManager manager;
private boolean isWifiP2pEnabled = false;
private boolean retryChannel = false;
private final IntentFilter intentFilter = new IntentFilter();
private WifiP2pManager.Channel channel;
private BroadcastReceiver receiver = null;
/**
* #param isWifiP2pEnabled the isWifiP2pEnabled to set
*/
public void setIsWifiP2pEnabled(boolean isWifiP2pEnabled) {
this.isWifiP2pEnabled = isWifiP2pEnabled;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_peers);
// add necessary intent values to be matched.
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
channel = manager.initialize(this, getMainLooper(), null);
}
/** register the BroadcastReceiver with the intent values to be matched */
#Override
public void onResume() {
super.onResume();
receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);
registerReceiver(receiver, intentFilter);
if (!amConnected) {
performSearch();
}
}
#Override
public void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
/**
* Remove all peers and clear all fields. This is called on
* BroadcastReceiver receiving a state change event.
*/
public void resetData() {
DeviceListFragment fragmentList = (DeviceListFragment) getFragmentManager()
.findFragmentById(R.id.frag_list);
DeviceDetailFragment fragmentDetails = (DeviceDetailFragment) getFragmentManager()
.findFragmentById(R.id.frag_detail);
if (fragmentList != null) {
fragmentList.clearPeers();
}
if (fragmentDetails != null) {
fragmentDetails.resetViews();
}
}
#Override
public void showDetails(WifiP2pDevice device) {
DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()
.findFragmentById(R.id.frag_detail);
fragment.showDetails(device);
}
#Override
public void connect(WifiP2pConfig config) {
manager.connect(channel, config, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
// WiFiDirectBroadcastReceiver will notify us. Ignore for now.
}
#Override
public void onFailure(int reason) {
Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void disconnect() {
final DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()
.findFragmentById(R.id.frag_detail);
fragment.resetViews();
manager.removeGroup(channel, new WifiP2pManager.ActionListener() {
#Override
public void onFailure(int reasonCode) {
Log.d(TAG, "Disconnect failed. Reason :" + reasonCode);
}
#Override
public void onSuccess() {
fragment.getView().setVisibility(View.GONE);
}
});
}
#Override
public void onChannelDisconnected() {
// we will try once more
if (manager != null && !retryChannel) {
Toast.makeText(this, "Channel lost. Trying again", Toast.LENGTH_LONG).show();
resetData();
retryChannel = true;
manager.initialize(this, getMainLooper(), this);
} else {
Toast.makeText(this,
"Severe! Channel is probably lost premanently. Try Disable/Re-Enable P2P.",
Toast.LENGTH_LONG).show();
}
}
#Override
public void cancelDisconnect() {
/*
* A cancel abort request by user. Disconnect i.e. removeGroup if
* already connected. Else, request WifiP2pManager to abort the ongoing
* request
*/
if (manager != null) {
final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()
.findFragmentById(R.id.frag_list);
if (fragment.getDevice() == null
|| fragment.getDevice().status == WifiP2pDevice.CONNECTED) {
disconnect();
} else if (fragment.getDevice().status == WifiP2pDevice.AVAILABLE
|| fragment.getDevice().status == WifiP2pDevice.INVITED) {
manager.cancelConnect(channel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Toast.makeText(WiFiDirectActivity.this, "Aborting connection",
Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(int reasonCode) {
Toast.makeText(WiFiDirectActivity.this,
"Connect abort request failed. Reason Code: " + reasonCode,
Toast.LENGTH_SHORT).show();
}
});
}
}
}
private void performSearch() {
final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()
.findFragmentById(R.id.frag_list);
fragment.onInitiateDiscovery();
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Toast.makeText(WiFiDirectActivity.this, "Discovery Initiated",
Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(int reasonCode) {
Toast.makeText(WiFiDirectActivity.this, "Discovery Failed : " + reasonCode,
Toast.LENGTH_SHORT).show();
}
});
}
}
When you close the app the execution will run through onPause() callback, there you should call the disconnect() method. However, take 5 minutes of your time to understand Android's life cycle principle: https://developer.android.com/guide/components/activities/activity-lifecycle.html. It's one of the first things to learn before developing an Android app.

DeadObjectException after service restart

I have activity in which is started service with startService() and in onStart() method binded to the same service. I'm sending some text using Messenger to service, then service open socket to the server in which this text is send to it, and server respond with some text (this working fine). The problem occurs when I'm trying to send response from service to activity using messenger. When application is started first time even that works fine, but after app is killed (so service restart) and I'm trying to send response from service to activity I get DeadObjectException. I commented line in service in which error occurse.
Here is my activity:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
TextView tvFromServer;
EditText etSend;
Button bSend, bStopSocket, bBound, bStopService;
boolean mRun;
Intent mIntent;
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
//SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
// .findFragmentById(R.id.map);
//mapFragment.getMapAsync(this);
initialize();
mIntent = new Intent(this, SocketService.class);
startService(mIntent);
}
#Override
protected void onStart() {
super.onStart();
Intent iBind = new Intent(this, SocketService.class);
iBind.putExtra("messenger", new Messenger(mHandler));
bindService(iBind, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
mConnection = null;
mHandler = null;
}
}
Handler mHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
//Log.v("MapsActivity", (String) msg.obj );(String) msg.obj
tvFromServer.setText(msg.getData().getString("text"));
}
};
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};
private void initialize(){
tvFromServer = (TextView) findViewById(R.id.tv_from_server);
bStopSocket = (Button) findViewById(R.id.b_stop);
mRun = false;
bStopSocket.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mRun = false;
}
});
etSend = (EditText) findViewById(R.id.et_send);
bSend = (Button) findViewById(R.id.b_send);
bSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!mBound) return;
Message msg = Message.obtain(null, 1, etSend.getText().toString());
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
bBound = (Button) findViewById(R.id.b_bound);
bBound.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!mBound) return;
Message msg = Message.obtain(null, 1, "Hello from Activity");
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
bStopService = (Button) findViewById(R.id.b_stop_service);
bStopService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mBound) {
unbindService(mConnection);
mBound = false;
}
stopService(mIntent);
}
});
}
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera. In this case,
* we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to install
* it inside the SupportMapFragment. This method will only be triggered once the user has
* installed Google Play services and returned to the app.
*/
#Override
public void onMapReady(GoogleMap googleMap) {
//mMap = googleMap;
// Add a marker in Sydney and move the camera
//LatLng sydney = new LatLng(-34, 151);
//mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
//mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
}
Service:
public class SocketService extends Service {
public SocketService() {
}
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
public Messenger mMessenger, mUiMessenger;
PrintWriter out;
Socket mSocket;
boolean mRun;
Thread threa;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
if(msg.what == 1){
out.println((String) msg.obj);
}
}
}
#Override
public void onCreate() {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("SocketService");
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
mMessenger = new Messenger(new ServiceHandler(mServiceLooper));
mRun = false;
threa = new Thread(new Runnable() {
#Override
public void run() {
socketConnection();
}
});
threa.start();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
Bundle extras = intent.getExtras();
mUiMessenger = (Messenger) extras.get("messenger");
return mMessenger.getBinder();
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
private void socketConnection() {
mRun = true;
try{
mSocket = new Socket("xxx.xxx.xxx.xxx", xxxxx);
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream())), true);
BufferedReader in = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
while(mRun){
String s = in.readLine();
Log.v("SocketServiceFromServer", s);
Bundle bundle = new Bundle();
bundle.putString("text", s);
Message msg = Message.obtain(null, 1);
msg.setData(bundle);
try {
mUiMessenger.send(msg); //DeadObjectException is thrown here after service is restarted
} catch (RemoteException e) {
e.printStackTrace();
}
}
mSocket.close();
}catch(Exception e){
Log.e("SocketService ", e.toString());
}
}
}
logcat:
My question is, why this error is thrown? Looks like activity do not send new messenger object to service when binding to it after restart.
I had exactly the same problem and I found solution. I lost 4 hours on debugging code, search Internet and try millions of different approaches. Solution is very easy. Remove calling startService in your onCreate. Use only bindService and unbindService. Yes it is so simple...

Categories